diff options
28 files changed, 875 insertions, 403 deletions
diff --git a/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantHealthCheck.java b/models/src/main/java/org/onap/policy/clamp/controlloop/models/controlloop/concepts/ControlLoopInfo.java index e472e15fe..bdf894125 100644 --- a/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantHealthCheck.java +++ b/models/src/main/java/org/onap/policy/clamp/controlloop/models/controlloop/concepts/ControlLoopInfo.java @@ -18,39 +18,31 @@ * ============LICENSE_END========================================================= */ -package org.onap.policy.clamp.controlloop.models.messages.dmaap.participant; +package org.onap.policy.clamp.controlloop.models.controlloop.concepts; -import lombok.Getter; -import lombok.Setter; +import lombok.Data; +import lombok.NoArgsConstructor; import lombok.ToString; -import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantState; /** - * Class to represent the PARTICIPANT_HEALTHCHECK message that the control loop runtime will send to - * participants to change the state of a control loop they are running. + * Class to represent a control loop info instance. */ -@Getter -@Setter -@ToString(callSuper = true) -public class ParticipantHealthCheck extends ParticipantMessage { - private ParticipantState state; +@NoArgsConstructor +@Data +@ToString +public class ControlLoopInfo { - /** - * Constructor for instantiating ParticipantHealthCheck class with message name. - * - */ - public ParticipantHealthCheck() { - super(ParticipantMessageType.PARTICIPANT_HEALTH_CHECK); - } + private ControlLoopState state = ControlLoopState.UNINITIALISED; + + private ControlLoopStatistics controlLoopStatistics; /** - * Constructs the object, making a deep copy. + * Copy constructor, does a deep copy but as all fields here are immutable, it's just a regular copy. * - * @param source source from which to copy + * @param otherElement the other element to copy from */ - public ParticipantHealthCheck(ParticipantHealthCheck source) { - super(source); - - this.state = source.state; + public ControlLoopInfo(final ControlLoopInfo otherElement) { + this.state = otherElement.state; + this.controlLoopStatistics = otherElement.controlLoopStatistics; } } diff --git a/models/src/main/java/org/onap/policy/clamp/controlloop/models/controlloop/concepts/ControlLoopStatistics.java b/models/src/main/java/org/onap/policy/clamp/controlloop/models/controlloop/concepts/ControlLoopStatistics.java new file mode 100644 index 000000000..685947b13 --- /dev/null +++ b/models/src/main/java/org/onap/policy/clamp/controlloop/models/controlloop/concepts/ControlLoopStatistics.java @@ -0,0 +1,50 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.models.controlloop.concepts; + +import java.time.Instant; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; + +@NoArgsConstructor +@Data +@ToString +public class ControlLoopStatistics { + + @NonNull + private ToscaConceptIdentifier controlLoopId; + + @NonNull + private Instant timeStamp; + + @NonNull + private ClElementStatisticsList clElementStatisticsList; + + private long eventCount; + private long lastExecutionTime; + private double averageExecutionTime; + private long upTime; + private long lastEnterTime; + private long lastStart; +} diff --git a/models/src/main/java/org/onap/policy/clamp/controlloop/models/controlloop/concepts/ParticipantHealthStatus.java b/models/src/main/java/org/onap/policy/clamp/controlloop/models/controlloop/concepts/ParticipantHealthStatus.java index 0cf41c9cd..e7c5fecda 100644 --- a/models/src/main/java/org/onap/policy/clamp/controlloop/models/controlloop/concepts/ParticipantHealthStatus.java +++ b/models/src/main/java/org/onap/policy/clamp/controlloop/models/controlloop/concepts/ParticipantHealthStatus.java @@ -43,5 +43,10 @@ public enum ParticipantHealthStatus { /** * The health status of the Participant is unknown. */ - UNKNOWN + UNKNOWN, + + /** + * The health status of the Participant is off line. + */ + OFF_LINE } diff --git a/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ControlLoopAck.java b/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ControlLoopAck.java index 6a72ec1f2..3411a0369 100644 --- a/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ControlLoopAck.java +++ b/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ControlLoopAck.java @@ -28,7 +28,6 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; import org.apache.commons.lang3.tuple.Pair; -import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElementDefinition; import org.onap.policy.models.base.PfUtils; import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; @@ -59,7 +58,7 @@ public class ControlLoopAck extends ParticipantAckMessage { // A map with ControlLoopElementID as its key, and a pair of result and message as value per // ControlLoopElement. - private Map<UUID, Map<UUID, Boolean>> controlLoopResultMap = new LinkedHashMap<>(); + private Map<UUID, Pair<Boolean, String>> controlLoopResultMap = new LinkedHashMap<>(); /** * Constructor for instantiating ParticipantRegisterAck class with message name. @@ -79,7 +78,6 @@ public class ControlLoopAck extends ParticipantAckMessage { this.participantId = source.participantId; this.participantType = source.participantType; this.controlLoopId = source.controlLoopId; - this.controlLoopResultMap = PfUtils.mapMap(source.controlLoopResultMap, - clElementResultMap -> PfUtils.mapMap(clElementResultMap, UnaryOperator.identity())); + this.controlLoopResultMap = PfUtils.mapMap(source.controlLoopResultMap, UnaryOperator.identity()); } } diff --git a/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantAckMessage.java b/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantAckMessage.java index 1e53921dd..8b59a1801 100644 --- a/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantAckMessage.java +++ b/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantAckMessage.java @@ -24,6 +24,7 @@ import java.util.UUID; import lombok.Getter; import lombok.Setter; import lombok.ToString; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; /** * Class to represent participant Ack message. @@ -45,6 +46,16 @@ public class ParticipantAckMessage { private ParticipantMessageType messageType; /** + * Participant Type, or {@code null} for messages from participants. + */ + private ToscaConceptIdentifier participantType; + + /** + * Participant ID, or {@code null} for messages from participants. + */ + private ToscaConceptIdentifier participantId; + + /** * Constructor for instantiating a participant ack message class. * * @param messageType the message type @@ -63,5 +74,7 @@ public class ParticipantAckMessage { this.result = source.result; this.message = source.message; this.messageType = source.messageType; + this.participantType = source.participantType; + this.participantId = source.participantId; } } diff --git a/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantRegister.java b/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantRegister.java index 7319d99db..af0149189 100644 --- a/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantRegister.java +++ b/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantRegister.java @@ -23,10 +23,6 @@ package org.onap.policy.clamp.controlloop.models.messages.dmaap.participant; import lombok.Getter; import lombok.Setter; import lombok.ToString; -import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops; -import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantHealthStatus; -import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantState; -import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantStatistics; /** * Class to represent the PARTICIPANT_REGISTER message that all the participants send to control loop runtime. diff --git a/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantResponseDetails.java b/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantResponseDetails.java deleted file mode 100644 index 4c771b405..000000000 --- a/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantResponseDetails.java +++ /dev/null @@ -1,64 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.policy.clamp.controlloop.models.messages.dmaap.participant; - -import java.util.UUID; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -/** - * Class to represent participant response details. - */ -@Getter -@Setter -@ToString -@NoArgsConstructor -public class ParticipantResponseDetails { - - // The responseTo field should match the original request id in the request. - private UUID responseTo; - private ParticipantResponseStatus responseStatus; - private String responseMessage; - - /** - * Constructs the object as a response to. - * - * @param triggerMessage the message to which this is a response - */ - public ParticipantResponseDetails(ParticipantMessage triggerMessage) { - this.responseMessage = null; - this.responseStatus = ParticipantResponseStatus.FAIL; - this.responseTo = triggerMessage.getMessageId(); - } - - /** - * Constructs the object, making a deep copy. - * - * @param source source from which to copy - */ - public ParticipantResponseDetails(ParticipantResponseDetails source) { - this.responseMessage = source.responseMessage; - this.responseStatus = source.responseStatus; - this.responseTo = source.responseTo; - } -} diff --git a/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantStatus.java b/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantStatus.java index 5b9284243..c3e630681 100644 --- a/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantStatus.java +++ b/models/src/main/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantStatus.java @@ -20,13 +20,19 @@ package org.onap.policy.clamp.controlloop.models.messages.dmaap.participant; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; import lombok.Getter; import lombok.Setter; import lombok.ToString; -import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElementDefinition; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopInfo; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantHealthStatus; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantState; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantStatistics; +import org.onap.policy.models.base.PfUtils; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; /** * Class to represent the PARTICIPANT_STATUS message that all the participants send to the control loop runtime. @@ -35,21 +41,21 @@ import org.onap.policy.clamp.controlloop.models.controlloop.concepts.Participant @Setter @ToString(callSuper = true) public class ParticipantStatus extends ParticipantMessage { - // The response should be completed if this message is a response to a request from the Control Loop Runtime - private ParticipantResponseDetails response; // State and health status of the participant private ParticipantState state; private ParticipantHealthStatus healthStatus; - // Control Loops on the participant - private ControlLoops controlLoops; - // Participant statistics private ParticipantStatistics participantStatistics; - // Description. May be left {@code null}. - private String message; + // A map with Participant ID as its key, and a map of ControlLoopElements as value. + // Returned in response to ParticipantStatusReq only + private Map<ToscaConceptIdentifier, Map<UUID, ControlLoopElementDefinition>> + participantDefinitionUpdateMap = new LinkedHashMap<>(); + + // Map of ControlLoopInfo types indexed by ControlLoopId, one entry for each control loop + private Map<ToscaConceptIdentifier, ControlLoopInfo> controlLoopInfoMap; /** * Constructor for instantiating ParticipantStatus class with message name. @@ -69,8 +75,10 @@ public class ParticipantStatus extends ParticipantMessage { this.state = source.state; this.healthStatus = source.healthStatus; - this.message = source.message; - this.controlLoops = (source.controlLoops == null ? null : new ControlLoops(source.controlLoops)); - this.response = (source.response == null ? null : new ParticipantResponseDetails(source.response)); + this.participantStatistics = (source.participantStatistics == null ? null : new ParticipantStatistics()); + this.participantDefinitionUpdateMap = PfUtils.mapMap(source.participantDefinitionUpdateMap, + clElementDefinitionMap -> PfUtils.mapMap(clElementDefinitionMap, + ControlLoopElementDefinition::new)); + this.controlLoopInfoMap = PfUtils.mapMap(source.controlLoopInfoMap, ControlLoopInfo::new); } } diff --git a/models/src/test/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ControlLoopAckTest.java b/models/src/test/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ControlLoopAckTest.java index 8734a435c..d7d7e4327 100644 --- a/models/src/test/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ControlLoopAckTest.java +++ b/models/src/test/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ControlLoopAckTest.java @@ -26,6 +26,7 @@ import static org.onap.policy.clamp.controlloop.models.messages.dmaap.participan import java.util.Map; import java.util.UUID; +import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.Test; import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; @@ -48,8 +49,8 @@ class ControlLoopAckTest { orig.setParticipantId(id); orig.setParticipantType(id); - Map<UUID, Boolean> clElementResult = Map.of(UUID.randomUUID(), true); - final Map<UUID, Map<UUID, Boolean>> controlLoopResultMap = Map.of(UUID.randomUUID(), clElementResult); + Pair<Boolean, String> clElementResult = Pair.of(true, "ControlLoopElement result"); + final Map<UUID, Pair<Boolean, String>> controlLoopResultMap = Map.of(UUID.randomUUID(), clElementResult); orig.setControlLoopResultMap(controlLoopResultMap); orig.setResponseTo(UUID.randomUUID()); diff --git a/models/src/test/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantHealthCheckTest.java b/models/src/test/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantHealthCheckTest.java deleted file mode 100644 index 52f1cc480..000000000 --- a/models/src/test/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantHealthCheckTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.policy.clamp.controlloop.models.messages.dmaap.participant; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.Assert.assertEquals; -import static org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantMessageUtils.removeVariableFields; - -import java.time.Instant; -import java.util.UUID; -import org.junit.jupiter.api.Test; -import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantState; -import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; - -/** - * Test the copy constructor and other methods. - */ -class ParticipantHealthCheckTest { - - @Test - void testCopyConstructor() { - assertThatThrownBy(() -> new ParticipantHealthCheck(null)).isInstanceOf(NullPointerException.class); - - ParticipantHealthCheck orig = new ParticipantHealthCheck(); - - // verify with null values - assertEquals(removeVariableFields(orig.toString()), - removeVariableFields(new ParticipantHealthCheck(orig).toString())); - - // verify with all values - ToscaConceptIdentifier id = new ToscaConceptIdentifier(); - id.setName("id"); - id.setVersion("1.2.3"); - orig.setControlLoopId(id); - orig.setParticipantId(id); - orig.setMessageId(UUID.randomUUID()); - orig.setState(ParticipantState.ACTIVE); - orig.setTimestamp(Instant.ofEpochMilli(3000)); - - assertEquals(removeVariableFields(orig.toString()), - removeVariableFields(new ParticipantHealthCheck(orig).toString())); - } -} diff --git a/models/src/test/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantStatusTest.java b/models/src/test/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantStatusTest.java index 706e58b91..05cfdd78b 100644 --- a/models/src/test/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantStatusTest.java +++ b/models/src/test/java/org/onap/policy/clamp/controlloop/models/messages/dmaap/participant/ParticipantStatusTest.java @@ -25,10 +25,20 @@ import static org.junit.Assert.assertEquals; import static org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantMessageUtils.removeVariableFields; import java.time.Instant; +import java.util.List; +import java.util.Map; import java.util.UUID; import org.junit.jupiter.api.Test; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatistics; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatisticsList; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElementDefinition; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopInfo; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopStatistics; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantHealthStatus; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantState; import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; class ParticipantStatusTest { @@ -43,20 +53,64 @@ class ParticipantStatusTest { removeVariableFields(new ParticipantStatus(orig).toString())); // verify with all values - ToscaConceptIdentifier id = new ToscaConceptIdentifier(); - id.setName("id"); - id.setVersion("1.2.3"); + ToscaConceptIdentifier id = new ToscaConceptIdentifier("id", "1.2.3"); orig.setControlLoopId(id); orig.setParticipantId(id); + orig.setParticipantType(id); orig.setMessageId(UUID.randomUUID()); orig.setState(ParticipantState.ACTIVE); + orig.setHealthStatus(ParticipantHealthStatus.HEALTHY); orig.setTimestamp(Instant.ofEpochMilli(3000)); - final ParticipantResponseDetails resp = new ParticipantResponseDetails(); - resp.setResponseMessage("my-response"); - orig.setResponse(resp); + ControlLoopInfo clInfo = getControlLoopInfo(id); + orig.setControlLoopInfoMap(Map.of(id, clInfo)); + + ControlLoopElementDefinition clDefinition = getClElementDefinition(); + Map<UUID, ControlLoopElementDefinition> clElementDefinitionMap = Map.of(UUID.randomUUID(), clDefinition); + Map<ToscaConceptIdentifier, Map<UUID, ControlLoopElementDefinition>> + participantDefinitionUpdateMap = Map.of(id, clElementDefinitionMap); + orig.setParticipantDefinitionUpdateMap(participantDefinitionUpdateMap); assertEquals(removeVariableFields(orig.toString()), removeVariableFields(new ParticipantStatus(orig).toString())); } + + private ControlLoopInfo getControlLoopInfo(ToscaConceptIdentifier id) { + ControlLoopInfo clInfo = new ControlLoopInfo(); + clInfo.setState(ControlLoopState.PASSIVE2RUNNING); + + ControlLoopStatistics clStatistics = new ControlLoopStatistics(); + clStatistics.setControlLoopId(id); + clStatistics.setAverageExecutionTime(12345); + clStatistics.setEventCount(12345); + clStatistics.setLastEnterTime(12345); + clStatistics.setLastExecutionTime(12345); + clStatistics.setLastStart(12345); + clStatistics.setTimeStamp(Instant.ofEpochMilli(3000)); + clStatistics.setUpTime(12345); + ClElementStatisticsList clElementStatisticsList = new ClElementStatisticsList(); + ClElementStatistics clElementStatistics = new ClElementStatistics(); + clElementStatistics.setParticipantId(new ToscaConceptIdentifier("defName", "0.0.1")); + clElementStatistics.setTimeStamp(Instant.now()); + clElementStatisticsList.setClElementStatistics(List.of(clElementStatistics)); + clStatistics.setClElementStatisticsList(clElementStatisticsList); + + clInfo.setControlLoopStatistics(clStatistics); + return clInfo; + } + + private ControlLoopElementDefinition getClElementDefinition() { + ToscaServiceTemplate toscaServiceTemplate = new ToscaServiceTemplate(); + toscaServiceTemplate.setName("serviceTemplate"); + toscaServiceTemplate.setDerivedFrom("parentServiceTemplate"); + toscaServiceTemplate.setDescription("Description of serviceTemplate"); + toscaServiceTemplate.setVersion("1.2.3"); + + ControlLoopElementDefinition clDefinition = new ControlLoopElementDefinition(); + clDefinition.setId(UUID.randomUUID()); + clDefinition.setControlLoopElementToscaServiceTemplate(toscaServiceTemplate); + Map<String, String> commonPropertiesMap = Map.of("Prop1", "PropValue"); + clDefinition.setCommonPropertiesMap(commonPropertiesMap); + return clDefinition; + } } diff --git a/participant/participant-impl/participant-impl-dcae/src/test/java/org/onap/policy/clamp/controlloop/participant/dcae/main/rest/TestListenerUtils.java b/participant/participant-impl/participant-impl-dcae/src/test/java/org/onap/policy/clamp/controlloop/participant/dcae/main/rest/TestListenerUtils.java index bb1021db6..136d0e500 100644 --- a/participant/participant-impl/participant-impl-dcae/src/test/java/org/onap/policy/clamp/controlloop/participant/dcae/main/rest/TestListenerUtils.java +++ b/participant/participant-impl/participant-impl-dcae/src/test/java/org/onap/policy/clamp/controlloop/participant/dcae/main/rest/TestListenerUtils.java @@ -32,7 +32,6 @@ import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantState; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopStateChange; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopUpdate; -import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantHealthCheck; import org.onap.policy.common.utils.coder.Coder; import org.onap.policy.common.utils.coder.CoderException; import org.onap.policy.common.utils.coder.StandardCoder; @@ -170,29 +169,6 @@ public class TestListenerUtils { } /** - * Method to create ParticipantHealthCheck message. - * - * @return ParticipantHealthCheck message - */ - public static ParticipantHealthCheck createParticipantHealthCheckMsg() { - ToscaConceptIdentifier participantId = new ToscaConceptIdentifier(); - participantId.setName("DCAEParticipant0"); - participantId.setVersion("1.0.0"); - - ToscaConceptIdentifier controlLoopId = new ToscaConceptIdentifier(); - controlLoopId.setName("PMSHInstance0"); - controlLoopId.setVersion("1.0.0"); - - final ParticipantHealthCheck participantHealthCheckMsg = new ParticipantHealthCheck(); - participantHealthCheckMsg.setParticipantId(participantId); - participantHealthCheckMsg.setControlLoopId(controlLoopId); - participantHealthCheckMsg.setTimestamp(Instant.now()); - participantHealthCheckMsg.setState(ParticipantState.PASSIVE); - - return participantHealthCheckMsg; - } - - /** * Method to create ControlLoopUpdate using the arguments passed. * * @param jsonFilePath the path of the controlloop content diff --git a/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/controlloop/participant/policy/endtoend/ParticipantMessagesTest.java b/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/controlloop/participant/policy/endtoend/ParticipantMessagesTest.java index 093ac190c..aed03556a 100644 --- a/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/controlloop/participant/policy/endtoend/ParticipantMessagesTest.java +++ b/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/controlloop/participant/policy/endtoend/ParticipantMessagesTest.java @@ -24,15 +24,22 @@ import static org.assertj.core.api.Assertions.assertThatCode; import java.time.Instant; import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.UUID; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatistics; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatisticsList; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElementDefinition; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopInfo; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopStatistics; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantDeregister; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantDeregisterAck; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantRegister; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantRegisterAck; -import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantResponseDetails; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantUpdate; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantUpdateAck; @@ -45,6 +52,7 @@ import org.onap.policy.clamp.controlloop.participant.policy.main.utils.TestListe import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; import org.onap.policy.common.endpoints.event.comm.TopicSink; import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -146,9 +154,16 @@ class ParticipantMessagesTest { @Test void testParticipantStatusHeartbeat() throws Exception { final ParticipantStatus heartbeat = new ParticipantStatus(); - heartbeat.setMessage("ParticipantStatus message"); - heartbeat.setResponse(new ParticipantResponseDetails()); heartbeat.setParticipantId(getParticipantId()); + ControlLoopInfo clInfo = getControlLoopInfo(getControlLoopId()); + heartbeat.setControlLoopInfoMap(Map.of(getControlLoopId(), clInfo)); + + ControlLoopElementDefinition clDefinition = getClElementDefinition(); + Map<UUID, ControlLoopElementDefinition> clElementDefinitionMap = Map.of(UUID.randomUUID(), clDefinition); + Map<ToscaConceptIdentifier, Map<UUID, ControlLoopElementDefinition>> + participantDefinitionUpdateMap = Map.of(getParticipantId(), clElementDefinitionMap); + heartbeat.setParticipantDefinitionUpdateMap(participantDefinitionUpdateMap); + synchronized (lockit) { ParticipantMessagePublisher publisher = new ParticipantMessagePublisher(Collections.singletonList(Mockito.mock(TopicSink.class))); @@ -156,7 +171,6 @@ class ParticipantMessagesTest { } } - private ToscaConceptIdentifier getParticipantId() { return new ToscaConceptIdentifier("org.onap.PM_Policy", "1.0.0"); } @@ -164,4 +178,47 @@ class ParticipantMessagesTest { private ToscaConceptIdentifier getParticipantType() { return new ToscaConceptIdentifier("org.onap.policy.controlloop.PolicyControlLoopParticipant", "2.3.1"); } + + private ToscaConceptIdentifier getControlLoopId() { + return new ToscaConceptIdentifier("PMSHInstance0", "1.0.0"); + } + + private ControlLoopInfo getControlLoopInfo(ToscaConceptIdentifier id) { + ControlLoopInfo clInfo = new ControlLoopInfo(); + clInfo.setState(ControlLoopState.PASSIVE2RUNNING); + + ControlLoopStatistics clStatistics = new ControlLoopStatistics(); + clStatistics.setControlLoopId(id); + clStatistics.setAverageExecutionTime(12345); + clStatistics.setEventCount(12345); + clStatistics.setLastEnterTime(12345); + clStatistics.setLastExecutionTime(12345); + clStatistics.setLastStart(12345); + clStatistics.setTimeStamp(Instant.ofEpochMilli(3000)); + clStatistics.setUpTime(12345); + ClElementStatisticsList clElementStatisticsList = new ClElementStatisticsList(); + ClElementStatistics clElementStatistics = new ClElementStatistics(); + clElementStatistics.setParticipantId(new ToscaConceptIdentifier("defName", "0.0.1")); + clElementStatistics.setTimeStamp(Instant.now()); + clElementStatisticsList.setClElementStatistics(List.of(clElementStatistics)); + clStatistics.setClElementStatisticsList(clElementStatisticsList); + + clInfo.setControlLoopStatistics(clStatistics); + return clInfo; + } + + private ControlLoopElementDefinition getClElementDefinition() { + ToscaServiceTemplate toscaServiceTemplate = new ToscaServiceTemplate(); + toscaServiceTemplate.setName("serviceTemplate"); + toscaServiceTemplate.setDerivedFrom("parentServiceTemplate"); + toscaServiceTemplate.setDescription("Description of serviceTemplate"); + toscaServiceTemplate.setVersion("1.2.3"); + + ControlLoopElementDefinition clDefinition = new ControlLoopElementDefinition(); + clDefinition.setId(UUID.randomUUID()); + clDefinition.setControlLoopElementToscaServiceTemplate(toscaServiceTemplate); + Map<String, String> commonPropertiesMap = Map.of("Prop1", "PropValue"); + clDefinition.setCommonPropertiesMap(commonPropertiesMap); + return clDefinition; + } } diff --git a/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/controlloop/participant/policy/main/utils/TestListenerUtils.java b/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/controlloop/participant/policy/main/utils/TestListenerUtils.java index fe7e17fd7..b91cff22c 100644 --- a/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/controlloop/participant/policy/main/utils/TestListenerUtils.java +++ b/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/controlloop/participant/policy/main/utils/TestListenerUtils.java @@ -37,7 +37,6 @@ import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantState; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopStateChange; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopUpdate; -import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantHealthCheck; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantUpdate; import org.onap.policy.clamp.controlloop.participant.policy.main.parameters.CommonTestData; import org.onap.policy.common.utils.coder.Coder; @@ -156,6 +155,7 @@ public class TestListenerUtils { clElementParticipantId.setName(toscaInputEntry.getKey()); clElementParticipantId.setVersion(toscaInputEntry.getValue().getVersion()); clElement.setParticipantId(clElementParticipantId); + clElement.setParticipantType(clElementParticipantId); clElement.setDefinition(clElementParticipantId); clElement.setState(ControlLoopState.UNINITIALISED); @@ -210,29 +210,6 @@ public class TestListenerUtils { } /** - * Method to create ParticipantHealthCheck message. - * - * @return ParticipantHealthCheck message - */ - public static ParticipantHealthCheck createParticipantHealthCheckMsg() { - ToscaConceptIdentifier participantId = new ToscaConceptIdentifier(); - participantId.setName("org.onap.PM_Policy"); - participantId.setVersion("0.0.0"); - - ToscaConceptIdentifier controlLoopId = new ToscaConceptIdentifier(); - controlLoopId.setName("PMSHInstance0"); - controlLoopId.setVersion("1.0.0"); - - final ParticipantHealthCheck participantHealthCheckMsg = new ParticipantHealthCheck(); - participantHealthCheckMsg.setParticipantId(participantId); - participantHealthCheckMsg.setControlLoopId(controlLoopId); - participantHealthCheckMsg.setTimestamp(Instant.now()); - participantHealthCheckMsg.setState(ParticipantState.PASSIVE); - - return participantHealthCheckMsg; - } - - /** * Method to create ControlLoopUpdate using the arguments passed. * * @param jsonFilePath the path of the controlloop content diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/comm/MessageSender.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/comm/MessageSender.java index 1741d9514..3ff420ffa 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/comm/MessageSender.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/comm/MessageSender.java @@ -22,7 +22,10 @@ package org.onap.policy.clamp.controlloop.participant.intermediary.comm; import java.io.Closeable; import java.time.Instant; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.TimerTask; +import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -30,9 +33,9 @@ import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantStatistics; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopAck; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantDeregister; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantRegister; -import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantResponseDetails; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantResponseStatus; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantUpdateAck; @@ -84,48 +87,23 @@ public class MessageSender extends TimerTask implements Closeable { /** * Send a response message for this participant. * - * @param response the details to include in the response message + * @param ackMessage the details to include in the response message */ - public void sendResponse(ParticipantResponseDetails response) { - sendResponse(null, response); + public void sendAckResponse(ControlLoopAck ackMessage) { + sendAckResponse(null, ackMessage); } /** * Dispatch a response message for this participant. * * @param controlLoopId the control loop to which this message is a response - * @param response the details to include in the response message + * @param ackMessage the details to include in the response message */ - public void sendResponse(ToscaConceptIdentifier controlLoopId, ParticipantResponseDetails response) { - var status = new ParticipantStatus(); - + public void sendAckResponse(ToscaConceptIdentifier controlLoopId, ControlLoopAck ackMessage) { // Participant related fields - status.setParticipantType(participantHandler.getParticipantType()); - status.setParticipantId(participantHandler.getParticipantId()); - status.setState(participantHandler.getState()); - status.setHealthStatus(participantHandler.getHealthStatus()); - - // Control loop related fields - var controlLoops = participantHandler.getControlLoopHandler().getControlLoops(); - status.setControlLoopId(controlLoopId); - status.setControlLoops(controlLoops); - status.setResponse(response); - - var participantStatistics = new ParticipantStatistics(); - participantStatistics.setTimeStamp(Instant.now()); - participantStatistics.setParticipantId(participantHandler.getParticipantId()); - participantStatistics.setHealthStatus(participantHandler.getHealthStatus()); - participantStatistics.setState(participantHandler.getState()); - status.setParticipantStatistics(participantStatistics); - - for (ControlLoopElementListener clElementListener : - participantHandler.getControlLoopHandler().getListeners()) { - updateClElementStatistics(controlLoops, clElementListener); - } - - status.setControlLoops(controlLoops); - - publisher.sendParticipantStatus(status); + ackMessage.setParticipantType(participantHandler.getParticipantType()); + ackMessage.setParticipantId(participantHandler.getParticipantId()); + publisher.sendControlLoopAck(ackMessage); } /** @@ -156,6 +134,21 @@ public class MessageSender extends TimerTask implements Closeable { } /** + * Send a ParticipantStatus message for this participant. + * + * @param participantStatus the ParticipantStatus message + */ + public void sendParticipantStatus(ParticipantStatus participantStatus) { + var controlLoops = participantHandler.getControlLoopHandler().getControlLoops(); + for (ControlLoopElementListener clElementListener : + participantHandler.getControlLoopHandler().getListeners()) { + updateClElementStatistics(controlLoops, clElementListener); + } + + publisher.sendParticipantStatus(participantStatus); + } + + /** * Dispatch a heartbeat for this participant. */ public void sendHeartbeat() { diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/comm/ParticipantMessagePublisher.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/comm/ParticipantMessagePublisher.java index 051f00095..d8cc9eb6b 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/comm/ParticipantMessagePublisher.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/comm/ParticipantMessagePublisher.java @@ -21,6 +21,7 @@ package org.onap.policy.clamp.controlloop.participant.intermediary.comm; import java.util.List; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopAck; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantDeregister; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantRegister; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus; @@ -92,6 +93,16 @@ public class ParticipantMessagePublisher { } /** + * Method to send ControlLoop Update/StateChange Ack message to runtime. + * + * @param controlLoopAck ControlLoop Update/StateChange Ack + */ + public void sendControlLoopAck(final ControlLoopAck controlLoopAck) { + topicSinkClient.send(controlLoopAck); + LOGGER.debug("Sent ControlLoop Update/StateChange Ack to runtime - {}", controlLoopAck); + } + + /** * Method to send Participant heartbeat to clamp on demand. * * @param participantStatus the Participant Status diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/comm/ParticipantHealthCheckListener.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/comm/ParticipantStatusReqListener.java index 15f5140eb..0881edb19 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/comm/ParticipantHealthCheckListener.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/comm/ParticipantStatusReqListener.java @@ -20,22 +20,22 @@ package org.onap.policy.clamp.controlloop.participant.intermediary.comm; -import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantHealthCheck; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatusReq; import org.onap.policy.clamp.controlloop.participant.intermediary.handler.ParticipantHandler; import org.springframework.stereotype.Component; /** - * Listener for Participant health status messages sent by CLAMP. + * Listener for Participant status request messages sent by runtime to all/one participant. */ @Component -public class ParticipantHealthCheckListener extends ParticipantListener<ParticipantHealthCheck> { +public class ParticipantStatusReqListener extends ParticipantListener<ParticipantStatusReq> { /** * Constructs the object. * * @param participantHandler the handler for managing the state and health of the participant */ - public ParticipantHealthCheckListener(final ParticipantHandler participantHandler) { - super(ParticipantHealthCheck.class, participantHandler, participantHandler::handleParticipantHealthCheck); + public ParticipantStatusReqListener(final ParticipantHandler participantHandler) { + super(ParticipantStatusReq.class, participantHandler, participantHandler::handleParticipantStatusReq); } } diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/handler/ControlLoopHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/handler/ControlLoopHandler.java index c9da1279d..876a4cc52 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/handler/ControlLoopHandler.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/handler/ControlLoopHandler.java @@ -28,6 +28,7 @@ import java.util.UUID; import lombok.Getter; import lombok.NoArgsConstructor; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.tuple.Pair; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatistics; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement; @@ -35,10 +36,10 @@ import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopOrderedState; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopAck; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopStateChange; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopUpdate; -import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantResponseDetails; -import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantResponseStatus; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantMessageType; import org.onap.policy.clamp.controlloop.participant.intermediary.api.ControlLoopElementListener; import org.onap.policy.clamp.controlloop.participant.intermediary.comm.MessageSender; import org.onap.policy.clamp.controlloop.participant.intermediary.parameters.ParticipantIntermediaryParameters; @@ -60,6 +61,7 @@ public class ControlLoopHandler { private final Map<ToscaConceptIdentifier, ControlLoop> controlLoopMap = new LinkedHashMap<>(); + @Getter private final Map<UUID, ControlLoopElement> elementsOnThisParticipant = new LinkedHashMap<>(); @Getter @@ -93,21 +95,23 @@ public class ControlLoopHandler { ControlLoopState newState) { if (id == null) { - return null; + LOGGER.warn("Cannot update Control loop element state, id is null"); } + ControlLoopAck controlLoopStateChangeAck = + new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_STATECHANGE_ACK); ControlLoopElement clElement = elementsOnThisParticipant.get(id); if (clElement != null) { clElement.setOrderedState(orderedState); clElement.setState(newState); + controlLoopStateChangeAck.getControlLoopResultMap().put(clElement.getId(), + Pair.of(true, "Control loop element {} state changed to {}\", id, newState)")); LOGGER.debug("Control loop element {} state changed to {}", id, newState); - var response = new ParticipantResponseDetails(); - response.setResponseStatus(ParticipantResponseStatus.SUCCESS); - response.setResponseMessage("ControlLoopElement state changed to {} " + newState); - messageSender.sendResponse(response); + controlLoopStateChangeAck.setMessage("ControlLoopElement state changed to {} " + newState); + controlLoopStateChangeAck.setResult(true); + messageSender.sendAckResponse(controlLoopStateChangeAck); return clElement; } - return null; } @@ -143,9 +147,11 @@ public class ControlLoopHandler { return; } - var response = new ParticipantResponseDetails(stateChangeMsg); - handleState(controlLoop, response, stateChangeMsg.getOrderedState()); - messageSender.sendResponse(response); + var controlLoopStateChangeAck = new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_STATECHANGE_ACK); + controlLoopStateChangeAck.setResponseTo(stateChangeMsg.getMessageId()); + controlLoopStateChangeAck.setControlLoopId(stateChangeMsg.getControlLoopId()); + handleState(controlLoop, controlLoopStateChangeAck, stateChangeMsg.getOrderedState()); + messageSender.sendAckResponse(controlLoopStateChangeAck); } /** @@ -155,7 +161,7 @@ public class ControlLoopHandler { * @param response participant response * @param orderedState controlloop ordered state */ - private void handleState(final ControlLoop controlLoop, final ParticipantResponseDetails response, + private void handleState(final ControlLoop controlLoop, final ControlLoopAck response, ControlLoopOrderedState orderedState) { switch (orderedState) { case UNINITIALISED: @@ -187,16 +193,17 @@ public class ControlLoopHandler { var controlLoop = controlLoopMap.get(updateMsg.getControlLoopId()); - var response = new ParticipantResponseDetails(updateMsg); + var controlLoopUpdateAck = new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_UPDATE_ACK); // TODO: Updates to existing ControlLoops are not supported yet (Addition/Removal of ControlLoop // elements to existing ControlLoop has to be supported). if (controlLoop != null) { - response.setResponseStatus(ParticipantResponseStatus.FAIL); - response.setResponseMessage("Control loop " + updateMsg.getControlLoopId() - + " already defined on participant " + participantId); - - messageSender.sendResponse(response); + controlLoopUpdateAck.setResponseTo(updateMsg.getMessageId()); + controlLoopUpdateAck.setControlLoopId(updateMsg.getControlLoopId()); + controlLoopUpdateAck.setMessage("Control loop " + updateMsg.getControlLoopId() + + " already defined on participant " + participantId); + controlLoopUpdateAck.setResult(false); + messageSender.sendAckResponse(controlLoopUpdateAck); return; } @@ -221,11 +228,12 @@ public class ControlLoopHandler { } } - response.setResponseStatus(ParticipantResponseStatus.SUCCESS); - response.setResponseMessage( - "Control loop " + updateMsg.getControlLoopId() + " defined on participant " + participantId); - - messageSender.sendResponse(response); + controlLoopUpdateAck.setResponseTo(updateMsg.getMessageId()); + controlLoopUpdateAck.setControlLoopId(updateMsg.getControlLoopId()); + controlLoopUpdateAck.setMessage("Control loop " + updateMsg.getControlLoopId() + + " defined on participant " + participantId); + controlLoopUpdateAck.setResult(true); + messageSender.sendAckResponse(controlLoopUpdateAck); } /** @@ -236,7 +244,7 @@ public class ControlLoopHandler { * @param response participant response */ private void handleUninitialisedState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState, - final ParticipantResponseDetails response) { + final ControlLoopAck response) { handleStateChange(controlLoop, orderedState, ControlLoopState.UNINITIALISED, response); controlLoopMap.remove(controlLoop.getKey().asIdentifier()); @@ -259,7 +267,7 @@ public class ControlLoopHandler { * @param response participant response */ private void handlePassiveState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState, - final ParticipantResponseDetails response) { + final ControlLoopAck response) { handleStateChange(controlLoop, orderedState, ControlLoopState.PASSIVE, response); } @@ -271,7 +279,7 @@ public class ControlLoopHandler { * @param response participant response */ private void handleRunningState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState, - final ParticipantResponseDetails response) { + final ControlLoopAck response) { handleStateChange(controlLoop, orderedState, ControlLoopState.RUNNING, response); } @@ -284,11 +292,11 @@ public class ControlLoopHandler { * @param response the response to the state change request */ private void handleStateChange(ControlLoop controlLoop, final ControlLoopOrderedState orderedState, - ControlLoopState newState, ParticipantResponseDetails response) { + ControlLoopState newState, ControlLoopAck response) { if (orderedState.equals(controlLoop.getOrderedState())) { - response.setResponseStatus(ParticipantResponseStatus.SUCCESS); - response.setResponseMessage("Control loop is already in state " + orderedState); + response.setMessage("Control loop is already in state " + orderedState); + response.setResult(false); return; } @@ -299,9 +307,8 @@ public class ControlLoopHandler { }); } - response.setResponseStatus(ParticipantResponseStatus.SUCCESS); - response.setResponseMessage( - "ControlLoop state changed from " + controlLoop.getOrderedState() + " to " + orderedState); + response.setMessage("ControlLoop state changed from " + controlLoop.getOrderedState() + " to " + orderedState); + response.setResult(true); controlLoop.setOrderedState(orderedState); } diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/handler/IntermediaryActivator.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/handler/IntermediaryActivator.java index f846b2d75..4fc0ae1b1 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/handler/IntermediaryActivator.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/handler/IntermediaryActivator.java @@ -28,8 +28,8 @@ import org.onap.policy.clamp.controlloop.participant.intermediary.api.Participan import org.onap.policy.clamp.controlloop.participant.intermediary.comm.ControlLoopStateChangeListener; import org.onap.policy.clamp.controlloop.participant.intermediary.comm.ControlLoopUpdateListener; import org.onap.policy.clamp.controlloop.participant.intermediary.comm.ParticipantDeregisterAckListener; -import org.onap.policy.clamp.controlloop.participant.intermediary.comm.ParticipantHealthCheckListener; import org.onap.policy.clamp.controlloop.participant.intermediary.comm.ParticipantRegisterAckListener; +import org.onap.policy.clamp.controlloop.participant.intermediary.comm.ParticipantStatusReqListener; import org.onap.policy.clamp.controlloop.participant.intermediary.comm.ParticipantUpdateListener; import org.onap.policy.clamp.controlloop.participant.intermediary.parameters.ParticipantParameters; import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager; @@ -119,8 +119,8 @@ public class IntermediaryActivator extends ServiceManagerContainer implements Cl private void registerMsgDispatcher() { MessageTypeDispatcher msgDispatcher = applicationContext.getBean(MessageTypeDispatcher.class); - msgDispatcher.register(ParticipantMessageType.PARTICIPANT_HEALTH_CHECK.name(), - applicationContext.getBean(ParticipantHealthCheckListener.class)); + msgDispatcher.register(ParticipantMessageType.PARTICIPANT_STATUS_REQ.name(), + applicationContext.getBean(ParticipantStatusReqListener.class)); msgDispatcher.register(ParticipantMessageType.CONTROL_LOOP_STATE_CHANGE.name(), applicationContext.getBean(ControlLoopStateChangeListener.class)); diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/handler/ParticipantHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/handler/ParticipantHandler.java index 9daff7239..6a0e758dd 100644 --- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/handler/ParticipantHandler.java +++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/controlloop/participant/intermediary/handler/ParticipantHandler.java @@ -32,17 +32,18 @@ import org.onap.policy.clamp.controlloop.models.controlloop.concepts.Participant import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantHealthStatus; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantState; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantStatistics; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopAck; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopStateChange; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopUpdate; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantDeregister; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantDeregisterAck; -import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantHealthCheck; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantMessage; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantMessageType; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantRegister; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantRegisterAck; -import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantResponseDetails; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantResponseStatus; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatusReq; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantUpdate; import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantUpdateAck; import org.onap.policy.clamp.controlloop.participant.intermediary.comm.MessageSender; @@ -99,14 +100,15 @@ public class ParticipantHandler implements Closeable { /** * Method which handles a participant health check event from clamp. * - * @param healthCheckMsg participant health check message + * @param participantStatusReqMsg participant participantStatusReq message */ - public void handleParticipantHealthCheck(final ParticipantHealthCheck healthCheckMsg) { - var response = new ParticipantResponseDetails(healthCheckMsg); - response.setResponseStatus(ParticipantResponseStatus.SUCCESS); - response.setResponseMessage(healthStatus.toString()); - - sender.sendResponse(response); + public void handleParticipantStatusReq(final ParticipantStatusReq participantStatusReqMsg) { + ParticipantStatus participantStatus = new ParticipantStatus(); + participantStatus.setParticipantId(participantId); + participantStatus.setParticipantStatistics(participantStatistics); + participantStatus.setParticipantType(participantType); + participantStatus.setHealthStatus(healthStatus); + sender.sendParticipantStatus(participantStatus); } /** @@ -127,13 +129,13 @@ public class ParticipantHandler implements Closeable { controlLoopHandler.handleControlLoopStateChange(stateChangeMsg); } - private void handleStateChange(ParticipantState newParticipantState, ParticipantResponseDetails response) { + private void handleStateChange(ParticipantState newParticipantState, ParticipantUpdateAck response) { if (state.equals(newParticipantState)) { - response.setResponseStatus(ParticipantResponseStatus.SUCCESS); - response.setResponseMessage("Participant already in state " + newParticipantState); + response.setResult(false); + response.setMessage("Participant already in state " + newParticipantState); } else { - response.setResponseStatus(ParticipantResponseStatus.SUCCESS); - response.setResponseMessage("Participant state changed from " + state + " to " + newParticipantState); + response.setResult(true); + response.setMessage("Participant state changed from " + state + " to " + newParticipantState); state = newParticipantState; } } @@ -150,9 +152,10 @@ public class ParticipantHandler implements Closeable { LOGGER.debug("No participant with this ID {}", definition.getName()); return null; } - var response = new ParticipantResponseDetails(); - handleStateChange(participantState, response); - sender.sendResponse(response); + + var participantUpdateAck = new ParticipantUpdateAck(); + handleStateChange(participantState, participantUpdateAck); + sender.sendParticipantUpdateAck(participantUpdateAck); return getParticipant(definition.getName(), definition.getVersion()); } @@ -257,6 +260,8 @@ public class ParticipantHandler implements Closeable { participantUpdateAck.setResponseTo(messageId); participantUpdateAck.setMessage("Participant Update Ack message"); participantUpdateAck.setResult(true); + participantUpdateAck.setParticipantId(participantId); + participantUpdateAck.setParticipantType(participantType); sender.sendParticipantUpdateAck(participantUpdateAck); } @@ -270,7 +275,6 @@ public class ParticipantHandler implements Closeable { heartbeat.setParticipantStatistics(participantStatistics); heartbeat.setParticipantType(participantType); heartbeat.setHealthStatus(healthStatus); - heartbeat.setMessage("Participant heartbeat message sent from -> " + participantId.getName()); return heartbeat; } } diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/HandleCounter.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/HandleCounter.java new file mode 100644 index 000000000..2151dc143 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/HandleCounter.java @@ -0,0 +1,91 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.runtime.supervision; + +import java.time.Instant; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import lombok.Getter; +import lombok.Setter; + +public class HandleCounter<K> { + @Getter + @Setter + private long maxWaitMs; + + @Getter + @Setter + private int maxRetryCount; + + private Map<K, Integer> mapCounter = new HashMap<>(); + private Set<K> mapFault = new HashSet<>(); + private Map<K, Long> mapTimer = new HashMap<>(); + + public long getDuration(K id) { + mapTimer.putIfAbsent(id, getEpochMilli()); + return getEpochMilli() - mapTimer.get(id); + } + + /** + * Reset timer and clear counter and fault by id. + * + * @param id the id + */ + public void clear(K id) { + mapFault.remove(id); + mapCounter.put(id, 0); + mapTimer.put(id, getEpochMilli()); + } + + public void setFault(K id) { + mapCounter.put(id, 0); + mapFault.add(id); + } + + /** + * Increment RetryCount by id e return true if minor or equal of maxRetryCount. + * + * @param id the identifier + * @return false if count is major of maxRetryCount + */ + public boolean count(K id) { + int counter = mapCounter.getOrDefault(id, 0) + 1; + if (counter <= maxRetryCount) { + mapCounter.put(id, counter); + return true; + } + return false; + } + + public boolean isFault(K id) { + return mapFault.contains(id); + } + + public int getCounter(K id) { + return mapCounter.getOrDefault(id, 0); + } + + protected long getEpochMilli() { + return Instant.now().toEpochMilli(); + } +} diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionAspect.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionAspect.java index 293b5d5da..d0d18ab1a 100644 --- a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionAspect.java +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionAspect.java @@ -28,6 +28,8 @@ import java.util.concurrent.TimeUnit; import lombok.RequiredArgsConstructor; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; @@ -64,6 +66,11 @@ public class SupervisionAspect implements Closeable { } } + @Before("@annotation(MessageIntercept) && args(participantStatusMessage,..)") + public void handleParticipantStatus(ParticipantStatus participantStatusMessage) { + executor.execute(() -> supervisionScanner.handleParticipantStatus(participantStatusMessage.getParticipantId())); + } + @Override public void close() throws IOException { executor.shutdown(); diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionHandler.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionHandler.java index dadfe0de2..16dba0f07 100644 --- a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionHandler.java +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionHandler.java @@ -21,12 +21,14 @@ package org.onap.policy.clamp.controlloop.runtime.supervision; import java.util.List; +import java.util.Map; import javax.ws.rs.core.Response; import lombok.AllArgsConstructor; import org.apache.commons.collections4.CollectionUtils; import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopInfo; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.Participant; import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider; @@ -310,39 +312,17 @@ public class SupervisionHandler { private void superviseControlLoops(ParticipantStatus participantStatusMessage) throws PfModelException, ControlLoopException { - if (CollectionUtils.isEmpty(participantStatusMessage.getControlLoops().getControlLoopList())) { - return; - } - - for (ControlLoop controlLoop : participantStatusMessage.getControlLoops().getControlLoopList()) { - if (controlLoop == null) { - exceptionOccured(Response.Status.NOT_FOUND, - "PARTICIPANT_STATUS message references unknown control loop: " + controlLoop); - } - - var dbControlLoop = controlLoopProvider - .getControlLoop(new ToscaConceptIdentifier(controlLoop.getName(), controlLoop.getVersion())); + for (Map.Entry<ToscaConceptIdentifier, ControlLoopInfo> clEntry : + participantStatusMessage.getControlLoopInfoMap().entrySet()) { + var dbControlLoop = controlLoopProvider.getControlLoop(new ToscaConceptIdentifier( + clEntry.getKey().getName(), clEntry.getKey().getVersion())); if (dbControlLoop == null) { exceptionOccured(Response.Status.NOT_FOUND, - "PARTICIPANT_STATUS control loop not found in database: " + controlLoop); + "PARTICIPANT_STATUS control loop not found in database: " + clEntry.getKey()); } - - for (ControlLoopElement element : controlLoop.getElements().values()) { - ControlLoopElement dbElement = dbControlLoop.getElements().get(element.getId()); - - if (dbElement == null) { - exceptionOccured(Response.Status.NOT_FOUND, - "PARTICIPANT_STATUS message references unknown control loop element: " + element); - } - - // Replace element entry in the database - dbControlLoop.getElements().put(element.getId(), element); - } - controlLoopProvider.updateControlLoop(dbControlLoop); - } - - for (ControlLoop controlLoop : participantStatusMessage.getControlLoops().getControlLoopList()) { - monitoringProvider.createClElementStatistics(controlLoop.getControlLoopElementStatisticsList(controlLoop)); + dbControlLoop.setState(clEntry.getValue().getState()); + monitoringProvider.createClElementStatistics(clEntry.getValue() + .getControlLoopStatistics().getClElementStatisticsList().getClElementStatistics()); } } diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScanner.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScanner.java index b360f6703..7be407c3f 100644 --- a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScanner.java +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScanner.java @@ -20,19 +20,18 @@ package org.onap.policy.clamp.controlloop.runtime.supervision; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import lombok.Getter; -import lombok.Setter; +import java.util.List; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement; import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.Participant; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantHealthStatus; import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider; +import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ParticipantProvider; import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup; import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopStateChangePublisher; import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopUpdatePublisher; +import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantStatusReqPublisher; import org.onap.policy.models.base.PfModelException; import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; import org.slf4j.Logger; @@ -46,67 +45,49 @@ import org.springframework.stereotype.Component; public class SupervisionScanner { private static final Logger LOGGER = LoggerFactory.getLogger(SupervisionScanner.class); - @Getter - @Setter - static class HandleCounter { - private int maxRetryCount; - private long maxWaitMs; - private Map<ToscaConceptIdentifier, Integer> mapCounter = new HashMap<>(); - private Set<ToscaConceptIdentifier> mapFault = new HashSet<>(); - - public void clear(ToscaConceptIdentifier id) { - mapCounter.put(id, 0); - mapFault.remove(id); - } - - public void setFault(ToscaConceptIdentifier id) { - mapCounter.put(id, 0); - mapFault.add(id); - } - - public boolean count(ToscaConceptIdentifier id) { - int counter = mapCounter.getOrDefault(id, 0) + 1; - if (counter <= maxRetryCount) { - mapCounter.put(id, counter); - return true; - } - return false; - } - - public boolean isFault(ToscaConceptIdentifier id) { - return mapFault.contains(id); - } - - public int getCounter(ToscaConceptIdentifier id) { - return mapCounter.getOrDefault(id, 0); - } - } - - private HandleCounter stateChange = new HandleCounter(); + private HandleCounter<ToscaConceptIdentifier> controlLoopCounter = new HandleCounter<>(); + private HandleCounter<ToscaConceptIdentifier> participantCounter = new HandleCounter<>(); private final ControlLoopProvider controlLoopProvider; private final ControlLoopStateChangePublisher controlLoopStateChangePublisher; private final ControlLoopUpdatePublisher controlLoopUpdatePublisher; + private final ParticipantProvider participantProvider; + private final ParticipantStatusReqPublisher participantStatusReqPublisher; + + private final long maxMessageAgeMs; /** * Constructor for instantiating SupervisionScanner. * * @param controlLoopProvider the provider to use to read control loops from the database - * @param controlLoopStateChangePublisher the ControlLoopStateChange Publisher + * @param controlLoopStateChangePublisher the ControlLoop StateChange Publisher + * @param controlLoopUpdatePublisher the ControlLoopUpdate Publisher + * @param participantProvider the Participant Provider + * @param participantStatusReqPublisher the Participant StatusReq Publisher * @param clRuntimeParameterGroup the parameters for the control loop runtime */ public SupervisionScanner(final ControlLoopProvider controlLoopProvider, final ControlLoopStateChangePublisher controlLoopStateChangePublisher, - ControlLoopUpdatePublisher controlLoopUpdatePublisher, + ControlLoopUpdatePublisher controlLoopUpdatePublisher, ParticipantProvider participantProvider, + ParticipantStatusReqPublisher participantStatusReqPublisher, final ClRuntimeParameterGroup clRuntimeParameterGroup) { this.controlLoopProvider = controlLoopProvider; this.controlLoopStateChangePublisher = controlLoopStateChangePublisher; this.controlLoopUpdatePublisher = controlLoopUpdatePublisher; + this.participantProvider = participantProvider; + this.participantStatusReqPublisher = participantStatusReqPublisher; + + controlLoopCounter.setMaxRetryCount( + clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxRetryCount()); + controlLoopCounter + .setMaxWaitMs(clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxWaitMs()); - stateChange.setMaxRetryCount( + participantCounter.setMaxRetryCount( clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxRetryCount()); - stateChange.setMaxWaitMs( - clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxWaitMs()); + participantCounter + .setMaxWaitMs(clRuntimeParameterGroup.getParticipantParameters().getUpdateParameters().getMaxWaitMs()); + + maxMessageAgeMs = clRuntimeParameterGroup.getParticipantParameters().getMaxMessageAgeMs(); } /** @@ -117,6 +98,17 @@ public class SupervisionScanner { public void run(boolean counterCheck) { LOGGER.debug("Scanning control loops in the database . . ."); + if (counterCheck) { + try { + for (Participant participant : participantProvider.getParticipants(null, null)) { + scanParticipant(participant); + } + } catch (PfModelException pfme) { + LOGGER.warn("error reading participant from database", pfme); + return; + } + } + try { for (ControlLoop controlLoop : controlLoopProvider.getControlLoops(null, null)) { scanControlLoop(controlLoop, counterCheck); @@ -128,6 +120,33 @@ public class SupervisionScanner { LOGGER.debug("Control loop scan complete . . ."); } + private void scanParticipant(Participant participant) throws PfModelException { + ToscaConceptIdentifier id = participant.getKey().asIdentifier(); + if (participantCounter.isFault(id)) { + LOGGER.debug("report Participant fault"); + return; + } + if (participantCounter.getDuration(id) > maxMessageAgeMs) { + if (participantCounter.count(id)) { + LOGGER.debug("retry message ParticipantStatusReq"); + participantStatusReqPublisher.send(id); + participant.setHealthStatus(ParticipantHealthStatus.NOT_HEALTHY); + } else { + LOGGER.debug("report Participant fault"); + participantCounter.setFault(id); + participant.setHealthStatus(ParticipantHealthStatus.OFF_LINE); + } + participantProvider.updateParticipants(List.of(participant)); + } + } + + /** + * handle participant Status message. + */ + public void handleParticipantStatus(ToscaConceptIdentifier id) { + participantCounter.clear(id); + } + private void scanControlLoop(final ControlLoop controlLoop, boolean counterCheck) throws PfModelException { LOGGER.debug("scanning control loop {} . . .", controlLoop.getKey().asIdentifier()); @@ -166,17 +185,17 @@ public class SupervisionScanner { } private void clearFaultAndCounter(ControlLoop controlLoop) { - stateChange.clear(controlLoop.getKey().asIdentifier()); + controlLoopCounter.clear(controlLoop.getKey().asIdentifier()); } private void handleCounter(ControlLoop controlLoop) { ToscaConceptIdentifier id = controlLoop.getKey().asIdentifier(); - if (stateChange.isFault(id)) { + if (controlLoopCounter.isFault(id)) { LOGGER.debug("report ControlLoop fault"); return; } - if (stateChange.count(id)) { + if (controlLoopCounter.count(id)) { if (ControlLoopState.UNINITIALISED2PASSIVE.equals(controlLoop.getState())) { LOGGER.debug("retry message ControlLoopUpdate"); controlLoopUpdatePublisher.send(controlLoop); @@ -186,7 +205,7 @@ public class SupervisionScanner { } } else { LOGGER.debug("report ControlLoop fault"); - stateChange.setFault(id); + controlLoopCounter.setFault(id); } } } diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStatusReqPublisher.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStatusReqPublisher.java new file mode 100644 index 000000000..69d598285 --- /dev/null +++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStatusReqPublisher.java @@ -0,0 +1,48 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.runtime.supervision.comm; + +import java.time.Instant; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatusReq; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class ParticipantStatusReqPublisher extends AbstractParticipantPublisher<ParticipantStatusReq> { + + private static final Logger LOGGER = LoggerFactory.getLogger(ParticipantStatusReqPublisher.class); + + /** + * Send ParticipantStatusReq to Participant. + * + * @param participantId the participant Id + */ + public void send(ToscaConceptIdentifier participantId) { + ParticipantStatusReq message = new ParticipantStatusReq(); + message.setParticipantId(participantId); + message.setTimestamp(Instant.now()); + + LOGGER.debug("Participant StatusReq sent {}", message); + super.send(message); + } +} diff --git a/runtime-controlloop/src/test/java/org/onap/policy/clamp/controlloop/runtime/supervision/HandleCounterTest.java b/runtime-controlloop/src/test/java/org/onap/policy/clamp/controlloop/runtime/supervision/HandleCounterTest.java new file mode 100644 index 000000000..51f3b4a32 --- /dev/null +++ b/runtime-controlloop/src/test/java/org/onap/policy/clamp/controlloop/runtime/supervision/HandleCounterTest.java @@ -0,0 +1,84 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.runtime.supervision; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class HandleCounterTest { + + private static final int ID = 1; + + @Test + void testCount() { + var handleCounter = new HandleCounter<Integer>(); + handleCounter.setMaxRetryCount(2); + assertThat(handleCounter.count(ID)).isTrue(); + assertThat(handleCounter.getCounter(ID)).isEqualTo(1); + assertThat(handleCounter.count(ID)).isTrue(); + assertThat(handleCounter.getCounter(ID)).isEqualTo(2); + assertThat(handleCounter.count(ID)).isFalse(); + assertThat(handleCounter.getCounter(ID)).isEqualTo(2); + + handleCounter.clear(ID); + assertThat(handleCounter.count(ID)).isTrue(); + assertThat(handleCounter.getCounter(ID)).isEqualTo(1); + } + + @Test + void testFault() { + var handleCounter = new HandleCounter<Integer>(); + handleCounter.setFault(ID); + assertThat(handleCounter.isFault(ID)).isTrue(); + handleCounter.clear(ID); + assertThat(handleCounter.isFault(ID)).isFalse(); + } + + @Test + void testDuration() throws InterruptedException { + + var handleCounter = new HandleCounter<Integer>() { + long epochMilli = 0; + + @Override + protected long getEpochMilli() { + return epochMilli; + } + }; + handleCounter.epochMilli = 100; + var result = handleCounter.getDuration(ID); + assertThat(result).isZero(); + + handleCounter.epochMilli += 100; + result = handleCounter.getDuration(ID); + assertThat(result).isEqualTo(100); + + handleCounter.epochMilli += 100; + result = handleCounter.getDuration(ID); + assertThat(result).isEqualTo(200); + + handleCounter.epochMilli += 100; + handleCounter.clear(ID); + result = handleCounter.getDuration(ID); + assertThat(result).isZero(); + } +} diff --git a/runtime-controlloop/src/test/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionAspectTest.java b/runtime-controlloop/src/test/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionAspectTest.java new file mode 100644 index 000000000..30ee9b1b9 --- /dev/null +++ b/runtime-controlloop/src/test/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionAspectTest.java @@ -0,0 +1,66 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.runtime.supervision; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; + +import org.junit.jupiter.api.Test; +import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; + +class SupervisionAspectTest { + + @Test + void testSchedule() throws Exception { + var supervisionScanner = spy(mock(SupervisionScanner.class)); + try (var supervisionAspect = new SupervisionAspect(supervisionScanner)) { + supervisionAspect.schedule(); + verify(supervisionScanner, timeout(500)).run(eq(true)); + } + } + + @Test + void testDoCheck() throws Exception { + var supervisionScanner = spy(mock(SupervisionScanner.class)); + try (var supervisionAspect = new SupervisionAspect(supervisionScanner)) { + supervisionAspect.doCheck(); + supervisionAspect.doCheck(); + verify(supervisionScanner, timeout(500).times(2)).run(eq(false)); + } + } + + @Test + void testHandleParticipantStatus() throws Exception { + var supervisionScanner = spy(mock(SupervisionScanner.class)); + var participantStatusMessage = new ParticipantStatus(); + var identifier = new ToscaConceptIdentifier("abc", "1.0.0"); + participantStatusMessage.setParticipantId(identifier); + + try (var supervisionAspect = new SupervisionAspect(supervisionScanner)) { + supervisionAspect.handleParticipantStatus(participantStatusMessage); + verify(supervisionScanner, timeout(500)).handleParticipantStatus(eq(identifier)); + } + } +} diff --git a/runtime-controlloop/src/test/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScannerTest.java b/runtime-controlloop/src/test/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScannerTest.java new file mode 100644 index 000000000..485f58dba --- /dev/null +++ b/runtime-controlloop/src/test/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScannerTest.java @@ -0,0 +1,160 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.runtime.supervision; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopOrderedState; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.Participant; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantHealthStatus; +import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantState; +import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider; +import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ParticipantProvider; +import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopStateChangePublisher; +import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ControlLoopUpdatePublisher; +import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantStatusReqPublisher; +import org.onap.policy.clamp.controlloop.runtime.util.CommonTestData; +import org.onap.policy.models.base.PfModelException; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; + +class SupervisionScannerTest { + + @Test + void testScannerOrderedStateEqualsToState() throws PfModelException { + var controlLoopProvider = mock(ControlLoopProvider.class); + var controlLoopStateChangePublisher = mock(ControlLoopStateChangePublisher.class); + var controlLoopUpdatePublisher = mock(ControlLoopUpdatePublisher.class); + var participantProvider = mock(ParticipantProvider.class); + var participantStatusReqPublisher = mock(ParticipantStatusReqPublisher.class); + var clRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner"); + + var controlLoop = new ControlLoop(); + when(controlLoopProvider.getControlLoops(null, null)).thenReturn(List.of(controlLoop)); + + var supervisionScanner = + new SupervisionScanner(controlLoopProvider, controlLoopStateChangePublisher, controlLoopUpdatePublisher, + participantProvider, participantStatusReqPublisher, clRuntimeParameterGroup); + supervisionScanner.run(false); + + verify(controlLoopProvider, times(0)).updateControlLoop(any(ControlLoop.class)); + } + + @Test + void testScannerOrderedStateDifferentToState() throws PfModelException { + var controlLoop = new ControlLoop(); + controlLoop.setState(ControlLoopState.UNINITIALISED2PASSIVE); + controlLoop.setOrderedState(ControlLoopOrderedState.UNINITIALISED); + controlLoop.setElements(Map.of(UUID.randomUUID(), new ControlLoopElement())); + var controlLoopProvider = mock(ControlLoopProvider.class); + when(controlLoopProvider.getControlLoops(null, null)).thenReturn(List.of(controlLoop)); + + var controlLoopUpdatePublisher = mock(ControlLoopUpdatePublisher.class); + var controlLoopStateChangePublisher = mock(ControlLoopStateChangePublisher.class); + var participantProvider = mock(ParticipantProvider.class); + var participantStatusReqPublisher = mock(ParticipantStatusReqPublisher.class); + var clRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner"); + + var supervisionScanner = + new SupervisionScanner(controlLoopProvider, controlLoopStateChangePublisher, controlLoopUpdatePublisher, + participantProvider, participantStatusReqPublisher, clRuntimeParameterGroup); + supervisionScanner.run(false); + + verify(controlLoopProvider, times(1)).updateControlLoop(any(ControlLoop.class)); + } + + @Test + void testScanner() throws PfModelException { + var controlLoopProvider = mock(ControlLoopProvider.class); + var controlLoop = new ControlLoop(); + when(controlLoopProvider.getControlLoops(null, null)).thenReturn(List.of(controlLoop)); + + var participantProvider = mock(ParticipantProvider.class); + var participant = new Participant(); + participant.setName("Participant0"); + participant.setVersion("1.0.0"); + when(participantProvider.getParticipants(null, null)).thenReturn(List.of(participant)); + + var controlLoopUpdatePublisher = mock(ControlLoopUpdatePublisher.class); + var participantStatusReqPublisher = mock(ParticipantStatusReqPublisher.class); + var controlLoopStateChangePublisher = mock(ControlLoopStateChangePublisher.class); + var clRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanner"); + + var supervisionScanner = + new SupervisionScanner(controlLoopProvider, controlLoopStateChangePublisher, controlLoopUpdatePublisher, + participantProvider, participantStatusReqPublisher, clRuntimeParameterGroup); + + supervisionScanner.handleParticipantStatus(participant.getKey().asIdentifier()); + supervisionScanner.run(true); + verify(controlLoopProvider, times(0)).updateControlLoop(any(ControlLoop.class)); + verify(participantStatusReqPublisher, times(0)).send(any(ToscaConceptIdentifier.class)); + } + + @Test + void testScanParticipant() throws PfModelException { + var controlLoopProvider = mock(ControlLoopProvider.class); + var controlLoop = new ControlLoop(); + when(controlLoopProvider.getControlLoops(null, null)).thenReturn(List.of(controlLoop)); + + var clRuntimeParameterGroup = CommonTestData.geParameterGroup("dbScanParticipant"); + clRuntimeParameterGroup.getParticipantParameters().setMaxMessageAgeMs(0); + + var participant = new Participant(); + participant.setName("Participant0"); + participant.setVersion("1.0.0"); + participant.setHealthStatus(ParticipantHealthStatus.HEALTHY); + participant.setParticipantState(ParticipantState.ACTIVE); + participant.setDefinition(new ToscaConceptIdentifier("unknown", "0.0.0")); + var participantProvider = new ParticipantProvider(clRuntimeParameterGroup.getDatabaseProviderParameters()); + participantProvider.updateParticipants(List.of(participant)); + + var controlLoopUpdatePublisher = mock(ControlLoopUpdatePublisher.class); + var participantStatusReqPublisher = mock(ParticipantStatusReqPublisher.class); + var controlLoopStateChangePublisher = mock(ControlLoopStateChangePublisher.class); + + var supervisionScanner = + new SupervisionScanner(controlLoopProvider, controlLoopStateChangePublisher, controlLoopUpdatePublisher, + participantProvider, participantStatusReqPublisher, clRuntimeParameterGroup); + + supervisionScanner.handleParticipantStatus(participant.getKey().asIdentifier()); + supervisionScanner.run(true); + verify(participantStatusReqPublisher, times(1)).send(any(ToscaConceptIdentifier.class)); + + List<Participant> participants = participantProvider.getParticipants(null, null); + assertThat(participants.get(0).getHealthStatus()).isEqualTo(ParticipantHealthStatus.NOT_HEALTHY); + + supervisionScanner.run(true); + supervisionScanner.run(true); + participants = participantProvider.getParticipants(null, null); + assertThat(participants.get(0).getHealthStatus()).isEqualTo(ParticipantHealthStatus.OFF_LINE); + } +} |