How to test a modified template rule in the KD Lab?

I have modified a template rule to include rule execution interval. This will allow template based rules to have this parameter.


To test the modified template rule I have created a rule file that includes a template based rule that is using the medified template. The rule case class in in the top of the file and the template rule is included below. Plese see the code below (it is attached as a .txt file as well, the forum does not allow .rule attachements) :CrossVendorSnmpCommunitiesComparisonREI.txt


package com.indeni.server.rules.library.templatebased.crossvendor

import com.indeni.server.rules.RuleContext
import com.indeni.server.rules.library.{ConditionalRemediationSteps, SnapshotComparisonTemplateRule}
import com.indeni.time.TimeSpan

/**
*
*/

// Modified template based rule.

case class CrossVendorSnmpCommunitiesComparisonREI(context: RuleContext) extends SnapshotComparisonTemplateRuleREI(context,
ruleName = “cross_vendor_snmp_communities_comparisonREI”,
ruleFriendlyName = “Clustered Devices: SNMP community settings do not match across cluster members REI”,
ruleExecutionInterval = TimeSpan.fromMinutes(1),
ruleDescription = “Indeni will identify when two devices are part of a cluster and alert if the SNMP settings do not match.”,
metricName = “snmp-communities”,
isArray = true,
alertDescription = “Devices that are part of a cluster should have the same SNMP configuration. Review the differences below.”,
baseRemediationText = “Ensure all of the SNMP settings are configured correctly on all cluster members.”)(
ConditionalRemediationSteps.OS_NXOS ->
"""|
|1. Ensure all of the SNMPv2 communities are configured correctly on all cluster members by using the “show snmp” NX-OS command.
|2. Configure the same SNMPv2c communities by using the next command “snmp-server community name group { ro | rw }” across the peer Switches.
|3. For more information please review the next CISCO configuration guide:
|https://www.cisco.com/c/en/us/td/docs/switches/datacenter/sw/5_x/nx-os/system_management/configuration/guide/sm_nx_os_cg/sm_9snmp.html
""".stripMargin
)

// Modified template

import com.indeni.data.conditions.{TagsStoreCondition, True}
import com.indeni.ruleengine.expressions.cluster.SelectClusterComparisonExpression
import com.indeni.ruleengine.expressions.conditions.{Equals, Not}
import com.indeni.ruleengine.expressions.core.{StatusTreeExpression, _}
import com.indeni.ruleengine.expressions.data.SelectTagsExpression
import com.indeni.server.metrics.DeviceStoreActor
import com.indeni.server.rules.{RuleContext, RuleMetadata, _}
import com.indeni.server.sensor.models.managementprocess.alerts.dto.AlertSeverity
import com.indeni.time.TimeSpan
import com.indeni.server.rules.library.{ConditionalRemediationSteps, PerDeviceRule, RuleHelper, SnapshotDiffExpression}

class SnapshotComparisonTemplateRuleREI(context: RuleContext, ruleName: String, ruleFriendlyName: String,
ruleExecutionInterval: TimeSpan = TimeSpan.fromMinutes(1),
severity: AlertSeverity = AlertSeverity.ERROR,
ruleDescription: String, metricName: String, isArray: Boolean, applicableMetricTag: String = null, alertDescription: String,
alertItemsHeader: String = “Mismatching Cluster Members”, descriptionMetricTag: String = null, descriptionStringFormat: String = “”, baseRemediationText: String, metaCondition: TagsStoreCondition = True,
includeSnapshotDiff: Boolean = true)(vendorToRemediationText: (String, String)*)
extends PerDeviceRule with RuleHelper {

override val metadata: RuleMetadata = RuleMetadata(ruleName, ruleFriendlyName, ruleDescription, severity)

override def expressionTree: StatusTreeExpression = {

val thisSnapshot = SelectClusterComparisonExpression.thisSnapshotExpression().mostRecent().noneable.withLazy
val clusterSnapshot = SelectClusterComparisonExpression.clusterSnapshotExpression().mostRecent().noneable.withLazy
val selectExpression = SelectClusterComparisonExpression(context.metaDao, DeviceStoreActor.TAG_DEVICE_ID,
  DeviceStoreActor.TAG_DEVICE_NAME, DeviceStoreActor.TAG_DEVICE_IP, "cluster-id", context.snapshotsDao, metricName)

StatusTreeExpression(
  // Which objects to pull (normally, devices)
  SelectTagsExpression(context.metaDao, Set(DeviceKey), metaCondition),

  // What constitutes an issue
  if (applicableMetricTag != null)
    StatusTreeExpression(

      // The additional tags we care about (we"ll be including this in alert data)
      SelectTagsExpression(context.snapshotsDao, if (descriptionMetricTag == null) Set(applicableMetricTag) else Set(applicableMetricTag, descriptionMetricTag), withTagsCondition(metricName)),

      StatusTreeExpression(
        if (isArray) selectExpression.multi() else selectExpression.single(),

        // The condition which, if true, we have an issue. Checked against the time-series we"ve collected
        Not(Equals(thisSnapshot, clusterSnapshot))

        // The Alert Item to add for this specific item
      ).withSecondaryInfo(
        scopableStringFormatExpression("${scope(\"" + applicableMetricTag + "\")}" + (if (descriptionMetricTag != null) " (${scope(\"" + descriptionMetricTag + "\")})" else "") + " on %s (%s)", SelectClusterComparisonExpression.clusterDeviceNameExpression(), SelectClusterComparisonExpression.clusterDeviceIpExpression()),
        if (includeSnapshotDiff) SnapshotDiffExpression(thisSnapshot, clusterSnapshot) else EMPTY_STRING,
        title = alertItemsHeader
      ).asCondition()
    ).withoutInfo().asCondition()
  else
    StatusTreeExpression(
      // The time-series we check the test condition against:
      if (isArray) selectExpression.multi() else selectExpression.single(),

      // The condition which, if true, we have an issue. Checked against the time-series we"ve collected
      Not(Equals(thisSnapshot, clusterSnapshot))
    ).withSecondaryInfo(
      scopableStringFormatExpression("%s (%s)", SelectClusterComparisonExpression.clusterDeviceNameExpression(), SelectClusterComparisonExpression.clusterDeviceIpExpression()),
      if (includeSnapshotDiff) SnapshotDiffExpression(thisSnapshot, clusterSnapshot) else EMPTY_STRING,
      title = alertItemsHeader
    ).asCondition()

  // Details of the alert itself
).withRootInfo(
  getHeadline(),
  ConstantExpression(alertDescription),
  ConditionalRemediationSteps(baseRemediationText, vendorToRemediationText: _*)
)

}
}

In the IntelliJ the code compiles without errors.

After loding it to the Indeni server on the KD Lab I have got the following error:


error_printout_from_KDL_for_rule_with_template_with_REI.txt

INFO  [2017-09-27 11:07:31,966] com.indeni.server.rules.manager.factory.FileSystemRuleFactory: Loading rule from file "/usr/share/indeni/rules/CrossVendorSnmpCommunitiesComparisonREI.rule"
ERROR [2017-09-27 11:07:32,650] com.indeni.server.rules.manager.factory.FileSystemRuleFactory: Failed to load rule from file "/usr/share/indeni/rules/CrossVendorSnmpCommunitiesComparisonREI.rule"
! com.indeni.server.rules.manager.factory.RuleCompilationException: Couldn"t compile rule code. Cause: Compiled rule "__wrapper$3227$6e8d7395959548c78a20fb2124988708.__wrapper$3227$6e8d7395959548c78a20fb2124988708$SnapshotComparisonTemplateRuleREI$2" constructor"s doesn"t have expected number of parameters
! Code:
! import com.indeni.server.rules.RuleContext
! import com.indeni.server.rules.library.{ConditionalRemediationSteps, SnapshotComparisonTemplateRule}
! import com.indeni.time.TimeSpan
!
! /**
!   *
!   */
!
! // Modified template based rule.
!
! case class CrossVendorSnmpCommunitiesComparisonREI(context: RuleContext) extends SnapshotComparisonTemplateRuleREI(context,



The same error with the statement that the template rule constructor doen't have the expecter number of parameters is printed out even when I included the original unmodified template rule in the file.


Looks like the rule compiler is triing to complite the template rule a normal rule and not as a template class.


Is there a way to overcome this behaviuor?

What are other ways currently used to test modified template rules?


import scala.util

case class TestClass(x: Int, y:Int) {
                           def test(): Int = x + y
}





I will take a look and get back to you

Loading rule dynamically is not 100% supported (and tested). In your case, you want to change the rule interval, I want to suggest 2 options to do that (none of them are currently implemented):

  1. Add a parameter to the rule template that'll allow to set the interval or;

  2. Bring back the feature which allows users to change the interval from the UI


Would love to hear your thoughts.


FYI