• Jakub@Work

    New Blog

    • 0 Comments

    We've started up a new blog dedicated to the Service Manager Platform (http://blogs.msdn.com/b/scplat/). New posts will be going there and I will stop posting new content here, although I'll leave the current content up for reference.

  • Jakub@Work

    Getting Closer

    • 0 Comments

    We’re pushing hard as a team to finish up Service Manager and we’re getting closer :-). I apologize for not having any content related posts in quite some time. I’ve still been fielding questions sent directly to me and please keep them coming.

    As folks start using the Service Manager platform, I hope to have more relevant content to post for folks to help. Also, the Service Manager team blog is very active going over various features of the products and customizations and extensions that are possible.

  • Jakub@Work

    Service Manager 2010 Beta 2 Released

    • 0 Comments

    We released our public Beta 2 on Friday and are busy making progress toward our RTM early next year. This is a fully public, feature complete Beta available on the Microsoft Connect site. The email sent from the Connect site to subscribers:

    The Service Manager team is very pleased to announce that Beta-2 for Service Manager 2010 is now available for download from connect. Details and links are below.

    Download

    To download, simply go to the Service Manager site on Connect www.connect.microsoft.com. Once you have successfully signed in, click the Downloads link, click Service Manager Beta-2 and then select all of the following files on the download details page:

    · SMCDImage_amd64.exe

    · SMCDImage_x86.exe (32 bit console)

    · InstallOMMPs.exe

    · AuthoringTool.exe (authoring console)

    · SM_B2_Public.zip

    Click download to begin the download process. Once the files have successfully downloaded, please be sure to read the release notes before installing the product as they contain some important information about the release

    You can also use the public forums for feedback and support, link below
    http://forums.microsoft.com/TechNet/ShowForum.aspx?ForumID=2066&SiteID=17

    Please read the “Preparing for Service Manager Deployment” section of the Installation and Configuration Guide for step-by-step guidance on how to install and configure the product.

  • Jakub@Work

    Object Templates

    • 0 Comments

    In Service Manager, we have a variety of scenarios where we needed to capture partial object state and apply that state at a later time. The state captured is usually all or part of some IT process. For example, when creating a Change Request from the UI, the user is prompted with a set of templates to choose from, each representing a different kind of change request. These templates are stored as Object Templates in MPs and pre-define a set of values to populate the change request form with.

    Object templates can be used to store data about classes as well as projections. They are neither classes, nor objects, but rather blueprints (stored in management packs) for creating or modifying objects. In the attached MP you will find three different object templates.

    1.     <ObjectTemplate ID="NFL.NFCDivision.Template" TypeID="NFL.Division" >
    2.       <Property Path="$Target/Property[Type='NFL.Conference']/Name$">NFC</Property>
    3.     </ObjectTemplate>

    The first two set properties of a class. The Path notation is the same as used in projections, with the exception that you specify the particular property you want to set as well. You can specify as many or as few properties as you want, these objects don’t need to be complete, but we do verification to ensure property values make sense.

    Projections can also be defined:

    1.     <ObjectTemplate ID="NFL.New.Template" TypeID="NFL.Conference.All" >
    2.       <Property Path="$Target/Property[Type='NFL.Conference']/Name$">New Conference</Property>
    3.       <Object Path="$Target/Path[Relationship='NFL.ConferenceHostsDivision']$">
    4.         <Property Path="$Target/Property[Type='NFL.Conference']/Name$">New Conference</Property>
    5.         <Property Path="$Target/Property[Type='NFL.Division']/Name$">New Division 1</Property>
    6.         <Object Path="$Target/Path[Relationship='NFL.DivisionContainsTeam' TypeConstraint='NFC.Team']$">
    7.           <Property Path="$Target/Property[Type='NFC.Team']/Name$">New Team</Property>
    8.         </Object>
    9.       </Object>
    10.       <Object Path="$Target/Path[Relationship='NFL.ConferenceHostsDivision']$">
    11.         <Property Path="$Target/Property[Type='NFL.Conference']/Name$">New Conference</Property>
    12.         <Property Path="$Target/Property[Type='NFL.Division']/Name$">New Division 2</Property>
    13.       </Object>
    14.     </ObjectTemplate>


    These are similar, but allow you to define a hierarchy of objects that conform to the projection definition the object template references. Whenever you want to define a new object, you need to ensure the endpoint of the relationship is concrete, or you need to specify the TypeConstraint to indicate what class you want to create, as in the example above.

    In code, you can use these templates to create new objects or update existing objects:

    1.             // Connect to the management group
    2.             EnterpriseManagementGroup managementGroup =
    3.                 new EnterpriseManagementGroup("localhost");
    4.             // Get the management pack
    5.             ManagementPack nflManagementPack =
    6.                 managementGroup.ManagementPacks.GetManagementPack("NFL", null, new Version("1.0.0.0"));
    7.             // Get the object templates
    8.             ManagementPackObjectTemplate nfcDivisionTemplate =
    9.                 nflManagementPack.GetObjectTemplate("NFL.NFCDivision.Template");
    10.             ManagementPackObjectTemplate afcDivisionTemplate =
    11.                 nflManagementPack.GetObjectTemplate("NFL.AFCDivision.Template");
    12.             ManagementPackObjectTemplate nflTemplate =
    13.                 nflManagementPack.GetObjectTemplate("NFL.New.Template");
    14.             // Create a new NFC division using the template
    15.             CreatableEnterpriseManagementObject newNfcDivision =
    16.                 new CreatableEnterpriseManagementObject(managementGroup, nfcDivisionTemplate);
    17.             // Change it to an AFC division
    18.             newNfcDivision.ApplyTemplate(afcDivisionTemplate);
    19.             // Create a new projection from a template
    20.             EnterpriseManagementObjectProjection newNfl =
    21.                 new EnterpriseManagementObjectProjection(managementGroup, nflTemplate);
    22.             // You can also apply a template to an existing projection
    23.             newNfl.ApplyTemplate(nflTemplate);
  • Jakub@Work

    Categorizing Management Pack Elements

    • 1 Comments

    If you are familiar with Management Packs and their elements in Operations Manager, you'll know that there were a few elements that had a "Category" attribute whose value was fixed from an enumeration defined in the schema. While this worked, it was quite limiting. For one, the enumeration itself did not make a lot of sense globally. There were values in the enumeration that only applied to some of the elements that used it. Next, it only allowed for some elements to be categorized and only once. Finally, the enumeration was not extensible.

    In Service Manager we've added the ability to categorize any Management Pack element via an extensible mechanism that leverages the new concept of enumerations as defined in management packs. Enumerations are essentially hierarchal extensible lists that are defined in management packs. They can be used both as values for class properties as well as values for categorization of elements. In this article, I'll be discussing the latter.

    I've updated the NFL management pack I have been using and introduced a new enumeration and some categories. First, you'll find the following enumeration defined:

    <EnumerationTypes>
            <EnumerationValue ID="NFL.VisibleToCallers" Accessibility="Public" />
    </EnumerationTypes>

    I can define enumerations to mean just about anything I want as the consumer of these enumerations and subsequent categories defines their behavior. In this case, I am defining an enumeration that categorizes elements as visible to callers via some API that I will be providing.

    Once I have defined my enumeration, the next step is to categorize my elements as "in" the value. For this, I need to define category elements:

    <Categories>
        <Category ID="NFL.Visibility1" Target="NFL.Conference" Value="NFL.VisibleToCallers" />
        <Category ID="NFL.Visibility2" Target="NFL.Division" Value="NFL.VisibleToCallers" />
        <Category ID="NFL.Visibility3" Target="NFL.Team" Value="NFL.VisibleToCallers" />
    </Categories>

    As you can see, I've placed three classes into my enumeration, thus categorizing them. But what does this mean from an API perspective? Really, it doesn't mean anything unless the consumer honors the categorization. As an example, the Service Manager UI uses various enumerations as defined in our management packs to categorize elements, such as console tasks or views, to control their visibility behavior in the UI.

    Next, I'll demonstrate how a caller of the API can use this categorization. In the following sample, after importing the attached management pack, I retrieve the defined enumeration and generate criteria from it to filter out the classes I want to retrieve based on the categorization:

                // Connect to the management group
                EnterpriseManagementGroup managementGroup =
                    new EnterpriseManagementGroup("localhost");
    
                // Get the management pack
                ManagementPack nflManagementPack = 
                    managementGroup.ManagementPacks.GetManagementPack("NFL", null, new Version("1.0.0.0"));
    
                // Get the type enumeration value
                ManagementPackEnumeration showToCallersEnumeration =
                    nflManagementPack.GetEnumeration("NFL.VisibleToCallers");
    
                // Create an in statement using the category property
                string classCriteria = string.Format(@"<Criteria xmlns=""http://Microsoft.EnterpriseManagement.Core.Criteria/"">
                                            <Expression>
                                                <In>
                                                  <GenericProperty>Category</GenericProperty>
                                                  <Values>
                                                    <Value>{0}</Value>
                                                  </Values>
                                                </In>
                                              </Expression>
                                            </Criteria>", showToCallersEnumeration.Id);
    
                // Create the criteria object
                ManagementPackClassCriteria classesVisibleToCallersCriteria = new ManagementPackClassCriteria(classCriteria);
                
                // Get classes using criteria
                IList<ManagementPackClass> classesVisibleToCallers = managementGroup.EntityTypes.GetClasses(classesVisibleToCallersCriteria);

    Important Note: The criteria behaves in the following way with respect to inclusion and exclusion of categories. If you generate a query as shown earlier, it will return any class that is in this category, regardless if it is in other categories. If you change the query to ask for classes where category is not of a particular id, it will exclude the classes that are ONLY in that particular category; so if a class is in category A and B and you ask for classes not in B, you will still get the class back because A is not in B. If a class is only in category B and you ask for classes not in B, you will not get it back because that class is exclusively in B and thus NOT IN B is false. Even if this didn't make sense to you (I had to read it back several times myself :-) ), essentially what this means is that NOT EQUAL to a particular category queries just don't work the way you would expect so please don't use them or understand how they work before you do. To mitigate this problem, we are planning on caching categories client-side so users can quickly do filtering post-query for "NOT" semantics.

  • Jakub@Work

    New Criteria

    • 1 Comments

    Well, we're back from MMS which was pretty successful for Service Manager in general. In a lot of ways, it was a coming out party of sorts as we were able to demonstrate a ton of functionality about the product and really show how well it is coming along. My session with Travis went great and I really feel we did a solid job in speaking to and demonstrating the power of the Service Manager platform. The team is heads down in Beta 2 right now as we push for an early Fall release. Beta 2 is very exciting for us because at that point the product is basically complete from a feature perspective and customers will be able to play with as close to the final bits as they've ever had. I really hope we get a lot more people interested in customization and extension at that time and I'll be excited to blog about a lot of the new features we're introducing in Beta 2 from an SDK perspective.

    In this post, I wanted to talk about our new criteria a little bit and introduce you to the new format and some of the new concepts. If you are familiar with OM, the criteria there was very similar to a WHERE clause in SQL. While this was very friendly from an end-user perspective, it actually was quite burdensome from a developer perspective, both in generating complicated criteria and for us in parsing it and ensuring its uniqueness. For Service Manager 2010 we decided to prioritize power and ease of programmatic manipulation over readability and the ability to quickly script the criteria. Criteria is used in for querying for a variety of objects in the SDK. Here's I'll use examples mostly around the instance and projection space.

    Simple Expression
    A simple expression just compares a property to a value. In this example we are comparing PropertyName on ClassName to the value 3.

                   <SimpleExpression>
                    <ValueExpressionLeft>
                      <Property>$Target/Property[Type='ClassName']/PropertyName$</Property>
                    </ValueExpressionLeft>
                    <Operator>Equal</Operator>
                    <ValueExpressionRight>
                      <Value>3</Value>
                    </ValueExpressionRight>
                  </SimpleExpression>

    For projections, a simple expression would look like this, where the property is actually a path:

                  <SimpleExpression>
                    <ValueExpressionLeft>
                      <Property>$Target/Path[Relationship='RelationshipName']/Property[Type='ClassName']/PropertyName$</Property>
                    </ValueExpressionLeft>
                    <Operator>Equal</Operator>
                    <ValueExpressionRight>
                      <Value>3</Value>
                    </ValueExpressionRight>
                  </SimpleExpression>

    Also, rather than using names, you can use Ids in the criteria:

                  <SimpleExpression>
                    <ValueExpressionLeft>
                      <Property>$Target/Property[Type='b982d3ec-96e0-4901-b1d9-a06196acf0bc']/b982d3ec-96e0-4901-b1d9-a06196acf0bc$</Property>
                    </ValueExpressionLeft>
                    <Operator>Equal</Operator>
                    <ValueExpressionRight>
                      <Value>3</Value>
                    </ValueExpressionRight>
                  </SimpleExpression>

    or:

                <SimpleExpression>
                    <ValueExpressionLeft>
                      <Property>$Target/Path[Relationship='b982d3ec-96e0-4901-b1d9-a06196acf0bc']/
    Property[Type='b982d3ec-96e0-4901-b1d9-a06196acf0bc']/b982d3ec-96e0-4901-b1d9-a06196acf0bc$</Property> </ValueExpressionLeft> <Operator>Equal</Operator> <ValueExpressionRight> <Value>3</Value> </ValueExpressionRight> </SimpleExpression>

    Unary/Regex Expression
    These are the same as the simple expressions, but instead Unary and RegEx operators are supported. RegExExpression supports ContainsSubstring, MatchesWildcard, MatchesRegularExpression, DoesNotContainSubstring, DoesNotMatchWildcard and DoesNotMatchRegularExpression. UnaryExpression supports IsNull and IsNotNull.

    Contains/NotContains/ContainedBy/NotContainedBy Expression
    These expressions allow a traversal to be made either down or up containment to either include or exclude results based the existence of a relationship to other instances. The concept here is similar to an EXISTS query in SQL.

              <Contains>
                <!-- A Contains query from a particular node in the projection; this can also just drop path and the implicit path is the seed -->
                <Path>$Target/Path[Relationship='RelationshipName']$</Path>
                <Class>$MPElement[Name='System!System.Computer']$</Class>
                <Expression>
                  <SimpleExpression>
                    <ValueExpressionLeft>
                      <Property>$Target/Property[Type='System!System.Computer']/FQDN$</Property>
                    </ValueExpressionLeft>
                    <Operator>Equal</Operator>
                    <ValueExpressionRight>
                      <Value>ComputerName</Value>
                    </ValueExpressionRight>
                  </SimpleExpression>
                </Expression>
              </Contains>

    The Path element indicates where within the context of the criteria you want to perform the containment based query. If the path is excluded, it means that you want the containment query to be done on the target of the criteria, namely the projection seed class or the class in the case of regular instance queries.

    As a more concrete example, given an Incident projection with System.Incident as the seed and a single related owner System.User, one can do containment based queries on either the incident or the user. If a query like “Give me all incidents that are contained by the ‘Tier 1 Incident Group’”, the path element would be excluded, the Class element would point to the Incident Group type and the expression would be empty. If there were some criteria on that group that needed to be matched, that expression would be used for that. If the query was like “Give me incidents that have a owner that is contained by “Tier 1 Users Group”, the path element would need to be populated pointing to System.User.

    It is important to note that the path element indicates where in the context of the criteria you want the containment query to apply, while the class and criteria indicate what you are searching for in that containment space.

    And/Or Expression
    These expressions allow you to combine other expressions.

    In Expression
    And finally we support a basic IN clause for use with Guid fields only. The other thing to note here is that I am using a GenericProperty instead of Property element. This allows you to refer to generic properties of instances that aren't modeled. This property also has a Path attribute that allows you to point to a node in a projection for which you want the generic property to apply.

        <Expression>
          <!-- Simple IN Clause (GUID support only) -->
          <In>
            <GenericProperty>Id</GenericProperty>
            <Values>
              <Value>b982d3ec-96e0-4901-b1d9-a06196acf0bc</Value>
              <Value>b982d3ec-96e0-4901-b1d9-a06197acf0bc</Value>
              <Value>b982d3ec-96e0-4901-b1d9-a06198acf0bc</Value>
            </Values>
          </In>
        </Expression>
  • Jakub@Work

    MMS 2009

    • 0 Comments

    I apologize for not posting lately, we've been really heads down on Beta 2 and working on our sessions for MMS. I plan to post more regularly again when things die down a bit, but in the meantime just wanted to let you know that I'll be at MMS doing the session below. If anyone is also going to be there, come check out our talk.

     SO23 Customizing and Extending Service Manager
    Speaker(s): Jakub Oleksy, Travis Wright
    Track(s): Operations Management
    Session Type(s): Breakout Session
    Products(s): Operations Manager 2007, Service Manager

    A technical drill-down on the customization capabilities of Service Manager: This session will articulate how to extend and deliver Service Manager through the delivery of management packs. We will demonstrate the authoring tools to learn how to create workflows, customize templates and extend the configuration management database

  • Jakub@Work

    Getting and Working With Type Projections - Basic

    • 1 Comments

    In my last post in gave you a more in-depth look at creating type projections in Service Manager. In this post, I'd like to provide you with some basic examples on how you can retrieve and work with instances of type projections. I'll be using the same Management Pack and data set from the previous post.

    On the Instances interface you will find a few methods for retrieving instances of EnterpriseManagementObjectProjection. Post Beta 1, we've actually cleaned up this interface significantly so I don't want to concentrate too much on the actual method call, but rather the parameters to it and working with the result.

    In Beta 1 the method you would use looks like this:

    IList<EnterpriseManagementObjectProjection> GetObjectProjections<T>(ManagementPackTypeProjection managementPackTypeProjection, 
    ObjectProjectionCriteria criteria, ObjectQueryOptions queryOptions) where T : EnterpriseManagementObject;

    Post Beta 1, this method is replaced by:

    IObjectProjectionReader<T> GetObjectProjectionReader<T>(ObjectProjectionCriteria criteria, ObjectQueryOptions queryOptions)
                where T : EnterpriseManagementObject;

    The ManagementPackTypeProjection from the first simply gets rolled into the ObjectProjectionCriteria object and the return type gets changed to support buffered reads to help with performance, however, IObjectProjectionReader can actually be used like an IList (although it doesn't implement IList); I'll discuss the reader more once we ship Beta 2 later this year. My examples below will use the latest code since that will be around longer; hopefully it is easily transferable for you to the Beta 1 API.

    The first thing you need in order to get a type projection instance, is the type projection definition. This narrows down the types of instances you will get back to those that fit the structure of the type projection:

          // Connect to the management group
          EnterpriseManagementGroup managementGroup =
                new EnterpriseManagementGroup("localhost");
    
          // Get the management pack
          ManagementPack nflManagementPack = 
                managementGroup.ManagementPacks.GetManagementPack("NFL", null, new Version("1.0.0.0"));
    
          // Get the type projection
          ManagementPackTypeProjection nflProjection =
                nflManagementPack.GetTypeProjection("NFL.Conference.All");

    Very simply you can retrieve projections by specifying the type:

    IObjectProjectionReader<EnterpriseManagementObject> objectProjections =
          managementGroup.Instances.GetObjectProjectionReader<EnterpriseManagementObject>(
          new ObjectProjectionCriteria(nflProjection), ObjectQueryOptions.Default);

    This will retrieve all projections in the system of that structure. A projection exists if the root object of the projection exists, regardless of whether any of the components are found. If you want to limit your result somehow, you will need to specify criteria to limit your result set. One example of simple criteria you could add is matching on one of the generic properties of the root object:

                string displayNameCriteria = @"<Criteria xmlns=""http://Microsoft.EnterpriseManagement.Core.Criteria/"">
                      <Expression>
                        <SimpleExpression>
                          <ValueExpressionLeft>
                            <GenericProperty>DisplayName</GenericProperty>
                          </ValueExpressionLeft>
                          <Operator>Equal</Operator>
                          <ValueExpressionRight>
                            <Value>National Football Conference</Value>
                          </ValueExpressionRight>
                        </SimpleExpression>
                      </Expression>
                    </Criteria>";
    
                IObjectProjectionReader<EnterpriseManagementObject> objectProjectionsByDisplayName =
                     managementGroup.Instances.GetObjectProjectionReader<EnterpriseManagementObject>(
                         new ObjectProjectionCriteria(displayNameCriteria, nflProjection, managementGroup), ObjectQueryOptions.Default);

    This would limit the results to any projections whose root object had a display name matching "National Football Conference." The list of available generic properties for Service Manager is Id, Name, DisplayName, LastModified and new post Beta 1 CreatedDate and LastModifiedBy.

    It is also possible to query on type specific properties of the root object:

                string propertyCriteria = @"<Criteria xmlns=""http://Microsoft.EnterpriseManagement.Core.Criteria/"">
                      <Expression>
                        <SimpleExpression>
                          <ValueExpressionLeft>
                            <Property>$Target/Property[Type='NFL.Conference']/Name$</Property>
                          </ValueExpressionLeft>
                          <Operator>Equal</Operator>
                          <ValueExpressionRight>
                            <Value>NFC</Value>
                          </ValueExpressionRight>
                        </SimpleExpression>
                      </Expression>
                    </Criteria>";
    
                IObjectProjectionReader<EnterpriseManagementObject> objectProjectionsByProperty =
                     managementGroup.Instances.GetObjectProjectionReader<EnterpriseManagementObject>(
                         new ObjectProjectionCriteria(propertyCriteria, nflProjection, nflManagementPack, managementGroup), ObjectQueryOptions.Default);

    Criteria can get more complicated than the above examples, including specifying criteria on the individual components of the projection, but an in-depth discussion will happen in a later post.

    Once you get an EnterpriseManagementObjectProjection, you essentially have a collection of instances organized in a hierarchy as defined by the type projection. If you run the following code:

                foreach (EnterpriseManagementObjectProjection projection in objectProjections)
                {
                    Console.WriteLine("Conference: {0}", projection.Object.DisplayName);
                    foreach (IComposableProjection division in projection[nflConferenceContainsDivision.Target])
                    {
                        Console.WriteLine("\tDivision: {0}", division.Object.DisplayName);
                        foreach (IComposableProjection team in division[divisionContainTeam.Target])
                        {
                            Console.WriteLine("\t\tTeam: {0}", team.Object.DisplayName);
                            foreach (IComposableProjection coach in team[coachCoachesTeam.Source])
                            {
                                Console.WriteLine("\t\t\tCoach: {0}", coach.Object.DisplayName);
                            }
                        }
                    }
                }

    You'll get a result that shows you the structure of the projection in memory:

    Conference: American Football Conference
            Division: AFC West
                    Team: Oakland Raiders
                    Team: Kansas City Chiefs
                    Team: San Diego Chargers
                    Team: Denver Broncos
            Division: AFC East
                    Team: Buffalo Bills
                    Team: New England Patriots
                    Team: New York Jets
                    Team: Miami Dolphins
            Division: AFC South
                    Team: Houston Texans
                    Team: Jacksonville Jaguars
                    Team: Tennessee Titans
                    Team: Indianapolis Colts
            Division: AFC North
                    Team: Cleveland Browns
                    Team: Pittsburgh Steelers
                    Team: Baltimore Ravens
                    Team: Cincinnati Bengals
    Conference: National Football Conference
            Division: NFC North
                    Team: Green Bay Packers
                    Team: Chicago Bears
                            Coach: Lovie Smith
                    Team: Detroit Lions
                    Team: Minnesota Vikings
            Division: NFC South
                    Team: Carolina Panthers
                    Team: Atlanta Falcons
                    Team: Tampa Bay Buccaneers
                    Team: New Orleans Saints
            Division: NFC East
                    Team: New York Giants
                    Team: Dallas Cowboys
                    Team: Philadelphia Eagles
                    Team: Washington Redskins
            Division: NFC West
                    Team: St. Louis Rams
                    Team: Seattle Seahawks
                    Team: Arizona Cardinals
                    Team: San Francisco 49ers

    If the line is indented, it indicates a jump across a relationship. The hierarchy is organized by the relationship types that bring in each component; put another way, as you traverse from parent to child you are traversing a relationship of a particular relationship type in one direction. So, as you go from American Football Conference to AFC West, you are moving from "Conference" to "Division" on the NFL.ConferenceHostsDivision relationship type.

    Each node in an EnterpriseManagementObjectProjection implements IComposableProjection which offers various ways at traversing through the hierarchy. The object is also IEnumerable<KeyValuePair<ManagementPackRelationshipEndpoint, IComposableProjection>>, which shows that the hierarchy is organized by the relationship endpoint that brings each node in. You also get a pointer to the actual object at the node:

            /// <summary>
            /// The current object in the projection.
            /// </summary>
            EnterpriseManagementObject Object
            {
                get;
            }

    The role that brought the current object into the current projection:

            /// <summary>
            /// Gets the role of this projection, relative to its parent.
            /// </summary>
            /// <value></value>
            ManagementPackRelationshipEndpoint ObjectRole
            {
                get;
            }

    The object that brought the current object into the current projection:

            /// <summary>
            /// The parent object, if any, for the current object in the projection.
            /// </summary>
            IComposableProjection ParentObject
            {
                get;
            }

    And a few indexers to aid in traversal, one of which we used in the sample above:

            /// <summary>
            /// Access all IComposableProjection elements of this IComposableProjection, optionally recursively.
            /// </summary>
            IList<IComposableProjection> this[TraversalDepth traversalDepth]
            {
                get;
            }
    
            /// <summary>
            /// Access IComposableProjection elements of this IComposableProjection, by role name.
            /// </summary>
            IList<IComposableProjection> this[string roleName]
            {
                get;
            }
    
            /// <summary>
            /// Access IComposableProjection elements of this IComposableProjection, by role name.
            /// </summary>
            IList<IComposableProjection> this[string roleName, ManagementPackClass classConstraint]
            {
                get;
            }
    
            /// <summary>
            /// Gets the IComposableProjection child of the projection by id of the contained object
            /// </summary>
            IComposableProjection this[string roleName, Guid id]
            {
                get;
            }
    
            /// <summary>
            /// Access IComposableProjection elements of this IComposableProjection, by relationship role.
            /// </summary>
            IList<IComposableProjection> this[ManagementPackRelationshipEndpoint role]
            {
                get;
            }
    
            /// <summary>
            /// Access IComposableProjection elements of this IComposableProjection, by relationship role and a class constraint.
            /// </summary>
            IList<IComposableProjection> this[ManagementPackRelationshipEndpoint role, ManagementPackClass classConstraint]
            {
                get;
            }
    
            /// <summary>
            /// Gets the IComposableProjection child of the projection by id of the contained object
            /// </summary>
            IComposableProjection this[ManagementPackRelationshipEndpoint role, Guid id]
            {
                get;
            }

    In future posts I'll discuss more advanced concepts around working with projections instances (they're IXPathNavigable!), a deeper look at criteria, creating and editing projections and a lot more. I hope that by the time Beta 2 ships later this year to have a comprehensive list of topics that help introduce you to new API concepts.

  • Jakub@Work

    More with Type Projections

    • 4 Comments

    In my last post I introduced you to a new concept in the Service Manager infrastructure called Type Projections. Here, I want to expand on my previous example and discuss type projection structure in more depth. I've attached the management pack that I will be discussing (NFL.xml) and there is reference code at the bottom of this post for populating sample data.

    Type projection definitions allow users to create virtual object collections through any relationships found in the system. In the previous post I showed how a projection could bring hosted and host objects together in one go. The following projection expands on that example by introducing System.Containment and System.Reference relationships, as well as introducing the concepts of type constraints and relationship direction:

          <TypeProjections>
            <TypeProjection ID="NFL.Conference.All" Accessibility="Public" Type="NFL.Conference">
              <Component Alias="Division" Path="$Target/Path[Relationship='NFL.ConferenceHostsDivision']$">
                <Component Alias="AFCTeam" Path="$Target/Path[Relationship='NFL.DivisionContainsTeam' SeedRole='Source' TypeConstraint='AFC.Team']$"/>
                <Component Alias="NFCTeam" Path="$Target/Path[Relationship='NFL.DivisionContainsTeam' SeedRole='Source' TypeConstraint='NFC.Team']$">
                  <Component Alias="Coach" Path="$Target/Path[Relationship='NFL.CoachCoachesTeam' SeedRole='Target']$" />
                </Component>
              </Component>
            </TypeProjection>
          </TypeProjections>

    Added to the Division component from the previous post, I've now introduced two components that bring in all the teams in the NFL. I've defined my NFL.Team class as abstract and created concrete classes, AFC.Team and NFC.Team. I've also created a relationship derived from System.Containment associating an NFL.Division to an NFL.Team. Of note is that this relationship ends with an abstract class as the target endpoint. While this is supported by type projection definitions, the projection I have defined actually demonstrates the ability to constraint a component to a particular class. The two components, AFCTeam and NFCTeam each share a relationship, however, they each add a different type constraint. Given something can't be an NFCTeam and AFCTeam at the same time, these components will introduce unique sets of data.

    If I were to change the AFCTeam component to remove the type constraint, that component would return all NFL.Team instances while the NFCTeam component would return only those NFL.Team objects that are NFC.Teams as well.

    Next, I added a System.Reference relationship between NFL.Coach and NFL.Team. Notice that the coach is the source of that relationship. If I want to include all coaches for NFC.Teams, I need to indicate in the type projection definition that I want to traverse the relationship from target to source. This is done via the SeedRole attribute. The "Seed" refers to the parent component of the current component; the attribute describes which role the seed should play relative to the relationship used in the current component. In the sample, the relationship is NFL.CoachCoachesTeam and the seed (NFCTeam) is the target of that relationship and hence SeedRole='Target'.

    Traversing this structure is similar to the example in my previous post:

                // Connect to the management group
                EnterpriseManagementGroup managementGroup =
                    new EnterpriseManagementGroup("localhost");
    
                // Get the management pack
                ManagementPack nflManagementPack = 
                    managementGroup.ManagementPacks.GetManagementPack("NFL", null, new Version("1.0.0.0"));
    
                // Get the type projection
                ManagementPackTypeProjection nflProjection =
                    nflManagementPack.GetTypeProjection("NFL.Conference.All");
    
                // Get the relationship types
                ManagementPackRelationship nflConferenceContainsDivision =
                    nflManagementPack.GetRelationship("NFL.ConferenceHostsDivision");
    
                ManagementPackRelationship divisionContainTeam =
                    nflManagementPack.GetRelationship("NFL.DivisionContainsTeam");
    
                ManagementPackRelationship coachCoachesTeam =
                    nflManagementPack.GetRelationship("NFL.CoachCoachesTeam");
    
                IObjectProjectionReader<EnterpriseManagementObject> objectProjections =
                    managementGroup.Instances.GetObjectProjectionReader<EnterpriseManagementObject>(
                        new ObjectProjectionCriteria(nflProjection), ObjectQueryOptions.Default);
    
                foreach (EnterpriseManagementObjectProjection projection in objectProjections)
                {
                    Console.WriteLine("Conference: {0}", projection.Object.DisplayName);
                    foreach (IComposableProjection division in projection[nflConferenceContainsDivision.Target])
                    {
                        Console.WriteLine("\tDivision: {0}", division.Object.DisplayName);
                        foreach (IComposableProjection team in division[divisionContainTeam.Target])
                        {
                            Console.WriteLine("\t\tTeam: {0}", team.Object.DisplayName);
                            foreach (IComposableProjection coach in team[coachCoachesTeam.Source])
                            {
                                Console.WriteLine("\t\t\tCoach: {0}", coach.Object.DisplayName);
                            }
                        }
                    }
                }

    When we traverse from team to coach, we use the .Source property of the relationship which corresponds to the SeedRole description in the type projection definition. As part of traversing the projection, think of the role as where you are traversing to; in this example you are traversing from the team (the .Target property) to the coach (the .Source property).

    When traversing from divisions to teams, I am using the abstract .Target property of the NFL.DivisionContainsTeam relationship. Type constraints are not expressed in the traversal mechanism for these objects; the key from going to one component to another is the relationship endpoint, save any type constraints.

    Reference Code

               // Connect to the management group
                EnterpriseManagementGroup managementGroup =
                    new EnterpriseManagementGroup("localhost");
    
                // Import the management pack
                ManagementPack managementPack = new ManagementPack("NFL.xml");
                managementGroup.ManagementPacks.ImportManagementPack(managementPack);
                managementPack = managementGroup.ManagementPacks.GetManagementPack(managementPack.Id);
    
                // Populate Data
                IncrementalDiscoveryData dataTransaction = new IncrementalDiscoveryData();
    
                // Get System.Entity class
                ManagementPackClass systemEntity = managementGroup.EntityTypes.GetClass(SystemClass.Entity);
    
                // Conferences
                ManagementPackClass conference = managementPack.GetClass("NFL.Conference");
                CreatableEnterpriseManagementObject nfc = new CreatableEnterpriseManagementObject(managementGroup, conference);
                nfc[conference, "Name"].Value = "NFC";
                nfc[systemEntity, "DisplayName"].Value = "National Football Conference";
                dataTransaction.Add(nfc);
    
                CreatableEnterpriseManagementObject afc = new CreatableEnterpriseManagementObject(managementGroup, conference);
                afc[conference, "Name"].Value = "AFC";
                afc[systemEntity, "DisplayName"].Value = "American Football Conference";
                dataTransaction.Add(afc);
    
                // Divisions
                Dictionary<string, List<string>> divisions = new Dictionary<string,List<string>>();
                divisions.Add("AFC North", new List<string>());
                divisions["AFC North"].Add("Baltimore Ravens");
                divisions["AFC North"].Add("Cincinnati Bengals");
                divisions["AFC North"].Add("Cleveland Browns");
                divisions["AFC North"].Add("Pittsburgh Steelers");
                divisions.Add("NFC North", new List<string>());
                divisions["NFC North"].Add("Chicago Bears");
                divisions["NFC North"].Add("Detroit Lions");
                divisions["NFC North"].Add("Green Bay Packers");
                divisions["NFC North"].Add("Minnesota Vikings");
                
                divisions.Add("AFC South", new List<string>());
                divisions["AFC South"].Add("Houston Texans");
                divisions["AFC South"].Add("Indianapolis Colts");
                divisions["AFC South"].Add("Jacksonville Jaguars");
                divisions["AFC South"].Add("Tennessee Titans");
                divisions.Add("NFC South", new List<string>());
                divisions["NFC South"].Add("Atlanta Falcons");
                divisions["NFC South"].Add("Carolina Panthers");
                divisions["NFC South"].Add("New Orleans Saints");
                divisions["NFC South"].Add("Tampa Bay Buccaneers");
    
                divisions.Add("AFC East", new List<string>());
                divisions["AFC East"].Add("Buffalo Bills");
                divisions["AFC East"].Add("Miami Dolphins");
                divisions["AFC East"].Add("New England Patriots");
                divisions["AFC East"].Add("New York Jets");
                divisions.Add("NFC East", new List<string>());
                divisions["NFC East"].Add("Dallas Cowboys");
                divisions["NFC East"].Add("New York Giants");
                divisions["NFC East"].Add("Philadelphia Eagles");
                divisions["NFC East"].Add("Washington Redskins");
    
                divisions.Add("AFC West", new List<string>());
                divisions["AFC West"].Add("Denver Broncos");
                divisions["AFC West"].Add("Kansas City Chiefs");
                divisions["AFC West"].Add("Oakland Raiders");
                divisions["AFC West"].Add("San Diego Chargers");
                divisions.Add("NFC West", new List<string>());
                divisions["NFC West"].Add("Arizona Cardinals");
                divisions["NFC West"].Add("San Francisco 49ers");
                divisions["NFC West"].Add("Seattle Seahawks");
                divisions["NFC West"].Add("St. Louis Rams");
    
                Dictionary<string, string> teamToCoach = new Dictionary<string, string>();
                teamToCoach.Add("Chicago Bears", "Lovie Smith");
    
                ManagementPackClass divisionClass = managementPack.GetClass("NFL.Division");
                ManagementPackClass nfcTeamClass = managementPack.GetClass("NFC.Team");
                ManagementPackClass afcTeamClass = managementPack.GetClass("AFC.Team");
                ManagementPackClass coachClass = managementPack.GetClass("NFL.Coach");
    
                ManagementPackRelationship divisionContainsTeamClass = managementPack.GetRelationship("NFL.DivisionContainsTeam");
                ManagementPackRelationship coachCoachesTeamClass = managementPack.GetRelationship("NFL.CoachCoachesTeam");
                foreach (string divisionName in divisions.Keys)
                {
                    CreatableEnterpriseManagementObject division = new CreatableEnterpriseManagementObject(managementGroup, divisionClass);
    
                    string conferenceName;
                    ManagementPackClass teamClass;
    
                    if (divisionName.Contains("AFC") == true)
                    {
                        conferenceName = "AFC";
                        teamClass = afcTeamClass;
                    }
                    else
                    {
                        conferenceName = "NFC";
                        teamClass = nfcTeamClass;
                    }
    
                    // Need to make sure to set key values of the host
                    division[conference, "Name"].Value = conferenceName;
                    division[divisionClass, "Name"].Value = divisionName;
                    division[systemEntity, "DisplayName"].Value = divisionName;
                    dataTransaction.Add(division);
                    foreach (string teamName in divisions[divisionName])
                    {
                        CreatableEnterpriseManagementObject team = new CreatableEnterpriseManagementObject(managementGroup, teamClass);
                        team[teamClass, "Name"].Value = teamName;
                        team[systemEntity, "DisplayName"].Value = teamName;
                        dataTransaction.Add(team);
    
                        CreatableEnterpriseManagementRelationshipObject divisionContainsTeam = 
    new CreatableEnterpriseManagementRelationshipObject(managementGroup, divisionContainsTeamClass); divisionContainsTeam.SetSource(division); divisionContainsTeam.SetTarget(team); dataTransaction.Add(divisionContainsTeam); if (teamToCoach.ContainsKey(teamName) == true) { CreatableEnterpriseManagementObject coach = new CreatableEnterpriseManagementObject(managementGroup, coachClass); coach[coachClass, "Name"].Value = teamToCoach[teamName]; coach[systemEntity, "DisplayName"].Value = teamToCoach[teamName]; dataTransaction.Add(coach); CreatableEnterpriseManagementRelationshipObject coachCoachesTeam =
    new CreatableEnterpriseManagementRelationshipObject(managementGroup, coachCoachesTeamClass); coachCoachesTeam.SetSource(coach); coachCoachesTeam.SetTarget(team); dataTransaction.Add(coachCoachesTeam); } } } // Use Overwrite instead of Commit here because I want to bypass optimistic concurrency checks which will not allow the // insertion of an existing instances and I want this method to be re-runnable dataTransaction.Overwrite(managementGroup);
  • Jakub@Work

    Getting Started with Type Projections

    • 1 Comments

    A very powerful new concept in the SDK is the notion of Type Projections. I always describe type projections as a view over our type system, much like a SQL view over tables, with the added capability that our type projections are hierarchal. What projections allow you to do is query over and retrieve collections of objects related somehow in our model in one go. In this post I want to provide a very simply example of how to define and retrieve a type projection, and in future posts I will dive much deeper into working with them.

    For this post, I've attached a management pack (NFL.xml) that defines the classes and type projection used in the sample code. Also, I have the code I used to populate my objects in the Reference Code section below.

    In the management pack you will notice two classes defined, NFL.Conference and NFL.Division, and a hosting relationship type, NFL.ConferenceHostsDivision, between them. My scenario is that I want to display all the conferences and divisions in the UI, so I define a type projection that puts the two classes together:

    <TypeProjection ID="NFL.Conference.All" Accessibility="Public" Type="NFL.Conference">
         <Component Alias="Division" Path="$Target/Path[Relationship='NFL.ConferenceHostsDivision']$" />
    </TypeProjection>

    The first thing you should notice in the above definition is the Type property on the root element; this defines the type of instance that should exist at the root or "seed" of the projection. Next, you'll notice the projection has one component, Division, that relates to the root. The Path attribute is what defines how to find the desired component, relative to the seed. I will describe the path notation in more depth in future posts, but here Target refers to the seed element of the projection (an instance of NFL.Conference), followed by a Path construct that tells the system to follow the NFL.ConferenceHostsDivision relationship type.  This will result in a structure that looks like this:

    * NFL.Conference

    * NFL.Division

    Now, in order to work with this, the SDK has introduced a new object type, EnterpriseManagementObjectProjection, along with an interface, IComposableProjection, to allow for retrieval of and then navigation of the returned structure. The following code shows a basic example of retrieving and iterating through the objects:

                // Connect to the management group
                EnterpriseManagementGroup managementGroup =
                    new EnterpriseManagementGroup("localhost");
    
                // Get the management pack
                ManagementPack nflManagementPack = 
                    managementGroup.ManagementPacks.GetManagementPack("NFL", null, new Version("1.0.0.0"));
    
                // Get the type projection
                ManagementPackTypeProjection nflProjection = 
                    nflManagementPack.GetTypeProjection("NFL.Conference.All");
    
                // Get the relationship type
                ManagementPackRelationship nflConferenceContainsDivision = 
                    nflManagementPack.GetRelationship("NFL.ConferenceHostsDivision");
    
                IObjectProjectionReader<EnterpriseManagementObject> objectProjections =
                    managementGroup.Instances.GetObjectProjectionReader<EnterpriseManagementObject>(
                        new ObjectProjectionCriteria(nflProjection), ObjectQueryOptions.Default);
    
                foreach (EnterpriseManagementObjectProjection projection in objectProjections)
                {
                    Console.WriteLine("Conference {0}", projection.Object.DisplayName);
                    foreach (IComposableProjection division in projection[nflConferenceContainsDivision.Target])
                    {
                        Console.WriteLine("Division {0}", division.Object.DisplayName);
                    }
                }

    The sample first connects to the server and retrieves the type projection definition which is necessary for interacting with the SDK and the relationship type definition which is necessary for navigating the resulting instance. Next, we get an IObjectProjectionReader<EnterpriseManagementObject> that will contain all instances of our type projection. Finally, the example shows how to iterate through the object and get data.

    Reference Code

                // Connect to the management group
                EnterpriseManagementGroup managementGroup =
                    new EnterpriseManagementGroup("localhost");
    
                // Import the management pack
                ManagementPack managementPack = new ManagementPack("NFL.xml");
                managementGroup.ManagementPacks.ImportManagementPack(managementPack);
                managementPack = managementGroup.ManagementPacks.GetManagementPack(managementPack.Id);
    
                // Populate Data
                IncrementalDiscoveryData dataTransaction = new IncrementalDiscoveryData();
    
                // Get System.Entity class
                ManagementPackClass systemEntity = managementGroup.EntityTypes.GetClass(SystemClass.Entity);
    
                // Conferences
                ManagementPackClass conference = managementPack.GetClass("NFL.Conference");
                CreatableEnterpriseManagementObject nfc = new CreatableEnterpriseManagementObject(managementGroup, conference);
                nfc[conference, "Name"].Value = "NFC";
                nfc[systemEntity, "DisplayName"].Value = "National Football Conference";
                dataTransaction.Add(nfc);
    
                CreatableEnterpriseManagementObject afc = new CreatableEnterpriseManagementObject(managementGroup, conference);
                afc[conference, "Name"].Value = "AFC";
                afc[systemEntity, "DisplayName"].Value = "American Football Conference";
                dataTransaction.Add(afc);
    
                // Divisions
                ManagementPackClass division = managementPack.GetClass("NFL.Division");
                string[] nfcDivisionNames = new string[] {
                    "NFC East",
                    "NFC North",
                    "NFC South",
                    "NFC West"
                };
                
                foreach (string nfcDivisionName in nfcDivisionNames)
                {
                    CreatableEnterpriseManagementObject nfcDivision = new CreatableEnterpriseManagementObject(managementGroup, division);
                    
                    // Need to make sure to set key values of the host
                    nfcDivision[conference, "Name"].Value = "NFC";
                    
                    nfcDivision[division, "Name"].Value = nfcDivisionName;
                    nfcDivision[systemEntity, "DisplayName"].Value = nfcDivisionName;
                    dataTransaction.Add(nfcDivision);
                }
    
                string[] afcDivisionNames = new string[] {
                    "AFC East",
                    "AFC North",
                    "AFC South",
                    "AFC West"
                };
    
                foreach (string afcDivisionName in afcDivisionNames)
                {
                    CreatableEnterpriseManagementObject afcDivision = new CreatableEnterpriseManagementObject(managementGroup, division);
    
                    // Need to make sure to set key values of the host
                    afcDivision[conference, "Name"].Value = "AFC";
    
                    afcDivision[division, "Name"].Value = afcDivisionName;
                    afcDivision[systemEntity, "DisplayName"].Value = afcDivisionName;
                    dataTransaction.Add(afcDivision);
                }
    
                // Use Overwrite instead of Commit here because I want to bypass optimistic concurrency checks which will not allow the 
                // insertion of an existing instances and I want this method to be re-runnable
                dataTransaction.Overwrite(managementGroup);
  • Jakub@Work

    Improvements in SDK from Operations Manager 2007

    • 9 Comments

    I wanted to provide a high level overview of feature additions to the SDK since Operations Manager 2007 shipped. The items are in no particular order and will be discussed in more depth in subsequent posts.

    Default Values for Properties

    Types can define default values on properties. The default values need to be literals and are implemented using the default value column construct in SQL.

    Required Properties

    Types can now defined non-key required properties. Values for these properties will always be required during insertion unless the property has a default value, in which case that value will be used.

    Auto-Increment Properties

    Integer and string properties can be defined as auto-increment in a management pack. When instances of types that contain auto-increment properties are created in the SDK client, the client retrieves the next available value from the database and removes it from future use (whether or not the instance is committed). This value is then embedded in the property using a standard .Net format string with {0} replaced by the value from the database. As an example, this can be used to create unique, readable ticket numbers: “SR_{0}”.

    Binary Properties

    Binary properties have been added and are written and returned as streams. The stream is retrieved on demand and will not be brought back as part of normal instance retrieval.

    Enumeration Properties

    Management packs can define enumerations that can be used as property values in the instance space. These enumerations are hierarchal and extensible via other management packs.

    Categories in Management Packs

    Categories allow the association of an enumeration (as defined above) and a management pack element. An element can have an unlimited number of associated enumerations that act to categorize the element for various uses.

    Type Extensions

    Type extensions are an MP schema change to allow a derived type to be defined as an extension of its base type. The design is such that a user can add a property to a type, for all instances of that type. Thus, when someone imports a type extension, the system adds this type to all instances of the base type and all new instances will automatically be an instance of the type extension class, regardless of the data the discovery packet that discovered them contains. Type extensions can be explicitly added or updated on an instance, but cannot be removed without removing the entire instance.

    Relationship Endpoints

    Relationships now have sub-elements that represent the source and target endpoints. These endpoints can define cardinality value of 0, 1 and MaxInt. If max and min cardinality are both 1, it is enforced by the infrastructure, otherwise it is used as a hint to the UI and other consumers.

    Instance History

    The CMDB stores property and relationship changes for instances. They can be retrieved per instance from the SDK and are used by other components internally that rely on instance space changes.

    Instance Change Subscriptions

    Similar to the alert change subscriptions in OM, we now have an instance space change subscription data source that allows you to trigger other actions because on an instance space change. We use this to trigger WWF workflows.

    Projections

    A new concept in the type space, type projections allow you to define a hierarchal view over the type space that gets materialized on retrieval by the SDK client. This allows retrieval of logical instances that are composed of many instances and the relationships that join them together. The mechanism is used as the backing instance space definition for all forms and views in Service Manager; that is a view or form is bound to a particular projection.

    SDK also have a lot of functionality built in to support this concept, including advanced query support over these definitions. For instance, you can define a projection that is an incident with its assigned to user and then query for all instances of this projection where to assigned to user is some particular user.

    Object Templates

    Object templates are essentially saved instances and projections defined in a management pack that can be used to create new instances or be applied to existing instances and set their values to those found in the template.

    Type indexes

    You can now define indexes on types in a management pack to help improve performance.

    Criteria Improvements

    Criteria has changed (we still support the old format as well) to be xml-based to remove ambiguity problems we had with the old model and to consolidate it with group calculation and subscription criteria. We’ve also enhanced criteria to support projection queries, containment queries and added an IN clause predicate. The new features are only available with the new xml format.

    Full-Text Queries

    Full-Text is supported for string and binary properties, with significant limitations in how the full-text query syntax can be leveraged within other criteria elements. Currently this is only being used for knowledge search within some limited scenarios.

    Partial Instances

    Users of the SDK can now specify which properties they want to get back when they retrieve instances. This functionality applies to all mechanisms of retrieving instances and is recommended to help with performance issues when dealing with large data sets.

    Paging and Sorting

    The SDK supports retrieving instances in a paged mode that returns some buffer worth of data on each remote call. This functionality should be combined with sorting that will allow for UI design that yields better performance.

    Abstract Query Support

    Abstract types are now supported within type-based queries, both for types and projections.

    Security Enhancements

    We’ve expanded the security model to support property level write granularity, group + type scoping on read and implied permissions based on the instance itself (i.e. the owner of an incident as some set of implied permissions on it).

    Resource Support in Management Packs

    Management packs can now have embedded resources (the file is msi-based) that separate large resources (reports, form binaries, etc) from the xml manifest of the management pack.

    XPath Navigable Instance Space

    With the introduction of projections it became important to have a friendlier way to traverse a large hierarchal instance on the client. To server that need projection instances and instances are both XPathNavigable.

    Optimistic Concurrency on Instances

    We support full-optimistic concurrency support, per instance.

    Unification of CustomMonitoringObject and MonitoringObject

    We’ve merged the two objects for a more consistent experience when working with the instance space in the SDK.

  • Jakub@Work

    Getting Started with the Service Manager SDK

    • 5 Comments

    The Service Manager public API is partitioned into several assemblies all of which can by found either in the GAC after install or in the Program Files folder in "SDK Binaries". The primary assembly is Microsoft.EnterpriseManagement.Core (Core) which replaces Microsoft.EnterpriseManagement.Common from Operations Manager 2007. Core also contains a lot of the functionality from the Microsoft.EnterpriseManagement.OperationsManager (OM) assembly that shipped with Operations Manager 2007. The OM assembly still exists, but contains only Operations Manager specific functionality. Service Manager also has a couple other assemblies with Service Manager and Data Warehouse functionality. The following table describes the relationships between Service Manager and Operations Manager assemblies.

    Service Manager Operations Manager 2007
    Microsoft.EnterpriseManagement.Core Microsoft.EnterpriseManagement.Common and Microsoft.EnterpriseManagement.OperationsManager
    Microsoft.EnterpriseManagement.ServiceManager (SM) None
    Microsoft.EnterpriseManagement.OperationsManager Microsoft.EnterpriseManagement.OperationsManager
    Microsoft.EnterpriseManagement.DataWarehouse (DW) None

    The refractoring of the Operations Manager assemblies was done to enable a better story for sharing functionality across the products. Core is delivered by the Service Manager team and provides the fundamental services that System Center products built on top of the OM CMDB share. Core contains the majority of the instance space functionality, all of the management pack infrastructure and other common features across the products. SM contains functionality on the Service Manager product needs while OM is for Operations Manager only. In Beta 1 Service Manager still uses the OM assembly as we haven't completely refractored everything as necessary. The DW assembly contains new data warehouse specific functionality; this is functionality specific to the new Service Manager based data warehouse and not the old Operations Manager data warehouse functionality, which can still be found in the OM assembly.

    In order to get started, you need to link at the very least against the Core assembly. Once you do that, the following code will establish a connection to the locally installed SDK service:

    using System;
    using Microsoft.EnterpriseManagement;
    
    namespace Jakub_WorkSamples
    {
        partial class Program
        {
            static void Connect()
            {
                // Connect to the local management group
                EnterpriseManagementGroup localManagementGroup = new EnterpriseManagementGroup("localhost");
            }
        }
    }

    You'll notice this part is almost identical to the Operations Manager. Once the connection is established, you can begin playing around with the various services provided by EnterpriseManagementGroup. Previously most functionality was actually directly on EnterpriseManagementGroup and while it is all still there (on ManagementGroup, derived from EnterpriseManagementGroup) , it is marked as obsolete as we are using a new paradigm for providing access to various features of the API. The new mechanism for accessing various parts of the API is centered around services (namely interfaces) exposed via properties on the EnterpriseManagementGroup object. You'll notice, for example, a property called Instances. This provides access to a variety of methods that deal with instance space access. A quick sample follows:

    using System;
    using Microsoft.EnterpriseManagement;
    using Microsoft.EnterpriseManagement.Common;
    
    namespace Jakub_WorkSamples
    {
        partial class Program
        {
            static void GetInstance()
            {
                // Connect to the local management group
                EnterpriseManagementGroup localManagementGroup = new EnterpriseManagementGroup("localhost");
    
                // Get an instance by Id (need to replace with valid Id of an instance)
                EnterpriseManagementObject instance = 
    localManagementGroup.Instances.GetObject<EnterpriseManagementObject>(Guid.NewGuid(), ObjectQueryOptions.Default); } } }

    Over time I'll be talking about the various interfaces and the specific functionality they provide and be talking much more about the Instances interface which introduces significant new functionality from the equivalent OM objects (MonitoringObject).

    This model of partitioning is used in all the assemblies and allows us to better group and share functionality across products. We're definitely still open to feedback on how to better partition our services; our goal is to group common functionality together such that most tasks would be done by using one or two services.

    Also, a couple things to note. First, this is Beta 1 and there will be changes moving forward. The Instances API in particular will undergo a significant facelift as we are added buffered read support for instances. We are also cleaning up the interfaces and trying to remove unnecessary overloads as much as possible.

  • Jakub@Work

    Service Manager Beta Released

    • 1 Comments

    You can find more details here.

    Also, community forums are setup for discussion and can be found here. These are the preferred medium for questions with the beta.

    I'm out for Thanksgiving this week, but plan to get started next week with a high-level post on fundamental changes in the SDK architecture to better accommodate Service and Operations Manager with a single, shared API.

  • Jakub@Work

    New Look, Evolving Content

    • 3 Comments

    Well, it has been a while since I had any posts with new content (and it will actually be just a bit longer), but I wanted to write a quick post on what has been going on with SDK development for Operations Manager and talk a bit about Service Manager.

    First off, Operations Manager is going well and tracking toward R2. I've been been involved in that development, but there is not a lot of new content that went into the SDK for that release; mostly important bug fixes were put in to address customer issues and other issues that were found. There were a few new features that went in to help with performance (such as a lightweight alert and event objects to help with views), but development on the SDK has been slow going on that front.

    After SP1 (although we've been keeping changes in sync since then) of Operations Manager was ready, we took the SDK code and have been evolving it to meet the needs of Service Manager. Service Manager has much deeper and more complicated requirements of the SDK and it makes sense for that product to push and own the charter of the SDK. The plan is to have the next major release of Operations Manager be built on top of the new SDK so that Service Manager and Operations Manager truly share a large part of the infrastructure. This will help enable a lot of "better together" scenarios for both products.

    We've been busy putting a ton of new features into the SDK and enabling all sorts of new scenarios to be possible over the infrastructure and we're tracking well to our Beta 1 that will be released shortly. For those that are familiar with Service Manager, formerly Service Desk, this is the first public release after the previously announced reset.

    I plan to evolve this blog into discussions around this new SDK, its new features, differences with the Operations Manager 2007 and general help for the Service Manager product. I will continue to field your questions either through email or through comments here on Operations Manager, but as is already self-evident if you look at my recent blog posts, I will not be writing new posts on SCOM 2007 SDK. When the next major release of SCOM has its first public milestone, all of the content here will be pertinent to that as well.

  • Jakub@Work

    Good Article on Management Pack Authoring

    • 1 Comments

    A co-worker of mine recently pointed this out to me, so I thought I would share here.

    http://www.developer.com/design/article.php/3740486

  • Jakub@Work

    System Center Virtual User Group

    • 1 Comments

    The System Center Virtual User Group recently had their first meeting where they presented on several topics about Operations Manager 2007. They recorded the live meeting and posted the content online. The topics were  Cross-platform monitoring in Operations Manager 2007 SP2, Powershell in Operations Manager 2007, and Audit Collection Services (ACS) Planning, Configuration and Tuning Tips. You can find all the videos linked here as well.

     

  • Jakub@Work

    System Center Service Manager

    • 1 Comments

    Well, I have been pretty quiet lately with posts and I can finally share why. Over the past few months I have been very busy working on System Center Service Manager and making the Operations Manager infrastructure work for that product as well. More information about the changes in that product can be found here:

    http://blogs.technet.com/systemcenter/

    Given these changes, I am actually no longer on the Operations Manager team, but actually exclusively on the Service Manager core development team. What does that mean for Operations Manager and the SDK? The infrastructure I am working on builds on the existing SDK of Operations Manager API and makes it work seamlessly with Service Manager and Operations Manager. The infrastructure will be structured as a System Center programmatic infrastructure with a lot of functionality being common across products (currently Service Manager and Operations Manager), with product specific functionality built on top of it.

    All this being said, the best way to use the API today is to make sure a proper level of abstraction is used in larger projects; this will minimize impact of any changes in the future.

  • Jakub@Work

    SCOM Connector QuickStart Guide

    • 24 Comments

    Ambrose Wong put together a great quick start guide to connectors in SCOM. I've attached the Word version below. Any feedback is appreciated and I can pass it on to Ambrose.

  • Jakub@Work

    V4 Planning

    • 17 Comments

    I am in the middle of working on changes for V4 to the SDK and am looking for feedback on the current iteration, and what things you would like me to add/change. Please comment on what things you like, don't like, what things are difficult or easy to use and what functionality you feel is missing. Your feedback is much appreciated and considered for the next release.

  • Jakub@Work

    Two New(er) Blog Resources

    • 2 Comments

    It's great how more and more people from the development team are blogging now, hopefully helping alleviate the lack of documentation of some of the more in-depth/advanced topics around SCOM. I've added two new links to the blog and you can also find them here:

    Sampa@Work

    and

    Notes on System Center Operations Manager

    The first is a blog from Sam Patton, a developer who works mainly on modules, and is a great resource for those looking for more information on how to use various modules in SCOM. The second is from Marius Sutara, a developer on the core infrastructure team, and can be referenced for a wide array of SCOM related issues, especially when related the to health service.

    Update : Fixed first link.

  • Jakub@Work

    SP1 RC Released Today

    • 3 Comments

    The RC of SP1 is being released today and can be downloaded from here. Upgrade from RC to RTM is supported. There is also new dedicated newsgroup to SP1 related issues: Microsoft.public.opsmgr.sp1.

  • Jakub@Work

    OpsMgr++

    • 6 Comments

    I added a link to Boris's blog, OpsMgr++, on my blog list on the right. Boris has been putting up many samples he does for various customers and partners. Most recently he has a few samples on programmatically creating monitors, which gives good insight not just on creating monitors, but on MP authoring scenarios in general.

    In terms of more posts from me, I am still looking for suggestions. I have been fielding many of your questions through email and the OpsMgr newsgroups but I haven't had a chance to put together a substantive post in a while, mostly due to lack of topics, so please, if you have any things you would like me to demonstrate using the SDK or any general questions about it, please let me know.

  • Jakub@Work

    SCOM Certifications

    • 1 Comments

    Not sure if anyone is interested, but here's some information about SCOM certification I was given:

    http://blogs.msdn.com/trika/archive/2007/09/19/certifications-for-tsfkasms-mom.aspx

  • Jakub@Work

    ObjectNotFoundException in Run As Profiles/Run As Accounts

    • 1 Comments

    Exception:

    Application: System Center Operations Manager 2007
    Application Version: 6.0.5000.0
    Severity: Error
    Message:
    Microsoft.EnterpriseManagement.Common.ObjectNotFoundException: Requested
    object(s) not found.
       at
    Microsoft.EnterpriseManagement.DataAbstractionLayer.SdkDataAbstractionLayer.HandleIndigoExceptions(Exception
    ex)
       at
    Microsoft.EnterpriseManagement.DataAbstractionLayer.InstanceSpaceOperations.GetMonitoringObjectByMonitoringObjectIds(List`1
    monitoringObjectIds, String languageCode, MonitoringObjectMode
    monitoringObjectMode)
       at
    Microsoft.EnterpriseManagement.ManagementGroup.GetPartialMonitoringObjects(ICollection`1
    ids)
       at
    Microsoft.EnterpriseManagement.Mom.Internal.UI.Administration.RunAsProfile.ProfileAccounts.<>c__DisplayClass1.<OnPageAdded>b__0(Object
    , ConsoleJobEventArgs )
       at
    Microsoft.EnterpriseManagement.Mom.Internal.UI.Console.ConsoleJobExceptionHandler.ExecuteJob(IComponent
    jobOwner, EventHandler`1 job, Object sender, ConsoleJobEventArgs args)

     

    If you encounter an ObjectNotFoundException in the UI when trying to browse run as profiles or accounts, the issue is an incorrect SQL query being used. It is fixed in SP1, but in the meantime, you can also fix the issue by running the following query against the SCOM database and then restarting the SDK service:

    declare @querydef xml
    set @querydef =
    N'<QueryDefinitions xmlns="urn:DataAccess" xmlns:dal="urn:DataAccess"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><QueryDefinition
    dal:fullyDescribed="true"><Name>SecureStorageSecureReferenceByCriteria</Name><ObjectName>SecureStorageSecureReference</ObjectName><UsedBy
    Component="Sdk" /><Description>Access to SecureStorageSecureReference
    table.</Description><DataObject
    xsi:type="SelectType"><Column><Name>SecureStorageReferenceId</Name><Source>SecureStorageSecureReference</Source><Type>uniqueidentifier</Type></Column><Column><Name>SecureStorageElementId</Name><Source>SecureStorageSecureReference</Source><Type>uniqueidentifier</Type></Column><Column><Name>SecureReferenceId</Name><Source>SecureStorageSecureReference</Source><Type>uniqueidentifier</Type></Column><Column><Name>HealthServiceId</Name><Source>SecureStorageSecureReference</Source><Type>uniqueidentifier</Type><IsNullable>true</IsNullable></Column><Column><Name>LastModified</Name><Source>SecureStorageSecureReference</Source><Type>datetime</Type></Column><Source><Table><Name>SecureStorageSecureReference</Name><Owner>dbo</Owner><Type>Table</Type></Table><Join><Type>Inner</Type><Table><Name>BaseManagedEntity</Name><Owner>dbo</Owner><Type>Table</Type></Table><JoinCondition>dbo.[BaseManagedEntity].[BaseManagedEntityId]
    = dbo.[SecureStorageSecureReference].[HealthServiceId] AND
    dbo.[BaseManagedEntity].[IsDeleted] =
    0</JoinCondition></Join></Source></DataObject></QueryDefinition></QueryDefinitions>'
    insert into dbo.DataAccessLayerSetting
    ([SettingType], [SettingData])
    values (0, @querydef)

  • Jakub@Work

    Setting the State of an Instance Programmatically

    • 10 Comments

    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.

Page 1 of 3 (62 items) 123