# Chapter 8

| # Rules

> This chapter covers the following topics:

- Rules Agent Infrastructure--page 126
- Rule Files--page 127
- Rule Placement in Hierarchy--page 129
- Rules Attributes--page 130
- Rule Functions--page 137
- Third Party Rule Engine Interface Functions--page 139
- How to Write A Tcl Rule--page 142

> Detection of alarm conditions and subsequent triggering of actions is the basic function of the Sun Management Center framework program. Alarm conditions are determined by:

- Simple alarm checking (for example, threshold checks and regular expression matching)
- Rule evaluation

> Both mechanisms achieve the same purpose. To provide comprehensive alarm capabilities, Sun Management Center supports both mechanisms. However, this chapter focuses only on the Rule evaluation.

> Rules can be considered an arbitrarily complex type of alarm check, which normally depends on other objects (that is, a rule is usually associated with a derived object.) The agent rules are implemented through an extension of alarm checking of derived objects. Thus, rules can be treated as another alarm check mechanism.

> Currently no industry standard exists for rule syntax; thus, rules must be introduced into the agent based on how they are written. However, there is a consistent convention for specifying which rule is to be fired in the agent configuration file (module configuration file format). Storage of any state or persistent data required by a rule is provided in the object that invokes the rule.

## Rules Agent Infrastructure

> This section covers the rules agent infrastructure.

### Rules and Derived Objects

> Rules and derived objects are inherently related. For the Sun Management Center framework, a rule is implemented in a derived object; consequently, there is a one-to-one relationship between a rule and a derived object.

### Rule Naming

> Each rule must be named as:

>     r<n>

> For example:`r231` represents rule 231.

> Module designers can create custom rules that use a wide variety of alarm criteria. These custom rules can examine the value of the node to which the rule is attached, or the value or status of a different node. A special category of rules, referred to as log rules, can be triggered to fire whenever a message matching a specified regular expression appears in a log file.

> Rules usually use parameters that are stored in separate files where they can be customized on a per-machine basis by site administrators. Certain rule parameters can be declared editable by end users through the Sun Management Center console.

### Rule Assignment

> Because a rule is considered a complex alarm check, it is natural to extend the existing agent alarm checking mechanism to encompass rules. A qualifier, <alarmRules`>`, specifies a particular rule for a given node. This variable is normally assigned to a node in the module `Model` file.

>     alarmRules = r231

> W henrever a rule is used, no other alarm check is allowed. If the agent detects both an `alarmChecks` and a valid rule in the <alarmRules> variable for a given node, only <alarmRules> is used in status determination; the `alarmChecks` are ignored.

> When an agent encounters alarmRules balarmRules, it invokes the rule directly using a `ruleFire` procedure, which is described later in this section.

> > Note - A log rule, described later in this chapter, can also be invoked by the file scan service through `ruleFire`, after the rule has been subscribed to that service (see the "Rules Attributes"" for a description of `ruleFire`).

> If the node is a vector (that is, it can have more than one row), there is no change in the preceding discussion. However, internally, the rule specified in <alarmRules> is available to each row in the vector node and any data stored in slices is distinct for each `rowName`.

## Rule Files

> The program provides module specific rules, general rules or base rules, and rules created by clients.

#### Module-Specific Rules

> Tcl rules associated with a particular module are placed within the file:

>     <module><-subspec>-d.rul

> Parameter definitions and message text definitions for such rules go in the associated files:

>     <module><-subspec>-ruleinit-d.x
>     <module><-subspec>-ruletext-d.x

> Any custom module rule must be made available within the context of the node that requires it. Here is an example of what you enter into the model file to achieve this:

>     _rules = { [ use PROC ]
>         [ source solaris-example-d.rul ]
>     }

>     system = { [ use MANAGED-OBJECT ]
>           consoleUser = { [ use STRINGRULE MANAGED-PROPERTY _rules ]
>             alarmRules = rUsrChk
>         }
>     }

> In this example, the `rUsrChk` rule is associated with the `consoleUser` node. This rule determines the state of the `consoleUser` node.

#### General Rules or Base Rules

> Rules that are more general in nature, and that can be used in many different modules, should be placed in the `base-rules.rul` file. To reduce overhead, only those rules that must be globally accessible should be made base rules.

> Parameter definitions and message text definitions for such rules go in the associated `base-ruleinit-d.x and base-ruletext-d.x files.`Rules placed in these files are automatically available within the context of all nodes and do not have to be sourced explicitly.

> An example of how to attach a rule that is defined `base-rules.rul` is:

>     system = { [ use MANAGED-OBJECT ]
>           consoleUser = { [ use STRINGRULE MANAGED-PROPERTY ]
>             alarmRules = rGenRule
>         }
>     }

> > Note - Base text messages are not loaded into a `_rules node`; these messages are added directly to the appropriate file.

#### Rules Created By Clients

> In the future, clients may wish to create their own custom rules. Such rules should be placed in the file:`
> `

>     user-rules.rul

> Parameter definitions and message text definitions for such rules go in the associated files:

>     user-ruleinit-d.x
>     user-ruletext-d.x

> Placing client rules in these separate files, allows the client rules to be saved readily across new code releases.

> > Note - The purpose of these files is to segregate possible client customizations from the base distributed code. The wider issues of code management, configuration, and distribution are outside the scope of this document.

> As in the case of `base-rules.rul`, rules placed in `user-rules.rul` are automatically available within the context of all nodes and do not have to be explicitly sourced. They are globally accessible.

## Rule Placement in Hierarchy

> Because a rule is connected to a node through the <alarmRules> variable, it is obvious that a rule must be associated with a node. However, some special circumstances require attention:

- A node can require more than one rule.
- A rule can have no natural node to which to be attached.
- A node can have a rule but no data.

> Each of these cases is described in the following sections.

### A Node Can Require More Than One Rule

> An example of this multiple rule requirement can be taken from the rules. Rules `rcr4u209`, `rcr4u212`, and `rcr4u300` all apply to memory SIMMs. In this case, if a module hierarchy has a node for a particular SIMM, for example, J3201, which has a leaf node for its status, these three rules cannot be associated with the leaf nodes <alarmRules> variable because only one rule is allowed.

> The solution is to create three more leaf nodes as inferiors of J3201, with one rule per node. By doing so, hierarchical summarization of status up to the SIMM node is handled by the existing agent status propagation mechanism.

> Alternatively, if possible, the three rules can be redesigned and collapsed into a single rule.

#### Rule Can Have No Natural Node to be Attached to

> Several existing rules (for example, rules `rknrd105`,) do not drive any alarms (that is, they do not affect the alarm status of any node). Instead, they simply generate events. Normally, a rule is attached to the node that it alarms. In this case, a node must be created specifically for hosting the rule.

> It would be wise to collect all such orphan nodes together into a single rules-only module.

#### Node Can Have a Rule but No Data

In the preceding sections, nodes have been introduced to host a rule. Such nodes have no real data associated with them. To identify such nodes, a new primitive, `RULE`, must be introduced.

## Rules Attributes

### Rule Data Storage

> This section describes the Tcl and C/C++ compiled rules.

> > Note - Wherever method names are mentioned, the method should be implemented in two forms: as a Tcl/TOE method and as a C/C++ function).

> Rules have four types of variables (also referred to as attributes or parameters in this description):

- Temporary: These variables are local (known only to the rule procedure proper); an example would be a loop counter like `loopCnt`. The rule procedure has read/write access to such variables. The values of the variables are strictly temporary and are not retained across invocations of the rule.
- Dynamic: These variables are local (known only to the rule procedure proper). The rule procedure has read/write access to such variables, and the variable values are retained across subsequent invocations of the rule. The rule can use such parameters to save information that is needed between one execution of the rule and the next.
- Static: These variables are read only. They are initialized in the `ruleinit` file. An example would be the rule group that a rule designer assigns to indicate that a rule is part of a larger group, like hardware or capacity planning. The rule procedure can read, but not modify, these variables.
- Editable: These variables are read-only to the rule procedure. An example of an editable parameter would be `swap_thresh`, which represents a threshold that a user can choose to tune. Global default values for such variables are defined in the `ruleinit` file, and those global defaults cannot be modified directly by end users. However, end users can specify local override values on an individual node/row basis. An end user makes such changes through the Sun Management Center console, and the modified values are stored in a <module><-subspec>.`dat` override file for the module, so that the overrides are saved across restarts of the agent. The rule procedure can read, but not modify, the editable parameters.

> Except for the first case of temporary variables, all other variables must be stored in a TOE slice, and accessed through the `getRuleParm/setRuleParm` (see "Rules Functions" for the Tcl/TOE implementation). Each node must have its own copy of whatever slices it requires whenever node-specific data is created:

> TABLE 8-1 Rule Variables
> Variable Type | Scope | Initialization | Persist Across | Slice | Read Access | Write Access
> temporary | local | `rule proc` | - | - | Tcl ${name} | Tcl set
> dynamic | local | `rule proc` | rule invocations | rule-dyn | getRuleParm | setRuleParm
> static | global | `ruleint` file | Agent restarts | rule | getRuleParm | -
> editable | global | `ruleint` file | Agent restarts | alarmlimit | getRuleParm | local overrides via shadowmap

> Another slice, `rulemsg`, must be created once for each loaded module, under the `node _rules`. This slice must contain a key-value pair for every text message.

> > Note - The `rulemsg` key name does NOT require a <ruleId> prefix, and normally should NOT have a prefix, since messages can apply to multiple rules.

> TABLE 8-2 Rule Message Key
> rulemsg key | Usage
> <msgId> | Contains a string defining the specified message (for example, ir209msg)

> Sun Management Center has no restriction on what data or how much data is saved by a rule between invocations. The name and usage of such data is strictly rule-specific (that is, up to the rule designer), and is accessed through the `getRuleParm`/`setRuleParm` methods (see "Rule Functions" for the Tcl/TOE implementation).

> > Note - The internal data is always available for every rule. It is maintained transparently by the underlying rule implementation). This data must never be modified by a rule designer.

> The rule designer has read-only access to certain internal data as shown in the following table.

> TABLE 8-3 Rule Designer Access to Internal Data
> Slice | Key | Description | Equivalent | Rule Read Access
> rule-state | $rule[($row)] | Last rule state (for example, 0=init, 1=open, 2=continue and 3=close) | ACTIVE | ruleActive
> rule-cond | $rule[($row)] | ast value of rule event status (blank for ok, info, warning or error) | n/a | n/a
> `rule-count` | $rule[($row)] | Number of consecutive iterations of the rule, counting from the last time it transitioned to open; (for example, a value of 3 means the rule has detected a true CONDITION for the last 3 iterations). | COUNT | getRuleCount
> `rule-statetime` | - | Time of the last transition in rule-state. | n/a | n/a
> `rule-starttime` | - | Time the rule CONDITION last transitioned from false to true. | START_TIME | getRuleStartTime
> `rule-fs` | - | Present for log rules only. Contains a list of File Scan service subscriptions made by the rule. | n/a | n/a
> `rule-match` | - | List of pattern matches from File Scan service, saved for subsequent log rule invocation. | n/a | Passed to rule as parameter to "open" or "continue" action
> `rule` | $rule-editparm | Present only if editable parameters have been identified by a rule; contains a list of the editable parameters. | n/a | n/a

### Rule State Transitions

> To generate alarms, a rule defines a CONDITION (in Tcl rules this is the Tcl script in the condition case) that is evaluated every time the rule is executed. If the condition is true, this is an active event.

> An event is an alarm generated by a rule. An inactive event is equivalent to saying that there is no alarm generated for a particular rule. Events can transition through various states; the underlying rule determines these transitions. TABLE 8-4 lists the allowed event states and transitions.

> TABLE 8-4 Rule State Transitions and Events
> State | Meaning | May Transition To
> init | Rule is initialized, ready to detect event transitions; event is inactive. | open
> open | Rule condition has just transitioned from false to true; event is now active. | continue, close
> continue | Rule condition was true in the last invocation, and is still true in the current invocation; event is still active. | continue, close
> close | Rule condition was true in the last invocation, and is now false in the current invocation; event is now inactive. | open

> The `ruleFire` procedure determines these states. The preceding section describes the state variables that persist between rule invocations.

> Of these are two special operations that can be performed on an active (that is, open or continue state) event; these operations are `ack` and `fix`. Presently, neither operation causes an event state transition; thus, if a rule detects either operation in effect, it executes the corresponding Tcl script specified in the rule, and returns with an empty string ("").

> The `ack` operation signifies a user action to acknowledge an event; the event remains active.

> The fix operation signifies a user action to manually repair a hardware-related event; whether this affects the event state isto be determined.

### Rule Invocation Procedure (`ruleFire`)

All rules must be coded as Tcl procedures (or C/C++ functions) that are invoked by the agent through the `ruleFire` procedure. This procedure must do the following:

1. Invoke the specified rule procedure, specifying an init action if initialization is required, otherwise, perform the steps that follow. Init is read the first time the rule is read. The rule remains in the Init state for as long as the rule condition evaluates to false.
2. Invoke the specified rule procedure to determine whether the event condition is true or false (as determined by the CONDITION script of the rule, described later in this document).
3. Determine the current rule/event state (for example, open, continue, close), based on the evaluated condition.
4. The following state transitions can occur based on evaluating the condition and the current rule state:
   - If condition is false, and if event is inactive, no new event is generated and the rule continues to be in init state.
   - If condition is true, and if event is inactive, the event transitions to the open state (which means that the event is now active). This could result in an info, warning, or error event status.
   - If condition is true, and if event is already active, the event transitions to the continue state (which means that the event is still active and continues to have an info, warning, or error status).
   - If condition is false, and if event is active, the event transitions to a close state (which means that the event is now inactive).
5. Invoke the specified rule procedure, whenever an end-user performs an `ack` or `fix` action on a currently active alarm.
6. Perform internal processing to maintain the state of the rule and related events.

### Rule Event Status

A rule must return a string value that corresponds to a valid event status. This string must then be used by the agent. TABLE 8-5 lists the allowed return strings:

TABLE 8-5 Rule Event Status
Event Status | Meaning
ok | Inactive event
info[-<qualifier>] | Informational event
warning[-<qualifier>] | Warning event
error[-<qualifier>] | Error event

> > Note - The mandatory portion of the return string (`ok, info, warning`, and `error`) must be used by the console to determine the icon.

> The optional <qualifier> allows additional descriptive text to be appended. The qualifier can be used to differentiate events (for example, e`rror-temp, error-parity`). The qualifier is a maximum of eight characters.

> Examples of valid return strings:

>     info
>     warning-rx

## Rule Functions

> This section lists the methods that Tcl rules can call. The following table summarizes all methods that rules can call directly.

> TABLE 8-6 Rule Functions
> Tcl Rule Method | Arguments | Description
> closeEvent | ruleId, rowName, estatus | Closes an event (see also logEvent).
> externalTableOK | ruleid, nodeId, [default] | Return a 1 if specified table has an active alarm; 0 if no active alarm; default if alarm status of specified table cannot be acquired.
> getExternalRowStatus | ruleId, nodeId, rowName, [default] | Get status of specified vector row of specified node.
> getExternalRowValue | ruleId, nodeId, rowName, [default] | Get value of specified vector row of specified node.
> getExternalStatus | ruleId, nodeId [default] | Get status of specified node.
> getExternalValue | ruleId, nodeId [default] | Get value of specified node.
> getGRuleParm | parm, [default_parm] | Get global rule parameter.
> getMyStatus | ruleId, rowName, [default_value] | Get current node status (that is, error-gt); default value returned if status cannot be acquired.
> getMyValue | ruleId, rowName, [default_value] | Get current node value; default value returned if data cannot be acquired.
> getRuleCount | ruleId, rowName | Get number of consecutive iterations of the rule, counting from the last time it transitioned to open (for example, a value of 3 means the rule has detected a true CONDITION for the last 3 iterations).
> getRuleMsg | msgId, [default] | Get specified message.
> getNodeName | level | Get portion of superior node name (level 0 means current node, level 1 means immediate superior, and so forth)
> getRuleParm | type, ruleId, rowName, parm, [default_parm] | Get rule parameter. <type> is one of: DYN (dynamic parameter) STATIC (static parameter) EDIT (editable parameter)
> getRuleStartTime | ruleid, rowname | Get the time (# of seconds since epoch) that rule last transitioned to open.
> getTime |  | Get current time as seconds since the epoch.
> logEvent | ruleId, rowName, estatus | Logs an event. Event is logged as opening and immediately closing, but the node does not go into the open alarm state.
> logSubscribe | ruleId, file, pattern, rowName, callback option | Log rule subscription to File Scan service. The pattern shall be enclosed in double quotes, and the callback shall be identical to the ruleId. Escape ([)_and (]) with backslashes (\).
> ruleActive | ruleId, rowName | Return a 1 if rule is current active (OPEN or CONTINUE state)
> setGRuleParm | parm, newval | Set global rule parameter.
> setRuleParm | type, ruleId, rowName, parm, newval | Set rule parameter. Valid only for <type> = DYN (dynamic)
> setRuleText | ruleid, rowname, estatus, shortmsg, longmsg | Set text message for indicated state for underlying node/row. longmsg is obsolete.

## Third Party Rule Engine Interface Functions

### Rule Loading

> Rules are usually implemented in the form of Tcl procedures. If necessary, for performance reasons, rules can also be created as C or C++ code.

> As an example, an agent configuration file in module configuration file format loads Tcl procedures from file `pfrules-d.prc`:

>     #
>     # Load pfrules procedures
>     	#
>     _procedures = { [use PROC ]
>     		[ source pfrules-d.prc ]
>     }

> Any rules written in C or C++ must be loaded as packages.

> As an example, an agent configuration file in module configuration file format loads package `pkgrules.so`:

>     #
>     # Load rules package
>     #
>     [ requires package rules ]

> The rules that actually are loaded into the agent must return a string indicating the detected rule state.

### Rule Assignment

> The actual rule to be invoked for a derived object must be assigned through the specification of the following refresh variables:

>     refreshTrigger = <node>[:<event>]
>     refreshCommand = r<n>
>     refreshService = _internal

> The effect of the `refreshCommand` is to invoke the specified rule, which has already been loaded either as a Tcl procedure or a C/C++ package (see "Rule Syntax & Loading").

### Key TOE Functions

> Function prototypes,typeddefs and so forth required to interface with TOE must be available in header file, `sdk/include/toeInt.h`. The convention for describing each function is to consider the first argument as arg1, the second argument as arg2, and so forth. The following TOE functions are listed to provide an indication of how a rule can access agent object data:

> TABLE 8-7 Key TOE Functions
> TOE Function | Description
> --- | ---
> ToePtr toeGetCurrentObject (Tcl_Interp*, int) | Returns pointer to current TOE object (for example, the object from which the rule has fired), or NULL if error. arg1 - pointer to interpreter handle arg2 - literal value of TOE_DICTIONARY, to access the object data (as opposed to the commands).
> ToeVal toeDefine (ToePtr, char*,char*,DataType*,void*) | Defines value in specified object's slice. Returns pointer to allocated value or NULL if error. arg1 - pointer to TOE object arg2 - pointer to slice arg3 - pointer to slice entry arg4 - pointer to data type of value Header file `converters.h` defines `dataGetType` to get a data type: `DataType * dataGetType( char * name );` You can use these data type names in program: `int, uint, long, longlong, ulong, ulonglong, short, double, float, char, boolean, string, pointer`. arg5 - pointer to value
> int toeUndefine (ToePtr,char*,char*) | Removes entry from specified object's slice. Returns 0 on success, -1 on failure. arg1 - pointer to TOE object arg2 - pointer to slice arg3 - pointer to slice entry
> ToeVal toeLookup (ToePtr,int,char*,char*) | Returns pointer to value from specified object's slice, entry, or NULL if error. arg1 - pointer to TOE object arg2 - tree search scope (TOE_SCOPE_INSTANCE, TOE_SCOPE_PARENTS,TOE_SCOPE_SUPERIORS,TOE_SCOPE_ALL) arg3 - pointer to slice arg4 - pointer to slice entry
> DataType *toeValGetDataType (ToeVal) | Returns pointer to data type structure for specified pointer to value, or NULL on error. arg1 - pointer to object slice,entry
> void *toeValGetType (ToeVal,DataType*) | Returns pointer to data, or NULL on error. Programer cannot free the pointer, because the memory is maintained by TOE. arg1 - pointer to object slice, entry arg2 - pointer to type to return (NULL if string)
> int toeValSetType (ToeVal,DataType*,void*) | Sets specified value into specified object's slice, entry. Returns CVT_OK on success, CVT_ERROR on failure. arg1 - pointer to object slice, entry arg2 - pointer to data type structure for arg1; normally set to arg1's type member (for example, arg1->type) arg3 - pointer to value

## How to Write A Tcl Rule

In Sun Management Center, rules are implemented as Tcl procedures, which provide the following advantages:

- Rules can be introduced to an agent directly, without parsing.
- Rules can be invoked directly through the ruleFire procedure.
- Rule syntax becomes more rigorous, since it must conform to the Tcl language.

> This section provides a guide to writing a Tcl rule by describing the following:

- An example of a Tcl rule and rule file
- Guidelines for writing a Tcl rule, including a template of a rule procedure
- Other rule support issues (sourcing a rule file, specifying initialization data and rule messages, specifying editable parameters)
  The examples in this section all pertain to the ConfigReader example introduced earlier. A rule designer can customize the example code to create the files needed to add new rules.

> The principles in this section apply to C/C++ compiled rules as well, since the basic structure of a rule is simply a procedure with a switch statement to allow the appropriate code to be invoked to handle each case. A natural way to proceed is to port the Tcl methods introduced in this section to C/C++; these methods constitute the interface between a C/C++ rule and the agent. The C/C++ rules would be made available to the agent by creating Tcl packages.

#### Tcl Rule Example

> Methods referenced in the example (for example, `getRuleParm`) are described in "Rule Functions"

> CODE EXAMPLE 8-1 Tcl Rule Example

>     # Rule:  rcr4u209
>     # Purpose:
>     # Log a message for SIMM errors with corrected ECC Data Bit
>     # Arguments:
>     #	 action - one of
>     #		   {condition|init|open|continue|ack|fix|close}
>     # ruleId - rule identifier
>     # rowName - name of vector row (only valid for vector nodes);
>     #		    defaults to ""
>     # rowIndex - index of vector row (only valid for vector
>     #		     nodes); defaults to 0
>     # matchList - set to matched substrings if this is a
>     #			 log rule and the File Scan service is
>     #			 notifying the rule about matches found;
>     #			 defaults to ""
>     # This rule is attached to the ConfigReader...memory.SIMM
>     # hierarchy.
>     #
>        proc rcr4u209 { action ruleId { rowName "" } { rowIndex 0} \
>     { matchList "" } } {
>        set estatus "error"

>        # Fire state transition actions
>     		switch $action {
>     		 init {
>     # Get names of board node & boardrefno node (e.g. SIMM(0), J3201)
>     	set node_logword1 [ getNodeName 2 ]
>     		set node_logword2 [ getNodeName 1 ]

>     # Derive values of logword1, logword2 from node names (e.g. 0, J3201)
>     	regexp {([0-9+])} $node_logword1 logword1
>     	regexp {([0-9+])} $node_logword2 logword2

>     		logSubscribe $ruleId /var/adm/messages "ECC \[Mm\]emory \ \[Ee\]rror.*SIMM\
>     Board ($logword1) ($logword2).*ECC Data Bit (\[0-9\]+) was corrected" $rowName\
>     $ruleId
>     	return ""
>       }
>       open
>         set boardno [ lindex $matchList 0 ]
>         set boardrefno [ lindex $matchList 1 ]
>         set bitno [ lindex $matchList 2 ]
>         set nodeName [ getNodeName 0 ]
>     # Note that reference to $target is here only to mimic the SyMON 1.x
>     # rule r209; global parameters that have to be available to all rules
>     # in a module should be set through get/setRuleGParm
>            set msg [ format[ getRuleMsg ir209msg ]"$target"
>     	 		 	 	 "$nodeName" "$bitno" ]
>     		 	setRuleText $ruleId $rowName $estatus $msg
>     # Note that logEvent is called here to mimic the SyMON 1.x
>     # behaviour of log rules (i.e. an event is opened and then
>     # closed immediately); however, it is possible and advisable to
>     # not call logEvent at all, and to instead return $estatus instead
>     # of returning "ok". That will cause the event to remain open until # closed
>     (e.g. by a Fix action);

>      	 	 logEvent  $ruleId $rowName $estatus
>      		return ok
>     		}
>     	fix {
>     # Any special "fix" actions would go here
>     		return
>     	}
>       }
>     }

> .

### Tcl Rules File Format

> All Tcl rule files must have an extension of `rul' (for example, `config-reader-d.rul`). A portion of a rule file for the running example in this section would look like this:

> CODE EXAMPLE 8-2 Tcl rules File Format

>     # File: config-reader-d.rul
>     #
>     # configReader rules
>     #
>     	proc rcr4u209 {action ruleId {rowName ""} {rowIndex 0} \
>     {matchList ""}} {
>     		body of procedure goes here ...
>     }
>     	proc rcr4u212 {action ruleId {rowName ""} {rowIndex 0} \
>     {matchList ""}} {
>     		body of procedure goes here ...
>     }
>     	proc rcr4u300 {action ruleId {rowName ""} {rowIndex 0} \
>     {matchList ""}} {
>     		body of procedure goes here ...
>     }

#### Tcl Rule Template

> The template in this section shows the minimum elements that must exist in every Tcl rule. Examples of all required files, based on the running example of `ConfigReader` rule `rcr4u209`), follow.

> The major steps required to create a rule are listed in the section below.

> Guidelines

1. Determine the name for the rule (for example, `rcr4u209`). The name must be unique across all rules.
2. Determine the initial values for any static (for example, group), or editable parameters required by the rule that will be stored in a slice. Create initial values for any of these parameters by adding them to a rule parameter initialization file named after the module that the rules belong to (for example, `config-reader-ruleinit-d.x`). The load of this file must be done at the bottom of the Model file.
   If any of the preceding parameters must be editable, a special static parameter, `editparm`, must be initialized in the <module><-subspec>-`ruleinit-d.x` file to contain a list of all parameters that are to be editable. For example, if parameters `alarmThresh` and deadband were to be editable in some rule `r` XXX, `editparm` would be initialized by

       _rule = {
            rule:rXXX-editparm = alarmThresh deadband
       }

   where `XXX` can be any name with or without numbers.

> > Note - The listed editable parameters do not have the rule identifier as a prefix.

> For the console, the displayed text describing the editable parameter must be internationalized. To internationalize, a special static parameter, `keypath`, is used to specify the path to the module's Properties file. Normally, the properties file must be in the Sun Management Center software proto tree under classes:

>     /com/sun/symon/base/modules/<module><-subspec>.properties

> For example, to internationalize the text for the editable parameters, include the following in the <module><-subspec>-`ruleinit-d.x` file:

>     rule:rXXX-keypath = "base.modules.<module>"

> The module properties file must contain entries for the internationalized text for the module's editable parameters like these:

>     editAtt.rXXX.alarmThresh=<internationalized text for threshold>
>     editAtt.rXXX.deadband=<internationalized text for deadband>

> You can also define datatypes for any of the editable parameters. The data types would then be enforced whenever an end-user modifies the parameters. Set an editable parameter datatype by including a line like the following in the <module><-subspec>-`ruleinit-d.x` file:`

>     ruledatatype:rXXX-alarmThresh = "float"

> `
> If no datatype is specified, the default is string. TABLE 8-8 lists the allowed datatypes:

> TABLE 8-8 Datatypes Allowed
> Datatype | Description
> --- | ---
> int | integer
> uint | unsigned integer
> float | floating point number
> string | character string (this is the default)

> > Note - The rule datatype definitions are same as the datatype definitions for the underlying OS.

1. Define a description of the rule and its parameters; this description is displayed in the Attribute Editor. The descriptions (like the preceding example of editable parameters) must be internationalized. The descriptions are specified in the same module Properties file as for the editable parameters. Typical entries would look like this:

       editAtt.rXXX.desc=<internationalized text for rule description>
       editAtt.rXXX.paramsdesc=<internationalized text for rule parameters description>

2. Determine any text messages (for example, `.rcr4u209msg`) that belong with the rule, and add them to a message file for the module (for example, `config-reader-ruletext-d.x`). The load of this file must be done in the module's Model file (for example, `config-reader-models-d.x`).
3. Design the Tcl code for all possible states/actions; the rule is basically a switch statement, with a case for each state/action (for example, in`it, condition, open, continue, ack, fix, close`). The case can be empty, or left out entirely, if there is no code to execute for a particular state.

> > Note - The code for the condition case is used to determine if the underlying event is active; therefore, the last statement in the condition case shall evaluate to 0 or 1; this is returned to the caller (`ruleFire`).

1. For log rules, use `logSubscribe` in the init case; specify the regular expressions in parentheses in the pattern argument to `logSubscribe`; ensure that the regular expressions are specific to the particular instance of the rule (for example, see `logword1, logword2` in the `rcr4u209` example). Note that to specify a left or right square bracket (that is, "[" or "]") in the regular expression, the bracket must be preceded with a backslash (`\`).
   The option argument is an optional argument. If option is specified as `-m`, it indicates that the pattern being subscribed for is to be applied across multiple lines. If option is not specified or is set to any value but
   `-m`, the corresponding pattern is applied to individual lines.
2. Only use `getRuleParm/setRuleParm` to read/write parameters that are saved between rule invocations (that is, in TOE slices). Do not use direct lookups with TOE methods.
3. Source the rules in the module configuration file through a special node, _rules (see `config-reader-models-d.x`).
4. Attach the rule to the appropriate node(s) in the module models configuration file (for example, `config-reader-models-d.x`).

> CODE EXAMPLE 8-3 Template

>     # Rule:  <ruleId>
>     # Purpose:
>     # Arguments:
>     # 	action - one of
>     #		   {condition|init|open|continue|ack|fix|close}
>     # 	ruleId - rule identifier
>     #	 rowName - name of vector row (only valid for vector nodes); default=""
>     # 	rowIndex - index of vector row (only valid for vector nodes); default=0
>     # 	matchList - set to matched substrings if this is a log rule and the
>     # File Scan service is notifying the rule about matches found; default=""
>     #
>     # Notes:
>     #	 The Tcl code for the various states may be empty, or the case left out
>     #	 entirely, if there are no actions to be taken.
>     #
>     	proc <ruleId> {action ruleId {rowName ""} {rowIndex 0} {matchList ""}} {
>     # Set state transition actions
>     # For a log rule, condition should always be true, since
>     # the log rule executes only when triggered by callback from
>     # the File Scan service upon matching the subscribed pattern
>                 switch $action {
>     		 	 	 	 		condition {
>     # Make sure that the last statement in the condition Tcl script
>     # evaluates to 0 for false, 1 for true; this is used to
>     # determine if the underlying event is active (i.e true) or not.
>      	          < code to evaluate the condition >
>      	 return {0 | 1}
>     					 	 	 	 }
>      	 init {
>     # Call logSubscribe  $ruleId <log file> <rowName> <pattern>
>     # <callback function>  if this is a log rule
>                        < code for init >
>               return
>      }
>               open {
>     # Call setRuleText <ruleId> <rowName> <pattern> <statusmsg>
>     #
>     # <callback> <option> if this is a log rule, there are two choices now:
>     # (a) Call logEvent to log the event, and return "ok"
>     # (b) Return a state-qualifier ("error-gt", for example). Do not call
>     #     logEvent. The event will be logged through the normal open event
>     #     mechanism. The event will remain open in this case.

>      < code for open >
>                return <event status >
>          }
>             continue {

>     # If the event status is to remain unchanged on continuation, simply
>     # return the previous event status, or return "".
>     #
>     # If the event status is to change (for example, perhaps the event is
>     # being escalating to "error" from "warning"), then:
>     #
>     #   (a) call setRuleText to set the message text for the new state
>     #   (b) return the new state.
>     #
>     # If there is a previous open event, and you return a new event state,
>     # the previous event will close automatically.
>     #
>     # Do not call setRuleText unless you are returning a new event state also.
>     #
>     # Note that if this is a log rule, and you let a previous file match
>     # remain in the open state, subsequent file matches will be sent
>     # as "continue" rather than open. In such cases, call "logEvent"
>     # to log the new event (if desired). You can call "closeEvent" to
>     # explicitly close the previous open event (if desired).
>     	< code for continue >
>     	return <event status>
>     	 	 	}
>     ack {
>     		< code for ack >
>     		return
>     		  }
>     fix {
>     		< code for fix >
>     		return
>     		  }
>     close {
>     		< code for close >
>         return
>     		  }
>     	}
>     }

> The following code example includes a template.

#### Attaching a Rule to the Module Configuration Files

> When a module requires a rule, the appropriate rule file (for example, `config-reader-d.rul`) is sourced. Rule files must be located in the same directory as their corresponding module configuration files. The source command must be put in a container node (typically called `_rules`) in the module's Model file. The `_rules` node must be inherited by any node requiring access to the rules.

> CODE EXAMPLE 8-4 Module Model File

>     # File:  config-reader-models-d.x
>     #
>     # Node _rules will contain the TOE slices for the ConfigReader
>     # rule initialization data and text messages.

>     _rules = { 		[ source config-reader-d.rul ] }
>     availability = {
>     		mediumDesc = Available
>     }

>     simm = { 		[ use MANAGED-OBJECT ]

>     		r209 = {	 [ use RULE _rules MANAGED-PROPERTY ]
>                  mediumDesc = Rule 209
>                    alarmRules = rcr4u209
>     		}

>     		r212 = { [ use RULE _rules MANAGED-PROPERTY ]
>                  mediumDesc = Rule 212
>                     alarmRules = rcr4u212
>     		}

>     		r300 = { [ use RULE _rules MANAGED-PROPERTY ]
>                  mediumDesc = Rule 300
>                      alarmRules = rcr4u300
>     		}
>     }

>     disk = {

>     }

>     cpu = {

>     }

>     	[ load config-reader-ruleinit-d.x ]
>     	[ load config-reader-ruletext-d.x ]

> The Module Agent File

> The `ConfigReader` module agent file, `config-reader-d.x`, might then look like this:

> CODE EXAMPLE 8-5 Module Agent File

>     # File: config-reader-d.x
>     #
>     # ConfigReader module configuration file
>     #

>     [ use MANAGED-MODULE ]
>     [ load config-reader-m.x ]
>     [ requires template config-reader-models-d ]

>     availability = {
>     	refreshInterval = ...
>     	refreshService = ...
>     	refreshCommand = ...
>     }

>     memory = {
>     SIMM(0) = { [ use templates.config-reader-models-d.simm ]
>     }

>     		...

>     		SIMM(31) = { [ use templates.config-reader-models-d.simm ]
>     		}
>     	}

>     # loading alarmlimit defaults now.
>     # config-reader-ruleinit-d.x and config-reader-ruletext-d.x
>     # loaded in models file

>     	[ load config-reader-d.def ]

#### Assigning Initial Values to Rule Parameters

> The initial values for static and editable parameters are assigned in the
> <module><-subspec>-`ruleinit-d.x` file. Note that editable parameters are placed in the alarmlimit slice; all others go in the rule slice.

> When this file is loaded into an Agent, the initialized parameters are available to be read by any rule attached to any node. If an end user modifies an editable parameter, a local slice shall be created for the affected node to contain the customized value; other nodes will not be affected.

> The datatype for an editable parameter can be specified in the <module><-subspec>-`ruleinit-d.x` file (see the example below). The default datatype is string.

> Dynamic parameters are not initialized in the <module><-subspec>-`ruleinit-d.x file`. Such parameters are set directly within the rule logic, either as needed, or in the init action section. Dynamic parameters are stored in a local slice for the affected nodes; their values are not available to other nodes (even other nodes running the same rule).

> A portion of the initialization file for the running example might be:

>     # File: config-reader-ruleinit-d.x
>     #

>     _rules = {
>     	 	rule:rcr4u209-group = hardware
>     	 	rule:rcr4u209-version = 0.1

>     		rule:rXXX-group = example
>     		rule:rXXX-version = 0.1
>     		rule:rXXX-editparm = "sample_thresh"
>     		alarmlimit:rXXX-sample_thresh = 0.10
>         ruledatatype:rXXX-sample_thresh = "float"
>         rule:rXXX-keypath = "base.modules.configReader"
>     }

#### Specifying Rule Text Messages

> Rules can use various text messages to convey status; these messages can be collected into a file for a particular module in order to centralize the messages. Note that messages can apply to more than one rule, so there need not be any rule identifier in the message name. Messages shall be assigned to the rulemsg slice.

> The text messages shall be associated with node `_rules`.

> A portion of the initialization file for the running example can be as follows:

>     # File: config-reader-ruletext-d.x
>     #
>     _rules = {
>     		rulemsg:rcr4u209msg = "%s: %s: Error. ECC Data bit %s was
>     corrected"
>            rulemsg:lowmsg = "has less than %s percent free space"
>     }

> For every event that occurs, a rule can create two different descriptive messages:

- English Status Message
- Internationalized Status Message

> English Status Message

The English status message appears in the Sun Management Center hierarchy and are also in the short message that appears in the Alarm Manager console for the event. The English status message is automatically prepended with host, module name, rowname, and medium description, so the rule designer should not include these in the message.

> For example, if the rule specifies a status message such as the following:

>     has less than 2 percent free space

> the actual message in the Alarm Manager console or the hierarchy is:

>     muskoka Solaris /export1 Filesystem has less than 2 percent free space

> where:

>     host = muskoka

>     module = Solaris

>     rowname = /export1

>     mediumDesc = Filesystem

> The message is set in within the rule logic in the open action as follows. If it is not set, it defaults to something like:

>     muskoka Solaris /export1 Filesystem rcr4u209 error

> Internationalized Status Message

> It is anticipated that the English status message will be supplemented in the future with an internationalized version of the status message. This internationalized status message will consist of a series of keywords that will be translated appropriately by the displaying console.

> The internationalized messages supplement, not replace, the English status messages. That is, both types of messages will have to be created explicitly by each rule. In the much longer term, the English status messages may eventually be phased out entirely, in favor of the internationalized message.

> Currently, rule designers should not attempt to format internationalized status strings.

### More Examples Of Rules

CODE EXAMPLE 8-6 Simple Rule

    #Rule:  rknrd402
    #Purpose:
    #Checks if available swap space drops below 10% for two 	hours.
    #Storage of the last time CPU load was below 6 is maintained
    #between rule invocations.  This parameter is initialized to
    #some date in the year 2001.
    #Note:
    #This rule should be attached to KernelReader.mem
    #
    proc rknrd402 { action ruleId {rowName ""} {rowIndex 0} {matchList ""} } {

             set estatus info
    # Fire state transition actions

    		switch $action {
    		   condition {
    			  set value [	 getExternalValue {
    					 $ruleId KernelReader.mem.swap_avail  ""
    		 	 } ]
    	 	set swapb [ getExternalValue {
    	 		 	$ruleId KernelReader.mem.swap_total ""
            } ]

         set cur [ getTime ]
      	 	    if { ($value/$swapb} > [ getRuleParm $ruleId swap_thresh 0.10 ] } {
    		  setRuleParm DYN $ruleId  $rowName user_timestamp $cur
    			}
    		  set 1st [ getRuleParm DYN $ruleId $rowName user_timestamp 999999999 ]
    			 return [expr { ($cur-$1st)>14400 }]
    			  }
    open {
    			 set msg [ getRuleMsg rknrd402msg ]

    			 setRuleText $ruleId $rowName $estatus $msg
    			 add_cpa SWAP $cur
    			 trim_cpa
    			 return $estatus
    		   }
    	}

#### Config Reader Rule

> See coding of rule `rcr4u209` in this document.

#### Log Rule

> In addition to the rule `rcr4u209` example, here is another:

> CODE EXAMPLE 8-7 Log Rule

>      Rule:  rknrd106
>     # Purpose:
>     # 	Check for no swap space left.
>     #
>     #
>      proc r106 { action ruleId {rowName ""} {rowIndex 0} {matchList ""} } {

>       set estatus warning

>       # Fire state transition actions

>       switch $action {

>     		init {
>     		 logSubscribe  $ruleId /var/adm/messages "no swap space.*pid (\[0-9\]+)}"\
>     $rowName $ruleId

>     				 return
>     			}

>     			open {
>     				 	 set pid [ lindex $matchList 0 ]
>     					  set rmsg [ getRuleMsg rknrd106msg ]
>     					  set msg [ format "$rmsg" "$pid" ]
>     	 	 	 	 	 setRuleText $ruleId $rowName $estatus $msg
>     					  logEvent $ruleId $rowName $estatus
>     				 	 return $estatus
>     			}
>     	}

Copyright © 2000 Sun Microsystems, Inc. All Rights Reserved.