Adding a new stored attribute to an existing class in Smarts/IONIX

The Smarts dynamic model (aka "DM") allows you very simply to a new custom attribute to an existing class. These attributes can be used to store additional data relating to the class instances, or to contain values that are auto-calculated from the values of other attributes.

In this page, I'll focus on the simplest case - "stored" attributes. This is the most trivial use-case for dynamic model.

This page goes into a lot of detail, much of which may be "obvious" to you. If so: please excuse me for stating the obvious - but I'm doing it on order to be as complete as possible.

Licenses

In order to write and compile your own dynamic model, you need the "dynamic model compiler" license.

You also need the "load" license in order to run/use the model enhancements you create.

If you don't have these, talk to your EMC customer manager or sales person. You dont need to install any new software - just to update your licenses.

The DM code

In order to explain the process, I'll start with a simple example. Here's the source we'll use ..

1: refine interface Card {
2:     stored attribute string CUST_PartNumber
3:            "The vendor's part number to be used to re-ordering this card."
4:     = "unknown";
5: }

This example should be quite easy to read, but here are the key points to note..

  • I'm going to assume that you call this file "mycard.mdl" (we'll talk more about the name of the file and its location later).
  • The line numbers are not part of the code - you should leave them out
  • The first line indicates that what follows is an extension (refinement) of the existing "Card" class.
  • The changes to the class are contained between the "{" character on line 1, and the "}" on line 5.
  • Line 2 indicates that we are creating a new attribute called "CUST_PartNumber".
  • The new attribute will hold a string.
  • The attribute type is given as "stored", which means that we can put values into it using the dmctl "put" command, or the equivalent in ASL, Perl, etc.
  • When an object of the Card class is viewed in the topology browser, the description column will contain the message contained on line 3.
  • Line 4 tells us that when a new Card object is created, the CUST_PartNumber will initially contain the string "unknown". This can be changed  using dmctl's "put" (etc).
  • The semi colon on line 4 marks the end of the definition of the new attribute.

Choosing a name for your attribute

In the example above, I have given the name as "CUST_PartNumber". The "CUST_" part of the name is there to indicate that this is a custom attribute.

Why is this important?

It I had simply called it "PartNumber" then I run the risk that my idea is so good that EMC may add the same attribute themselves in a future release, and my code and theirs will clash with each other. For this reason it is generally good practice to take care to give your attribute a name that EMC are unlikely to choose for their own use.

Some companies use a tag that is derived from the company name, some use the tag "CUST", and some dont bother with tagging the names at all. My generally suggested convention for tagging is to ..

  • use a tag that reflects your company name and doesn't clash with any commonly used by EMC themselves.
  • use a tag of all uppercase letters.
  • put the tag at the start of the name
  • separate the tag from the name using an underbar.

An important warning

If you have multiple refinements to make to a single class, they should all be placed in the same "refine interface" block.

It is particularly important that you don't refine a single class in multiple source files. Sadly, the compiler allows you to break this rule but it can cause some very hard to resolve problems in the domain once it runs - including different values for the attribute in dmctl and the GUI, memory leaks, random core dumps - etc.

EMC support say that multiple refinements of a single class is "unsupported". My own experience is that it can be catastrophic.

Where to put your source code

The above code should be placed in a text file in the folder smarts/local/model, with an extension of ".mdl". So, since our example is for the IP domain, this will be /opt/InCharge9/IP/smarts/local/model (or your equivalent). There are some points to note here..

  • You can also put the file in a subfolder of the "model" folder - where the subfolder name matches the string specified in the "-c" argument used to start the sm_server binary. For example, the IP domain is trivially started with the command..
    sm_server -n INCHARGE-AMPM -c icf --output
    In this case, the .mdl file can be placed in smarts/local/mode/icf. This option can be useful if you have multiple instances of the IP domain with different configuration and model refinements.
  • The name of the file should not be too long, or contain characters other than letters, digits, hyphens and underbars. This is a new limitation and relates to the fact that the compilation process creates "l10n" internationalization files - which have some limitations on how they are named.

For our example, I am going to assume that you will call this file "mycard.mdl".

Compiling your code

Once you have written your DM source file, and stored it in the right place, you need to compile it using the commands (this example is for linux/solaris)..

dynmodel mycard.mdl
cp mycard_mmbe.dat ../l10n/
cp mycard_mmle.dat ../l10n/

The dynmodel command is in the smarts/bin folder of your installation. You may need to set your PATH appropriately, or to call dynmodel using the full path name.

This creates a file with the same name, but a ".ldm" extension in the same folder as the .mdl file. This is the file that the domain manager (sm_server) will load.

A number of other files are created in the folder by the V9 compiler to support internationalization using the "l10n" mechanism. Two of these (the _mmle.dat and _mmbe.dat files) need to be copied to the smarts/local/l10n directory. This step is not required for Smarts installations before version 9.

Running the refined model

You'll need to stop and restart the domain in order to allow it to include the new attribute into the Card class. For most unix installations, this can be done by executing these commands (replace "DOMAIN_NAME" with the name of the domain)..

sm_service stop DOMAIN_NAME
sm_service show DOMAIN_NAME (repeat until "NOT RUNNING" is shown)
sm_service show --cmdline DOMAIN_NAME > service.txt
vi service.txt (check/add the option '--dynamic' to the end of the file)
sh service.txt
sm_service start DOMAIN_NAME

Once the domain is running, check the log file for dynamic mode errors. The command "fgrep DYN- DOMAIN_NAME*.log" should show a few lines containing the text "DYN-W" but none containing "DYN-E".

Next: use the topology browser console to view some objects of the Card class, and check that they do now have an attribute called "CUST_PartNumber" and that it has the value "unknown". If you don't see the new attribute then you may have better luck if  you close and restart the console (I have known this to be required from time to time).

Populating the attribute

Now that your domain knows about the custom attribute, the next challenge is to determine the value that should be placed in it and write the code to do this.

You may want to do this with reference to a configuration file, an SNMP get query or some other source of data. For the sake of keeping our example simple, let's assume that you have a text file that is already filled with the required details. We'll further assume that the file is formatted as a tab-delimited file where each line contains three fields which contain the name of the system, the index of the card and the part number string to be used. For example:

lon_sw01    10    IMETH78712-A
lon_sw01    11    IM7GIG87192-2.2

Let's store this file in "smarts/local/conf/discovery/card-part-numbers.conf"

In this case, one approach is to write an ASL script that reads this file, finds the relevant cards in the domain topology and applies the value to the attribute. Here's a trivial example of a script to do this..

START {
    system: word
    card_index: word
    partnumber: rep(word)
    eol
} do {
    cardObj = object("Card", "CARD-".system."/".card_index);
    if (!cardObj->isNull()) {
        cardObj->CUST_PartNumber = partnumber;
    }
}

We'll store this script in "smarts/local/rules/discovery/custom/populate-card-part-numbers.asl".

With all these bits in place, you can run the script by hand using the commands..

cd smarts/local/conf/discovery
sm_adapter -s DOMAIN_NAME -f card-part-numbers.conf 
                discovery/custom/populate-card-part-numbers.asl

Alternatively, you can configure the domain to run this automatically at the end of the discovery. There are several ways to do this, but my usual approach is to create a driver using a ".import" file, and to edit the custom-end-post script to run it. To allow this, there are two things you need to do..

1: Place the following configuration in the file local/conf/icf/MYCARD.import :

GA_Driver::MyCard-Driver
{
    ReadsRulesFrom = GA_RuleSet::MyCard-RS
    {
        fileName = "discovery/custom/populate-card-part-numbers.asl"
    }
    ReadsInputFrom = GA_FileFE::MyCard-FE
    {
        fileName = "$SM_SITEMOD/conf/discovery/card-part-numbers.conf"
    }
    waitForCompletion = FALSE
}

2: Modify local/rules/discovery/custom/custom-end-post.asl to call the above driver, like this..

object("MyCard-Driver")->start();
Scroll to Top