From b2d602aaedfcc9356e07dd94b6baec70c38815b8 Mon Sep 17 00:00:00 2001 From: daniel Date: Tue, 17 Oct 2017 17:56:08 -0500 Subject: Fix Target Entity Lock This fix enables the pdp to stop events with different requestId's/controlLoopNames from being processed if the target entity specified is already being processed by another event/controlloop. The lock timeout rule is removed to stop the event from waiting to obtain a lock if it was not already required. Handling of the lock denied case is taken care of in the event manager rule instead of in a seperate rule to avoid further unneccessary computation. A new control loop test case was created that is for testing general control loop errors. The test case included is for testing that the lock will deny a request with a duplicate target entity. Issue-ID: POLICY-341 Change-Id: Ib62286eff74ec22b2c645f32f385cb331fb1dff3 Signed-off-by: Daniel Cruz --- .../main/resources/__closedLoopControlName__.drl | 96 ++--- .../template/demo/ControlLoopFailureTest.java | 399 +++++++++++++++++++++ 2 files changed, 436 insertions(+), 59 deletions(-) create mode 100644 controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopFailureTest.java (limited to 'controlloop') 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 44501bf5e..3d6d89c69 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 @@ -428,36 +428,53 @@ rule "${policyName}.EVENT.MANAGER" // ControlLoopOperationManager operation = $manager.processControlLoop(); if (operation != null) { - logger.info("{}: {}: starting operation={}", - $params.getClosedLoopControlName(), drools.getRule().getName(), - operation); - // - // insert into memory - // - insert(operation); - // - // insert operation timeout object - // - OperationTimer opTimer = new OperationTimer(); - opTimer.setClosedLoopControlName($event.closedLoopControlName); - opTimer.setRequestID($event.requestID.toString()); - opTimer.setDelay(operation.getOperationTimeout().toString() + "s"); - insert(opTimer); - // // Let's ask for a lock right away // LockResult result = $manager.lockCurrentOperation(); - if (result.getA().equals(GuardResult.LOCK_ACQUIRED)) { - logger.info("{}: {}: guard lock acquired={}", + logger.info("{}: {}: guard lock acquired={}", $params.getClosedLoopControlName(), drools.getRule().getName(), result.getB()); - + if (result.getA().equals(GuardResult.LOCK_ACQUIRED)) { // - // Insert into memory + // insert the operation into memory + // + insert(operation); + + // + // insert operation timeout object + // + OperationTimer opTimer = new OperationTimer(); + opTimer.setClosedLoopControlName($event.closedLoopControlName); + opTimer.setRequestID($event.requestID.toString()); + opTimer.setDelay(operation.getOperationTimeout().toString() + "s"); + insert(opTimer); + + // + // Insert lock into memory // insert(result.getB()); } + else { + logger.debug("The target resource {} is already processing", + $event.AAI.get($event.target)); + notification = new VirtualControlLoopNotification($event); + notification.notification = ControlLoopNotificationType.REJECTED; + notification.message = "The target " + $event.AAI.get($event.target) + " is already locked"; + notification.from = "policy"; + notification.policyName = drools.getRule().getName(); + notification.policyScope = "${policyScope}"; + notification.policyVersion = "${policyVersion}"; + + PolicyEngine.manager.deliver("POLICY-CL-MGT", notification); + + retract($event); + retract($manager); + retract($clTimer); + } + logger.info("{}: {}: starting operation={}", + $params.getClosedLoopControlName(), drools.getRule().getName(), + operation); } else { // // Probably waiting for abatement @@ -478,45 +495,6 @@ rule "${policyName}.EVENT.MANAGER" end - - -/* -* -* -* -*/ -rule "${policyName}.EVENT.MANAGER.OPERATION.NOT_LOCKED.TIMEOUT" - timer (int: 5s 5s) - when - $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" ) - $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID ) - $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.closedLoopControlName, onset.requestID == $event.requestID ) - not ( TargetLock (requestID == $event.requestID) ) - then - - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}: event={} manager={} operation={}", - $params.getClosedLoopControlName(), drools.getRule().getName(), - $event, $manager, $operation); - - // - // Need to ask for a Lock - // - LockResult result = $manager.lockCurrentOperation(); - if (result.getA().equals(GuardResult.LOCK_ACQUIRED)) { - logger.info("{}: {}: guard lock acquired={}", - $params.getClosedLoopControlName(), drools.getRule().getName(), - result.getB()); - - // - // Insert into memory - // - insert(result.getB()); - } - -end - /* * * Guard Permitted, let's send request to the actor. 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 new file mode 100644 index 000000000..f07ecbfae --- /dev/null +++ b/controlloop/templates/template.demo/src/test/java/org/onap/policy/template/demo/ControlLoopFailureTest.java @@ -0,0 +1,399 @@ +/*- + * ============LICENSE_START======================================================= + * demo + * ================================================================================ + * Copyright (C) 2017 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.*; + +import java.io.IOException; +import java.net.URLEncoder; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Properties; +import java.util.UUID; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.kie.api.runtime.KieSession; +import org.kie.api.runtime.rule.FactHandle; +import org.onap.policy.appclcm.LCMRequest; +import org.onap.policy.appclcm.LCMRequestWrapper; +import org.onap.policy.appclcm.LCMResponse; +import org.onap.policy.appclcm.LCMResponseWrapper; +import org.onap.policy.controlloop.ControlLoopEventStatus; +import org.onap.policy.controlloop.ControlLoopNotificationType; +import org.onap.policy.controlloop.VirtualControlLoopEvent; +import org.onap.policy.controlloop.VirtualControlLoopNotification; +import org.onap.policy.controlloop.policy.ControlLoopPolicy; +import org.onap.policy.drools.event.comm.Topic.CommInfrastructure; +import org.onap.policy.drools.event.comm.TopicEndpoint; +import org.onap.policy.drools.event.comm.TopicListener; +import org.onap.policy.drools.event.comm.TopicSink; +import org.onap.policy.drools.http.server.HttpServletServer; +import org.onap.policy.drools.properties.PolicyProperties; +import org.onap.policy.drools.protocol.coders.EventProtocolCoder; +import org.onap.policy.drools.protocol.coders.JsonProtocolFilter; +import org.onap.policy.drools.system.PolicyEngine; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ControlLoopFailureTest implements TopicListener { + + private static final Logger logger = LoggerFactory.getLogger(VCPEControlLoopTest.class); + + private static List noopTopics; + + private KieSession kieSession; + private Util.Pair pair; + private UUID requestId; + private UUID requestId2; + private UUID requestId3; + private int eventCount; + + static { + /* Set environment properties */ + Util.setAAIProps(); + Util.setGuardProps(); + Util.setPUProp(); + } + + @BeforeClass + public static void setUpSimulator() { + PolicyEngine.manager.configure(new Properties()); + assertTrue(PolicyEngine.manager.start()); + Properties noopSinkProperties = new Properties(); + noopSinkProperties.put(PolicyProperties.PROPERTY_NOOP_SINK_TOPICS, "APPC-LCM-READ,POLICY-CL-MGT"); + noopSinkProperties.put("noop.sink.topics.APPC-LCM-READ.events", "org.onap.policy.appclcm.LCMRequestWrapper"); + noopSinkProperties.put("noop.sink.topics.APPC-LCM-READ.events.custom.gson", "org.onap.policy.appclcm.util.Serialization,gson"); + noopSinkProperties.put("noop.sink.topics.POLICY-CL-MGT.events", "org.onap.policy.controlloop.VirtualControlLoopNotification"); + noopSinkProperties.put("noop.sink.topics.POLICY-CL-MGT.events.custom.gson", "org.onap.policy.controlloop.util.Serialization,gsonPretty"); + noopTopics = TopicEndpoint.manager.addTopicSinks(noopSinkProperties); + + EventProtocolCoder.manager.addEncoder("junit.groupId", "junit.artifactId", "POLICY-CL-MGT", "org.onap.policy.controlloop.VirtualControlLoopNotification", new JsonProtocolFilter(), null, null, 1111); + EventProtocolCoder.manager.addEncoder("junit.groupId", "junit.artifactId", "APPC-LCM-READ", "org.onap.policy.appclcm.LCMRequestWrapper", new JsonProtocolFilter(), null, null, 1111); + try { + Util.buildAaiSim(); + Util.buildGuardSim(); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @AfterClass + public static void tearDownSimulator() { + HttpServletServer.factory.destroy(); + PolicyEngine.manager.shutdown(); + } + + /** + * This test case tests the scenario where 3 events occur + * and 2 of the requests refer to the same target entity + * while the 3rd is for another entity. The expected result + * is that the event with the duplicate target entity will have + * a final success result for one of the events, and a rejected + * message for the one that was unable to obtain the lock. The + * event that is referring to a different target entity should + * be able to obtain a lock since it is a different target. After + * processing of all events there should only be the params object + * left in memory. + */ + @Test + public void targetLockedTest() { + /* + * Start the kie session + */ + try { + kieSession = startSession("../archetype-cl-amsterdam/src/main/resources/archetype-resources/src/main/resources/__closedLoopControlName__.drl", + "src/test/resources/yaml/policy_ControlLoop_vCPE.yaml", + "service=ServiceDemo;resource=Res1Demo;type=operational", + "CL_vCPE", + "org.onap.closed_loop.ServiceDemo:VNFS:1.0.0"); + } catch (IOException e) { + e.printStackTrace(); + logger.debug("Could not create kieSession"); + fail("Could not create kieSession"); + } + + /* + * Allows the PolicyEngine to callback to this object to + * notify that there is an event ready to be pulled + * from the queue + */ + for (TopicSink sink : noopTopics) { + assertTrue(sink.start()); + sink.register(this); + } + + /* + * Create a unique requestId + */ + requestId = UUID.randomUUID(); + + /* + * This will be a unique request for another target entity + */ + requestId2 = UUID.randomUUID(); + + /* + * This will be a request duplicating the target entity + * of the first request + */ + requestId3 = UUID.randomUUID(); + + /* + * Simulate an onset event the policy engine will + * receive from DCAE to kick off processing through + * the rules + */ + sendEvent(pair.a, requestId, ControlLoopEventStatus.ONSET, "vnf01"); + + /* + * Send a second event requesting an action for a different target entity + */ + sendEvent(pair.a, requestId2, ControlLoopEventStatus.ONSET, "vnf02"); + + /* + * Send a second event for a different target to ensure there + * are no problems with obtaining a lock for a different + */ + kieSession.fireUntilHalt(); + + /* + * The only fact in memory should be Params + */ + assertEquals(1, kieSession.getFactCount()); + + /* + * Print what's left in memory + */ + dumpFacts(kieSession); + + /* + * Gracefully shut down the kie session + */ + kieSession.dispose(); + } + + /** + * This method will start a kie session and instantiate + * the Policy Engine. + * + * @param droolsTemplate + * the DRL rules file + * @param yamlFile + * the yaml file containing the policies + * @param policyScope + * scope for policy + * @param policyName + * name of the policy + * @param policyVersion + * version of the policy + * @return the kieSession to be used to insert facts + * @throws IOException + */ + private KieSession startSession(String droolsTemplate, + String yamlFile, + String policyScope, + String policyName, + String policyVersion) throws IOException { + + /* + * Load policies from yaml + */ + pair = Util.loadYaml(yamlFile); + assertNotNull(pair); + assertNotNull(pair.a); + assertNotNull(pair.a.getControlLoop()); + assertNotNull(pair.a.getControlLoop().getControlLoopName()); + assertTrue(pair.a.getControlLoop().getControlLoopName().length() > 0); + + /* + * Construct a kie session + */ + final KieSession kieSession = Util.buildContainer(droolsTemplate, + pair.a.getControlLoop().getControlLoopName(), + policyScope, + policyName, + policyVersion, + URLEncoder.encode(pair.b, "UTF-8")); + + /* + * Retrieve the Policy Engine + */ + + logger.debug("============"); + logger.debug(URLEncoder.encode(pair.b, "UTF-8")); + logger.debug("============"); + + return kieSession; + } + + /* + * (non-Javadoc) + * @see org.onap.policy.drools.PolicyEngineListener#newEventNotification(java.lang.String) + */ + public void onTopicEvent(CommInfrastructure commType, String topic, String event) { + /* + * Pull the object that was sent out to DMAAP and make + * sure it is a ControlLoopNoticiation of type active + */ + Object obj = null; + if ("POLICY-CL-MGT".equals(topic)) { + obj = org.onap.policy.controlloop.util.Serialization.gsonJunit.fromJson(event, org.onap.policy.controlloop.VirtualControlLoopNotification.class); + } + else if ("APPC-LCM-READ".equals(topic)) + obj = org.onap.policy.appclcm.util.Serialization.gsonJunit.fromJson(event, org.onap.policy.appclcm.LCMRequestWrapper.class); + assertNotNull(obj); + if (obj instanceof VirtualControlLoopNotification) { + VirtualControlLoopNotification notification = (VirtualControlLoopNotification) obj; + String policyName = notification.policyName; + if (policyName.endsWith("EVENT")) { + logger.debug("Rule Fired: " + notification.policyName); + assertTrue(ControlLoopNotificationType.ACTIVE.equals(notification.notification)); + } + else if (policyName.endsWith("GUARD_NOT_YET_QUERIED")) { + logger.debug("Rule Fired: " + notification.policyName); + assertTrue(ControlLoopNotificationType.OPERATION.equals(notification.notification)); + assertNotNull(notification.message); + assertTrue(notification.message.startsWith("Sending guard query")); + } + else if (policyName.endsWith("GUARD.RESPONSE")) { + logger.debug("Rule Fired: " + notification.policyName); + assertTrue(ControlLoopNotificationType.OPERATION.equals(notification.notification)); + assertNotNull(notification.message); + assertTrue(notification.message.toLowerCase().endsWith("permit")); + } + else if (policyName.endsWith("GUARD_PERMITTED")) { + logger.debug("Rule Fired: " + notification.policyName); + assertTrue(ControlLoopNotificationType.OPERATION.equals(notification.notification)); + assertNotNull(notification.message); + assertTrue(notification.message.startsWith("actor=APPC")); + } + else if (policyName.endsWith("OPERATION.TIMEOUT")) { + logger.debug("Rule Fired: " + notification.policyName); + kieSession.halt(); + logger.debug("The operation timed out"); + fail("Operation Timed Out"); + } + else if (policyName.endsWith("APPC.LCM.RESPONSE")) { + logger.debug("Rule Fired: " + notification.policyName); + assertTrue(ControlLoopNotificationType.OPERATION_SUCCESS.equals(notification.notification)); + assertNotNull(notification.message); + assertTrue(notification.message.startsWith("actor=APPC")); + if (requestId.equals(notification.requestID)) { + sendEvent(pair.a, requestId, ControlLoopEventStatus.ABATED, "vnf01"); + } + else if (requestId2.equals(notification.requestID)) { + sendEvent(pair.a, requestId2, ControlLoopEventStatus.ABATED, "vnf02"); + } + } + else if (policyName.endsWith("EVENT.MANAGER")) { + logger.debug("Rule Fired: " + notification.policyName); + if (requestId3.equals(notification.requestID)) { + /* + * The event with the duplicate target should be rejected + */ + assertTrue(ControlLoopNotificationType.REJECTED.equals(notification.notification)); + } + else { + assertTrue(ControlLoopNotificationType.FINAL_SUCCESS.equals(notification.notification)); + } + if (++eventCount == 3) { + kieSession.halt(); + } + } + else if (policyName.endsWith("EVENT.MANAGER.TIMEOUT")) { + logger.debug("Rule Fired: " + notification.policyName); + kieSession.halt(); + logger.debug("The control loop timed out"); + fail("Control Loop Timed Out"); + } + } + else if (obj instanceof LCMRequestWrapper) { + /* + * The request should be of type LCMRequestWrapper + * and the subrequestid should be 1 + */ + LCMRequestWrapper dmaapRequest = (LCMRequestWrapper) obj; + LCMRequest appcRequest = dmaapRequest.getBody(); + assertTrue(appcRequest.getCommonHeader().getSubRequestId().equals("1")); + + logger.debug("\n============ APPC received the request!!! ===========\n"); + + /* + * Simulate a success response from APPC and insert + * the response into the working memory + */ + LCMResponseWrapper dmaapResponse = new LCMResponseWrapper(); + LCMResponse appcResponse = new LCMResponse(appcRequest); + appcResponse.getStatus().setCode(400); + appcResponse.getStatus().setMessage("AppC success"); + dmaapResponse.setBody(appcResponse); + + /* + * Interrupting with a different request for the same + * target entity to check if lock will be denied + */ + if (requestId.equals(appcResponse.getCommonHeader().getRequestId())) { + sendEvent(pair.a, requestId3, ControlLoopEventStatus.ONSET, "vnf01"); + } + kieSession.insert(dmaapResponse); + } + } + + /** + * This method is used to simulate event messages from DCAE + * that start the control loop (onset message) or end the + * control loop (abatement message). + * + * @param policy the controlLoopName comes from the policy + * @param requestID the requestId for this event + * @param status could be onset or abated + * @param target, the target entity to take an action on + */ + protected void sendEvent(ControlLoopPolicy policy, UUID requestID, + ControlLoopEventStatus status, String target) { + VirtualControlLoopEvent event = new VirtualControlLoopEvent(); + event.closedLoopControlName = policy.getControlLoop().getControlLoopName(); + event.requestID = requestID; + event.target = "generic-vnf.vnf-id"; + event.closedLoopAlarmStart = Instant.now(); + event.AAI = new HashMap<>(); + event.AAI.put("generic-vnf.vnf-id", target); + event.closedLoopEventStatus = status; + kieSession.insert(event); + } + + /** + * This method will dump all the facts in the working memory. + * + * @param kieSession the session containing the facts + */ + public void dumpFacts(KieSession kieSession) { + logger.debug("Fact Count: {}", kieSession.getFactCount()); + for (FactHandle handle : kieSession.getFactHandles()) { + logger.debug("FACT: {}", handle); + } + } + +} + -- cgit 1.2.3-korg