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: _*) ) } }