diff options
14 files changed, 184 insertions, 21 deletions
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFsm.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFsm.java index b99953ed..97f3574e 100644 --- a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFsm.java +++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFsm.java @@ -283,6 +283,10 @@ public class LifecycleFsm implements Startable { policiesMap.put(policy.getIdentifier(), policy); } + protected void undeployedPolicyAction(@NonNull ToscaPolicy policy) { + policiesMap.remove(policy.getIdentifier()); + } + /* ** Action Helpers ** */ private boolean startIo() { diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateActive.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateActive.java index d481b8bd..38ea0917 100644 --- a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateActive.java +++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateActive.java @@ -61,8 +61,6 @@ public class LifecycleStateActive extends LifecycleStateRunning { protected boolean deployPolicy(@NonNull PolicyController controller, @NonNull ToscaPolicy policy) { logger.info("{}: deploy {} into {}", this, policy.getIdentifier(), controller.getName()); - // TODO: This is the latest version - retract policy with same id but different version - if (!controller.offer(policy)) { return false; } @@ -75,8 +73,12 @@ public class LifecycleStateActive extends LifecycleStateRunning { protected boolean undeployPolicy(@NonNull PolicyController controller, @NonNull ToscaPolicy policy) { logger.info("{}: undeploy {} from {}", this, policy.getIdentifier(), controller.getName()); - // TODO: retract policy. + if (!controller.getDrools().delete(policy)) { + logger.warn("Policy {}:{}:{}:{} was not deployed.", + policy.getType(), policy.getTypeVersion(), policy.getName(), policy.getVersion()); + } + fsm.undeployedPolicyAction(policy); return true; } } diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateRunning.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateRunning.java index 405dbeba..ed200ea6 100644 --- a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateRunning.java +++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateRunning.java @@ -134,8 +134,15 @@ public abstract class LifecycleStateRunning extends LifecycleStateDefault { return true; } - boolean success = deployPolicies(policies); - return undeployPolicies(policies) && success; + // Note that PAP sends the list of all ACTIVE policies with every + // UPDATE message. First, we will undeploy all policies that are + // running but are not present in this list. This will include + // policies that are overridden by a different version. Second, + // we will deploy those policies that are not installed but + // resent in this list. + + boolean success = undeployPolicies(policies); + return deployPolicies(policies) && success; } protected boolean deployPolicies(List<ToscaPolicy> policies) { diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/ControllerSupport.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/ControllerSupport.java index 1beee552..d5ac423a 100644 --- a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/ControllerSupport.java +++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/ControllerSupport.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.nio.file.Paths; import java.util.List; import java.util.Properties; -import java.util.stream.Collectors; import lombok.Getter; import lombok.NonNull; import org.kie.api.builder.ReleaseId; @@ -103,10 +102,6 @@ public class ControllerSupport { public <T> List<T> getFacts(Class<T> clazz) { return PolicyController.factory.get(name) .getDrools() - .facts(SESSION_NAME, clazz.getCanonicalName(), false) - .stream() - .filter(clazz::isInstance) - .map(clazz::cast) - .collect(Collectors.toList()); + .facts(SESSION_NAME, clazz); } } diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateActiveTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateActiveTest.java index 32006425..b8afd200 100644 --- a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateActiveTest.java +++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateActiveTest.java @@ -33,6 +33,7 @@ import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import org.junit.Before; @@ -73,6 +74,7 @@ public class LifecycleStateActiveTest extends LifecycleStateRunningTest { change.setName(fsm.getName()); fsm.source.offer(new StandardCoder().encode(change)); + controllerSupport.getController().start(); } @Test @@ -197,7 +199,6 @@ public class LifecycleStateActiveTest extends LifecycleStateRunningTest { long interval = 2 * originalInterval; update.setPdpHeartbeatIntervalMs(interval * 1000L); - controllerSupport.getController().start(); fsm.start(controllerSupport.getController()); assertTrue(fsm.update(update)); @@ -207,20 +208,83 @@ public class LifecycleStateActiveTest extends LifecycleStateRunningTest { assertEquals("Z", fsm.getGroup()); assertEquals("z", fsm.getSubgroup()); - String rawPolicy = - new String(Files.readAllBytes(Paths.get("src/test/resources/tosca-policy.json"))); - ToscaPolicy toscaPolicy = new StandardCoder().decode(rawPolicy, ToscaPolicy.class); - update.setPolicies(Arrays.asList(toscaPolicy)); + String restartV1 = + new String(Files.readAllBytes(Paths.get("src/test/resources/tosca-policy-operational-restart.json"))); + ToscaPolicy toscaPolicyRestartV1 = new StandardCoder().decode(restartV1, ToscaPolicy.class); + update.setPolicies(Arrays.asList(toscaPolicyRestartV1)); + + // update with an operational.restart policy assertTrue(fsm.update(update)); assertEquals(1, fsm.policyTypesMap.size()); List<ToscaPolicy> factPolicies = controllerSupport.getFacts(ToscaPolicy.class); assertEquals(1, factPolicies.size()); - assertEquals(toscaPolicy, factPolicies.get(0)); + assertEquals(toscaPolicyRestartV1, factPolicies.get(0)); + assertEquals(1, fsm.policiesMap.size()); + + // dup update with the same operational.restart policy - nothing changes + + assertTrue(fsm.update(update)); + assertEquals(1, fsm.policyTypesMap.size()); + + factPolicies = controllerSupport.getFacts(ToscaPolicy.class); + assertEquals(1, factPolicies.size()); + assertEquals(toscaPolicyRestartV1, factPolicies.get(0)); assertEquals(1, fsm.policiesMap.size()); - controllerSupport.getController().stop(); + // undeploy operational.restart policy + + update.setPolicies(Collections.emptyList()); + assertTrue(fsm.update(update)); + assertEquals(1, fsm.policyTypesMap.size()); + + factPolicies = controllerSupport.getFacts(ToscaPolicy.class); + assertEquals(0, factPolicies.size()); + assertEquals(0, fsm.policiesMap.size()); + + // redeploy operational.restart policy + + update.setPolicies(Arrays.asList(toscaPolicyRestartV1)); + assertTrue(fsm.update(update)); + assertEquals(1, fsm.policyTypesMap.size()); + + factPolicies = controllerSupport.getFacts(ToscaPolicy.class); + assertEquals(1, factPolicies.size()); + assertEquals(toscaPolicyRestartV1, factPolicies.get(0)); + assertEquals(1, fsm.policiesMap.size()); + + // deploy a new version of the operational.restart policy + + String restartV2 = + new String(Files.readAllBytes(Paths.get("src/test/resources/tosca-policy-operational-restart.v2.json"))); + ToscaPolicy toscaPolicyRestartV2 = new StandardCoder().decode(restartV2, ToscaPolicy.class); + update.setPolicies(Arrays.asList(toscaPolicyRestartV2)); + assertTrue(fsm.update(update)); + assertEquals(1, fsm.policyTypesMap.size()); + + factPolicies = controllerSupport.getFacts(ToscaPolicy.class); + assertEquals(1, factPolicies.size()); + assertNotEquals(toscaPolicyRestartV1, factPolicies.get(0)); + assertEquals(toscaPolicyRestartV2, factPolicies.get(0)); + assertEquals(1, fsm.policiesMap.size()); + + // deploy another policy : firewall + + String firewall = + new String(Files.readAllBytes(Paths.get("src/test/resources/tosca-policy-operational-firewall.json"))); + ToscaPolicy toscaPolicyFirewall = new StandardCoder().decode(firewall, ToscaPolicy.class); + update.setPolicies(Arrays.asList(toscaPolicyRestartV2, toscaPolicyFirewall)); + assertTrue(fsm.update(update)); + assertEquals(1, fsm.policyTypesMap.size()); + + factPolicies = controllerSupport.getFacts(ToscaPolicy.class); + assertEquals(2, factPolicies.size()); + assertTrue(factPolicies.stream().noneMatch((ff) -> Objects.equals(toscaPolicyRestartV1, ff))); + assertTrue(factPolicies.stream().anyMatch((ff) -> Objects.equals(toscaPolicyRestartV2, ff))); + assertTrue(factPolicies.stream().anyMatch((ff) -> Objects.equals(toscaPolicyFirewall, ff))); + assertEquals(2, fsm.policiesMap.size()); + fsm.shutdown(); } } diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStatePassiveTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStatePassiveTest.java index 100bcef5..775dc8b0 100644 --- a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStatePassiveTest.java +++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStatePassiveTest.java @@ -187,7 +187,7 @@ public class LifecycleStatePassiveTest extends LifecycleStateRunningTest { assertBasicPassive(); String rawPolicy = - new String(Files.readAllBytes(Paths.get("src/test/resources/tosca-policy.json"))); + new String(Files.readAllBytes(Paths.get("src/test/resources/tosca-policy-operational-restart.json"))); ToscaPolicy toscaPolicy = new StandardCoder().decode(rawPolicy, ToscaPolicy.class); update.setPolicies(Arrays.asList(toscaPolicy)); diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateRunningTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateRunningTest.java index d7bb6d75..6da9dc12 100644 --- a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateRunningTest.java +++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateRunningTest.java @@ -41,7 +41,7 @@ public abstract class LifecycleStateRunningTest { public static void setUp() throws IOException { LoggerUtil.setLevel(LoggerUtil.ROOT_LOGGER, "INFO"); LoggerUtil.setLevel("org.onap.policy.common.endpoints", "WARN"); - LoggerUtil.setLevel("org.onap.policy.drools", "WARN"); + LoggerUtil.setLevel("org.onap.policy.drools", "INFO"); SystemPersistence.manager.setConfigurationDir("src/test/resources"); controllerSupport.createController(); } diff --git a/feature-lifecycle/src/test/resources/tosca-policy-operational-firewall.json b/feature-lifecycle/src/test/resources/tosca-policy-operational-firewall.json new file mode 100644 index 00000000..7bfb16bb --- /dev/null +++ b/feature-lifecycle/src/test/resources/tosca-policy-operational-firewall.json @@ -0,0 +1,9 @@ +{ + "type": "onap.policies.controlloop.Operational", + "typeVersion": "1.0.0", + "properties": { + "content": "controlLoop%3A%0D%0A++version%3A+2.0.0%0D%0A++controlLoopName%3A+ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a%0D%0A++trigger_policy%3A+unique-policy-id-1-modifyConfig%0D%0A++timeout%3A+1200%0D%0A++abatement%3A+false%0D%0A+%0D%0Apolicies%3A%0D%0A++-+id%3A+unique-policy-id-1-modifyConfig%0D%0A++++name%3A+modify+packet+gen+config%0D%0A++++description%3A%0D%0A++++actor%3A+APPC%0D%0A++++recipe%3A+ModifyConfig%0D%0A++++target%3A%0D%0A++++++%23+TBD+-+Cannot+be+known+until+instantiation+is+done%0D%0A++++++resourceID%3A+Eace933104d443b496b8.nodes.heat.vpg%0D%0A++++++type%3A+VNF%0D%0A++++retry%3A+0%0D%0A++++timeout%3A+300%0D%0A++++success%3A+final_success%0D%0A++++failure%3A+final_failure%0D%0A++++failure_timeout%3A+final_failure_timeout%0D%0A++++failure_retries%3A+final_failure_retries%0D%0A++++failure_exception%3A+final_failure_exception%0D%0A++++failure_guard%3A+final_failure_guard" + }, + "name": "operational.firewall", + "version": "1.0.0" +} diff --git a/feature-lifecycle/src/test/resources/tosca-policy.json b/feature-lifecycle/src/test/resources/tosca-policy-operational-restart.json index 5258ca1c..5258ca1c 100644 --- a/feature-lifecycle/src/test/resources/tosca-policy.json +++ b/feature-lifecycle/src/test/resources/tosca-policy-operational-restart.json diff --git a/feature-lifecycle/src/test/resources/tosca-policy-operational-restart.v2.json b/feature-lifecycle/src/test/resources/tosca-policy-operational-restart.v2.json new file mode 100644 index 00000000..39d16181 --- /dev/null +++ b/feature-lifecycle/src/test/resources/tosca-policy-operational-restart.v2.json @@ -0,0 +1,9 @@ +{ + "type": "onap.policies.controlloop.Operational", + "typeVersion": "1.0.0", + "properties": { + "content": "controlLoop%3A%0A%20%20version%3A%202.0.0%0A%20%20controlLoopName%3A%20ControlLoop-vCPEv2-48f0c2c3-a172-4192-9ae3-052274181b6e%0A%20%20trigger_policy%3A%20unique-policy-id-1-restart%0A%20%20timeout%3A%203600%0A%20%20abatement%3A%20true%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": "operational.restart", + "version": "2.0.0" +} diff --git a/policy-management/lombok.config b/policy-management/lombok.config new file mode 100644 index 00000000..2384843f --- /dev/null +++ b/policy-management/lombok.config @@ -0,0 +1,3 @@ +config.stopBubbling = true +lombok.addLombokGeneratedAnnotation = true +lombok.nonNull.exceptionType = IllegalArgumentException diff --git a/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsController.java b/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsController.java index 344725ff..78a2f4e8 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsController.java +++ b/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsController.java @@ -22,7 +22,7 @@ package org.onap.policy.drools.controller; import java.util.List; import java.util.Map; - +import org.checkerframework.checker.nullness.qual.NonNull; import org.onap.policy.common.capabilities.Lockable; import org.onap.policy.common.capabilities.Startable; import org.onap.policy.common.endpoints.event.comm.TopicSink; @@ -215,6 +215,11 @@ public interface DroolsController extends Startable, Lockable { List<Object> facts(String sessionName, String className, boolean delete); /** + * Gets facts. + */ + <T> List<T> facts(@NonNull String sessionName, @NonNull Class<T> clazz); + + /** * gets the facts associated with a query for a give session for a given queried entity. * * @param sessionName the session @@ -228,6 +233,16 @@ public interface DroolsController extends Startable, Lockable { Object... queryParams); /** + * Deletes a fact from a session. + */ + <T> boolean delete(@NonNull String sessionName, @NonNull T fact); + + /** + * Delete a fact object of type T. + */ + <T> boolean delete(@NonNull T fact); + + /** * halts and permanently releases all resources. * */ diff --git a/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java b/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java index 95b053fb..a9ab212b 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java +++ b/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java @@ -27,7 +27,10 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; import org.apache.commons.collections4.queue.CircularFifoQueue; +import org.checkerframework.checker.nullness.qual.NonNull; import org.drools.core.ClassObjectFilter; import org.kie.api.definition.KiePackage; import org.kie.api.definition.rule.Query; @@ -833,6 +836,15 @@ public class MavenDroolsController implements DroolsController { } @Override + public <T> List<T> facts(@NonNull String sessionName, @NonNull Class<T> clazz) { + return facts(sessionName, clazz.getCanonicalName(), false) + .stream() + .filter(clazz::isInstance) + .map(clazz::cast) + .collect(Collectors.toList()); + } + + @Override public List<Object> factQuery(String sessionName, String queryName, String queriedEntity, boolean delete, Object... queryParams) { if (sessionName == null || sessionName.isEmpty()) { @@ -881,6 +893,33 @@ public class MavenDroolsController implements DroolsController { } @Override + public <T> boolean delete(@NonNull String sessionName, @NonNull T fact) { + String factClassName = fact.getClass().getName(); + + PolicySession session = getSession(sessionName); + KieSession kieSession = session.getKieSession(); + + Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(fact.getClass())); + for (FactHandle factHandle : factHandles) { + try { + if (Objects.equals(fact, kieSession.getObject(factHandle))) { + logger.info("Deleting {} from {}", factClassName, sessionName); + kieSession.delete(factHandle); + return true; + } + } catch (Exception e) { + logger.warn("Object cannot be retrieved from fact {}", factHandle, e); + } + } + return false; + } + + @Override + public <T> boolean delete(@NonNull T fact) { + return this.getSessionNames().stream().map((ss) -> delete(ss, fact)).reduce(false, Boolean::logicalOr); + } + + @Override public Class<?> fetchModelClass(String className) { return ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className); } diff --git a/policy-management/src/main/java/org/onap/policy/drools/controller/internal/NullDroolsController.java b/policy-management/src/main/java/org/onap/policy/drools/controller/internal/NullDroolsController.java index 815aaabb..5bf51efa 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/controller/internal/NullDroolsController.java +++ b/policy-management/src/main/java/org/onap/policy/drools/controller/internal/NullDroolsController.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.checkerframework.checker.nullness.qual.NonNull; import org.onap.policy.common.endpoints.event.comm.TopicSink; import org.onap.policy.drools.controller.DroolsController; import org.onap.policy.drools.core.PolicyContainer; @@ -181,12 +182,27 @@ public class NullDroolsController implements DroolsController { } @Override + public <T> List<T> facts(@NonNull String sessionName, @NonNull Class<T> clazz) { + return new ArrayList<>(); + } + + @Override public List<Object> factQuery(String sessionName, String queryName, String queriedEntity, boolean delete, Object... queryParams) { return new ArrayList<>(); } + @Override + public <T> boolean delete(@NonNull String sessionName, @NonNull T fact) { + return false; + } + + @Override + public <T> boolean delete(@NonNull T fact) { + return false; + } + private String makeInvokeMsg() { return this.getClass().getCanonicalName() + " invoked"; } |