Modeling

Table of Contents

Adding New Modeled Class

All modeled classes will be defined in Models section of ModelConfiguration.xml file. If there are relations for that modeled class to be defined, then Relations section should also be updated. For a new modeled class, following needs to be defined:

  • Name: model name
    The model name should start with a capital letter.
  • Parent: optional attribute for parent model name. If users set the Parent property of a model, this model's properties, datasources, operations, and relations will be inherited from the new model.
  • StorageType: optional attribute that defines where the model data is to be stored, if needed. Refer to RapidCMDB Persistence Operations document for more information on how to set this attribute.
  • IndexName: optional attribute that specify how the index data is to be kept. Refer to Persistent Index Data Management document for more information on how to set this attribute.
  • its properties and their external datasources if there are any. Refer to Adding New Properties.
  • its relations to other modeled classes if there are any. Refer to Adding New Relations.

In order to make the modifications effective, first generate the model and then restart the application.

Extending A Modeled Class

Adding New Properties

Each property for a modeled class is to be defined in the Property subsection within the appropriate Model/Properties section of a modeled class to which the property belongs. Property section supports the following attributes :

  • Name: property name
    The first two characters of the property name must be small case letters.
  • Default: optional attribute to set the default value for the property if the user does not provide one when creating the model instance
  • Type: Supported property types are date, boolean, number (corresponds to Java long),  float (corresponds to Java double), and string.
  • IsKey:  is a special, optional attribute which marks a property as key for that model in local repository.
  • Datasource, DatasourceProperty and NameInDatasource: There are two categories of properties:
  1. Local properties: They are stored and indexed by RapidOSS. These properties do not need to have Datasource, DatasourceProperty and NameInDatasource fields.
  2. Federated properties: These properties should provide one of Datasource or DatasourceProperty attributes and optionally NameInDatasource and Lazy properties.

Federated Properties

  • Datasource property specifies which datasource definition will be used to get property from external system. In the sample xml model configuration below, the datasource from which the value of severity will be retrieved is set as "sam1" (Datasource="sam1")
  • DatasourceProperty adds dynamism to datasource definition for the property. When the datasource from which the value will be retrieved is not fixed, its value is put in the DatasourceProperty. When RapidOSS retrieves the value of the federated property, it uses the datasource name assigned to DatasourceProperty. In the sample xml model configuration below, the datasource name for the "creationClassName" property is placed in another property (dsDefinition). RapidOSS will read the value of the dsDefinition property (DatasourceProperty="dsDefinition") to determine from which datasource the value should be retrieved. This value can potentially be different for every instance of the RsEvent model.
  • NameInDatasource is the name of the corresponding datasource property. If NameInDatasource property is not specified, it will be the same as the name of the property.
  • Lazy: The value of the property will be retrieved from datasource every time the property is accessed, if Lazy property is specified as true. If it is false, it will be retrieved once and will be served from memory for subsequent access to the property.

In Datasources section of Model, datasources which will be used in federation are defined. Each datasource has Definition, Name, NameProperty attributes and a set of Key nested tags which specify the key properties for that datasource to uniquely identify the object while retrieving the federated property value.

  • Definition is the reference name of datasource in model configuration file. In order to refer to the Datasource, federated properties should use the value of this property.
  • Name is the name of corresponding real BaseDatasource instance
  • NameProperty provides users to get corresponding real datasource name dynamically. For example if we have 100 datasources with same key set, we can store real datasource name in a model property and assign the name of property to NameProperty attribute. It will automatically read the property value and will get the BaseDatasource instance that corresponds to the value of property.
  • Each Key nested tag must have Name attribute. This attributes specifies the key property name. Multiple keys can be defined for a single datasource. Also corresponding name of the key property in datasource can be sepecified in NameInDatasource property.
<RsModel>
    <Models>
        <Model Name="RsEvent" StorageType="FileAndMemory">
            <Properties>
                <Property Name='name' Type='string' IsKey="true"/>
                <Property Name='creationClassName' Type='string' DatasourceProperty="dsDefinition" NameInDatasource="CreationClassName"/>
                <Property Name='severity' Type='number' Datasource="ds1" NameInDatasource="Severity" Default="5"/>
                <Property Name='rsDatasource' Type='string'/>
                <Property Name='dsDefinition' Type='string'/>
            </Properties>

            <Datasources>
                <Datasource Definition="ds1" Name="sam1">
                    <Keys>
                        <Key Name="name" NameInDatasource="Name"></Key>
                    </Keys>
                </Datasource>
                <Datasource Definition="ds2" NameProperty="rsDatasource">
                    <Keys>
                        <Key Name="name" NameInDatasource="Name"></Key>
                        <Key Name="creationClassName" NameInDatasource="CreationClassName"></Key>
                    </Keys>
                </Datasource>
            </Datasources>
        </Model>
    </Models>
</RsModel>
Notes
  • If any errors occur during retrieving federated properties from datasources these errors will be added to errors property of model. Users will be able to check hasErrors() and hasErrors(propertyName) methods to check if any errors occurred. If any exception occurs default property value (as defined for the property in the model configuration) will be returned to the user.
def event = RsEvent.get(name:"event1");

//assume that when accessing severity property a connection exception occurs, the default value of the severity will be returned
assert event.severity == 5

//after accessing federated property users can use hasErrors method to check that any errors occurred
assert event.hasErrors() == true
assert event.hasErrors("severity") == true
  • The datasource used to retrieve federated property value is on-demand, that is, if any connection exception occurs, reconnection will not block execution. Exception will be logged and also exception will be added to the errors property of model.
  • When object is loaded from repository federated properties will not be populated. They will be retrieved after first access to a federated property.

In order to make the modifications effective, first generate the model and then restart the application.

Adding New Relations

Relations between Models will be defined in the Relations tag. Each Relation tag should provide the following attributes:

  • From: One side of the relation (model name)
  • To: Other side of the relation (model name)
  • Name: Relation name at the "From" side
  • ReverseName: Relation name at the "To" side
  • Type: Supported relation types are one-to-one, one-to-many and many-to-many.
<RsModel>
        <Model Name="RsSmartsObject">
            <Properties>
                <Property Name='name' Type='string' IsKey="true"/>
            </Properties>
        </Model>

        <Model Name="RsEvent" StorageType="FileAndMemory">
            <Properties>
                <Property Name='name' Type='string' IsKey="true"/>
                <Property Name='creationClassName' Type='string' />
                <Property Name='severity' Type='number' Datasource="sam1" NameInDatasource="Severity"/>
            </Properties>

            <Datasources>
                <Datasource Name="sam1">
                    <Keys>
                        <Key Name="name" NameInDatasource="Name"></Key>
                    </Keys>
                </Datasource>
            </Datasources>
        </Model>
    </Models>

    <Relations>
        <Relation From ="RsSmartsObject" To="RsEvent" Name="events" ReverseName="device" Type="OneToMany" ></Relation>
    </Relations>
</RsModel>
A cascading relationship can be constructed between 2 modeled classes. Although this is not supported in the modelConfiguration.xml file, you can edit the modeled class groovy file (e.g. RS_HOME/RapidSuite/grails-app/domain/Author.groovy) If you want to make sure that all related Book instances are deleted when teh Author instance is deleted, insert the followin in Author.groovy:
static cascaded = ["books":true]

It is assumed that Author is related to Book instances via the "books" relation.

In order to make the modifications effective, first generate the model and then reload the application.

Removing Properties, Relations, or Modeled Classes

Removing is as easy as deleting the related lines that define property/relation/model from the ModelConfiguration.xml file.

When a model is deleted, make sure that the relations that are defined for the model are also removed.

In order to make the modifications effective, first generate the model and then restart the application.

Activating Modeling Changes

Generating Models

After making the necessary changes to the ModelConfiguration.xml,  new modeled classes must be generated.

Go to the Scripts tab in the admin UI and run modelCreator script.

After the script is executed successfully, new domain classes will have been generated in the temporary location: RS_HOME/RapidSuite/generatedModels/grails-app/domain.

Application has to be restarted to finalize the deployment.

Restarting Application

In order to generate rest of the modeling artifacts (model operations) and correcting the data according to the model changes, the application must be restarted. These artifacts are groovy files generated under RS_HOME/RapidSuite/operations folder.

Model Operations

Implementing Model Operations

During model generation, some operations including basic CRUD operations are automatically generated. You can learn more about these basic operations here.

If model requires additional operations, they have to be manually implemented in a separate groovy script. Static operations are supported. After the model generation, another file named <ModelName>Operations.groovy is saved under RS_HOME/RapidSuite/operations folder. This file can be edited to have the additional operations required for the model. For example, a sample operation for a Ticket model could be:

def assign(username){
  owner = username;
  writeToJournal("Ticket is assigned to $owner");
}
All operations must be implemented in operations files, not directly in model files, as there is no access to relations from code within model file.

Note that if there is a modification to the operations file, the model operation file has to be reloaded. Refer to Reload Model Operations section.

Description Annotation

Descriptions for the operations can be supplied by using Description annotation. These descriptions will be displayed in the Repository Browser along with the browsed instance's properties and relations.

import com.ifountain.annotations.*;

 class RsEventOperations {
  ...
  @Description("This is the description for this operation")
  String operations1() {
     ...
  }

  @Description("""
   <span><b>Users can define html description for operations by using Description tag</b></span>
  """)
  def operations2(String param1){
    ...
  }
}

onLoad

A special onLoad method can be defined in the operations file. onLoad will be executed to initialize the model instance when it is retrieved from the repository through the get operation or search. Following example shows the onLoad method for HypericDatasourceOperations:

def onLoad(){
       this.adapter = new HypericAdapter(getProperty("connection").name, reconnectInterval*1000, Logger.getRootLogger());
    }
static code block is also available if a code block that will be common to all the instaces of a model is needed.
static{
   ...
}

Actions Before and After Repository Updates

Add, delete, and update operations can be intercepted and actions can be executed just before or right after these operations. In order to enable these, one or more of the following operations must be implemented in the operations file for the corresponding modeled class:

  • beforeInsert - executed just before the add operation is executed
  • afterInsert - executed right after the add operation is executed
  • beforeDelete - executed just before the delete operation is executed
  • afterDelete - executed right after the delete operation is executed
  • beforeUpdate(params) - executed just before the update operation is executed
  • afterUpdate(params) - executed right after the update operation is executed

    Operations should be implemented according to the operation signatures (arguments). Note that only the beforeUpdate and afterUpdate operations take a params map argument. The params argument contains the updatedProps property which includes old property values for the updated properties of the object. Users can get new property values by calling the property directly.

    // following 2 lines is from a user script
    def topObj =  RsTopologyObject.add(name:"obj1", className:"cls1");
    topObj.update(className:"cls2");
    
    //following implementation is from the RsTopologyObjectOperations.groovy (following the example script above)
    def beforeUpdate(params)
    {
       assert params.updatedProps.className == "cls1"
       params.updatedProps.each{propName, oldValue->
          //do stuff
       }
    }
    When values are assigned to object's properties or setProperty operation is used in one of the before operations (beforeInsert, beforeUpdate, beforeDelete), the object's property value is changed but it is not saved to the repository. Changing the value of a property in before operations is equivalent to using the setPropertyWithoutUpdate operation.

beforeInsert, beforeUpdate, and beforeDelete operations are called before the object is actually added, updated, or deleted. Validation is performed after the before operations and there is always the possibility that the operation may fail. Before operations are good places to filter the properties of the object being added/updated. Since the operation is not yet finished successfully, other instances of any class should not be updated in before methods. beforeDelete is not used in processors since property filtering on an object does not make much sense.

Sample usage : InMaintenanceCalculator changes the event property inMaintenance if there is a related device which is in maintenance :

class InMaintenanceCalculator {

    static def eventInBeforeInsert(event){
        def inMaintenance=RsInMaintenance.isEventInMaintenance(event);
        event.setProperty("inMaintenance",inMaintenance);
    }
    ...

}

afterInsert/afterUpdate/afterDelete operations are called after the add / update / delete operations are successfully completed. After operations are good places to update other objects. The object which is processed should not be updated in after operations ( may cause recursion and performance decrease). setPropertyWithoutUpdate is also not effective since the object is already inserted / added.

Sample usage: State calculator changes the related devices state after an event is added

class StateCalculator {
    ...

    static def eventIsAdded(event){
        def objects=getObjectsOfEvent(event);
        objects.each{ object ->
            setObjectState(object,event.severity,Constants.NOTSET);
        }

    }
   ...
}
In order to eliminate potential problems, use of repository operations (add/remove/update/addRelation/removeRelation) are not allowed in the before operations, for the instance at hand. For example, when an instance of RsEvent (myEvent) is being added, you cannot call update(...) while in the beforeInsert operation. An exception will be thrown.

Accessing the Current User in Operations

You may want to process the current user (who has triggered the invocation) in your operations. In order to access the user in the current execution context, use RsUser.getCurrentUserName():

static def record(String param, value) {
        def t = Date.now()
        def user = auth.RsUser.getCurrentUserName()
        Statistics.add([timestamp: t, user: user, parameter: param, value: value])
    }

if operation is invoked from a gsp, controller or a user invoked script, such as an on-demand script, the current user will be returned. However, some actions are triggered not by any particular user but by the system, such as the execution of a scheduled script. In this case "system" will be returned as the current user.

Logging from Operations

See Writing Log Messages From Model Operation Scripts.

Reloading Model Operations

There are several ways to reload the changes in an operation file:

  • In Admin Web UI:
  • In RapidOSS UI:
  • From script: <ModelName>.reloadOperations()

Views and Controllers

Views and controllers are used to manage the Web UI for modeled classes. By implementing the methods in controllers, you can define the operations on a modeled class that can be called from Web UI. list, show, add, update, and delete methods, along with any other method required can be implemented in the model's controller file depending on your needs.

Using views, it is possible to set which properties to be displayed and how, in certain UI pages for the model. You can create views for those methods you implement in the model's controller.

The views are located in RS_HOME/RapidSuite/grails-app/views folder under individual folders with model names whereas all model controller files are in RS_HOME/RapidSuite/grails-app/controllers folder.

When you modify the views or controllers, you need to reload them for the changes to be effective.

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.