From 618f2c87e877ecb00818894a8313fe713af4e8e7 Mon Sep 17 00:00:00 2001 From: "Francois @fanf42 Armand" Date: Mon, 30 May 2016 16:19:16 +0200 Subject: [PATCH] ISsue #8409: WIP. Add the missing case for UnexpectedUnknownVersion --- .../domain/logger/ComplianceDebugLogger.scala | 32 ++--- .../services/reports/ExecutionBatch.scala | 119 ++++++++++++------ .../reports/ReportingServiceImpl.scala | 6 +- .../services/reports/ExecutionBatchTest.scala | 55 +++++++- .../rudder/web/services/ReportDisplayer.scala | 34 ++--- 5 files changed, 171 insertions(+), 75 deletions(-) diff --git a/rudder-core/src/main/scala/com/normation/rudder/domain/logger/ComplianceDebugLogger.scala b/rudder-core/src/main/scala/com/normation/rudder/domain/logger/ComplianceDebugLogger.scala index 4fa259ea81c..aded1dba83e 100644 --- a/rudder-core/src/main/scala/com/normation/rudder/domain/logger/ComplianceDebugLogger.scala +++ b/rudder-core/src/main/scala/com/normation/rudder/domain/logger/ComplianceDebugLogger.scala @@ -84,10 +84,10 @@ object ComplianceDebugLogger extends Logger { implicit class RunAndConfigInfoToLog(c: RunAndConfigInfo) { val logDetails: String = c match { - case NoRunNoInit => + case NoRunNoExpectedReport => "expected NodeConfigId: not found | last run: not found" - case VersionNotFound(lastRunDateTime, lastRunConfigId) => + case NoExpectedReport(lastRunDateTime, lastRunConfigId) => s"expected NodeConfigId: not found |"+ s" last run: nodeConfigId: ${lastRunConfigId.fold("none")(_.value)} received at ${lastRunDateTime}" @@ -99,31 +99,31 @@ object ComplianceDebugLogger extends Logger { s"until ${expirationDateTime} expected NodeConfigId: ${expectedConfigId.toLog} |"+ s" last run: ${optLastRun.fold("none (or too old)")(x => s"nodeConfigId: ${x._2.toLog} received at ${x._1}")}" - case UnexpectedVersion(lastRunDateTime, lastRunConfigId, lastRunExpiration, expectedConfigId, expectedExpiration) => + case UnexpectedVersion(lastRunDateTime, Some(lastRunConfigInfo), lastRunExpiration, expectedConfigId, expectedExpiration) => s"expected NodeConfigId: ${expectedConfigId.toLog} |"+ - s" last run: nodeConfigId: ${lastRunConfigId.toLog} received at ${lastRunDateTime} |"+ + s" last run: nodeConfigInfo: ${lastRunConfigInfo.toLog} received at ${lastRunDateTime} |"+ s" expired at ${lastRunExpiration}" - case UnexpectedNoVersion(lastRunDateTime, lastRunConfigId, lastRunExpiration, expectedConfigId, expectedExpiration) => - s"expected NodeConfigId: ${expectedConfigId.toLog} |"+ + case UnexpectedNoVersion(lastRunDateTime, Some(lastRunConfigInfo), lastRunExpiration, expectedConfigInfo, expectedExpiration) => + s"expected NodeConfigId: ${expectedConfigInfo.toLog} |"+ s" last run: no configId, received at ${lastRunDateTime} |"+ s" expired at ${lastRunExpiration}" - case ComputeCompliance(lastRunDateTime, lastRunConfigId, expectedConfigId, expirationDateTime, missingStatus) => - s"expected NodeConfigId: ${expectedConfigId.toLog} |"+ - s" last run: nodeConfigId: ${lastRunConfigId.toLog} received at ${lastRunDateTime} |"+ + case x@ComputeCompliance(lastRunDateTime, expectedConfigInfo, expirationDateTime, missingStatus) => + s"expected NodeConfigId: ${expectedConfigInfo.toLog} |"+ + s" last run: nodeConfigId: ${x.lastRunConfigId.value} received at ${lastRunDateTime} |"+ s" expired at ${expirationDateTime}" } val logName = c match { - case NoRunNoInit => "NoRunNoInit" - case _: VersionNotFound => "VersionNotFound" - case _: NoReportInInterval => "NoReportInInterval" - case _: Pending => "Pending" - case _: UnexpectedVersion => "UnexpectedVersion" - case _: UnexpectedNoVersion => "UnexpectedNoVersion" - case _: ComputeCompliance => "ComputeCompliance" + case NoRunNoExpectedReport => "NoRunNoRunNoExpectedReport" + case _: NoExpectedReport => "NoRunNoExpectedReport" + case _: NoReportInInterval => "NoReportInInterval" + case _: Pending => "Pending" + case _: UnexpectedVersion => "UnexpectedVersion" + case _: UnexpectedNoVersion => "UnexpectedNoVersion" + case _: ComputeCompliance => "ComputeCompliance" } val toLog: String = logName + ": " + logDetails diff --git a/rudder-core/src/main/scala/com/normation/rudder/services/reports/ExecutionBatch.scala b/rudder-core/src/main/scala/com/normation/rudder/services/reports/ExecutionBatch.scala index e007af8f8e1..9b2f1b8cd9a 100644 --- a/rudder-core/src/main/scala/com/normation/rudder/services/reports/ExecutionBatch.scala +++ b/rudder-core/src/main/scala/com/normation/rudder/services/reports/ExecutionBatch.scala @@ -107,7 +107,7 @@ sealed trait RunAndConfigInfo sealed trait ErrorNoConfigData extends RunAndConfigInfo sealed trait ExpectedConfigAvailable extends RunAndConfigInfo { - def expectedConfigId: NodeConfigIdInfo + def expectedConfigInfo: NodeConfigIdInfo } sealed trait NoReport extends ExpectedConfigAvailable @@ -128,9 +128,16 @@ sealed trait LastRunAvailable extends RunAndConfigInfo { //config id because we must have some logic to find WHAT //is the applicable version in all case - it's one of the //major goal of ExecutionBatch#computeNodeRunInfo - def lastRunConfigId: NodeConfigIdInfo + def lastRunConfigId: NodeConfigId + + // most of the time, we do have the corresponding + // configId in base. But not always, for example + // if the node configId was corrupted + def lastRunConfigInfo: Option[NodeConfigIdInfo] } + + /** * The date and time when the status expires. * Depends of the compliance mode and the heartbeat period @@ -151,7 +158,7 @@ sealed trait ExpiringStatus extends RunAndConfigInfo { /* * Really, that node exists ? */ -case object NoRunNoInit extends ErrorNoConfigData +case object NoRunNoExpectedReport extends ErrorNoConfigData /* * We don't have the needed configId in the expected @@ -160,7 +167,7 @@ case object NoRunNoInit extends ErrorNoConfigData * (it is some weird data lost in the server, or a node * not yet initialized) */ -case class VersionNotFound( +case class NoExpectedReport( lastRunDateTime: DateTime , lastRunConfigId: Option[NodeConfigId] ) extends ErrorNoConfigData @@ -171,13 +178,13 @@ case class VersionNotFound( * some but too old for our situation) */ case class NoReportInInterval( - expectedConfigId: NodeConfigIdInfo + expectedConfigInfo: NodeConfigIdInfo ) extends NoReport case class Pending( - expectedConfigId : NodeConfigIdInfo + expectedConfigInfo : NodeConfigIdInfo , optLastRun : Option[(DateTime, NodeConfigIdInfo)] , expirationDateTime : DateTime , missingReportStatus: ReportType @@ -191,11 +198,13 @@ case class Pending( */ case class UnexpectedVersion( lastRunDateTime : DateTime - , lastRunConfigId : NodeConfigIdInfo + , lastRunConfigInfo : Some[NodeConfigIdInfo] , lastRunExpiration : DateTime - , expectedConfigId : NodeConfigIdInfo + , expectedConfigInfo: NodeConfigIdInfo , expectedExpiration: DateTime -) extends Unexpected with LastRunAvailable +) extends Unexpected with LastRunAvailable { + val lastRunConfigId = lastRunConfigInfo.get.configId +} /** * A case where we have a run without version, @@ -204,20 +213,39 @@ case class UnexpectedVersion( */ case class UnexpectedNoVersion( lastRunDateTime : DateTime - , lastRunConfigId : NodeConfigIdInfo + , lastRunConfigInfo : Some[NodeConfigIdInfo] , lastRunExpiration : DateTime - , expectedConfigId : NodeConfigIdInfo + , expectedConfigInfo: NodeConfigIdInfo , expectedExpiration: DateTime -) extends Unexpected with LastRunAvailable +) extends Unexpected with LastRunAvailable { + val lastRunConfigId = lastRunConfigInfo.get.configId +} + +/** + * A case where we have a run with a version, + * but we didn't find it in db, + * but we really should, because versions are init + * in the server for that node + */ +case class UnexpectedUnknowVersion( + lastRunDateTime : DateTime + , lastRunConfigId : NodeConfigId + , expectedConfigInfo: NodeConfigIdInfo + , expectedExpiration: DateTime +) extends Unexpected with LastRunAvailable { + val lastRunConfigInfo = None +} case class ComputeCompliance( lastRunDateTime : DateTime - , lastRunConfigId : NodeConfigIdInfo - , expectedConfigId : NodeConfigIdInfo + , expectedConfigInfo : NodeConfigIdInfo , expirationDateTime : DateTime , missingReportStatus: ReportType -) extends Ok with LastRunAvailable with ExpiringStatus with MissingReportStatus +) extends Ok with LastRunAvailable with ExpiringStatus with MissingReportStatus { + val lastRunConfigId = expectedConfigInfo.configId + val lastRunConfigInfo = Some(expectedConfigInfo) +} @@ -320,6 +348,7 @@ object ExecutionBatch extends Loggable { } val now = DateTime.now + nodeIds.foreach { case (nodeId, runInfos) => ComplianceDebugLogger.node(nodeId).debug(s"Node run configuration: ${(nodeId, complianceMode, runInfos).toLog }") } @@ -346,12 +375,12 @@ object ExecutionBatch extends Loggable { (optRun, optInfo) match { case (None, None) => - runType("", NoRunNoInit) + runType("", NoRunNoExpectedReport) // There is no run for this node case (None, Some(configs)) => if(configs.isEmpty) { - runType("nodeId exists in DB but has no version (due to cleaning?)", NoRunNoInit) + runType("nodeId exists in DB but has no version (due to cleaning?)", NoRunNoExpectedReport) } else { val currentConfig = configs.maxBy(_.creation.getMillis) val expireTime = currentConfig.creation.plus(updateValidityTime(intervalInfo)) @@ -392,12 +421,12 @@ object ExecutionBatch extends Loggable { (runToCheck, nodeConfigIdInfos) match { case (AgentRun(AgentRunId(_, t), optConfigId, _, _), None) => - runType("need to regenerate policies?", VersionNotFound(t, optConfigId)) + runType("need to regenerate policies?", NoExpectedReport(t, optConfigId)) //The run does not have a configId: migration, new nodes, etc. case (AgentRun(AgentRunId(_, t), None, _, _), Some(configs)) => if(configs.isEmpty) { - runType("nodeId exists in DB but has no version (due to cleaning?)", VersionNotFound(t, None)) + runType("nodeId exists in DB but has no version (due to cleaning?)", NoExpectedReport(t, None)) } else { /* * Here, we want to check two things: @@ -415,7 +444,7 @@ object ExecutionBatch extends Loggable { val currentConfig = configs.maxBy( _.creation.getMillis) val currentExpiration = currentConfig.creation.plus(updateValidityDuration) runType(s"node send reports without nodeConfigId but the oldest configId (${oldestConfigId.configId.value}) expired since ${oldestExpiration})" - , UnexpectedNoVersion(t, oldestConfigId, oldestExpiration, currentConfig, currentExpiration) + , UnexpectedNoVersion(t, Some(oldestConfigId), oldestExpiration, currentConfig, currentExpiration) ) } else { val currentConfigId = configs.maxBy( _.creation.getMillis ) @@ -437,17 +466,20 @@ object ExecutionBatch extends Loggable { if(configs.isEmpty) { //error: we have a MISSING config id. Contrary to the case where any config id is missing //for the node, here we have a BAD id. - runType("nodeId exists in DB but has no version (due to cleaning?)", VersionNotFound(t, Some(rv))) + runType("nodeId exists in DB but has no version (due to cleaning?)", NoExpectedReport(t, Some(rv))) } else { configs.find { i => i.configId == rv} match { case None => - //it's a bad version. - runType(s"nodeId exists in DB and has configId, but not ${rv.value} (due to cleaning?)", VersionNotFound(t, Some(rv))) + //it's a bad version, but we have config id in DB => likelly a corruption on node + val expectedVersion = configs.maxBy( _.creation.getMillis ) + //expirationTime is the date after which we must have gotten a report for that version + val expirationTime = expectedVersion.creation.plus(updateValidityDuration) - case Some(v) => //nominal case ! - //check if the run is not too old for the version, i.e if endOflife + grace is before run - val currentConfigId = configs.maxBy( _.creation.getMillis ) + runType(s"nodeId exists in DB and has configId, expected configId is ${expectedVersion.configId.value}, but ${rv.value} was not found (node corruption?)", + UnexpectedUnknowVersion(t, rv, expectedVersion, expirationTime) + ) + case Some(v) => //nominal case ! v.endOfLife match { case None => //nominal (bis)! The node is answering to current config ! @@ -458,11 +490,14 @@ object ExecutionBatch extends Loggable { ) } else { //nominal case runType(s"Last run at ${t} is for the correct configId ${v.configId.value} and not expired, compute compliance" - , ComputeCompliance(t, v, currentConfigId, expirationTime, missingReportType) + , ComputeCompliance(t, v, expirationTime, missingReportType) ) } case Some(eol) => + //check if the run is not too old for the version, i.e if endOflife + grace is before run + val currentConfigId = configs.maxBy( _.creation.getMillis ) + // a more recent version exists, so we are either awaiting reports // for it, or in some error state (completely unexpected version or "just" no report val eolExpiration = eol.plus(updateValidityDuration) @@ -470,7 +505,7 @@ object ExecutionBatch extends Loggable { if(eolExpiration.isBefore(t)) { //we should have had a more recent run runType(s"node sent reports at ${t} for configId ${rv.value} (which expired at ${eol}) but should have been for configId ${currentConfigId.configId.value}" - , UnexpectedVersion(t, v, eolExpiration, currentConfigId, expirationTime) + , UnexpectedVersion(t, Some(v), eolExpiration, currentConfigId, expirationTime) ) } else { if(expirationTime.isBefore(timeToCompareTo)) { @@ -522,7 +557,7 @@ object ExecutionBatch extends Loggable { } } - def buildUnexpectedVersion(runTime: DateTime, runVersion: NodeConfigIdInfo, runExpiration: DateTime, expectedVersion: NodeConfigIdInfo, expectedExpiration: DateTime, nodeStatusReports: Seq[ResultReports]) = { + def buildUnexpectedVersion(runTime: DateTime, runVersion: Option[NodeConfigIdInfo], runExpiration: DateTime, expectedVersion: NodeConfigIdInfo, expectedExpiration: DateTime, nodeStatusReports: Seq[ResultReports]) = { //mark all report of run unexpected, //all expected missing buildRuleNodeStatusReport( @@ -530,7 +565,7 @@ object ExecutionBatch extends Loggable { , getExpectedReports(expectedVersion.configId) , MissingReportType ) ++ - buildUnexpectedReports(MergeInfo(nodeId, Some(runTime), Some(runVersion.configId), runExpiration), nodeStatusReports) + buildUnexpectedReports(MergeInfo(nodeId, Some(runTime), runVersion.map(_.configId), runExpiration), nodeStatusReports) } //only interesting reports: for that node, with a status @@ -540,12 +575,12 @@ object ExecutionBatch extends Loggable { val ruleNodeStatusReports = runInfo match { - case ComputeCompliance(lastRunDateTime, lastRunConfigId, expectedConfigId, expirationTime, missingReportStatus) => - ComplianceDebugLogger.node(nodeId).trace(s"Using merge/compare strategy between last reports from run ${lastRunConfigId.toLog} and expect reports ${expectedConfigId.toLog}") + case ComputeCompliance(lastRunDateTime, expectedConfigId, expirationTime, missingReportStatus) => + ComplianceDebugLogger.node(nodeId).trace(s"Using merge/compare strategy between last reports from run at ${lastRunDateTime} and expect reports ${expectedConfigId.toLog}") mergeCompareByRule( - MergeInfo(nodeId, Some(lastRunDateTime), Some(lastRunConfigId.configId), expirationTime) + MergeInfo(nodeId, Some(lastRunDateTime), Some(expectedConfigId.configId), expirationTime) , nodeStatusReports - , getExpectedReports(lastRunConfigId.configId) + , getExpectedReports(expectedConfigId.configId) , getExpectedReports(expectedConfigId.configId) , missingReportStatus ) @@ -588,22 +623,26 @@ object ExecutionBatch extends Loggable { , NoAnswerReportType ) - case UnexpectedVersion(runTime, runVersion, runExpiration, expectedVersion, expectedExpiration) => + case UnexpectedVersion(runTime, Some(runVersion), runExpiration, expectedVersion, expectedExpiration) => ComplianceDebugLogger.node(nodeId).warn(s"Received a run at ${runTime} for node '${nodeId.value}' with configId '${runVersion.configId.value}' but that node should be sending reports for configId ${expectedVersion.configId.value}") - buildUnexpectedVersion(runTime, runVersion, runExpiration, expectedVersion, expectedExpiration, nodeStatusReports) + buildUnexpectedVersion(runTime, Some(runVersion), runExpiration, expectedVersion, expectedExpiration, nodeStatusReports) - case UnexpectedNoVersion(runTime, runVersion, runExpiration, expectedVersion, expectedExpiration) => //same as unextected, different log + case UnexpectedNoVersion(runTime, Some(runVersion), runExpiration, expectedVersion, expectedExpiration) => //same as unextected, different log ComplianceDebugLogger.node(nodeId).warn(s"Received a run at ${runTime} for node '${nodeId.value}' without any configId but that node should be sending reports for configId ${expectedVersion.configId.value}") - buildUnexpectedVersion(runTime, runVersion, runExpiration, expectedVersion, expectedExpiration, nodeStatusReports) + buildUnexpectedVersion(runTime, Some(runVersion), runExpiration, expectedVersion, expectedExpiration, nodeStatusReports) + + case UnexpectedUnknowVersion(runTime, runId, expectedVersion, expectedExpiration) => //same as unextected, different log + ComplianceDebugLogger.node(nodeId).warn(s"Received a run at ${runTime} for node '${nodeId.value}' configId '${runId.value}' which is not known by Rudder, and that node should be sending reports for configId ${expectedVersion.configId.value}") + buildUnexpectedVersion(runTime, None, runTime, expectedVersion, expectedExpiration, nodeStatusReports) - case VersionNotFound(runTime, optConfigId) => + case NoExpectedReport(runTime, optConfigId) => // these reports where not expected ComplianceDebugLogger.node(nodeId).warn(s"Node '${nodeId.value}' sent reports for run at '${runInfo}' (with ${ optConfigId.map(x => s" configuration ID: '${x.value}'").getOrElse(" no configuration ID") }). No expected configuration matches these reports.") buildUnexpectedReports(MergeInfo(nodeId, Some(runTime), optConfigId, END_OF_TIME), nodeStatusReports) - case NoRunNoInit => + case NoRunNoExpectedReport => /* * Really, this node exists ? Shouldn't we just declare Ragnarök at that point ? */ diff --git a/rudder-core/src/main/scala/com/normation/rudder/services/reports/ReportingServiceImpl.scala b/rudder-core/src/main/scala/com/normation/rudder/services/reports/ReportingServiceImpl.scala index 7823b1d16cc..d01a2595676 100644 --- a/rudder-core/src/main/scala/com/normation/rudder/services/reports/ReportingServiceImpl.scala +++ b/rudder-core/src/main/scala/com/normation/rudder/services/reports/ReportingServiceImpl.scala @@ -274,9 +274,9 @@ trait DefaultFindRuleNodeStatusReports extends ReportingService { runInfos <- getNodeRunInfos(nodeIds) // that gives us configId for runs, and expected configId (some may be in both set) - expectedConfigId = runInfos.collect { case (nodeId, x:ExpectedConfigAvailable) => NodeAndConfigId(nodeId, x.expectedConfigId.configId) } + expectedConfigIds = runInfos.collect { case (nodeId, x:ExpectedConfigAvailable) => NodeAndConfigId(nodeId, x.expectedConfigInfo.configId) } lastrunConfigId = runInfos.collect { - case (nodeId, x:LastRunAvailable) => NodeAndConfigId(nodeId, x.lastRunConfigId.configId) + case (nodeId, x:LastRunAvailable) => NodeAndConfigId(nodeId, x.lastRunConfigId) case (nodeId, Pending(_, Some(run), _, _)) => NodeAndConfigId(nodeId, run._2.configId) } @@ -284,7 +284,7 @@ trait DefaultFindRuleNodeStatusReports extends ReportingService { _ = TimingDebugLogger.debug(s"Compliance: get run infos: ${t1-t0}ms") // so now, get all expected reports for these config id - allExpectedReports <- confExpectedRepo.getExpectedReports(expectedConfigId.toSet++lastrunConfigId, ruleIds) + allExpectedReports <- confExpectedRepo.getExpectedReports(expectedConfigIds.toSet++lastrunConfigId, ruleIds) t2 = System.currentTimeMillis _ = TimingDebugLogger.debug(s"Compliance: get expected reports: ${t2-t1}ms") diff --git a/rudder-core/src/test/scala/com/normation/rudder/services/reports/ExecutionBatchTest.scala b/rudder-core/src/test/scala/com/normation/rudder/services/reports/ExecutionBatchTest.scala index 097085fd218..35ea5071b65 100644 --- a/rudder-core/src/test/scala/com/normation/rudder/services/reports/ExecutionBatchTest.scala +++ b/rudder-core/src/test/scala/com/normation/rudder/services/reports/ExecutionBatchTest.scala @@ -55,6 +55,10 @@ import com.normation.rudder.domain.policies.SerialedRuleId import com.normation.rudder.domain.policies.SerialedRuleId import com.normation.rudder.domain.policies.SerialedRuleId import org.specs2.specification.Fragments +import com.normation.rudder.reports.ResolvedAgentRunInterval +import org.joda.time.Duration +import com.normation.rudder.reports.execution.AgentRunId +import com.normation.rudder.reports.execution.AgentRun @RunWith(classOf[JUnitRunner]) class ExecutionBatchTest extends Specification { @@ -91,8 +95,8 @@ class ExecutionBatchTest extends Specification { val runTime = reportsParam.headOption.map( _.executionTimestamp).getOrElse(DateTime.now) val info = NodeConfigIdInfo(expected.keySet.head, DateTime.now.minusDays(1), None) val runInfo = complianceMode match { - case FullCompliance => ComputeCompliance(runTime, info, info, runTime.plusMinutes(5), MissingReportType) - case ChangesOnly(heartbeatPeriod) => ComputeCompliance(runTime, info, info, runTime.plusMinutes(5), SuccessReportType) + case FullCompliance => ComputeCompliance(runTime, info, runTime.plusMinutes(5), MissingReportType) + case ChangesOnly(heartbeatPeriod) => ComputeCompliance(runTime, info, runTime.plusMinutes(5), SuccessReportType) } ExecutionBatch.getNodeStatusReports(nodeId, runInfo, expected, reportsParam) @@ -107,6 +111,53 @@ class ExecutionBatchTest extends Specification { val one = NodeId("one") + /* + * Test the general run information (do we have a run, is it an expected version, etc) + */ + "A node, an expected version, and a run" should { + + // general configuration option: node id, run period... + val root = NodeId("root") + + val insertionId = 102030 + val isCompleted = true + + val nodeConfigIdInfos = Map(root -> ResolvedAgentRunInterval(Duration.parse("PT300S"),1)) + val mode = FullCompliance + + val now = DateTime.now() + + // known configuration in Rudder database + val startConfig0 = now.minusMinutes(60) + val startConfig1 = now.minusMinutes(37) + val startConfig2 = now.minusMinutes(16) + val configId0 = NodeConfigId("-1000") + val configId1 = NodeConfigId( "2000") + val configId2 = NodeConfigId("-4000") + + val config0 = NodeConfigIdInfo( configId0, startConfig0, Some(startConfig1) ) + val config1 = NodeConfigIdInfo( configId1, startConfig1, Some(startConfig2) ) + val config2 = NodeConfigIdInfo( configId2, startConfig2, None ) + + val knownConfigs = Map(root -> Some(Vector(config0, config1, config2))) + + "have no report in interval if the run is older than 10 minutes" in { + val runs = Map(root -> Some(AgentRun(AgentRunId(root, now.minusMinutes(11)), Some(configId2), isCompleted, insertionId))) + ExecutionBatch.computeNodesRunInfo(nodeConfigIdInfos, runs, knownConfigs, mode) === Map(root -> NoReportInInterval(config2)) + } + + "raise UnexpectedVersion when the run version is not know" in { + val runTime = now.minusMinutes(3) + val epoch = new DateTime(0) + val runs = Map(root -> Some(AgentRun(AgentRunId(root, runTime), Some(NodeConfigId("123456")), isCompleted, insertionId))) + ExecutionBatch.computeNodesRunInfo(nodeConfigIdInfos, runs, knownConfigs, mode) === Map(root -> + UnexpectedVersion(runTime, Some(NodeConfigIdInfo(NodeConfigId("123456"), epoch, Some(epoch))), epoch, config2, startConfig2.plusMinutes(10)) + ) + } + } + + + //Test the component part "A component, with two different keys" should { val executionTimestamp = new DateTime() diff --git a/rudder-web/src/main/scala/com/normation/rudder/web/services/ReportDisplayer.scala b/rudder-web/src/main/scala/com/normation/rudder/web/services/ReportDisplayer.scala index 51fead06bc5..3724ee14497 100644 --- a/rudder-web/src/main/scala/com/normation/rudder/web/services/ReportDisplayer.scala +++ b/rudder-web/src/main/scala/com/normation/rudder/web/services/ReportDisplayer.scala @@ -137,18 +137,18 @@ class ReportDisplayer( def explainCompliance(info: RunAndConfigInfo): NodeSeq = { val dateFormat = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss") - def currentConfigId(expectedConfigId: NodeConfigIdInfo) = { - s"Current configuration ID for the node is '${expectedConfigId.configId.value}' (generated on ${expectedConfigId.creation.toString(dateFormat)})" + def currentConfigId(expectedConfigInfo: NodeConfigIdInfo) = { + s"Current configuration ID for the node is '${expectedConfigInfo.configId.value}' (generated on ${expectedConfigInfo.creation.toString(dateFormat)})" } info match { - case ComputeCompliance(lastRunDateTime, lastRunConfigId, expectedConfigId, expirationDateTime, missingReportStatus) => + case ComputeCompliance(lastRunDateTime, expectedConfigInfo, expirationDateTime, missingReportStatus) => ( -

{currentConfigId(expectedConfigId)}.

+

{currentConfigId(expectedConfigInfo)}.

Last complete agent run has correct configuration ID, started on node at {lastRunDateTime.toString(dateFormat)}.

) - case Pending(expectedConfigId, optLastRun, expirationDateTime, missingReportStatus) => + case Pending(expectedConfigInfo, optLastRun, expirationDateTime, missingReportStatus) => val runInfo = optLastRun match { case None => "No recent runs received for this node" case Some((date, id)) => @@ -159,22 +159,22 @@ class ReportDisplayer( }) } ( -

{currentConfigId(expectedConfigId)}.

+

{currentConfigId(expectedConfigInfo)}.

The node is expected to send a report for that configuration ID before {expirationDateTime.toString(dateFormat)} but no reports have been received yet. If that state persists, check that the agent is running.

{runInfo}

) - case NoReportInInterval(expectedConfigId) => + case NoReportInInterval(expectedConfigInfo) => ( -

{currentConfigId(expectedConfigId)}.

+

{currentConfigId(expectedConfigInfo)}.

No recent runs were received for node, and the grace period to receive them expired.

) - case NoRunNoInit => + case NoRunNoExpectedReport =>

No configuration ID and no run received for that node. It may have been accepted after the last policy regeneration.

- case VersionNotFound(lastRunDateTime, lastRunConfigId) => + case NoExpectedReport(lastRunDateTime, lastRunConfigId) => val configIdmsg = lastRunConfigId match { case None => "without any configuration ID, when one was required" case Some(id) => s"with configuration ID '${id.value}' that is unknown in Rudder base" @@ -184,20 +184,26 @@ class ReportDisplayer( the node is sending a corrupted configuration ID. You should update node policies with "rudder agent update -f", and if the problem persists, try to regenerate policies with the "clear cache" action.

- case UnexpectedVersion(lastRunDateTime, lastRunConfigId, lastRunExpiration, expectedConfigId, expectedExpiration) => + case UnexpectedVersion(lastRunDateTime, Some(lastRunConfigInfo), lastRunExpiration, expectedConfigInfo, expectedExpiration) => ( -

{currentConfigId(expectedConfigId)} but we received a run, started at {lastRunDateTime.toString(dateFormat)}, with a different configuration ID ({lastRunConfigId.configId.value}).

+

{currentConfigId(expectedConfigInfo)} but we received a run, started at {lastRunDateTime.toString(dateFormat)}, with a different configuration ID ({expectedConfigInfo.configId.value}).

The time since generation of the new configuration is longer than the grace period, so all received reports will be mark as unexpected. Please check that the node is able to get its new policies by running 'rudder agent update' on the node.

) - case UnexpectedNoVersion(lastRunDateTime, lastRunConfigId, lastRunExpiration, expectedConfigId, expectedExpiration) => + case UnexpectedNoVersion(lastRunDateTime, Some(lastRunConfigInfo), lastRunExpiration, expectedConfigInfo, expectedExpiration) => ( -

{currentConfigId(expectedConfigId)} but we received a run, started at {lastRunDateTime.toString(dateFormat)}, without any configuration ID.

+

{currentConfigId(expectedConfigInfo)} but we received a run, started at {lastRunDateTime.toString(dateFormat)}, without any configuration ID.

The time since generation of the new configuration is longer than the grace period, so all received reports will be marked as unexpected. Please check that the node is able to get its new policies.

) + case UnexpectedUnknowVersion(lastRunDateTime, lastRunConfigId, expectedConfigInfo, expectedExpiration) => + ( +

{currentConfigId(expectedConfigInfo)} but we received a run, started at {lastRunDateTime.toString(dateFormat)}, without any configuration ID.

+

The time since generation of the new configuration is longer than the grace period, so all received reports will be marked as unexpected. + Please check that the node is able to get its new policies.

+ ) } }