diff options
20 files changed, 1139 insertions, 278 deletions
diff --git a/controlloop/templates/archetype-cl-amsterdam/src/main/resources/archetype-resources/src/main/resources/__closedLoopControlName__.drl b/controlloop/templates/archetype-cl-amsterdam/src/main/resources/archetype-resources/src/main/resources/__closedLoopControlName__.drl index cac512eac..53b4ca8bd 100644 --- a/controlloop/templates/archetype-cl-amsterdam/src/main/resources/archetype-resources/src/main/resources/__closedLoopControlName__.drl +++ b/controlloop/templates/archetype-cl-amsterdam/src/main/resources/archetype-resources/src/main/resources/__closedLoopControlName__.drl @@ -81,6 +81,8 @@ import org.slf4j.Logger; import java.time.Instant; import java.util.LinkedList; import java.util.Iterator; +import java.util.HashSet; +import java.util.Set; import org.onap.policy.drools.system.PolicyEngine; @@ -109,7 +111,8 @@ end */ declare ParamsCleaner closedLoopControlName : String - controlLoopYaml : String + identified : boolean // true if all active Params have been identified + active : Set // Params that are still active end /* @@ -127,7 +130,8 @@ end /* * -* Called once and only once to insert the parameters into working memory for this Closed Loop policy. +* Called to insert the parameters into working memory for this Closed Loop policy. This is called +* once each time a closed loop is added or its YAML is updated. * This has a higher salience so we can ensure that the Params is created before we have a chance to * discard any events. * @@ -143,6 +147,12 @@ rule "${policyName}.SETUP" params.setClosedLoopControlName("${closedLoopControlName}"); params.setControlLoopYaml("${controlLoopYaml}"); insert(params); + + ParamsCleaner cleaner = new ParamsCleaner(); + cleaner.setClosedLoopControlName("${closedLoopControlName}"); + cleaner.setIdentified(false); + cleaner.setActive(new HashSet()); + insert(cleaner); // Note: globals have bad behavior when persistence is used, // hence explicitly getting the logger vs using a global @@ -378,8 +388,9 @@ rule "${policyName}.EVENT.MANAGER" $params.getClosedLoopControlName(), drools.getRule().getName()); retract($manager.getOnsetEvent()); - retract($manager); - retract($clTimer); + + // don't retract manager, etc. - a clean-up rule will do that + // // TODO - what if we get subsequent Events for this RequestId? // By default, it will all start over again. May be confusing for Ruby. @@ -422,8 +433,8 @@ rule "${policyName}.EVENT.MANAGER" $params.getClosedLoopControlName(), drools.getRule().getName()); retract($manager.getOnsetEvent()); - retract($manager); - retract($clTimer); + + // don't retract manager, etc. - a clean-up rule will do that } } } else { @@ -473,8 +484,8 @@ rule "${policyName}.EVENT.MANAGER" PolicyEngine.manager.deliver("POLICY-CL-MGT", notification); retract($event); - retract($manager); - retract($clTimer); + + // don't retract manager, etc. - a clean-up rule will do that if (result.getB() != null) { retract(result.getB()); @@ -507,8 +518,8 @@ rule "${policyName}.EVENT.MANAGER" PolicyEngine.manager.deliver("POLICY-CL-MGT", notification); retract($event); - retract($manager); - retract($clTimer); + + // don't retract manager, etc. - a clean-up rule will do that } end @@ -1568,95 +1579,75 @@ rule "${policyName}.EVENT.CLEANUP" end /* -* -* When rules are deleted, the associated Params (and its subordinate objects) -* remain in working memory, because there are no longer any rules to clean -* them up. However, ANY time new rules are loaded, this rule will trigger -* a clean-up of ALL Params, regardless of their name & yaml, thus removing -* any that no longer have associated rules. -* This has a higher salience so that we immediately check Params when the -* rules change, before processing any events. -* +* Indicates to the cleaner that this Params object is still active. +* This has a higher salience so that it is fired before processing any events. */ -rule "${policyName}.PARAMS.CHECKUP" - salience 2 +rule "${policyName}.PARAMS.ACTIVE" + salience 4 when - Params( $clName: closedLoopControlName, $yaml: controlLoopYaml ) + $params: Params( getClosedLoopControlName() == "${closedLoopControlName}", + getControlLoopYaml() == "${controlLoopYaml}" ) + ParamsCleaner( !identified, $active: active ) then Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {} : YAML=[{}]", $clName, drools.getRule().getName(), $yaml); - - ParamsCleaner cleaner = new ParamsCleaner(); - cleaner.setClosedLoopControlName($clName); - cleaner.setControlLoopYaml($yaml); + logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(), + $params.getControlLoopYaml()); + + $active.add($params); - insert(cleaner); + // do NOT update anything at this point end /* -* -* This rule removes "cleaner" objects for rules that are still active, thus -* preventing the associated Params objects from being removed. Any cleaners -* that are left after this rule has fired will cause their corresponding Params -* to be removed. -* This has a higher salience so that we discard the cleaner before it has -* a chance to force the removal of the associated Params. -* +* Finished identifying active Params objects. Begin deleting inactive Params. +* This has a higher salience so that it is fired before processing any events. */ -rule "${policyName}.CLEANER.ACTIVE" - salience 2 +rule "${policyName}.PARAMS.IDENTIFIED" + salience 3 when - $cleaner: ParamsCleaner( getClosedLoopControlName() == "${closedLoopControlName}", - getControlLoopYaml() == "${controlLoopYaml}" ) + $cleaner: ParamsCleaner( !identified ) then Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(), - $cleaner.getControlLoopYaml()); + logger.info("{}: {}", $cleaner.getClosedLoopControlName(), drools.getRule().getName()); - retract($cleaner); + $cleaner.setIdentified(true); + update($cleaner); end /* -* -* This rule removes Params objects that no longer have associated rules; if a -* Params still had associated rules, then the cleaner would have been removed -* by those rules and thus this rule would not fire. -* This has a higher salience so that we remove old Params before it causes any -* events to be processed. -* +* Delete Params objects that have not been identified as being active. +* This has a higher salience so that it is fired before processing any events. */ -rule "${policyName}.PARAMS.CLEANUP" - salience 1 +rule "${policyName}.PARAMS.DELETE" + salience 2 when - $params: Params( $clName: closedLoopControlName, $yaml: controlLoopYaml ) - ParamsCleaner( getClosedLoopControlName() == $clName, getControlLoopYaml() == $yaml ) + $params: Params( ) + ParamsCleaner( identified, !active.contains($params) ) then Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(), + logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(), $params.getControlLoopYaml()); - + retract($params); - // Note: the cleaner may be needed for cleaning additional params, thus - // we do not retract it here - we'll leave that to another rule + // do NOT update anything at this point end /* -* -* This rule removes "cleaner" objects when they're no longer needed. -* +* Finished deleting inactive Params objects, so remove the cleaner. +* This has a higher salience so that it is fired before processing any events. */ -rule "${policyName}.CLEANER.CLEANUP" +rule "${policyName}.PARAMS.CLEANED" + salience 1 when - $cleaner: ParamsCleaner( ) + $cleaner: ParamsCleaner( identified ) then Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(), - $cleaner.getControlLoopYaml()); + logger.info("{}: {}", $cleaner.getClosedLoopControlName(), drools.getRule().getName()); retract($cleaner); end diff --git a/controlloop/templates/archetype-cl-casablanca/src/main/resources/archetype-resources/src/main/resources/__closedLoopControlName__.drl b/controlloop/templates/archetype-cl-casablanca/src/main/resources/archetype-resources/src/main/resources/__closedLoopControlName__.drl index f2aa7ef16..dc585261c 100644 --- a/controlloop/templates/archetype-cl-casablanca/src/main/resources/archetype-resources/src/main/resources/__closedLoopControlName__.drl +++ b/controlloop/templates/archetype-cl-casablanca/src/main/resources/archetype-resources/src/main/resources/__closedLoopControlName__.drl @@ -337,8 +337,9 @@ rule "EVENT.MANAGER" $clName, $params.getPolicyName() + "." + drools.getRule().getName()); retract($manager.getOnsetEvent()); - retract($manager); - retract($clTimer); + + // don't retract manager, etc. - a clean-up rule will do that + // // TODO - what if we get subsequent Events for this RequestID? // By default, it will all start over again. May be confusing for Ruby. @@ -381,8 +382,8 @@ rule "EVENT.MANAGER" $clName, $params.getPolicyName() + "." + drools.getRule().getName()); retract($manager.getOnsetEvent()); - retract($manager); - retract($clTimer); + + // don't retract manager, etc. - a clean-up rule will do that } } } else { @@ -434,8 +435,8 @@ rule "EVENT.MANAGER" PolicyEngine.manager.deliver("POLICY-CL-MGT", notification); retract($event); - retract($manager); - retract($clTimer); + + // don't retract manager, etc. - a clean-up rule will do that if(result.getB() != null) { retract(result.getB()); @@ -468,8 +469,8 @@ rule "EVENT.MANAGER" PolicyEngine.manager.deliver("POLICY-CL-MGT", notification); retract($event); - retract($manager); - retract($clTimer); + + // don't retract manager, etc. - a clean-up rule will do that } end diff --git a/controlloop/templates/template.demo.clc/src/main/resources/__closedLoopControlName__.drl b/controlloop/templates/template.demo.clc/src/main/resources/__closedLoopControlName__.drl index 15de89850..f4fcba96e 100644 --- a/controlloop/templates/template.demo.clc/src/main/resources/__closedLoopControlName__.drl +++ b/controlloop/templates/template.demo.clc/src/main/resources/__closedLoopControlName__.drl @@ -75,6 +75,8 @@ import org.slf4j.Logger; import java.time.Instant; import java.util.LinkedList; import java.util.Iterator; +import java.util.HashSet; +import java.util.Set; import org.onap.policy.drools.system.PolicyEngine; @@ -103,7 +105,8 @@ end */ declare ParamsCleaner closedLoopControlName : String - controlLoopYaml : String + identified : boolean // true if all active Params have been identified + active : Set // Params that are still active end @@ -127,7 +130,8 @@ end /* * -* Called once and only once to insert the parameters into working memory for this Closed Loop policy. +* Called to insert the parameters into working memory for this Closed Loop policy. This is called +* once each time a closed loop is added or its YAML is updated. * This has a higher salience so we can ensure that the Params is created before we have a chance to * discard any events. * @@ -142,6 +146,12 @@ rule "${policyName}.SETUP" params.setClosedLoopControlName("${closedLoopControlName}"); params.setControlLoopYaml("${controlLoopYaml}"); insert(params); + + ParamsCleaner cleaner = new ParamsCleaner(); + cleaner.setClosedLoopControlName("${closedLoopControlName}"); + cleaner.setIdentified(false); + cleaner.setActive(new HashSet()); + insert(cleaner); // Note: globals have bad behavior when persistence is used, // hence explicitly getting the logger vs using a global @@ -379,8 +389,8 @@ rule "${policyName}.EVENT.MANAGER" $params.getClosedLoopControlName(), drools.getRule().getName()); retract($manager.getOnsetEvent()); - retract($manager); - retract($clTimer); + + // don't retract manager, etc. - a clean-up rule will do that // // TODO - what if we get subsequent Events for this RequestId? // By default, it will all start over again. May be confusing for Ruby. @@ -427,8 +437,8 @@ rule "${policyName}.EVENT.MANAGER" $params.getClosedLoopControlName(), drools.getRule().getName()); retract($manager.getOnsetEvent()); - retract($manager); - retract($clTimer); + + // don't retract manager, etc. - a clean-up rule will do that } } // @@ -482,8 +492,8 @@ rule "${policyName}.EVENT.MANAGER" PolicyEngine.manager.deliver("POLICY-CL-MGT", notification); retract($event); - retract($manager); - retract($clTimer); + + // don't retract manager, etc. - a clean-up rule will do that if(result.getB() != null) { retract(result.getB()); @@ -516,8 +526,8 @@ rule "${policyName}.EVENT.MANAGER" PolicyEngine.manager.deliver("POLICY-CL-MGT", notification); retract($event); - retract($manager); - retract($clTimer); + + // don't retract manager, etc. - a clean-up rule will do that } end @@ -1324,91 +1334,75 @@ rule "${policyName}.EVENT.CLEANUP" end /* -* -* When rules are deleted, the associated Params (and its subordinate objects) -* remain in working memory, because there are no longer any rules to clean -* them up. However, ANY time new rules are loaded, this rule will trigger -* a clean-up of ALL Params, regardless of their name & yaml, thus removing -* any that no longer have associated rules. -* This has a higher salience so that we immediately check Params when the -* rules change, before processing any events. -* +* Indicates to the cleaner that this Params object is still active. +* This has a higher salience so that it is fired before processing any events. */ -rule "${policyName}.PARAMS.CHECKUP" - salience 2 +rule "${policyName}.PARAMS.ACTIVE" + salience 4 when - Params( $clName: closedLoopControlName, $yaml: controlLoopYaml ) + $params: Params( getClosedLoopControlName() == "${closedLoopControlName}", + getControlLoopYaml() == "${controlLoopYaml}" ) + ParamsCleaner( !identified, $active: active ) then Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {} : YAML=[{}]", $clName, drools.getRule().getName(), $yaml); - - ParamsCleaner cleaner = new ParamsCleaner(); - cleaner.setClosedLoopControlName($clName); - cleaner.setControlLoopYaml($yaml); + logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(), + $params.getControlLoopYaml()); + + $active.add($params); - insert(cleaner); + // do NOT update anything at this point end /* -* -* This rule removes "cleaner" objects for rules that are still active, thus -* preventing the associated Params objects from being removed. Any cleaners -* that are left after this rule has fired will cause their corresponding Params -* to be removed. -* This has a higher salience so that we discard the cleaner before it has -* a chance to force the removal of the associated Params. -* +* Finished identifying active Params objects. Begin deleting inactive Params. +* This has a higher salience so that it is fired before processing any events. */ -rule "${policyName}.CLEANER.ACTIVE" - salience 2 +rule "${policyName}.PARAMS.IDENTIFIED" + salience 3 when - $cleaner: ParamsCleaner( getClosedLoopControlName() == "${closedLoopControlName}", getControlLoopYaml() == "${controlLoopYaml}" ) + $cleaner: ParamsCleaner( !identified ) then Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(), $cleaner.getControlLoopYaml()); + logger.info("{}: {}", $cleaner.getClosedLoopControlName(), drools.getRule().getName()); - retract($cleaner); + $cleaner.setIdentified(true); + update($cleaner); end /* -* -* This rule removes Params objects that no longer have associated rules; if a -* Params still had associated rules, then the cleaner would have been removed -* by those rules and thus this rule would not fire. -* This has a higher salience so that we remove old Params before it causes any -* events to be processed. -* +* Delete Params objects that have not been identified as being active. +* This has a higher salience so that it is fired before processing any events. */ -rule "${policyName}.PARAMS.CLEANUP" - salience 1 +rule "${policyName}.PARAMS.DELETE" + salience 2 when - $params: Params( $clName: closedLoopControlName, $yaml: controlLoopYaml ) - ParamsCleaner( getClosedLoopControlName() == $clName, getControlLoopYaml() == $yaml ) + $params: Params( ) + ParamsCleaner( identified, !active.contains($params) ) then Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(), $params.getControlLoopYaml()); - + logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(), + $params.getControlLoopYaml()); + retract($params); - // Note: the cleaner may be needed for cleaning additional params, thus - // we do not retract it here - we'll leave that to another rule + // do NOT update anything at this point end /* -* -* This rule removes "cleaner" objects when they're no longer needed. -* +* Finished deleting inactive Params objects, so remove the cleaner. +* This has a higher salience so that it is fired before processing any events. */ -rule "${policyName}.CLEANER.CLEANUP" +rule "${policyName}.PARAMS.CLEANED" + salience 1 when - $cleaner: ParamsCleaner( ) + $cleaner: ParamsCleaner( identified ) then Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(), $cleaner.getControlLoopYaml()); + logger.info("{}: {}", $cleaner.getClosedLoopControlName(), drools.getRule().getName()); retract($cleaner); end diff --git a/controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/ControlLoopParamsCleanupTest.java b/controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/ControlLoopParamsCleanupTest.java new file mode 100644 index 000000000..257aeb350 --- /dev/null +++ b/controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/ControlLoopParamsCleanupTest.java @@ -0,0 +1,231 @@ +/*- + * ============LICENSE_START======================================================= + * demo + * ================================================================================ + * Copyright (C) 2018 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.template.demo.clc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.kie.api.runtime.KieSession; +import org.onap.policy.controlloop.policy.ControlLoopPolicy; +import org.onap.policy.drools.utils.logging.LoggerUtil; +import org.onap.policy.template.demo.clc.Util.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Verifies that Params objects are cleaned up when rules are updated. This loads + * <b>two</b> copies of the rule set into a single policy to ensure that the two copies + * interact appropriately with each other's Params objects. + */ +public class ControlLoopParamsCleanupTest { + private static final Logger logger = LoggerFactory.getLogger(ControlLoopParamsCleanupTest.class); + + private static final String YAML = "src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test.yaml"; + + /** + * YAML to be used when the first rule set is updated. + */ + private static final String YAML2 = "src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test2.yaml"; + + private static final String POLICY_VERSION = "v2.0"; + + private static final String POLICY_NAME = "CL_CleanupTest"; + + private static final String POLICY_SCOPE = "type=operational"; + + private static final String CONTROL_LOOP_NAME = "ControlLoop-Params-Cleanup-Test"; + + private static final String DROOLS_TEMPLATE = "src/main/resources/__closedLoopControlName__.drl"; + + // values specific to the second copy of the rules + + private static final String YAML_B = "src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test-B.yaml"; + private static final String POLICY_NAME_B = "CL_CleanupTest_B"; + private static final String CONTROL_LOOP_NAME_B = "ControlLoop-Params-Cleanup-Test-B"; + + private static KieSession kieSession; + private static Util.RuleSpec[] specifications; + + /** + * Setup the simulator. + */ + @BeforeClass + public static void setUpSimulator() { + LoggerUtil.setLevel(LoggerUtil.ROOT_LOGGER, "INFO"); + + try { + specifications = new Util.RuleSpec[2]; + + specifications[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME, POLICY_SCOPE, POLICY_NAME, + POLICY_VERSION, loadYaml(YAML)); + + specifications[1] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME_B, POLICY_SCOPE, POLICY_NAME_B, + POLICY_VERSION, loadYaml(YAML_B)); + + kieSession = Util.buildContainer(POLICY_VERSION, specifications); + + } catch (IOException e) { + logger.error("Could not create kieSession", e); + fail("Could not create kieSession"); + } + } + + /** + * Tear down. + */ + @AfterClass + public static void tearDown() { + kieSession.dispose(); + } + + @Test + public void test() throws IOException { + + /* + * Let rules create Params objects. There should be one object for each set of + * rules. + */ + kieSession.fireAllRules(); + List<Object> facts = getSessionObjects(); + assertEquals(specifications.length, facts.size()); + Iterator<Object> iter = facts.iterator(); + + final Object fact1 = iter.next(); + assertTrue(fact1.toString().contains(loadYaml(YAML))); + + final Object fact1b = iter.next(); + assertTrue(fact1b.toString().contains(loadYaml(YAML_B))); + + logger.info("UPDATING VERSION TO v3.0"); + updatePolicy(YAML2, "v3.0"); + + /* + * Let rules update Params objects. The Params for the first set of rules should + * now be deleted and replaced with a new one, while the Params for the second set + * should be unchanged. + */ + kieSession.fireAllRules(); + facts = getSessionObjects(); + assertEquals(specifications.length, facts.size()); + iter = facts.iterator(); + + final Object fact2 = iter.next(); + assertTrue(fact2 != fact1); + assertTrue(fact2 != fact1b); + assertTrue(fact2.toString().contains(loadYaml(YAML2))); + + assertTrue(iter.next() == fact1b); + + logger.info("UPDATING VERSION TO v4.0"); + updatePolicy(YAML, "v4.0"); + + /* + * Let rules update Params objects. The Params for the first set of rules should + * now be deleted and replaced with a new one, while the Params for the second set + * should be unchanged. + */ + kieSession.fireAllRules(); + facts = getSessionObjects(); + assertEquals(specifications.length, facts.size()); + iter = facts.iterator(); + + final Object fact3 = iter.next(); + assertTrue(fact3.toString().contains(loadYaml(YAML))); + assertTrue(fact3 != fact2); + assertTrue(fact3 != fact1b); + + assertTrue(iter.next() == fact1b); + + logger.info("UPDATING VERSION TO v4.0 (i.e., unchanged)"); + updatePolicy(YAML, "v4.0"); + + /* + * Let rules update Params objects. As the version (and YAML) are unchanged for + * either rule set, both Params objects should be unchanged. + */ + kieSession.fireAllRules(); + facts = getSessionObjects(); + assertEquals(specifications.length, facts.size()); + iter = facts.iterator(); + assertTrue(iter.next() == fact3); + assertTrue(iter.next() == fact1b); + } + + /** + * Updates the policy, changing the YAML associated with the first rule set. + * + * @param yamlFile name of the YAML file + * @param policyVersion policy version + * @throws IOException if an error occurs + */ + private static void updatePolicy(String yamlFile, String policyVersion) throws IOException { + + specifications[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME, POLICY_SCOPE, POLICY_NAME, + policyVersion, loadYaml(yamlFile)); + + /* + * Update the policy within the container. + */ + Util.updateContainer(policyVersion, specifications); + } + + /** + * Loads a YAML file and URL-encodes it. + * + * @param yamlFile name of the YAML file + * @return the contents of the specified file, URL-encoded + * @throws UnsupportedEncodingException if an error occurs + */ + private static String loadYaml(String yamlFile) throws UnsupportedEncodingException { + Pair<ControlLoopPolicy, String> pair = Util.loadYaml(yamlFile); + assertNotNull(pair); + assertNotNull(pair.first); + assertNotNull(pair.first.getControlLoop()); + assertNotNull(pair.first.getControlLoop().getControlLoopName()); + assertTrue(pair.first.getControlLoop().getControlLoopName().length() > 0); + + return URLEncoder.encode(pair.second, "UTF-8"); + } + + /** + * Gets the session objects. + * + * @return the session objects + */ + private static List<Object> getSessionObjects() { + // sort the objects so we know the order + LinkedList<Object> lst = new LinkedList<>(kieSession.getObjects()); + lst.sort((left, right) -> left.toString().compareTo(right.toString())); + + return lst; + } +} diff --git a/controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/Util.java b/controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/Util.java index 6001331de..1d105911c 100644 --- a/controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/Util.java +++ b/controlloop/templates/template.demo.clc/src/test/java/org/onap/policy/template/demo/clc/Util.java @@ -33,12 +33,10 @@ import java.nio.file.Paths; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; - import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.Query; - import org.apache.commons.io.IOUtils; import org.kie.api.KieServices; import org.kie.api.builder.KieBuilder; @@ -46,7 +44,6 @@ import org.kie.api.builder.KieFileSystem; import org.kie.api.builder.Message; import org.kie.api.builder.ReleaseId; import org.kie.api.builder.Results; -import org.kie.api.builder.model.KieModuleModel; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession; import org.onap.policy.common.endpoints.http.server.HttpServletServer; @@ -64,6 +61,11 @@ public final class Util { private static final String OPSHISTPUPROP = "OperationsHistoryPU"; private static final Logger logger = LoggerFactory.getLogger(Util.class); + // values from the last call to buildContainer() + + private static KieServices kieServices; + private static KieContainer keyContainer; + public static class Pair<A, B> { public final A first; public final B second; @@ -123,39 +125,9 @@ public final class Util { return org.onap.policy.simulators.Util.buildAaiSim(); } - private static String generatePolicy(String ruleContents, - String closedLoopControlName, - String policyScope, - String policyName, - String policyVersion, - String controlLoopYaml) { - - Pattern pattern = Pattern.compile("\\$\\{closedLoopControlName\\}"); - Matcher matcher = pattern.matcher(ruleContents); - ruleContents = matcher.replaceAll(closedLoopControlName); - - pattern = Pattern.compile("\\$\\{policyScope\\}"); - matcher = pattern.matcher(ruleContents); - ruleContents = matcher.replaceAll(policyScope); - - pattern = Pattern.compile("\\$\\{policyName\\}"); - matcher = pattern.matcher(ruleContents); - ruleContents = matcher.replaceAll(policyName); - - pattern = Pattern.compile("\\$\\{policyVersion\\}"); - matcher = pattern.matcher(ruleContents); - ruleContents = matcher.replaceAll(policyVersion); - - pattern = Pattern.compile("\\$\\{controlLoopYaml\\}"); - matcher = pattern.matcher(ruleContents); - ruleContents = matcher.replaceAll(controlLoopYaml); - - return ruleContents; - } - /** - * Build the container. - * + * Build a container containing a single set of rules. + * * @param droolsTemplate template * @param closedLoopControlName control loop id * @param policyScope policy scope @@ -165,41 +137,82 @@ public final class Util { * @return the Kie session * @throws IOException if the container cannot be built */ - public static KieSession buildContainer(String droolsTemplate, String closedLoopControlName, - String policyScope, String policyName, String policyVersion, - String yamlSpecification) throws IOException { + public static KieSession buildContainer(String droolsTemplate, String closedLoopControlName, String policyScope, + String policyName, String policyVersion, String yamlSpecification) throws IOException { + + RuleSpec spec = new RuleSpec(droolsTemplate, closedLoopControlName, policyScope, policyName, policyVersion, + yamlSpecification); + + return buildContainer(policyVersion, new RuleSpec[] {spec}); + } + + /** + * Build a container containing all of the specified rules. + * + * @param policyVersion policy version + * @param specifications rule specifications + * @return the Kie session + * @throws IOException if the container cannot be built + */ + public static KieSession buildContainer(String policyVersion, RuleSpec[] specifications) throws IOException { // // Get our Drools Kie factory // - KieServices ks = KieServices.Factory.get(); + kieServices = KieServices.Factory.get(); + + ReleaseId releaseId = buildPolicy(policyVersion, specifications); + logger.debug(releaseId.toString()); - KieModuleModel kieModule = ks.newKieModuleModel(); + // + // Create our kie Session and container + // + keyContainer = kieServices.newKieContainer(releaseId); + + return keyContainer.newKieSession(); + } + + /** + * Update the container with new rules. + * + * @param policyVersion new policy version + * @param specifications new rule specifications + * @throws IOException if the container cannot be built + */ + public static void updateContainer(String policyVersion, RuleSpec[] specifications) throws IOException { + ReleaseId releaseId = buildPolicy(policyVersion, specifications); + logger.debug(releaseId.toString()); - logger.debug("KMODULE:" + System.lineSeparator() + kieModule.toXML()); + keyContainer.updateToVersion(releaseId); + } + /** + * Build the Policy so it can be loaded into a KIE container. + * + * @param policyVersion policy version + * @param specifications rule specifications + * @return the release + * @throws IOException if the container cannot be built + */ + private static ReleaseId buildPolicy(String policyVersion, RuleSpec[] specifications) throws IOException { // // Generate our drools rule from our template // - KieFileSystem kfs = ks.newKieFileSystem(); + KieFileSystem kfs = kieServices.newKieFileSystem(); + ReleaseId releaseId = kieServices.getRepository().getDefaultReleaseId(); + releaseId = kieServices.newReleaseId(releaseId.getGroupId(), releaseId.getArtifactId(), policyVersion); - kfs.writeKModuleXML(kieModule.toXML()); - { - Path rule = Paths.get(droolsTemplate); - String ruleTemplate = new String(Files.readAllBytes(rule)); - String drlContents = generatePolicy(ruleTemplate, - closedLoopControlName, - policyScope, - policyName, - policyVersion, - yamlSpecification); - - kfs.write("src/main/resources/" + policyName + ".drl", - ks.getResources().newByteArrayResource(drlContents.getBytes())); + kfs.generateAndWritePomXML(releaseId); + + for (RuleSpec spec : specifications) { + String drlContents = spec.generateRules(); + kfs.write("src/main/resources/" + spec.policyName + ".drl", + kieServices.getResources().newByteArrayResource(drlContents.getBytes())); } + // // Compile the rule // - KieBuilder builder = ks.newKieBuilder(kfs).buildAll(); + KieBuilder builder = kieServices.newKieBuilder(kfs).buildAll(); Results results = builder.getResults(); if (results.hasMessages(Message.Level.ERROR)) { for (Message msg : results.getMessages()) { @@ -210,14 +223,8 @@ public final class Util { for (Message msg : results.getMessages()) { logger.debug(msg.toString()); } - // - // Create our kie Session and container - // - ReleaseId releaseId = ks.getRepository().getDefaultReleaseId(); - logger.debug(releaseId.toString()); - KieContainer keyContainer = ks.newKieContainer(releaseId); - return keyContainer.newKieSession(); + return releaseId; } /** @@ -291,4 +298,70 @@ public final class Util { emf.close(); return results; } + + /** + * Rule specification. + */ + public static class RuleSpec { + private String droolsTemplate; + private String closedLoopControlName; + private String policyScope; + private String policyName; + private String policyVersion; + private String yamlSpecification; + + /** + * Constructs the object. + * + * @param droolsTemplate template + * @param closedLoopControlName control loop id + * @param policyScope policy scope + * @param policyName policy name + * @param policyVersion policy version + * @param yamlSpecification incoming yaml specification + */ + public RuleSpec(String droolsTemplate, String closedLoopControlName, String policyScope, String policyName, + String policyVersion, String yamlSpecification) { + + this.droolsTemplate = droolsTemplate; + this.closedLoopControlName = closedLoopControlName; + this.policyScope = policyScope; + this.policyName = policyName; + this.policyVersion = policyVersion; + this.yamlSpecification = yamlSpecification; + } + + /** + * Generates the rules by reading the template and making variable substitutions. + * + * @return the rules + * @throws IOException if an error occurs + */ + private String generateRules() throws IOException { + Path rule = Paths.get(droolsTemplate); + String ruleTemplate = new String(Files.readAllBytes(rule)); + + Pattern pattern = Pattern.compile("\\$\\{closedLoopControlName\\}"); + Matcher matcher = pattern.matcher(ruleTemplate); + ruleTemplate = matcher.replaceAll(closedLoopControlName); + + pattern = Pattern.compile("\\$\\{policyScope\\}"); + matcher = pattern.matcher(ruleTemplate); + ruleTemplate = matcher.replaceAll(policyScope); + + pattern = Pattern.compile("\\$\\{policyName\\}"); + matcher = pattern.matcher(ruleTemplate); + ruleTemplate = matcher.replaceAll(policyName); + + pattern = Pattern.compile("\\$\\{policyVersion\\}"); + matcher = pattern.matcher(ruleTemplate); + ruleTemplate = matcher.replaceAll(policyVersion); + + pattern = Pattern.compile("\\$\\{controlLoopYaml\\}"); + matcher = pattern.matcher(ruleTemplate); + ruleTemplate = matcher.replaceAll(yamlSpecification); + + return ruleTemplate; + } + } } diff --git a/controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test-B.yaml b/controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test-B.yaml new file mode 100644 index 000000000..e19cb498e --- /dev/null +++ b/controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test-B.yaml @@ -0,0 +1,35 @@ +# Copyright 2018 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. +controlLoop: + version: 2.0.0 + controlLoopName: ControlLoop-Params-Cleanup-Test-B + trigger_policy: unique-policy-id-1-scale-up + timeout: 60 + +policies: + - id: unique-policy-id-1-scale-up + name: Create a new VF Module + description: + actor: SO + recipe: VF Module Create + target: + type: VNF + retry: 0 + timeout: 30 + 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/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test.yaml b/controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test.yaml new file mode 100644 index 000000000..6d89d58c4 --- /dev/null +++ b/controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test.yaml @@ -0,0 +1,35 @@ +# Copyright 2018 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. +controlLoop: + version: 2.0.0 + controlLoopName: ControlLoop-Params-Cleanup-Test + trigger_policy: unique-policy-id-1-scale-up + timeout: 60 + +policies: + - id: unique-policy-id-1-scale-up + name: Create a new VF Module + description: + actor: SO + recipe: VF Module Create + target: + type: VNF + retry: 0 + timeout: 30 + 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/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test2.yaml b/controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test2.yaml new file mode 100644 index 000000000..358bbfbea --- /dev/null +++ b/controlloop/templates/template.demo.clc/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test2.yaml @@ -0,0 +1,39 @@ +# Copyright 2018 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. + +# +# This YAML must be slightly different from test.yaml. +# +controlLoop: + version: 3.0.0 + controlLoopName: ControlLoop-Params-Cleanup-Test + trigger_policy: unique-policy-id-1-scale-up + timeout: 60 + +policies: + - id: unique-policy-id-1-scale-up + name: Create a new VF Module + description: + actor: SO + recipe: VF Module Create + target: + type: VNF + retry: 0 + timeout: 30 + 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/templates/template.demo/src/test/java/org/onap/policy/template/demo/CcvpnControlLoopTest.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/CcvpnControlLoopTest.java index 1da3ff70c..ea9e85709 100644 --- a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/CcvpnControlLoopTest.java +++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/CcvpnControlLoopTest.java @@ -152,6 +152,9 @@ public class CcvpnControlLoopTest implements TopicListener { sendEvent(pair.first); kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); /* * The only fact in memory should be Params @@ -195,6 +198,9 @@ public class CcvpnControlLoopTest implements TopicListener { kieSession.insert(event); kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); /* * The only fact in memory should be Params diff --git a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopFailureTest.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopFailureTest.java index 31b6b2e30..344e888ac 100644 --- a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopFailureTest.java +++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopFailureTest.java @@ -195,6 +195,9 @@ public class ControlLoopFailureTest implements TopicListener { * a lock for a different */ kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); /* * The only fact in memory should be Params diff --git a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopParamsCleanupTest.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopParamsCleanupTest.java new file mode 100644 index 000000000..52155376d --- /dev/null +++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopParamsCleanupTest.java @@ -0,0 +1,232 @@ +/*- + * ============LICENSE_START======================================================= + * demo + * ================================================================================ + * Copyright (C) 2018 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.template.demo; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.kie.api.runtime.KieSession; +import org.onap.policy.controlloop.policy.ControlLoopPolicy; +import org.onap.policy.drools.utils.logging.LoggerUtil; +import org.onap.policy.template.demo.Util.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Verifies that Params objects are cleaned up when rules are updated. This loads + * <b>two</b> copies of the rule set into a single policy to ensure that the two copies + * interact appropriately with each other's Params objects. + */ +public class ControlLoopParamsCleanupTest { + private static final Logger logger = LoggerFactory.getLogger(ControlLoopParamsCleanupTest.class); + + private static final String YAML = "src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test.yaml"; + + /** + * YAML to be used when the first rule set is updated. + */ + private static final String YAML2 = "src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test2.yaml"; + + private static final String POLICY_VERSION = "v2.0"; + + private static final String POLICY_NAME = "CL_CleanupTest"; + + private static final String POLICY_SCOPE = "type=operational"; + + private static final String CONTROL_LOOP_NAME = "ControlLoop-Params-Cleanup-Test"; + + private static final String DROOLS_TEMPLATE = "../archetype-cl-amsterdam/src/main/resources/archetype-resources/" + + "src/main/resources/__closedLoopControlName__.drl"; + + // values specific to the second copy of the rules + + private static final String YAML_B = "src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test-B.yaml"; + private static final String POLICY_NAME_B = "CL_CleanupTest_B"; + private static final String CONTROL_LOOP_NAME_B = "ControlLoop-Params-Cleanup-Test-B"; + + private static KieSession kieSession; + private static Util.RuleSpec[] specifications; + + /** + * Setup the simulator. + */ + @BeforeClass + public static void setUpSimulator() { + LoggerUtil.setLevel(LoggerUtil.ROOT_LOGGER, "INFO"); + + try { + specifications = new Util.RuleSpec[2]; + + specifications[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME, POLICY_SCOPE, POLICY_NAME, + POLICY_VERSION, loadYaml(YAML)); + + specifications[1] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME_B, POLICY_SCOPE, POLICY_NAME_B, + POLICY_VERSION, loadYaml(YAML_B)); + + kieSession = Util.buildContainer(POLICY_VERSION, specifications); + + } catch (IOException e) { + logger.error("Could not create kieSession", e); + fail("Could not create kieSession"); + } + } + + /** + * Tear down. + */ + @AfterClass + public static void tearDown() { + kieSession.dispose(); + } + + @Test + public void test() throws IOException { + + /* + * Let rules create Params objects. There should be one object for each set of + * rules. + */ + kieSession.fireAllRules(); + List<Object> facts = getSessionObjects(); + assertEquals(specifications.length, facts.size()); + Iterator<Object> iter = facts.iterator(); + + final Object fact1 = iter.next(); + assertTrue(fact1.toString().contains(loadYaml(YAML))); + + final Object fact1b = iter.next(); + assertTrue(fact1b.toString().contains(loadYaml(YAML_B))); + + logger.info("UPDATING VERSION TO v3.0"); + updatePolicy(YAML2, "v3.0"); + + /* + * Let rules update Params objects. The Params for the first set of rules should + * now be deleted and replaced with a new one, while the Params for the second set + * should be unchanged. + */ + kieSession.fireAllRules(); + facts = getSessionObjects(); + assertEquals(specifications.length, facts.size()); + iter = facts.iterator(); + + final Object fact2 = iter.next(); + assertTrue(fact2 != fact1); + assertTrue(fact2 != fact1b); + assertTrue(fact2.toString().contains(loadYaml(YAML2))); + + assertTrue(iter.next() == fact1b); + + logger.info("UPDATING VERSION TO v4.0"); + updatePolicy(YAML, "v4.0"); + + /* + * Let rules update Params objects. The Params for the first set of rules should + * now be deleted and replaced with a new one, while the Params for the second set + * should be unchanged. + */ + kieSession.fireAllRules(); + facts = getSessionObjects(); + assertEquals(specifications.length, facts.size()); + iter = facts.iterator(); + + final Object fact3 = iter.next(); + assertTrue(fact3.toString().contains(loadYaml(YAML))); + assertTrue(fact3 != fact2); + assertTrue(fact3 != fact1b); + + assertTrue(iter.next() == fact1b); + + logger.info("UPDATING VERSION TO v4.0 (i.e., unchanged)"); + updatePolicy(YAML, "v4.0"); + + /* + * Let rules update Params objects. As the version (and YAML) are unchanged for + * either rule set, both Params objects should be unchanged. + */ + kieSession.fireAllRules(); + facts = getSessionObjects(); + assertEquals(specifications.length, facts.size()); + iter = facts.iterator(); + assertTrue(iter.next() == fact3); + assertTrue(iter.next() == fact1b); + } + + /** + * Updates the policy, changing the YAML associated with the first rule set. + * + * @param yamlFile name of the YAML file + * @param policyVersion policy version + * @throws IOException if an error occurs + */ + private static void updatePolicy(String yamlFile, String policyVersion) throws IOException { + + specifications[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME, POLICY_SCOPE, POLICY_NAME, + policyVersion, loadYaml(yamlFile)); + + /* + * Update the policy within the container. + */ + Util.updateContainer(policyVersion, specifications); + } + + /** + * Loads a YAML file and URL-encodes it. + * + * @param yamlFile name of the YAML file + * @return the contents of the specified file, URL-encoded + * @throws UnsupportedEncodingException if an error occurs + */ + private static String loadYaml(String yamlFile) throws UnsupportedEncodingException { + Pair<ControlLoopPolicy, String> pair = Util.loadYaml(yamlFile); + assertNotNull(pair); + assertNotNull(pair.first); + assertNotNull(pair.first.getControlLoop()); + assertNotNull(pair.first.getControlLoop().getControlLoopName()); + assertTrue(pair.first.getControlLoop().getControlLoopName().length() > 0); + + return URLEncoder.encode(pair.second, "UTF-8"); + } + + /** + * Gets the session objects. + * + * @return the session objects + */ + private static List<Object> getSessionObjects() { + // sort the objects so we know the order + LinkedList<Object> lst = new LinkedList<>(kieSession.getObjects()); + lst.sort((left, right) -> left.toString().compareTo(right.toString())); + + return lst; + } +} diff --git a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/Util.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/Util.java index 575068086..758c65568 100644 --- a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/Util.java +++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/Util.java @@ -7,9 +7,9 @@ * 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. @@ -23,7 +23,6 @@ package org.onap.policy.template.demo; import static org.junit.Assert.fail; import com.att.research.xacml.util.XACMLProperties; - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -35,7 +34,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.regex.Matcher; import java.util.regex.Pattern; - import org.apache.commons.io.IOUtils; import org.kie.api.KieServices; import org.kie.api.builder.KieBuilder; @@ -43,7 +41,6 @@ import org.kie.api.builder.KieFileSystem; import org.kie.api.builder.Message; import org.kie.api.builder.ReleaseId; import org.kie.api.builder.Results; -import org.kie.api.builder.model.KieModuleModel; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession; import org.onap.policy.common.endpoints.http.server.HttpServletServer; @@ -72,9 +69,14 @@ public final class Util { } } + // values from the last call to buildContainer() + + private static KieServices kieServices; + private static KieContainer keyContainer; + /** * Load YAML. - * + * * @param testFile test file to load * @return the Pair of a policy and the yaml contents */ @@ -100,7 +102,7 @@ public final class Util { /** * Load the YAML guard policy. - * + * * @param testFile the test file to load * @return return the guard object */ @@ -141,39 +143,9 @@ public final class Util { return org.onap.policy.simulators.Util.buildSdncSim(); } - private static String generatePolicy(String ruleContents, - String closedLoopControlName, - String policyScope, - String policyName, - String policyVersion, - String controlLoopYaml) { - - Pattern pattern = Pattern.compile("\\$\\{closedLoopControlName\\}"); - Matcher matcher = pattern.matcher(ruleContents); - ruleContents = matcher.replaceAll(closedLoopControlName); - - pattern = Pattern.compile("\\$\\{policyScope\\}"); - matcher = pattern.matcher(ruleContents); - ruleContents = matcher.replaceAll(policyScope); - - pattern = Pattern.compile("\\$\\{policyName\\}"); - matcher = pattern.matcher(ruleContents); - ruleContents = matcher.replaceAll(policyName); - - pattern = Pattern.compile("\\$\\{policyVersion\\}"); - matcher = pattern.matcher(ruleContents); - ruleContents = matcher.replaceAll(policyVersion); - - pattern = Pattern.compile("\\$\\{controlLoopYaml\\}"); - matcher = pattern.matcher(ruleContents); - ruleContents = matcher.replaceAll(controlLoopYaml); - - return ruleContents; - } - /** - * Build the container. - * + * Build a container containing a single set of rules. + * * @param droolsTemplate template * @param closedLoopControlName control loop id * @param policyScope policy scope @@ -183,41 +155,82 @@ public final class Util { * @return the Kie session * @throws IOException if the container cannot be built */ - public static KieSession buildContainer(String droolsTemplate, String closedLoopControlName, - String policyScope, String policyName, String policyVersion, - String yamlSpecification) throws IOException { + public static KieSession buildContainer(String droolsTemplate, String closedLoopControlName, String policyScope, + String policyName, String policyVersion, String yamlSpecification) throws IOException { + + RuleSpec spec = new RuleSpec(droolsTemplate, closedLoopControlName, policyScope, policyName, policyVersion, + yamlSpecification); + + return buildContainer(policyVersion, new RuleSpec[] {spec}); + } + + /** + * Build a container containing all of the specified rules. + * + * @param policyVersion policy version + * @param specifications rule specifications + * @return the Kie session + * @throws IOException if the container cannot be built + */ + public static KieSession buildContainer(String policyVersion, RuleSpec[] specifications) throws IOException { // // Get our Drools Kie factory // - KieServices ks = KieServices.Factory.get(); + kieServices = KieServices.Factory.get(); + + ReleaseId releaseId = buildPolicy(policyVersion, specifications); + logger.debug(releaseId.toString()); + + // + // Create our kie Session and container + // + keyContainer = kieServices.newKieContainer(releaseId); - KieModuleModel kieModule = ks.newKieModuleModel(); + return setupSession(keyContainer.newKieSession()); + } - logger.debug("KMODULE:" + System.lineSeparator() + kieModule.toXML()); + /** + * Update the container with new rules. + * + * @param policyVersion new policy version + * @param specifications new rule specifications + * @throws IOException if the container cannot be built + */ + public static void updateContainer(String policyVersion, RuleSpec[] specifications) throws IOException { + ReleaseId releaseId = buildPolicy(policyVersion, specifications); + logger.debug(releaseId.toString()); + keyContainer.updateToVersion(releaseId); + } + + /** + * Build the Policy so it can be loaded into a KIE container. + * + * @param policyVersion policy version + * @param specifications rule specifications + * @return the release + * @throws IOException if the container cannot be built + */ + private static ReleaseId buildPolicy(String policyVersion, RuleSpec[] specifications) throws IOException { // // Generate our drools rule from our template // - KieFileSystem kfs = ks.newKieFileSystem(); + KieFileSystem kfs = kieServices.newKieFileSystem(); + ReleaseId releaseId = kieServices.getRepository().getDefaultReleaseId(); + releaseId = kieServices.newReleaseId(releaseId.getGroupId(), releaseId.getArtifactId(), policyVersion); - kfs.writeKModuleXML(kieModule.toXML()); - { - Path rule = Paths.get(droolsTemplate); - String ruleTemplate = new String(Files.readAllBytes(rule)); - String drlContents = generatePolicy(ruleTemplate, - closedLoopControlName, - policyScope, - policyName, - policyVersion, - yamlSpecification); - - kfs.write("src/main/resources/" + policyName + ".drl", - ks.getResources().newByteArrayResource(drlContents.getBytes())); + kfs.generateAndWritePomXML(releaseId); + + for (RuleSpec spec : specifications) { + String drlContents = spec.generateRules(); + kfs.write("src/main/resources/" + spec.policyName + ".drl", + kieServices.getResources().newByteArrayResource(drlContents.getBytes())); } + // // Compile the rule // - KieBuilder builder = ks.newKieBuilder(kfs).buildAll(); + KieBuilder builder = kieServices.newKieBuilder(kfs).buildAll(); Results results = builder.getResults(); if (results.hasMessages(Message.Level.ERROR)) { for (Message msg : results.getMessages()) { @@ -228,14 +241,8 @@ public final class Util { for (Message msg : results.getMessages()) { logger.debug(msg.toString()); } - // - // Create our kie Session and container - // - ReleaseId releaseId = ks.getRepository().getDefaultReleaseId(); - logger.debug(releaseId.toString()); - KieContainer keyContainer = ks.newKieContainer(releaseId); - return setupSession(keyContainer.newKieSession()); + return releaseId; } private static KieSession setupSession(KieSession kieSession) { @@ -245,29 +252,29 @@ public final class Util { // Create XACML Guard policy from YAML // We prepare 4 Guards. Notice that Rebuilds recipe has two Guards (for checking policy combining algorithm) // - PolicyGuardYamlToXacml.fromYamlToXacml("src/test/resources/yaml/policy_guard_appc_restart.yaml", - "src/main/resources/frequency_limiter_template.xml", + PolicyGuardYamlToXacml.fromYamlToXacml("src/test/resources/yaml/policy_guard_appc_restart.yaml", + "src/main/resources/frequency_limiter_template.xml", "src/test/resources/xacml/autogenerated_frequency_limiter_restart.xml"); - PolicyGuardYamlToXacml.fromYamlToXacml("src/test/resources/yaml/policy_guard_appc_rebuild.yaml", - "src/main/resources/frequency_limiter_template.xml", + PolicyGuardYamlToXacml.fromYamlToXacml("src/test/resources/yaml/policy_guard_appc_rebuild.yaml", + "src/main/resources/frequency_limiter_template.xml", "src/test/resources/xacml/autogenerated_frequency_limiter_rebuild.xml"); - PolicyGuardYamlToXacml.fromYamlToXacml("src/test/resources/yaml/policy_guard_appc_rebuild_1.yaml", - "src/main/resources/frequency_limiter_template.xml", + PolicyGuardYamlToXacml.fromYamlToXacml("src/test/resources/yaml/policy_guard_appc_rebuild_1.yaml", + "src/main/resources/frequency_limiter_template.xml", "src/test/resources/xacml/autogenerated_frequency_limiter_rebuild_1.xml"); - PolicyGuardYamlToXacml.fromYamlToXacml("src/test/resources/yaml/policy_guard_appc_migrate.yaml", - "src/main/resources/frequency_limiter_template.xml", + PolicyGuardYamlToXacml.fromYamlToXacml("src/test/resources/yaml/policy_guard_appc_migrate.yaml", + "src/main/resources/frequency_limiter_template.xml", "src/test/resources/xacml/autogenerated_frequency_limiter_migrate.xml"); - PolicyGuardYamlToXacml.fromYamlToXacml("src/test/resources/yaml/policy_guard_appc_modifyconfig.yaml", - "src/main/resources/frequency_limiter_template.xml", + PolicyGuardYamlToXacml.fromYamlToXacml("src/test/resources/yaml/policy_guard_appc_modifyconfig.yaml", + "src/main/resources/frequency_limiter_template.xml", "src/test/resources/xacml/autogenerated_frequency_limiter_modifyconfig.xml"); PolicyGuardYamlToXacml.fromYamlToXacmlBlacklist( - "src/test/resources/yaml/policy_guard_appc_restart_blacklist.yaml", - "src/main/resources/blacklist_template.xml", + "src/test/resources/yaml/policy_guard_appc_restart_blacklist.yaml", + "src/main/resources/blacklist_template.xml", "src/test/resources/xacml/autogenerated_blacklist.xml"); // @@ -329,7 +336,7 @@ public final class Util { PolicyEngine.manager.setEnvironmentProperty("vfc.username", "VFC"); PolicyEngine.manager.setEnvironmentProperty("vfc.password", "VFC"); } - + /** * Set the operation history properties. */ @@ -337,4 +344,69 @@ public final class Util { System.setProperty(OPSHISTPUPROP, "TestOperationsHistoryPU"); } + /** + * Rule specification. + */ + public static class RuleSpec { + private String droolsTemplate; + private String closedLoopControlName; + private String policyScope; + private String policyName; + private String policyVersion; + private String yamlSpecification; + + /** + * Constructs the object. + * + * @param droolsTemplate template + * @param closedLoopControlName control loop id + * @param policyScope policy scope + * @param policyName policy name + * @param policyVersion policy version + * @param yamlSpecification incoming yaml specification + */ + public RuleSpec(String droolsTemplate, String closedLoopControlName, String policyScope, String policyName, + String policyVersion, String yamlSpecification) { + + this.droolsTemplate = droolsTemplate; + this.closedLoopControlName = closedLoopControlName; + this.policyScope = policyScope; + this.policyName = policyName; + this.policyVersion = policyVersion; + this.yamlSpecification = yamlSpecification; + } + + /** + * Generates the rules by reading the template and making variable substitutions. + * + * @return the rules + * @throws IOException if an error occurs + */ + private String generateRules() throws IOException { + Path rule = Paths.get(droolsTemplate); + String ruleTemplate = new String(Files.readAllBytes(rule)); + + Pattern pattern = Pattern.compile("\\$\\{closedLoopControlName\\}"); + Matcher matcher = pattern.matcher(ruleTemplate); + ruleTemplate = matcher.replaceAll(closedLoopControlName); + + pattern = Pattern.compile("\\$\\{policyScope\\}"); + matcher = pattern.matcher(ruleTemplate); + ruleTemplate = matcher.replaceAll(policyScope); + + pattern = Pattern.compile("\\$\\{policyName\\}"); + matcher = pattern.matcher(ruleTemplate); + ruleTemplate = matcher.replaceAll(policyName); + + pattern = Pattern.compile("\\$\\{policyVersion\\}"); + matcher = pattern.matcher(ruleTemplate); + ruleTemplate = matcher.replaceAll(policyVersion); + + pattern = Pattern.compile("\\$\\{controlLoopYaml\\}"); + matcher = pattern.matcher(ruleTemplate); + ruleTemplate = matcher.replaceAll(yamlSpecification); + + return ruleTemplate; + } + } } diff --git a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VcpeControlLoopTest.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VcpeControlLoopTest.java index c1e8e1e66..a27dbd026 100644 --- a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VcpeControlLoopTest.java +++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VcpeControlLoopTest.java @@ -166,6 +166,9 @@ public class VcpeControlLoopTest implements TopicListener { sendEvent(pair.first, requestId, ControlLoopEventStatus.ONSET, "vCPEInfraVNF13", true); kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); /* * The only fact in memory should be Params @@ -203,6 +206,9 @@ public class VcpeControlLoopTest implements TopicListener { kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); /* * The only fact in memory should be Params diff --git a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VdnsControlLoopTest.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VdnsControlLoopTest.java index d1fb29e0a..564b12a47 100644 --- a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VdnsControlLoopTest.java +++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VdnsControlLoopTest.java @@ -160,6 +160,9 @@ public class VdnsControlLoopTest implements TopicListener { sendEvent(pair.first, requestId, ControlLoopEventStatus.ONSET); kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); /* * The only fact in memory should be Params @@ -196,6 +199,9 @@ public class VdnsControlLoopTest implements TopicListener { sendEvent(pair.first, requestId, ControlLoopEventStatus.ONSET, "error"); kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); /* * The only fact in memory should be Params @@ -233,6 +239,10 @@ public class VdnsControlLoopTest implements TopicListener { try { kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); + } catch (Exception e) { e.printStackTrace(); logger.warn(e.toString()); diff --git a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VfcControlLoopTest.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VfcControlLoopTest.java index 449a90fe0..d03bac27f 100644 --- a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VfcControlLoopTest.java +++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VfcControlLoopTest.java @@ -163,6 +163,9 @@ public class VfcControlLoopTest implements TopicListener { sendEvent(pair.first, requestId, ControlLoopEventStatus.ONSET); kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); /* * The only fact in memory should be Params @@ -211,6 +214,9 @@ public class VfcControlLoopTest implements TopicListener { kieSession.insert(event); kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); /* * The only fact in memory should be Params diff --git a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VfwControlLoopTest.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VfwControlLoopTest.java index b06f4c695..adc9a2ac0 100644 --- a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VfwControlLoopTest.java +++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VfwControlLoopTest.java @@ -167,6 +167,10 @@ public class VfwControlLoopTest implements TopicListener { try { kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); + } catch (Exception e) { e.printStackTrace(); logger.warn(e.toString()); @@ -209,6 +213,10 @@ public class VfwControlLoopTest implements TopicListener { sendEvent(pair.first, requestId, ControlLoopEventStatus.ONSET, "error"); try { kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); + } catch (Exception e) { e.printStackTrace(); logger.warn(e.toString()); @@ -239,6 +247,10 @@ public class VfwControlLoopTest implements TopicListener { try { kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); + } catch (Exception e) { e.printStackTrace(); logger.warn(e.toString()); diff --git a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VpciControlLoopTest.java b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VpciControlLoopTest.java index 06e2aca9b..6d1086d87 100644 --- a/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VpciControlLoopTest.java +++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/VpciControlLoopTest.java @@ -165,6 +165,9 @@ public class VpciControlLoopTest implements TopicListener { sendEvent(pair.first, requestId, ControlLoopEventStatus.ONSET, true); kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); /* * The only fact in memory should be Params @@ -202,6 +205,9 @@ public class VpciControlLoopTest implements TopicListener { sendEvent(pair.first, requestId, ControlLoopEventStatus.ONSET, false); kieSession.fireUntilHalt(); + + // allow object clean-up + kieSession.fireAllRules(); /* * The only fact in memory should be Params diff --git a/controlloop/templates/template.demo/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test-B.yaml b/controlloop/templates/template.demo/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test-B.yaml new file mode 100644 index 000000000..e19cb498e --- /dev/null +++ b/controlloop/templates/template.demo/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test-B.yaml @@ -0,0 +1,35 @@ +# Copyright 2018 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. +controlLoop: + version: 2.0.0 + controlLoopName: ControlLoop-Params-Cleanup-Test-B + trigger_policy: unique-policy-id-1-scale-up + timeout: 60 + +policies: + - id: unique-policy-id-1-scale-up + name: Create a new VF Module + description: + actor: SO + recipe: VF Module Create + target: + type: VNF + retry: 0 + timeout: 30 + 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/templates/template.demo/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test.yaml b/controlloop/templates/template.demo/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test.yaml new file mode 100644 index 000000000..6d89d58c4 --- /dev/null +++ b/controlloop/templates/template.demo/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test.yaml @@ -0,0 +1,35 @@ +# Copyright 2018 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. +controlLoop: + version: 2.0.0 + controlLoopName: ControlLoop-Params-Cleanup-Test + trigger_policy: unique-policy-id-1-scale-up + timeout: 60 + +policies: + - id: unique-policy-id-1-scale-up + name: Create a new VF Module + description: + actor: SO + recipe: VF Module Create + target: + type: VNF + retry: 0 + timeout: 30 + 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/templates/template.demo/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test2.yaml b/controlloop/templates/template.demo/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test2.yaml new file mode 100644 index 000000000..358bbfbea --- /dev/null +++ b/controlloop/templates/template.demo/src/test/resources/yaml/policy_ControlLoop_ParamsCleanup-test2.yaml @@ -0,0 +1,39 @@ +# Copyright 2018 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. + +# +# This YAML must be slightly different from test.yaml. +# +controlLoop: + version: 3.0.0 + controlLoopName: ControlLoop-Params-Cleanup-Test + trigger_policy: unique-policy-id-1-scale-up + timeout: 60 + +policies: + - id: unique-policy-id-1-scale-up + name: Create a new VF Module + description: + actor: SO + recipe: VF Module Create + target: + type: VNF + retry: 0 + timeout: 30 + 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 |