From aa8225b5211485b3c1150c21e51fd3e93b7f31d3 Mon Sep 17 00:00:00 2001 From: Jim Hahn Date: Mon, 9 Mar 2020 15:15:05 -0400 Subject: Retool rules tests Extracted common code from various XxxBaseTest classes into: - Topics class to manage messages for test topics - HttpClients class to manage HttpClient objects for tests - Simulators class to manage simulators for tests - Rules class to manage start up and shutdown of rules Merged remaining code from XxxBaseTest classes into a single class. Modified the Frankfurt and Usescases tests to subclass from this new class and specify just the relevant tests to be executed. Issue-ID: POLICY-2385 Signed-off-by: Jim Hahn Change-Id: Iaf83c9d2b205a4c343e0dde23ec86508f5773693 --- .../common/rules/test/BaseRuleTest.java | 496 +++++++++++++++++++++ .../controlloop/common/rules/test/HttpClients.java | 67 +++ .../controlloop/common/rules/test/Listener.java | 180 ++++++++ .../controlloop/common/rules/test/NamedRunner.java | 85 ++++ .../controlloop/common/rules/test/Rules.java | 413 +++++++++++++++++ .../common/rules/test/SimulatorException.java | 44 ++ .../controlloop/common/rules/test/Simulators.java | 85 ++++ .../controlloop/common/rules/test/TestNames.java | 44 ++ .../common/rules/test/TopicException.java | 44 ++ .../controlloop/common/rules/test/Topics.java | 176 ++++++++ .../duplicates/duplicates.appc.success.json | 22 + .../resources/duplicates/duplicates.onset.1.json | 16 + .../resources/duplicates/duplicates.onset.2.json | 16 + .../duplicates/tosca-compliant-duplicates.json | 37 ++ .../service123.appc.migrate.success.json | 22 + .../service123.appc.rebuild.failure.json | 22 + .../service123.appc.restart.failure.json | 22 + .../resources/service123/service123.onset.json | 17 + .../service123/tosca-compliant-service123.json | 75 ++++ .../main/resources/vcpe/tosca-compliant-vcpe.json | 37 ++ .../src/main/resources/vcpe/tosca-legacy-vcpe.json | 9 + .../src/main/resources/vcpe/vcpe.appc.success.json | 22 + .../src/main/resources/vcpe/vcpe.onset.1.json | 16 + .../src/main/resources/vcpe/vcpe.onset.2.json | 16 + .../src/main/resources/vcpe/vcpe.onset.3.json | 17 + .../main/resources/vdns/tosca-compliant-vdns.json | 48 ++ .../src/main/resources/vdns/vdns.onset.json | 16 + .../main/resources/vfw/tosca-compliant-vfw.json | 40 ++ .../src/main/resources/vfw/tosca-vfw.json | 9 + .../src/main/resources/vfw/vfw.appc.success.json | 17 + .../src/main/resources/vfw/vfw.onset.json | 17 + .../main/resources/vlb/tosca-compliant-vlb.json | 48 ++ .../src/main/resources/vlb/tosca-vlb.json | 9 + .../src/main/resources/vlb/vlb.onset.json | 16 + 34 files changed, 2220 insertions(+) create mode 100644 controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/BaseRuleTest.java create mode 100644 controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/HttpClients.java create mode 100644 controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Listener.java create mode 100644 controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/NamedRunner.java create mode 100644 controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Rules.java create mode 100644 controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/SimulatorException.java create mode 100644 controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Simulators.java create mode 100644 controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TestNames.java create mode 100644 controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TopicException.java create mode 100644 controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Topics.java create mode 100644 controlloop/common/rules-test/src/main/resources/duplicates/duplicates.appc.success.json create mode 100644 controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.1.json create mode 100644 controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.2.json create mode 100644 controlloop/common/rules-test/src/main/resources/duplicates/tosca-compliant-duplicates.json create mode 100644 controlloop/common/rules-test/src/main/resources/service123/service123.appc.migrate.success.json create mode 100644 controlloop/common/rules-test/src/main/resources/service123/service123.appc.rebuild.failure.json create mode 100644 controlloop/common/rules-test/src/main/resources/service123/service123.appc.restart.failure.json create mode 100644 controlloop/common/rules-test/src/main/resources/service123/service123.onset.json create mode 100644 controlloop/common/rules-test/src/main/resources/service123/tosca-compliant-service123.json create mode 100644 controlloop/common/rules-test/src/main/resources/vcpe/tosca-compliant-vcpe.json create mode 100644 controlloop/common/rules-test/src/main/resources/vcpe/tosca-legacy-vcpe.json create mode 100644 controlloop/common/rules-test/src/main/resources/vcpe/vcpe.appc.success.json create mode 100644 controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.1.json create mode 100644 controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.2.json create mode 100644 controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.3.json create mode 100644 controlloop/common/rules-test/src/main/resources/vdns/tosca-compliant-vdns.json create mode 100644 controlloop/common/rules-test/src/main/resources/vdns/vdns.onset.json create mode 100644 controlloop/common/rules-test/src/main/resources/vfw/tosca-compliant-vfw.json create mode 100644 controlloop/common/rules-test/src/main/resources/vfw/tosca-vfw.json create mode 100644 controlloop/common/rules-test/src/main/resources/vfw/vfw.appc.success.json create mode 100644 controlloop/common/rules-test/src/main/resources/vfw/vfw.onset.json create mode 100644 controlloop/common/rules-test/src/main/resources/vlb/tosca-compliant-vlb.json create mode 100644 controlloop/common/rules-test/src/main/resources/vlb/tosca-vlb.json create mode 100644 controlloop/common/rules-test/src/main/resources/vlb/vlb.onset.json (limited to 'controlloop/common/rules-test/src/main') diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/BaseRuleTest.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/BaseRuleTest.java new file mode 100644 index 000000000..711a61738 --- /dev/null +++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/BaseRuleTest.java @@ -0,0 +1,496 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.common.rules.test; + +import static org.junit.Assert.assertEquals; + +import java.util.List; +import java.util.UUID; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import lombok.AccessLevel; +import lombok.Getter; +import org.junit.Test; +import org.onap.policy.appc.Request; +import org.onap.policy.appclcm.AppcLcmDmaapWrapper; +import org.onap.policy.common.utils.coder.Coder; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.common.utils.coder.StandardCoderInstantAsMillis; +import org.onap.policy.controlloop.ControlLoopNotificationType; +import org.onap.policy.controlloop.VirtualControlLoopNotification; +import org.onap.policy.drools.system.PolicyController; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; + +/** + * Superclass used for rule tests. + */ +public abstract class BaseRuleTest { + /* + * Canonical Topic Names. + */ + protected static final String DCAE_TOPIC = "DCAE_TOPIC"; + protected static final String APPC_LCM_WRITE_TOPIC = "APPC-LCM-WRITE"; + protected static final String POLICY_CL_MGT_TOPIC = "POLICY-CL-MGT"; + protected static final String APPC_LCM_READ_TOPIC = "APPC-LCM-READ"; + protected static final String APPC_CL_TOPIC = "APPC-CL"; + + /* + * Constants for each test case. + */ + + // service123 (i.e., multi-operation policy) + private static final String SERVICE123_TOSCA_COMPLIANT_POLICY = "service123/tosca-compliant-service123.json"; + private static final String SERVICE123_ONSET = "service123/service123.onset.json"; + private static final String SERVICE123_APPC_RESTART_FAILURE = "service123/service123.appc.restart.failure.json"; + private static final String SERVICE123_APPC_REBUILD_FAILURE = "service123/service123.appc.rebuild.failure.json"; + private static final String SERVICE123_APPC_MIGRATE_SUCCESS = "service123/service123.appc.migrate.success.json"; + + // duplicates (i.e., mutliple events in the engine at the same time) + private static final String DUPLICATES_TOSCA_COMPLIANT_POLICY = "duplicates/tosca-compliant-duplicates.json"; + private static final String DUPLICATES_ONSET_1 = "duplicates/duplicates.onset.1.json"; + private static final String DUPLICATES_ONSET_2 = "duplicates/duplicates.onset.2.json"; + private static final String DUPLICATES_APPC_SUCCESS = "duplicates/duplicates.appc.success.json"; + + // VCPE + private static final String VCPE_TOSCA_LEGACY_POLICY = "vcpe/tosca-legacy-vcpe.json"; + private static final String VCPE_TOSCA_COMPLIANT_POLICY = "vcpe/tosca-compliant-vcpe.json"; + private static final String VCPE_ONSET_1 = "vcpe/vcpe.onset.1.json"; + private static final String VCPE_ONSET_2 = "vcpe/vcpe.onset.2.json"; + private static final String VCPE_ONSET_3 = "vcpe/vcpe.onset.3.json"; + private static final String VCPE_APPC_SUCCESS = "vcpe/vcpe.appc.success.json"; + + // VDNS + private static final String VDNS_TOSCA_COMPLIANT_POLICY = "vdns/tosca-compliant-vdns.json"; + private static final String VDNS_ONSET = "vdns/vdns.onset.json"; + + // VFW + private static final String VFW_TOSCA_LEGACY_POLICY = "vfw/tosca-vfw.json"; + private static final String VFW_TOSCA_COMPLIANT_POLICY = "vfw/tosca-compliant-vfw.json"; + private static final String VFW_ONSET = "vfw/vfw.onset.json"; + private static final String VFW_APPC_SUCCESS = "vfw/vfw.appc.success.json"; + + // VLB + private static final String VLB_TOSCA_LEGACY_POLICY = "vlb/tosca-vlb.json"; + private static final String VLB_TOSCA_COMPLIANT_POLICY = "vlb/tosca-compliant-vlb.json"; + private static final String VLB_ONSET = "vlb/vlb.onset.json"; + + /* + * Coders used to decode requests and responses. + */ + private static final Coder APPC_LEGACY_CODER = new StandardCoderInstantAsMillis(); + private static final Coder APPC_LCM_CODER = new StandardCoder(); + + // these may be overridden by junit tests + private static Function ruleMaker = Rules::new; + private static Supplier httpClientMaker = HttpClients::new; + private static Supplier simMaker = Simulators::new; + private static Supplier topicMaker = Topics::new; + + protected static Rules rules; + protected static HttpClients httpClients; + protected static Simulators simulators; + + + // used to inject and wait for messages + @Getter(AccessLevel.PROTECTED) + private Topics topics; + + // used to wait for messages on SINK topics + protected Listener policyClMgt; + protected Listener appcClSink; + protected Listener appcLcmRead; + + protected PolicyController controller; + + /* + * Tosca Policy that was loaded. + */ + protected ToscaPolicy policy; + + + /** + * Initializes {@link #rules}, {@link #httpClients}, and {@link #simulators}. + * + * @param controllerName the rule controller name + */ + public static void initStatics(String controllerName) { + rules = ruleMaker.apply(controllerName); + httpClients = httpClientMaker.get(); + simulators = simMaker.get(); + } + + /** + * Destroys {@link #httpClients}, {@link #simulators}, and {@link #rules}. + */ + public static void finishStatics() { + httpClients.destroy(); + simulators.destroy(); + rules.destroy(); + } + + /** + * Initializes {@link #topics} and {@link #controller}. + */ + public void init() { + topics = topicMaker.get(); + controller = rules.getController(); + } + + /** + * Destroys {@link #topics} and resets the rule facts. + */ + public void finish() { + topics.destroy(); + rules.resetFacts(); + } + + // Service123 (i.e., Policy with multiple operations) + + /** + * Service123 with Tosca Compliant Policy. + */ + @Test + public void testService123Compliant() { + policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller); + appcLcmRead = topics.createListener(APPC_LCM_READ_TOPIC, AppcLcmDmaapWrapper.class, APPC_LCM_CODER); + + assertEquals(0, controller.getDrools().factCount(rules.getControllerName())); + policy = rules.setupPolicyFromFile(SERVICE123_TOSCA_COMPLIANT_POLICY); + assertEquals(2, controller.getDrools().factCount(rules.getControllerName())); + + // inject an ONSET event over the DCAE topic + topics.inject(DCAE_TOPIC, SERVICE123_ONSET); + + /* Wait to acquire a LOCK and a PDP-X PERMIT */ + waitForLockAndPermit(policy, policyClMgt); + + // restart request should be sent and fail four times (i.e., because retry=3) + for (int count = 0; count < 4; ++count) { + AppcLcmDmaapWrapper appcreq = appcLcmRead.await(req -> "restart".equals(req.getRpcName())); + + topics.inject(APPC_LCM_WRITE_TOPIC, SERVICE123_APPC_RESTART_FAILURE, + appcreq.getBody().getInput().getCommonHeader().getSubRequestId()); + } + + // rebuild request should be sent and fail once + AppcLcmDmaapWrapper appcreq = appcLcmRead.await(req -> "rebuild".equals(req.getRpcName())); + + topics.inject(APPC_LCM_WRITE_TOPIC, SERVICE123_APPC_REBUILD_FAILURE, + appcreq.getBody().getInput().getCommonHeader().getSubRequestId()); + + // migrate request should be sent and succeed + appcreq = appcLcmRead.await(req -> "migrate".equals(req.getRpcName())); + + topics.inject(APPC_LCM_WRITE_TOPIC, SERVICE123_APPC_MIGRATE_SUCCESS, + appcreq.getBody().getInput().getCommonHeader().getSubRequestId()); + + /* --- Operation Completed --- */ + + waitForOperationSuccess(); + + /* --- Transaction Completed --- */ + waitForFinalSuccess(policy, policyClMgt); + } + + // Duplicate events + + /** + * This test case tests the scenario where 3 events occur and 2 of the requests refer + * to the same target entity while the 3rd is for another entity. The expected result + * is that the event with the duplicate target entity will have a final success result + * for one of the events, and a rejected message for the one that was unable to obtain + * the lock. The event that is referring to a different target entity should be able + * to obtain a lock since it is a different target. After processing of all events + * there should only be the policy and params objects left in memory. + */ + @Test + public void testDuplicatesEvents() { + policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller); + appcLcmRead = topics.createListener(APPC_LCM_READ_TOPIC, AppcLcmDmaapWrapper.class, APPC_LCM_CODER); + + assertEquals(0, controller.getDrools().factCount(rules.getControllerName())); + policy = rules.setupPolicyFromFile(DUPLICATES_TOSCA_COMPLIANT_POLICY); + assertEquals(2, controller.getDrools().factCount(rules.getControllerName())); + + /* + * Inject ONSET events over the DCAE topic. First and last have the same target + * entity, but different request IDs - only one should succeed. The middle one is + * for a different target entity, so it should succeed. + */ + topics.inject(DCAE_TOPIC, DUPLICATES_ONSET_1, UUID.randomUUID().toString()); + topics.inject(DCAE_TOPIC, DUPLICATES_ONSET_2); + topics.inject(DCAE_TOPIC, DUPLICATES_ONSET_1, UUID.randomUUID().toString()); + + // one should immediately generate a FINAL failure + waitForFinal(policy, policyClMgt, ControlLoopNotificationType.FINAL_FAILURE); + + // should see two restarts + for (int count = 0; count < 2; ++count) { + AppcLcmDmaapWrapper appcreq = appcLcmRead.await(req -> "restart".equals(req.getRpcName())); + + // indicate success + topics.inject(APPC_LCM_WRITE_TOPIC, DUPLICATES_APPC_SUCCESS, + appcreq.getBody().getInput().getCommonHeader().getSubRequestId()); + } + + // should see two FINAL successes + VirtualControlLoopNotification notif1 = waitForFinalSuccess(policy, policyClMgt); + VirtualControlLoopNotification notif2 = waitForFinalSuccess(policy, policyClMgt); + + // get the list of target names so we can ensure there's one of each + List actual = List.of(notif1, notif2).stream().map(notif -> notif.getAai().get("generic-vnf.vnf-id")) + .sorted().collect(Collectors.toList()); + + assertEquals(List.of("duplicate-VNF", "vCPE_Infrastructure_vGMUX_demo_app").toString(), actual.toString()); + } + + // VCPE + + /** + * Sunny Day with Legacy Tosca Policy. + */ + @Test + public void testVcpeSunnyDayLegacy() { + appcLcmSunnyDay(VCPE_TOSCA_LEGACY_POLICY, VCPE_ONSET_1, "restart"); + } + + /** + * Sunny Day with Tosca Compliant Policy. + */ + @Test + public void testVcpeSunnyDayCompliant() { + appcLcmSunnyDay(VCPE_TOSCA_COMPLIANT_POLICY, VCPE_ONSET_1, "restart"); + } + + /** + * An ONSET flood prevention test that injects a few ONSETs at once. It attempts to + * simulate the flooding behavior of the DCAE TCA microservice. TCA could blast tens + * or hundreds of ONSETs within sub-second intervals. + */ + @Test + public void testVcpeOnsetFloodPrevention() { + appcLcmSunnyDay(VCPE_TOSCA_COMPLIANT_POLICY, List.of(VCPE_ONSET_1, VCPE_ONSET_2, VCPE_ONSET_3), "restart"); + } + + // VDNS + + /** + * Sunny Day with Tosca Compliant Policy. + */ + @Test + public void testVdnsSunnyDayCompliant() { + httpSunnyDay(VDNS_TOSCA_COMPLIANT_POLICY, VDNS_ONSET); + } + + // VFW + + /** + * VFW Sunny Day with Legacy Tosca Policy. + */ + @Test + public void testVfwSunnyDayLegacy() { + appcLegacySunnyDay(VFW_TOSCA_LEGACY_POLICY, VFW_ONSET, "ModifyConfig"); + } + + /** + * VFW Sunny Day with Tosca Compliant Policy. + */ + @Test + public void testVfwSunnyDayCompliant() { + appcLegacySunnyDay(VFW_TOSCA_COMPLIANT_POLICY, VFW_ONSET, "ModifyConfig"); + } + + // VLB + + /** + * Sunny Day with Legacy Tosca Policy. + */ + @Test + public void testVlbSunnyDayLegacy() { + httpSunnyDay(VLB_TOSCA_LEGACY_POLICY, VLB_ONSET); + } + + /** + * Sunny Day with Tosca Compliant Policy. + */ + @Test + public void testVlbSunnyDayCompliant() { + httpSunnyDay(VLB_TOSCA_COMPLIANT_POLICY, VLB_ONSET); + } + + /** + * Sunny day scenario for use cases that use APPC-LCM. + * + * @param policyFile file containing the ToscaPolicy to be loaded + * @param onsetFile file containing the ONSET to be injected + * @param operation expected APPC operation request + */ + protected void appcLcmSunnyDay(String policyFile, String onsetFile, String operation) { + appcLcmSunnyDay(policyFile, List.of(onsetFile), operation); + } + + /** + * Sunny day scenario for use cases that use APPC-LCM. + * + * @param policyFile file containing the ToscaPolicy to be loaded + * @param onsetFiles list of files containing the ONSET to be injected + * @param operation expected APPC operation request + */ + protected void appcLcmSunnyDay(String policyFile, List onsetFiles, String operation) { + policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller); + appcLcmRead = topics.createListener(APPC_LCM_READ_TOPIC, AppcLcmDmaapWrapper.class, APPC_LCM_CODER); + + assertEquals(0, controller.getDrools().factCount(rules.getControllerName())); + policy = rules.setupPolicyFromFile(policyFile); + assertEquals(2, controller.getDrools().factCount(rules.getControllerName())); + + // inject several ONSET events over the DCAE topic + for (String onsetFile : onsetFiles) { + topics.inject(DCAE_TOPIC, onsetFile); + } + + /* Wait to acquire a LOCK and a PDP-X PERMIT */ + waitForLockAndPermit(policy, policyClMgt); + + /* + * Ensure that an APPC RESTART request was sent in response to the matching ONSET + */ + AppcLcmDmaapWrapper appcreq = appcLcmRead.await(req -> operation.equals(req.getRpcName())); + + /* + * Inject a 400 APPC Response Return over the APPC topic, with appropriate + * subRequestId + */ + topics.inject(APPC_LCM_WRITE_TOPIC, VCPE_APPC_SUCCESS, + appcreq.getBody().getInput().getCommonHeader().getSubRequestId()); + + /* --- Operation Completed --- */ + + waitForOperationSuccess(); + + /* --- Transaction Completed --- */ + waitForFinalSuccess(policy, policyClMgt); + } + + /** + * Sunny day scenario for use cases that use Legacy APPC. + * + * @param policyFile file containing the ToscaPolicy to be loaded + * @param onsetFile file containing the ONSET to be injected + * @param operation expected APPC operation request + */ + protected void appcLegacySunnyDay(String policyFile, String onsetFile, String operation) { + policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller); + appcClSink = topics.createListener(APPC_CL_TOPIC, Request.class, APPC_LEGACY_CODER); + + assertEquals(0, controller.getDrools().factCount(rules.getControllerName())); + policy = rules.setupPolicyFromFile(policyFile); + assertEquals(2, controller.getDrools().factCount(rules.getControllerName())); + + /* Inject an ONSET event over the DCAE topic */ + topics.inject(DCAE_TOPIC, onsetFile); + + /* Wait to acquire a LOCK and a PDP-X PERMIT */ + waitForLockAndPermit(policy, policyClMgt); + + /* + * Ensure that an APPC RESTART request was sent in response to the matching ONSET + */ + Request appcreq = appcClSink.await(req -> operation.equals(req.getAction())); + + /* + * Inject a 400 APPC Response Return over the APPC topic, with appropriate + * subRequestId + */ + topics.inject(APPC_CL_TOPIC, VFW_APPC_SUCCESS, appcreq.getCommonHeader().getSubRequestId()); + + /* --- Operation Completed --- */ + + waitForOperationSuccess(); + + /* --- Transaction Completed --- */ + waitForFinalSuccess(policy, policyClMgt); + } + + /** + * Sunny day scenario for use cases that use an HTTP simulator. + * + * @param policyFile file containing the ToscaPolicy to be loaded + * @param onsetFile file containing the ONSET to be injected + * @param operation expected APPC operation request + */ + protected void httpSunnyDay(String policyFile, String onsetFile) { + policyClMgt = topics.createListener(POLICY_CL_MGT_TOPIC, VirtualControlLoopNotification.class, controller); + + assertEquals(0, controller.getDrools().factCount(rules.getControllerName())); + policy = rules.setupPolicyFromFile(policyFile); + assertEquals(2, controller.getDrools().factCount(rules.getControllerName())); + + /* Inject an ONSET event over the DCAE topic */ + topics.inject(DCAE_TOPIC, onsetFile); + + /* Wait to acquire a LOCK and a PDP-X PERMIT */ + waitForLockAndPermit(policy, policyClMgt); + + /* --- Operation Completed --- */ + + waitForOperationSuccess(); + + /* --- Transaction Completed --- */ + waitForFinalSuccess(policy, policyClMgt); + } + + /** + * Waits for a OPERATION SUCCESS transaction notification. + */ + protected void waitForOperationSuccess() { + policyClMgt.await(notif -> notif.getNotification() == ControlLoopNotificationType.OPERATION_SUCCESS); + } + + /** + * Waits for a FINAL SUCCESS transaction notification. + * + * @return the FINAL SUCCESS notification + */ + protected VirtualControlLoopNotification waitForFinalSuccess(ToscaPolicy policy, + Listener policyClMgt) { + + return this.waitForFinal(policy, policyClMgt, ControlLoopNotificationType.FINAL_SUCCESS); + } + + /** + * Waits for notifications for LOCK acquisition and GUARD Permit so that event + * processing may proceed. + */ + protected abstract void waitForLockAndPermit(ToscaPolicy policy, + Listener policyClMgt); + + /** + * Waits for a FINAL transaction notification. + * + * @param finalType FINAL_xxx type for which to wait + * + * @return the FINAL notification + */ + protected abstract VirtualControlLoopNotification waitForFinal(ToscaPolicy policy, + Listener policyClMgt, ControlLoopNotificationType finalType); +} diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/HttpClients.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/HttpClients.java new file mode 100644 index 000000000..eb729ad6d --- /dev/null +++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/HttpClients.java @@ -0,0 +1,67 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.common.rules.test; + +import org.onap.policy.common.endpoints.http.client.HttpClientConfigException; +import org.onap.policy.common.endpoints.http.client.HttpClientFactory; +import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance; +import org.onap.policy.drools.persistence.SystemPersistenceConstants; + +/** + * Mechanism by which junit tests can manage HTTP Clients. + */ +public class HttpClients { + + /** + * Constructs the object. + */ + public HttpClients() { + super(); + } + + + /** + * Adds Http Clients specified in the property file. + * + * @param propFilePrefix prefix prepended to "-http-client.properties" to yield the + * full name of the property file containing http client properties + */ + public void addClients(String propFilePrefix) { + try { + getClientFactory().build(SystemPersistenceConstants.getManager().getHttpClientProperties(propFilePrefix)); + } catch (HttpClientConfigException e) { + throw new IllegalArgumentException("cannot initialize HTTP clients", e); + } + } + + /** + * Destroys all Http Clients. + */ + public void destroy() { + getClientFactory().destroy(); + } + + // these methods may be overridden by junit tests + + protected HttpClientFactory getClientFactory() { + return HttpClientFactoryInstance.getClientFactory(); + } +} diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Listener.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Listener.java new file mode 100644 index 000000000..5042f54c6 --- /dev/null +++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Listener.java @@ -0,0 +1,180 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.common.rules.test; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Function; +import java.util.function.Predicate; +import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; +import org.onap.policy.common.endpoints.event.comm.TopicEndpoint; +import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager; +import org.onap.policy.common.endpoints.event.comm.TopicListener; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Listener for messages published on a topic SINK. + * + * @param message type + */ +public class Listener implements TopicListener { + private static final Logger logger = LoggerFactory.getLogger(Listener.class); + public static long DEFAULT_WAIT_SEC = 5L; + + private final TopicSink sink; + private final Function decoder; + private final BlockingQueue messages = new LinkedBlockingQueue<>(); + + /** + * Constructs the object. + * + * @param topicName name of the NOOP topic SINK on which to listen + * @param decoder function that takes a topic name and a message and decodes it into + * the desired type + */ + public Listener(String topicName, Function decoder) { + this.sink = getTopicManager().getNoopTopicSink(topicName); + this.decoder = decoder; + this.sink.register(this); + } + + /** + * Determines if there are any messages waiting. + * + * @return {@code true} if there are no messages waiting, {@code false} otherwise + */ + public boolean isEmpty() { + return messages.isEmpty(); + } + + /** + * Waits, for the default amount of time, for a message to be published to the topic. + * + * @return the message that was published + * @throws TopicException if interrupted or no message is received within the + * specified time + */ + public T await() { + return await(DEFAULT_WAIT_SEC, TimeUnit.SECONDS); + } + + /** + * Waits, for the specified period of time, for a message to be published to the + * topic. + * + * @param twait maximum time to wait + * @param unit time unit + * @return the message that was published + * @throws TopicException if interrupted or no message is received within the + * specified time + */ + public T await(long twait, TimeUnit unit) { + return await(twait, unit, msg -> true); + } + + /** + * Waits, for the default amount of time, for a message to be published to the topic. + * + * @param filter filter used to select the message of interest; preceding messages + * that do not pass the filter are discarded + * @return the message that was published + * @throws TopicException if interrupted or no message is received within the + * specified time + */ + public T await(Predicate filter) { + return await(DEFAULT_WAIT_SEC, TimeUnit.SECONDS, filter); + } + + /** + * Waits, for the specified period of time, for a message to be published to the + * topic. + * + * @param twait maximum time to wait + * @param unit time unit + * @param filter filter used to select the message of interest; preceding messages + * that do not pass the filter are discarded + * @return the message that was published + * @throws TopicException if interrupted or no message is received within the + * specified time + */ + public T await(long twait, TimeUnit unit, Predicate filter) { + long endMs = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(twait, unit); + + for (;;) { + try { + long remainingMs = endMs - System.currentTimeMillis(); + if (remainingMs < 0) { + throw new TimeoutException(); + } + + T msg = pollMessage(remainingMs); + if (msg == null) { + throw new TimeoutException(); + } + + if (filter.test(msg)) { + return msg; + } + + logger.info("message discarded by the filter on topic {}", sink.getTopic()); + + } catch (InterruptedException e) { + logger.warn("'await' interrupted on topic {}", sink.getTopic()); + Thread.currentThread().interrupt(); + throw new TopicException(e); + + } catch (TimeoutException e) { + logger.warn("'await' timed out on topic {}", sink.getTopic()); + throw new TopicException(e); + } + } + } + + /** + * Unregisters the listener. + */ + public void unregister() { + sink.unregister(this); + } + + @Override + public void onTopicEvent(CommInfrastructure commType, String topic, String event) { + try { + messages.add(decoder.apply(event)); + } catch (RuntimeException e) { + logger.warn("cannot decode message on topic {} for event {}", topic, event, e); + } + } + + // these methods may be overridden by junit tests + + protected TopicEndpoint getTopicManager() { + return TopicEndpointManager.getManager(); + } + + protected T pollMessage(long remainingMs) throws InterruptedException { + return messages.poll(remainingMs, TimeUnit.MILLISECONDS); + } +} diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/NamedRunner.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/NamedRunner.java new file mode 100644 index 000000000..1cbe5a56a --- /dev/null +++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/NamedRunner.java @@ -0,0 +1,85 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.common.rules.test; + +import org.junit.Ignore; +import org.junit.runner.Description; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; + +/** + * Runs tests listed via the {@link TestNames} annotation. + */ +public class NamedRunner extends BlockJUnit4ClassRunner { + + /** + * Constructs the object. + */ + public NamedRunner(Class testClass) throws InitializationError { + super(testClass); + } + + @Override + protected void runChild(final FrameworkMethod method, RunNotifier notifier) { + Description description = describeChild(method); + + if (method.getAnnotation(Ignore.class) != null) { + notifier.fireTestIgnored(description); + + } else if (!isNamed(description.getTestClass(), method.getName())) { + notifier.fireTestIgnored(description); + + } else { + runLeaf(methodBlock(method), description, notifier); + } + } + + /** + * Determines if the test is in the list of tests to be included. + * + * @param testClass class under test + * @param testName name of the test of interest + * @return {@code true} if the test is in the list, {@code false} otherwise + */ + private boolean isNamed(Class testClass, String testName) { + TestNames annot = testClass.getAnnotation(TestNames.class); + if (annot == null) { + // no annotation - everything passes + return true; + } + + for (String name : annot.names()) { + if (testName.equals(name)) { + return true; + } + } + + for (String prefix : annot.prefixes()) { + if (testName.startsWith(prefix)) { + return true; + } + } + + return false; + } +} diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Rules.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Rules.java new file mode 100644 index 000000000..7549c6be6 --- /dev/null +++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Rules.java @@ -0,0 +1,413 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.common.rules.test; + +import static org.awaitility.Awaitility.await; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; +import org.kie.api.event.rule.AfterMatchFiredEvent; +import org.kie.api.event.rule.BeforeMatchFiredEvent; +import org.kie.api.event.rule.DefaultAgendaEventListener; +import org.kie.api.event.rule.DefaultRuleRuntimeEventListener; +import org.kie.api.event.rule.MatchCancelledEvent; +import org.kie.api.event.rule.MatchCreatedEvent; +import org.kie.api.event.rule.ObjectDeletedEvent; +import org.kie.api.event.rule.ObjectInsertedEvent; +import org.kie.api.event.rule.ObjectUpdatedEvent; +import org.kie.api.event.rule.RuleRuntimeEventListener; +import org.kie.api.runtime.KieSession; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.common.utils.resources.ResourceUtils; +import org.onap.policy.controlloop.ControlLoopEvent; +import org.onap.policy.controlloop.drl.legacy.ControlLoopParams; +import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager; +import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2; +import org.onap.policy.drools.controller.DroolsController; +import org.onap.policy.drools.persistence.SystemPersistence; +import org.onap.policy.drools.persistence.SystemPersistenceConstants; +import org.onap.policy.drools.system.PolicyController; +import org.onap.policy.drools.system.PolicyControllerConstants; +import org.onap.policy.drools.system.PolicyControllerFactory; +import org.onap.policy.drools.system.PolicyEngine; +import org.onap.policy.drools.system.PolicyEngineConstants; +import org.onap.policy.drools.util.KieUtils; +import org.onap.policy.drools.utils.logging.LoggerUtil; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Mechanism by which junit tests can manage the rule engine. + */ +public class Rules { + private static final Logger logger = LoggerFactory.getLogger(Rules.class); + private static final StandardCoder coder = new StandardCoder(); + + /** + * PDP-D Engine. + */ + @Getter + private final PolicyEngine pdpd = makeEngine(); + + /** + * PDP-D Configuration Repository. + */ + @Getter + private final SystemPersistence pdpdRepo = makePdpdRepo(); + + + @Getter + private final String controllerName; + + @Getter + private PolicyController controller; + + + /** + * Constructs the object. + * + * @param controllerName name of the controller + */ + public Rules(String controllerName) { + this.controllerName = controllerName; + } + + /** + * Configures various items, including the PDP-D Engine. + * + * @param resourceDir path to resource directory + */ + public void configure(String resourceDir) { + pdpdRepo.setConfigurationDir("src/test/resources/config"); + + try { + File kmoduleFile = new File(resourceDir + "/META-INF/kmodule.xml"); + File pomFile = new File("src/test/resources/" + controllerName + ".pom"); + String resourceDir2 = resourceDir + "/org/onap/policy/controlloop/"; + File ruleFile = new File(resourceDir + "/" + controllerName + ".drl"); + List ruleFiles = Collections.singletonList(ruleFile); + + installArtifact(kmoduleFile, pomFile, resourceDir2, ruleFiles); + } catch (IOException e) { + throw new IllegalArgumentException("cannot configure KIE session for " + controllerName, e); + } + + setupLogging(); + + pdpd.configure(new Properties()); + } + + /** + * Starts various items, including the PDP-D Engine. + */ + public void start() { + controller = pdpd.createPolicyController(controllerName, pdpdRepo.getControllerProperties(controllerName)); + pdpd.start(); + + setupDroolsLogging(); + } + + /** + * Stop PDP-D. + */ + public void destroy() { + getControllerFactory().shutdown(controllerName); + pdpd.stop(); + } + + /** + * Removes various facts from working memory, including the Policy and Params, as well + * as any event managers and events. + */ + public void resetFacts() { + List> classes = List.of(ToscaPolicy.class, ControlLoopParams.class, ControlLoopEventManager2.class, + ControlLoopEvent.class); + + // delete all objects of the listed classes + DroolsController drools = controller.getDrools(); + classes.forEach(drools::delete); + + // wait for them to be deleted + for (Class clazz : classes) { + await(clazz.getSimpleName()).atMost(5, TimeUnit.SECONDS) + .until(() -> drools.facts(controllerName, clazz).isEmpty()); + } + + /* + * We can't delete this class directly; we have to wait for the rules to clean it + * up, because the rule also cleans up a number of other associated objects. + */ + Class clazz = ControlLoopEventManager.class; + await(clazz.getSimpleName()).atMost(5, TimeUnit.SECONDS) + .until(() -> drools.facts(controllerName, clazz).isEmpty()); + } + + /** + * Installs a policy from policy/models (examples) repo. + */ + public ToscaPolicy setupPolicyFromTemplate(String templatePath, String policyName) { + try { + return setupPolicy(getPolicyFromTemplate(templatePath, policyName)); + + } catch (InterruptedException | CoderException e) { + throw new IllegalArgumentException("policy " + policyName, e); + } + } + + private ToscaPolicy getPolicyFromTemplate(String resourcePath, String policyName) throws CoderException { + String policyJson = ResourceUtils.getResourceAsString(resourcePath); + if (policyJson == null) { + throw new CoderException(new FileNotFoundException(resourcePath)); + } + + ToscaServiceTemplate serviceTemplate = coder.decode(policyJson, ToscaServiceTemplate.class); + ToscaPolicy policy = serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName); + assertNotNull(policy); + + /* + * name and version are used within a drl. api component and drools core will + * ensure that these are populated. + */ + if (StringUtils.isBlank(policy.getName())) { + policy.setName(policyName); + } + + if (StringUtils.isBlank(policy.getVersion())) { + policy.setVersion(policy.getTypeVersion()); + } + + return serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName); + } + + /** + * Installs a given policy. + */ + public ToscaPolicy setupPolicyFromFile(String policyPath) { + try { + return setupPolicy(getPolicyFromFile(policyPath)); + + } catch (InterruptedException | IOException | CoderException e) { + throw new IllegalArgumentException("policy " + policyPath, e); + } + } + + private ToscaPolicy getPolicyFromFile(String policyPath) throws IOException, CoderException { + String policyJson = ResourceUtils.getResourceAsString(policyPath); + if (policyJson == null) { + throw new CoderException(new FileNotFoundException(policyPath)); + } + + return coder.decode(policyJson, ToscaPolicy.class); + } + + private ToscaPolicy setupPolicy(ToscaPolicy policy) throws InterruptedException { + final KieObjectExpectedCallback policyTracker = new KieObjectInsertedExpectedCallback<>(policy); + final KieObjectExpectedCallback paramsTracker = + new KieClassInsertedExpectedCallback<>(ControlLoopParams.class); + + controller.getDrools().offer(policy); + + assertTrue(policyTracker.isNotified()); + assertTrue(paramsTracker.isNotified()); + + assertEquals(1, controller.getDrools().facts(controllerName, ToscaPolicy.class).stream() + .filter((anotherPolicy) -> anotherPolicy == policy).count()); + + assertEquals(1, controller.getDrools().facts(controllerName, ControlLoopParams.class).stream() + .filter((params) -> params.getToscaPolicy() == policy).count()); + return policy; + } + + /** + * Sets up overall logging. + */ + private void setupLogging() { + LoggerUtil.setLevel(LoggerUtil.ROOT_LOGGER, "WARN"); + LoggerUtil.setLevel("org.eclipse.jetty", "WARN"); + LoggerUtil.setLevel("org.onap.policy.controlloop", "INFO"); + LoggerUtil.setLevel("network", "INFO"); + } + + /** + * Sets up Drools Logging for events of interest. + */ + private void setupDroolsLogging() { + KieSession session = getKieSession(); + + session.addEventListener(new RuleListenerLogger()); + session.addEventListener(new AgendaListenerLogger()); + } + + /** + * Logs Modifications to Working Memory. + */ + private static class RuleListenerLogger implements RuleRuntimeEventListener { + @Override + public void objectInserted(ObjectInsertedEvent event) { + String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null"; + logger.info("RULE {}: inserted {}", ruleName, event.getObject()); + } + + @Override + public void objectUpdated(ObjectUpdatedEvent event) { + String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null"; + logger.info("RULE {}: updated {}", ruleName, event.getObject()); + + } + + @Override + public void objectDeleted(ObjectDeletedEvent event) { + String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null"; + logger.info("RULE {}: deleted {}", ruleName, event.getOldObject()); + } + } + + /** + * Logs Rule Matches. + */ + private static class AgendaListenerLogger extends DefaultAgendaEventListener { + @Override + public void matchCreated(MatchCreatedEvent event) { + logger.info("RULE {}: match created", event.getMatch().getRule().getName()); + } + + @Override + public void matchCancelled(MatchCancelledEvent event) { + logger.info("RULE {}: match cancelled", event.getMatch().getRule().getName()); + } + + @Override + public void beforeMatchFired(BeforeMatchFiredEvent event) { + logger.info("RULE {}: before match fired", event.getMatch().getRule().getName()); + } + + @Override + public void afterMatchFired(AfterMatchFiredEvent event) { + logger.info("RULE {}: after match fired", event.getMatch().getRule().getName()); + } + } + + /** + * Base Class to track Working Memory updates for objects of type T. + */ + private abstract class KieObjectExpectedCallback extends DefaultRuleRuntimeEventListener { + protected T subject; + + protected CountDownLatch countDownLatch = new CountDownLatch(1); + + public KieObjectExpectedCallback(T affected) { + subject = affected; + register(); + } + + public boolean isNotified() throws InterruptedException { + return countDownLatch.await(9L, TimeUnit.SECONDS); + } + + protected void callbacked() { + unregister(); + countDownLatch.countDown(); + } + + public KieObjectExpectedCallback register() { + getKieSession().addEventListener(this); + return this; + } + + public KieObjectExpectedCallback unregister() { + getKieSession().removeEventListener(this); + return this; + } + } + + /** + * Tracks inserts in Working Memory for an object of type T. + */ + private class KieObjectInsertedExpectedCallback extends KieObjectExpectedCallback { + public KieObjectInsertedExpectedCallback(T affected) { + super(affected); + } + + @Override + public void objectInserted(ObjectInsertedEvent event) { + if (subject == event.getObject()) { + callbacked(); + } + } + } + + /** + * Tracks inserts in Working Memory for any object of class T. + */ + private class KieClassInsertedExpectedCallback extends KieObjectInsertedExpectedCallback { + + public KieClassInsertedExpectedCallback(T affected) { + super(affected); + } + + public void objectInserted(ObjectInsertedEvent event) { + if (subject == event.getObject().getClass()) { + callbacked(); + } + } + } + + // these may be overridden by junit tests + + + protected PolicyEngine makeEngine() { + return PolicyEngineConstants.getManager(); + } + + + protected SystemPersistence makePdpdRepo() { + return SystemPersistenceConstants.getManager(); + } + + protected KieSession getKieSession() { + return getControllerFactory().get(controllerName).getDrools().getContainer().getPolicySession(controllerName) + .getKieSession(); + } + + protected PolicyControllerFactory getControllerFactory() { + return PolicyControllerConstants.getFactory(); + } + + protected void installArtifact(File kmoduleFile, File pomFile, String resourceDir, List ruleFiles) + throws IOException { + + KieUtils.installArtifact(kmoduleFile, pomFile, resourceDir, ruleFiles); + } +} diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/SimulatorException.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/SimulatorException.java new file mode 100644 index 000000000..f9880d07a --- /dev/null +++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/SimulatorException.java @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.common.rules.test; + +/** + * Exception thrown by Simulators. + */ +public class SimulatorException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public SimulatorException() { + super(); + } + + public SimulatorException(String message) { + super(message); + } + + public SimulatorException(Throwable cause) { + super(cause); + } + + public SimulatorException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Simulators.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Simulators.java new file mode 100644 index 000000000..4a325a447 --- /dev/null +++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Simulators.java @@ -0,0 +1,85 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.common.rules.test; + +import java.util.LinkedList; +import java.util.List; +import lombok.AccessLevel; +import lombok.Getter; +import org.onap.policy.common.endpoints.http.server.HttpServletServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Simulator container. + */ +public class Simulators { + private static final Logger logger = LoggerFactory.getLogger(Simulators.class); + + @Getter(AccessLevel.PROTECTED) + private final List servers = new LinkedList<>(); + + /** + * Constructs the object. + */ + public Simulators() { + super(); + } + + /** + * Invokes the given functions to start the simulators. Destroys all of the + * simulators if any fail to start. + * + * @param builders functions to invoke to build the simulators + */ + public void start(SimulatorBuilder... builders) { + try { + for (SimulatorBuilder builder : builders) { + servers.add(builder.build()); + } + } catch (InterruptedException e) { + logger.warn("interrupted building the simulators"); + destroy(); + Thread.currentThread().interrupt(); + throw new SimulatorException(e); + } + } + + /** + * Stops all of the simulators. + */ + public void destroy() { + for (HttpServletServer server : servers) { + try { + server.shutdown(); + } catch (RuntimeException e) { + logger.warn("error stopping simulator {}", server.getName(), e); + } + } + + servers.clear(); + } + + @FunctionalInterface + public static interface SimulatorBuilder { + public HttpServletServer build() throws InterruptedException; + } +} diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TestNames.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TestNames.java new file mode 100644 index 000000000..5f3d856ef --- /dev/null +++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TestNames.java @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.common.rules.test; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for a test CLASS. Used to identify the names of the test methods to be + * included. + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface TestNames { + /** + * Test method names to be included. + */ + String[] names() default {}; + + /** + * Prefixes of test method names to be included. + */ + String[] prefixes() default {}; +} diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TopicException.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TopicException.java new file mode 100644 index 000000000..01a866966 --- /dev/null +++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/TopicException.java @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.common.rules.test; + +/** + * Exception thrown by Topics. + */ +public class TopicException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public TopicException() { + super(); + } + + public TopicException(String message) { + super(message); + } + + public TopicException(Throwable cause) { + super(cause); + } + + public TopicException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Topics.java b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Topics.java new file mode 100644 index 000000000..f71559acb --- /dev/null +++ b/controlloop/common/rules-test/src/main/java/org/onap/policy/controlloop/common/rules/test/Topics.java @@ -0,0 +1,176 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.common.rules.test; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Function; +import org.onap.policy.common.endpoints.event.comm.TopicEndpoint; +import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager; +import org.onap.policy.common.utils.coder.Coder; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.resources.ResourceUtils; +import org.onap.policy.drools.protocol.coders.EventProtocolCoder; +import org.onap.policy.drools.protocol.coders.EventProtocolCoderConstants; +import org.onap.policy.drools.system.PolicyController; + +/** + * Mechanism by which junit tests can manage topic messages. + */ +public class Topics { + /** + * Wherever this string appears within an input file, it is replaced by a value passed + * as a parameter to the {@link #inject(String, String, String)} method. + */ + private static final String REPLACE_ME = "${replaceMe}"; + + /** + * Listeners that have been created and registered by "this" object. + */ + private final List> listeners = new LinkedList<>(); + + + /** + * Constructs the object. + */ + public Topics() { + super(); + } + + /** + * Unregisters all of the listeners. + */ + public void destroy() { + listeners.forEach(Listener::unregister); + } + + /** + * Injects the content of the given file onto a NOOP topic SOURCE. + * + * @param topicName topic on which to inject + * @param file file whose content is to be injected + */ + public void inject(String topicName, String file) { + inject(topicName, file, REPLACE_ME); + } + + /** + * Injects the content of the given file onto a NOOP topic SOURCE, with the given + * substitution. + * + * @param topicName topic on which to inject + * @param file file whose content is to be injected + * @param newText text to be substituted for occurrences of "${replaceMe}" in the + * source file + */ + public void inject(String topicName, String file, String newText) { + try { + String text = ResourceUtils.getResourceAsString(file); + if (text == null) { + throw new FileNotFoundException(file); + } + text = text.replace(REPLACE_ME, newText); + getTopicManager().getNoopTopicSource(topicName).offer(text); + } catch (IOException e) { + throw new TopicException(e); + } + } + + /** + * Creates a listener for messages published on a NOOP topic SINK. Messages are + * decoded using the coder associated with the controller. + * + * @param message type + * @param topicName name of the topic on which to listen + * @param expectedClass type of message expected + * @param controller controller whose decoders are to be used + * @return a new listener + */ + public Listener createListener(String topicName, Class expectedClass, PolicyController controller) { + EventProtocolCoder mgr = getProtocolCoder(); + String groupId = controller.getDrools().getGroupId(); + String artifactId = controller.getDrools().getArtifactId(); + + // @formatter:off + return createListener(topicName, + event -> expectedClass.cast(mgr.decode(groupId, artifactId, topicName, event))); + // @formatter:on + } + + /** + * Creates a listener for messages published on a NOOP topic SINK. Messages are + * decoded using the specified coder. + * + * @param message type + * @param topicName name of the topic on which to listen + * @param expectedClass type of message expected + * @param coder coder to decode the messages + * @return a new listener + */ + public Listener createListener(String topicName, Class expectedClass, Coder coder) { + Function decoder = event -> { + try { + return coder.decode(event, expectedClass); + } catch (CoderException e) { + throw new IllegalArgumentException("cannot decode message", e); + } + }; + + return createListener(topicName, decoder); + } + + /** + * Creates a listener for messages published on a NOOP topic SINK. Messages are + * decoded using the specified decoder. + * + * @param message type + * @param topicName name of the topic on which to listen + * @param decoder function that takes a message and decodes it into the desired type + * @return a new listener + */ + public Listener createListener(String topicName, Function decoder) { + Listener listener = makeListener(topicName, decoder); + listeners.add(listener); + + return listener; + } + + // these methods may be overridden by junit tests + + protected TopicEndpoint getTopicManager() { + return TopicEndpointManager.getManager(); + } + + protected EventProtocolCoder getProtocolCoder() { + return EventProtocolCoderConstants.getManager(); + } + + protected Listener makeListener(String topicName, Function decoder) { + return new Listener<>(topicName, decoder) { + @Override + protected TopicEndpoint getTopicManager() { + return Topics.this.getTopicManager(); + } + }; + } +} diff --git a/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.appc.success.json b/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.appc.success.json new file mode 100644 index 000000000..331021798 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.appc.success.json @@ -0,0 +1,22 @@ +{ + "body": { + "output": { + "common-header": { + "timestamp": "2017-08-25T21:06:23.037Z", + "api-ver": "5.00", + "originator-id": "664be3d2-6c12-4f4b-a3e7-c349acced200", + "request-id": "664be3d2-6c12-4f4b-a3e7-c349acced200", + "sub-request-id": "${replaceMe}", + "flags": {} + }, + "status": { + "code": 400, + "message": "Restart Successful" + } + } + }, + "version": "2.0", + "rpc-name": "restart", + "correlation-id": "664be3d2-6c12-4f4b-a3e7-c349acced200-1", + "type": "response" +} diff --git a/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.1.json b/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.1.json new file mode 100644 index 000000000..70f48442f --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.1.json @@ -0,0 +1,16 @@ +{ + "closedLoopControlName": "ControlLoop-Duplicates-48f0c2c3-a172-4192-9ae3-052274181b6e", + "closedLoopAlarmStart": 1463679805324, + "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca", + "closedLoopEventStatus": "ONSET", + "requestID": "${replaceMe}", + "target_type": "VNF", + "target": "generic-vnf.vnf-id", + "AAI": { + "vserver.is-closed-loop-disabled": "false", + "vserver.prov-status": "ACTIVE", + "generic-vnf.vnf-id": "duplicate-VNF" + }, + "from": "DCAE", + "version": "1.0.2" +} diff --git a/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.2.json b/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.2.json new file mode 100644 index 000000000..046243609 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/duplicates/duplicates.onset.2.json @@ -0,0 +1,16 @@ +{ + "closedLoopControlName": "ControlLoop-Duplicates-48f0c2c3-a172-4192-9ae3-052274181b6e", + "closedLoopAlarmStart": 1463679805324, + "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca", + "closedLoopEventStatus": "ONSET", + "requestID": "664be3d2-6c12-4f4b-a3e7-c349acced200", + "target_type": "VNF", + "target": "generic-vnf.vnf-id", + "AAI": { + "vserver.is-closed-loop-disabled": "false", + "vserver.prov-status": "ACTIVE", + "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app" + }, + "from": "DCAE", + "version": "1.0.2" +} diff --git a/controlloop/common/rules-test/src/main/resources/duplicates/tosca-compliant-duplicates.json b/controlloop/common/rules-test/src/main/resources/duplicates/tosca-compliant-duplicates.json new file mode 100644 index 000000000..d67472743 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/duplicates/tosca-compliant-duplicates.json @@ -0,0 +1,37 @@ +{ + "type": "onap.policies.controlloop.operational.common.Drools", + "type_version": "1.0.0", + "version": "1.0.0", + "name": "operational.restart", + "metadata": { + "policy-id": "operational.restart" + }, + "properties": { + "controllerName": "usecases", + "id": "ControlLoop-Duplicates-48f0c2c3-a172-4192-9ae3-052274181b6e", + "timeout": 3600, + "abatement": false, + "trigger": "unique-policy-id-1-restart", + "operations": [ + { + "id": "unique-policy-id-1-restart", + "description": "Restart the VM", + "operation": { + "actor": "APPC", + "operation": "Restart", + "target": { + "targetType": "VM" + } + }, + "timeout": 1200, + "retries": 3, + "success": "final_success", + "failure": "final_failure", + "failure_timeout": "final_failure_timeout", + "failure_retries": "final_failure_retries", + "failure_exception": "final_failure_exception", + "failure_guard": "final_failure_guard" + } + ] + } +} diff --git a/controlloop/common/rules-test/src/main/resources/service123/service123.appc.migrate.success.json b/controlloop/common/rules-test/src/main/resources/service123/service123.appc.migrate.success.json new file mode 100644 index 000000000..563e2c762 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/service123/service123.appc.migrate.success.json @@ -0,0 +1,22 @@ +{ + "body": { + "output": { + "common-header": { + "timestamp": "2017-08-25T21:06:23.037Z", + "api-ver": "5.00", + "originator-id": "664be3d2-6c12-4f4b-a3e7-c349acced200", + "request-id": "664be3d2-6c12-4f4b-a3e7-c349acced200", + "sub-request-id": "${replaceMe}", + "flags": {} + }, + "status": { + "code": 400, + "message": "Migrate Successful" + } + } + }, + "version": "2.0", + "rpc-name": "migrate", + "correlation-id": "664be3d2-6c12-4f4b-a3e7-c349acced200-1", + "type": "response" +} diff --git a/controlloop/common/rules-test/src/main/resources/service123/service123.appc.rebuild.failure.json b/controlloop/common/rules-test/src/main/resources/service123/service123.appc.rebuild.failure.json new file mode 100644 index 000000000..88e70ef7a --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/service123/service123.appc.rebuild.failure.json @@ -0,0 +1,22 @@ +{ + "body": { + "output": { + "common-header": { + "timestamp": "2017-08-25T21:06:23.037Z", + "api-ver": "5.00", + "originator-id": "664be3d2-6c12-4f4b-a3e7-c349acced200", + "request-id": "664be3d2-6c12-4f4b-a3e7-c349acced200", + "sub-request-id": "${replaceMe}", + "flags": {} + }, + "status": { + "code": 401, + "message": "Rebuild Failed" + } + } + }, + "version": "2.0", + "rpc-name": "rebuild", + "correlation-id": "664be3d2-6c12-4f4b-a3e7-c349acced200-1", + "type": "response" +} diff --git a/controlloop/common/rules-test/src/main/resources/service123/service123.appc.restart.failure.json b/controlloop/common/rules-test/src/main/resources/service123/service123.appc.restart.failure.json new file mode 100644 index 000000000..7376c8ad3 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/service123/service123.appc.restart.failure.json @@ -0,0 +1,22 @@ +{ + "body": { + "output": { + "common-header": { + "timestamp": "2017-08-25T21:06:23.037Z", + "api-ver": "5.00", + "originator-id": "664be3d2-6c12-4f4b-a3e7-c349acced200", + "request-id": "664be3d2-6c12-4f4b-a3e7-c349acced200", + "sub-request-id": "${replaceMe}", + "flags": {} + }, + "status": { + "code": 401, + "message": "Restart Failed" + } + } + }, + "version": "2.0", + "rpc-name": "restart", + "correlation-id": "664be3d2-6c12-4f4b-a3e7-c349acced200-1", + "type": "response" +} diff --git a/controlloop/common/rules-test/src/main/resources/service123/service123.onset.json b/controlloop/common/rules-test/src/main/resources/service123/service123.onset.json new file mode 100644 index 000000000..a78cb5337 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/service123/service123.onset.json @@ -0,0 +1,17 @@ +{ + "closedLoopControlName": "ControlLoop-Service123-cbed919f-2212-4ef7-8051-fe6308da1bda", + "closedLoopAlarmStart": 1463679805324, + "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca", + "closedLoopEventStatus": "ONSET", + "requestID": "664be3d2-6c12-4f4b-a3e7-c349acced200", + "target_type": "VNF", + "target": "generic-vnf.vnf-name", + "AAI": { + "vserver.is-closed-loop-disabled": "false", + "vserver.prov-status": "ACTIVE", + "generic-vnf.vnf-name": "fw0002vm002fw002", + "vserver.vserver-name": "OzVServer" + }, + "from": "DCAE", + "version": "1.0.2" +} diff --git a/controlloop/common/rules-test/src/main/resources/service123/tosca-compliant-service123.json b/controlloop/common/rules-test/src/main/resources/service123/tosca-compliant-service123.json new file mode 100644 index 000000000..4eaa9e70a --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/service123/tosca-compliant-service123.json @@ -0,0 +1,75 @@ +{ + "type": "onap.policies.controlloop.operational.common.Drools", + "type_version": "1.0.0", + "version": "1.0.0", + "name": "operational.service123", + "metadata": { + "policy-id": "operational.service123" + }, + "properties": { + "controllerName": "usecases", + "id": "ControlLoop-Service123-cbed919f-2212-4ef7-8051-fe6308da1bda", + "timeout": 60, + "abatement": true, + "trigger": "unique-policy-id-1-restart", + "operations": [ + { + "id": "unique-policy-id-1-restart", + "description": "Restart the VM", + "operation": { + "actor": "APPC", + "operation": "Restart", + "target": { + "targetType": "VM" + } + }, + "timeout": 20, + "retries": 3, + "success": "final_success", + "failure": "unique-policy-id-2-rebuild", + "failure_timeout": "unique-policy-id-2-rebuild", + "failure_retries": "unique-policy-id-2-rebuild", + "failure_guard": "unique-policy-id-2-rebuild", + "failure_exception": "final_failure_exception" + }, + { + "id": "unique-policy-id-2-rebuild", + "name": "Rebuild Policy", + "operation": { + "actor": "APPC", + "operation": "Rebuild", + "target": { + "targetType": "VM" + } + }, + "timeout": 10, + "retries": 0, + "success": "final_success", + "failure": "unique-policy-id-3-migrate", + "failure_timeout": "unique-policy-id-3-migrate", + "failure_retries": "unique-policy-id-3-migrate", + "failure_guard": "unique-policy-id-3-migrate", + "failure_exception": "final_failure_exception" + }, + { + "id": "unique-policy-id-3-migrate", + "name": "Migrate Policy", + "operation": { + "actor": "APPC", + "operation": "Migrate", + "target": { + "targetType": "VM" + } + }, + "timeout": 30, + "retries": 0, + "success": "final_success", + "failure": "final_failure", + "failure_timeout": "final_failure_timeout", + "failure_retries": "final_failure_retries", + "failure_guard": "final_failure_guard", + "failure_exception": "final_failure_exception" + } + ] + } +} diff --git a/controlloop/common/rules-test/src/main/resources/vcpe/tosca-compliant-vcpe.json b/controlloop/common/rules-test/src/main/resources/vcpe/tosca-compliant-vcpe.json new file mode 100644 index 000000000..b70145b68 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vcpe/tosca-compliant-vcpe.json @@ -0,0 +1,37 @@ +{ + "type": "onap.policies.controlloop.operational.common.Drools", + "type_version": "1.0.0", + "version": "1.0.0", + "name": "operational.restart", + "metadata": { + "policy-id": "operational.restart" + }, + "properties": { + "controllerName": "usecases", + "id": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e", + "timeout": 3600, + "abatement": false, + "trigger": "unique-policy-id-1-restart", + "operations": [ + { + "id": "unique-policy-id-1-restart", + "description": "Restart the VM", + "operation": { + "actor": "APPC", + "operation": "Restart", + "target": { + "targetType": "VM" + } + }, + "timeout": 1200, + "retries": 3, + "success": "final_success", + "failure": "final_failure", + "failure_timeout": "final_failure_timeout", + "failure_retries": "final_failure_retries", + "failure_exception": "final_failure_exception", + "failure_guard": "final_failure_guard" + } + ] + } +} diff --git a/controlloop/common/rules-test/src/main/resources/vcpe/tosca-legacy-vcpe.json b/controlloop/common/rules-test/src/main/resources/vcpe/tosca-legacy-vcpe.json new file mode 100644 index 000000000..cb0806e16 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vcpe/tosca-legacy-vcpe.json @@ -0,0 +1,9 @@ +{ + "type": "onap.policies.controlloop.Operational", + "type_version": "1.0.0", + "properties": { + "content": "controlLoop%3A%0A%20%20version%3A%202.0.0%0A%20%20controlLoopName%3A%20ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e%0A%20%20trigger_policy%3A%20unique-policy-id-1-restart%0A%20%20timeout%3A%203600%0A%20%20abatement%3A%20false%0A%20%0Apolicies%3A%0A%20%20-%20id%3A%20unique-policy-id-1-restart%0A%20%20%20%20name%3A%20Restart%20the%20VM%0A%20%20%20%20description%3A%0A%20%20%20%20actor%3A%20APPC%0A%20%20%20%20recipe%3A%20Restart%0A%20%20%20%20target%3A%0A%20%20%20%20%20%20type%3A%20VM%0A%20%20%20%20retry%3A%203%0A%20%20%20%20timeout%3A%201200%0A%20%20%20%20success%3A%20final_success%0A%20%20%20%20failure%3A%20final_failure%0A%20%20%20%20failure_timeout%3A%20final_failure_timeout%0A%20%20%20%20failure_retries%3A%20final_failure_retries%0A%20%20%20%20failure_exception%3A%20final_failure_exception%0A%20%20%20%20failure_guard%3A%20final_failure_guard" + }, + "name": "vcpe", + "version": "1.0.0" +} diff --git a/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.appc.success.json b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.appc.success.json new file mode 100644 index 000000000..331021798 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.appc.success.json @@ -0,0 +1,22 @@ +{ + "body": { + "output": { + "common-header": { + "timestamp": "2017-08-25T21:06:23.037Z", + "api-ver": "5.00", + "originator-id": "664be3d2-6c12-4f4b-a3e7-c349acced200", + "request-id": "664be3d2-6c12-4f4b-a3e7-c349acced200", + "sub-request-id": "${replaceMe}", + "flags": {} + }, + "status": { + "code": 400, + "message": "Restart Successful" + } + } + }, + "version": "2.0", + "rpc-name": "restart", + "correlation-id": "664be3d2-6c12-4f4b-a3e7-c349acced200-1", + "type": "response" +} diff --git a/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.1.json b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.1.json new file mode 100644 index 000000000..d08ee47cd --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.1.json @@ -0,0 +1,16 @@ +{ + "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e", + "closedLoopAlarmStart": 1463679805324, + "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca", + "closedLoopEventStatus": "ONSET", + "requestID": "664be3d2-6c12-4f4b-a3e7-c349acced200", + "target_type": "VNF", + "target": "generic-vnf.vnf-id", + "AAI": { + "vserver.is-closed-loop-disabled": "false", + "vserver.prov-status": "ACTIVE", + "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app" + }, + "from": "DCAE", + "version": "1.0.2" +} \ No newline at end of file diff --git a/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.2.json b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.2.json new file mode 100644 index 000000000..b8c76514e --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.2.json @@ -0,0 +1,16 @@ +{ + "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e", + "closedLoopAlarmStart": 1463679805324, + "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca", + "closedLoopEventStatus": "ONSET", + "requestID": "8cf3cd05-1218-4224-931b-601494ffe55b", + "target_type": "VNF", + "target": "generic-vnf.vnf-id", + "AAI": { + "vserver.is-closed-loop-disabled": "false", + "vserver.prov-status": "ACTIVE", + "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app" + }, + "from": "DCAE", + "version": "1.0.2" +} \ No newline at end of file diff --git a/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.3.json b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.3.json new file mode 100644 index 000000000..40f29b3cf --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vcpe/vcpe.onset.3.json @@ -0,0 +1,17 @@ +{ + "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e", + "closedLoopAlarmStart": 1570722876324905, + "closedLoopAlarmEnd": 1570722876324999, + "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca", + "closedLoopEventStatus": "ONSET", + "requestID": "8cf3cd05-1218-4224-931b-601494ffe55b", + "target_type": "VNF", + "target": "generic-vnf.vnf-id", + "AAI": { + "vserver.is-closed-loop-disabled": "false", + "vserver.prov-status": "ACTIVE", + "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app" + }, + "from": "DCAE", + "version": "1.0.2" +} \ No newline at end of file diff --git a/controlloop/common/rules-test/src/main/resources/vdns/tosca-compliant-vdns.json b/controlloop/common/rules-test/src/main/resources/vdns/tosca-compliant-vdns.json new file mode 100644 index 000000000..918e92770 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vdns/tosca-compliant-vdns.json @@ -0,0 +1,48 @@ +{ + "type": "onap.policies.controlloop.operational.common.Drools", + "type_version": "1.0.0", + "version": "1.0.0", + "name": "operational.scale.up", + "metadata": { + "policy-id": "operational.scale.up" + }, + "properties": { + "controllerName": "usecases", + "id": "ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3", + "timeout": 60, + "abatement": false, + "trigger": "unique-policy-id-1-scale-up", + "operations": [ + { + "id": "unique-policy-id-1-scale-up", + "description": "Scale up", + "operation": { + "actor": "SO", + "operation": "VF Module Create", + "target": { + "targetType": "VFMODULE", + "entityIds": { + "modelInvariantId": "e6130d03-56f1-4b0a-9a1d-e1b2ebc30e0e", + "modelVersionId": "94b18b1d-cc91-4f43-911a-e6348665f292", + "modelName": "VfwclVfwsnkBbefb8ce2bde..base_vfw..module-0", + "modelVersion": 1, + "modelCustomizationId": "47958575-138f-452a-8c8d-d89b595f8164" + } + }, + "payload": { + "requestParameters": "{\"usePreload\":true,\"userParams\":[]}", + "configurationParameters": "[{\"ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[9]\",\"oam-ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[16]\",\"enabled\":\"$.vf-module-topology.vf-module-parameters.param[23]\"}]" + } + }, + "timeout": 30, + "retries": 0, + "success": "final_success", + "failure": "final_failure", + "failure_timeout": "final_failure_timeout", + "failure_retries": "final_failure_retries", + "failure_exception": "final_failure_exception", + "failure_guard": "final_failure_guard" + } + ] + } +} diff --git a/controlloop/common/rules-test/src/main/resources/vdns/vdns.onset.json b/controlloop/common/rules-test/src/main/resources/vdns/vdns.onset.json new file mode 100644 index 000000000..13f690952 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vdns/vdns.onset.json @@ -0,0 +1,16 @@ +{ + "closedLoopControlName": "ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3", + "closedLoopAlarmStart": 1463679805324, + "closedLoopEventClient": "microservice.stringmatcher", + "closedLoopEventStatus": "ONSET", + "requestID": "c7c6a4aa-bb61-4a15-b831-ba1472dd4a65", + "target_type": "VNF", + "target": "vserver.vserver-name", + "AAI": { + "vserver.is-closed-loop-disabled": "false", + "vserver.prov-status": "ACTIVE", + "vserver.vserver-name": "OzVServer" + }, + "from": "DCAE", + "version": "1.0.2" +} diff --git a/controlloop/common/rules-test/src/main/resources/vfw/tosca-compliant-vfw.json b/controlloop/common/rules-test/src/main/resources/vfw/tosca-compliant-vfw.json new file mode 100644 index 000000000..f55145658 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vfw/tosca-compliant-vfw.json @@ -0,0 +1,40 @@ +{ + "type": "onap.policies.controlloop.operational.common.Drools", + "type_version": "1.0.0", + "name": "operational.modifyconfig", + "version": "1.0.0", + "metadata": { + "policy-id": "operational.modifyconfig" + }, + "properties": { + "controllerName": "usecases", + "id": "ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a", + "timeout": 60, + "abatement": false, + "trigger": "unique-policy-id-1-modifyConfig", + "operations": [ + { + "id": "unique-policy-id-1-modifyConfig", + "description": "Modify the packet generator", + "operation": { + "actor": "APPC", + "operation": "ModifyConfig", + "target": { + "targetType": "VNF", + "entityIds": { + "resourceID": "bbb3cefd-01c8-413c-9bdd-2b92f9ca3d38" + } + } + }, + "timeout": 300, + "retries": 0, + "success": "final_success", + "failure": "final_failure", + "failure_timeout": "final_failure_timeout", + "failure_retries": "final_failure_retries", + "failure_exception": "final_failure_exception", + "failure_guard": "final_failure_guard" + } + ] + } +} diff --git a/controlloop/common/rules-test/src/main/resources/vfw/tosca-vfw.json b/controlloop/common/rules-test/src/main/resources/vfw/tosca-vfw.json new file mode 100644 index 000000000..35a839698 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vfw/tosca-vfw.json @@ -0,0 +1,9 @@ +{ + "type": "onap.policies.controlloop.Operational", + "type_version": "1.0.0", + "properties": { + "content": "controlLoop%3A%0A%20%20version%3A%202.0.0%0A%20%20controlLoopName%3A%20ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a%0A%20%20services%3A%0A%20%20%20%20-%20serviceInvariantUUID%3A%20f6937c86-584c-47ae-ad29-8d41d6f0cc7c%0A%20%20%20%20%20%20serviceUUID%3A%207be584e2-0bb2-4126-adaf-ced2c77ca0b3%0A%20%20%20%20%20%20serviceName%3A%20Service_Ete_Name7ba1fbde-6187-464a-a62d-d9dd25bdf4e8%0A%20%20trigger_policy%3A%20unique-policy-id-1-modifyConfig%0A%20%20timeout%3A%2060%0A%20%20abatement%3A%20false%0A%20%0Apolicies%3A%0A%20%20-%20id%3A%20unique-policy-id-1-modifyConfig%0A%20%20%20%20name%3A%20modify%20packet%20gen%20config%0A%20%20%20%20description%3A%0A%20%20%20%20actor%3A%20APPC%0A%20%20%20%20recipe%3A%20ModifyConfig%0A%20%20%20%20target%3A%0A%20%20%20%20%20%20resourceID%3A%20bbb3cefd-01c8-413c-9bdd-2b92f9ca3d38%0A%20%20%20%20%20%20type%3A%20VNF%0A%20%20%20%20retry%3A%200%0A%20%20%20%20timeout%3A%2030%0A%20%20%20%20success%3A%20final_success%0A%20%20%20%20failure%3A%20final_failure%0A%20%20%20%20failure_timeout%3A%20final_failure_timeout%0A%20%20%20%20failure_retries%3A%20final_failure_retries%0A%20%20%20%20failure_exception%3A%20final_failure_exception%0A%20%20%20%20failure_guard%3A%20final_failure_guard" + }, + "name": "vfw", + "version": "1.0.0" +} diff --git a/controlloop/common/rules-test/src/main/resources/vfw/vfw.appc.success.json b/controlloop/common/rules-test/src/main/resources/vfw/vfw.appc.success.json new file mode 100644 index 000000000..eb19c6832 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vfw/vfw.appc.success.json @@ -0,0 +1,17 @@ +{ + "CommonHeader": { + "TimeStamp": 1506051879001, + "APIver": "1.01", + "RequestID": "c7c6a4aa-bb61-4a15-b831-ba1472dd4a65", + "SubRequestID": "${replaceMe}", + "RequestTrack": [], + "Flags": [] + }, + "Status": { + "Code": 400, + "Value": "SUCCESS" + }, + "Payload": { + "generic-vnf.vnf-id": "jimmy-test-vnf2" + } +} diff --git a/controlloop/common/rules-test/src/main/resources/vfw/vfw.onset.json b/controlloop/common/rules-test/src/main/resources/vfw/vfw.onset.json new file mode 100644 index 000000000..7782867a1 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vfw/vfw.onset.json @@ -0,0 +1,17 @@ +{ + "closedLoopControlName": "ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a", + "closedLoopAlarmStart": 1463679805324, + "closedLoopEventClient": "microservice.stringmatcher", + "closedLoopEventStatus": "ONSET", + "requestID": "c7c6a4aa-bb61-4a15-b831-ba1472dd4a65", + "target_type": "VNF", + "target": "generic-vnf.vnf-name", + "AAI": { + "vserver.is-closed-loop-disabled": "false", + "vserver.prov-status": "ACTIVE", + "generic-vnf.vnf-name": "fw0002vm002fw002", + "vserver.vserver-name": "OzVServer" + }, + "from": "DCAE", + "version": "1.0.2" +} diff --git a/controlloop/common/rules-test/src/main/resources/vlb/tosca-compliant-vlb.json b/controlloop/common/rules-test/src/main/resources/vlb/tosca-compliant-vlb.json new file mode 100644 index 000000000..8a3b64a0a --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vlb/tosca-compliant-vlb.json @@ -0,0 +1,48 @@ +{ + "type": "onap.policies.controlloop.operational.common.Drools", + "type_version": "1.0.0", + "name": "operational.scaleout", + "version": "1.0.0", + "metadata": { + "policy-id": "operational.scaleout" + }, + "properties": { + "controllerName": "usecases", + "id": "ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3", + "timeout": 1200, + "abatement": false, + "trigger": "unique-policy-id-1-scale-up", + "operations": [ + { + "id": "unique-policy-id-1-scale-up", + "description": "Create a new VF Module", + "operation": { + "actor": "SO", + "operation": "VF Module Create", + "target": { + "targetType": "VFMODULE", + "entityIds": { + "modelInvariantId": "e6130d03-56f1-4b0a-9a1d-e1b2ebc30e0e", + "modelVersionId": "94b18b1d-cc91-4f43-911a-e6348665f292", + "modelName": "VfwclVfwsnkBbefb8ce2bde..base_vfw..module-0", + "modelVersion": 1, + "modelCustomizationId": "47958575-138f-452a-8c8d-d89b595f8164" + } + }, + "payload": { + "requestParameters": "{\"usePreload\":true,\"userParams\":[]}", + "configurationParameters": "[{\"ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[9]\",\"oam-ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[16]\",\"enabled\":\"$.vf-module-topology.vf-module-parameters.param[23]\"}]" + } + }, + "timeout": 1200, + "retries": 0, + "success": "final_success", + "failure": "final_failure", + "failure_timeout": "final_failure_timeout", + "failure_retries": "final_failure_retries", + "failure_exception": "final_failure_exception", + "failure_guard": "final_failure_guard" + } + ] + } +} diff --git a/controlloop/common/rules-test/src/main/resources/vlb/tosca-vlb.json b/controlloop/common/rules-test/src/main/resources/vlb/tosca-vlb.json new file mode 100644 index 000000000..c4f2b8179 --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vlb/tosca-vlb.json @@ -0,0 +1,9 @@ +{ + "type": "onap.policies.controlloop.Operational", + "type_version": "1.0.0", + "properties": { + "content": "controlLoop%3A%0A%20%20version%3A%202.0.0%0A%20%20controlLoopName%3A%20ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3%0A%20%20services%3A%0A%20%20%20%20-%20serviceName%3A%20d4738992-6497-4dca-9db9%0A%20%20%20%20%20%20serviceInvariantUUID%3A%20dc112d6e-7e73-4777-9c6f-1a7fb5fd1b6f%0A%20%20%20%20%20%20serviceUUID%3A%202eea06c6-e1d3-4c3a-b9c4-478c506eeedf%0A%20%20trigger_policy%3A%20unique-policy-id-1-scale-up%0A%20%20timeout%3A%2060%0A%0Apolicies%3A%0A%20%20-%20id%3A%20unique-policy-id-1-scale-up%0A%20%20%20%20name%3A%20Create%20a%20new%20VF%20Module%0A%20%20%20%20description%3A%0A%20%20%20%20actor%3A%20SO%0A%20%20%20%20recipe%3A%20VF%20Module%20Create%0A%20%20%20%20target%3A%0A%20%20%20%20%20%20type%3A%20VFMODULE%0A%20%20%20%20%20%20modelInvariantId%3A%20e6130d03-56f1-4b0a-9a1d-e1b2ebc30e0e%0A%20%20%20%20%20%20modelVersionId%3A%2094b18b1d-cc91-4f43-911a-e6348665f292%0A%20%20%20%20%20%20modelName%3A%20VfwclVfwsnkBbefb8ce2bde..base_vfw..module-0%0A%20%20%20%20%20%20modelVersion%3A%201%0A%20%20%20%20%20%20modelCustomizationId%3A%2047958575-138f-452a-8c8d-d89b595f8164%0A%20%20%20%20payload%3A%0A%20%20%20%20%20%20requestParameters%3A%20%27%7B%22usePreload%22%3Atrue%2C%22userParams%22%3A%5B%5D%7D%27%0A%20%20%20%20%20%20configurationParameters%3A%20%27%5B%7B%22ip-addr%22%3A%22%24.vf-module-topology.vf-module-parameters.param%5B9%5D%22%2C%22oam-ip-addr%22%3A%22%24.vf-module-topology.vf-module-parameters.param%5B16%5D%22%2C%22enabled%22%3A%22%24.vf-module-topology.vf-module-parameters.param%5B23%5D%22%7D%5D%27%0A%20%20%20%20retry%3A%200%0A%20%20%20%20timeout%3A%2030%0A%20%20%20%20success%3A%20final_success%0A%20%20%20%20failure%3A%20final_failure%0A%20%20%20%20failure_timeout%3A%20final_failure_timeout%0A%20%20%20%20failure_retries%3A%20final_failure_retries%0A%20%20%20%20failure_exception%3A%20final_failure_exception%0A%20%20%20%20failure_guard%3A%20final_failure_guard%0A" + }, + "name": "vlb", + "version": "1.0.0" +} diff --git a/controlloop/common/rules-test/src/main/resources/vlb/vlb.onset.json b/controlloop/common/rules-test/src/main/resources/vlb/vlb.onset.json new file mode 100644 index 000000000..23ad03cbb --- /dev/null +++ b/controlloop/common/rules-test/src/main/resources/vlb/vlb.onset.json @@ -0,0 +1,16 @@ +{ + "closedLoopControlName": "ControlLoop-vDNS-6f37f56d-a87d-4b85-b6a9-cc953cf779b3", + "closedLoopAlarmStart": 1484677482204798, + "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca", + "closedLoopEventStatus": "ONSET", + "requestID": "e4f95e0c-a013-4530-8e59-c5c5f9e539b6", + "target_type": "VNF", + "target": "vserver.vserver-name", + "AAI": { + "vserver.is-closed-loop-disabled": "false", + "vserver.prov-status": "ACTIVE", + "vserver.vserver-name": "OzVServer" + }, + "from": "DCAE", + "version": "1.0.2" +} -- cgit 1.2.3-korg