X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=controlloop%2Fcommon%2Fcontroller-usecases%2Fsrc%2Fmain%2Fresources%2Fusecases.drl;h=c10b8d0c9de25573aa6053c492a4a03749a0b386;hb=fc19178b956f4474ed14e810fd911ac60f3756d0;hp=3fe2cddbf809c44f38c77b4e1b538103ff96c1fd;hpb=6dfab64262ea5816c761042192c84e498a3177ab;p=policy%2Fdrools-applications.git diff --git a/controlloop/common/controller-usecases/src/main/resources/usecases.drl b/controlloop/common/controller-usecases/src/main/resources/usecases.drl index 3fe2cddbf..c10b8d0c9 100644 --- a/controlloop/common/controller-usecases/src/main/resources/usecases.drl +++ b/controlloop/common/controller-usecases/src/main/resources/usecases.drl @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2018-2020 AT&T Intellectual Property. All rights reserved. - * Modifications Copyright (C) 2019 Bell Canada. + * Copyright (C) 2020-2022 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2023 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,85 +21,60 @@ package org.onap.policy.controlloop; -import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceInput; -import org.onap.policy.controlloop.drl.legacy.ControlLoopParams; +import java.time.Instant; +import java.util.Collections; +import java.util.stream.Collectors; import org.onap.policy.controlloop.CanonicalOnset; import org.onap.policy.controlloop.VirtualControlLoopEvent; import org.onap.policy.controlloop.VirtualControlLoopNotification; -import org.onap.policy.controlloop.ControlLoopEventStatus; -import org.onap.policy.controlloop.ControlLoopException; import org.onap.policy.controlloop.ControlLoopNotificationType; -import org.onap.policy.controlloop.ControlLoopResponse; -import org.onap.policy.controlloop.policy.PolicyResult; -import org.onap.policy.controlloop.policy.ControlLoopPolicy; -import org.onap.policy.controlloop.policy.Policy; -import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager; -import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager.NewEventStatus; -import org.onap.policy.controlloop.eventmanager.ControlLoopOperationManager; -import org.onap.policy.controlloop.eventmanager.LockCallbackWorkingMemory; +import org.onap.policy.controlloop.actor.xacml.XacmlActor; +import org.onap.policy.controlloop.actorserviceprovider.Operation; +import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome; +import org.onap.policy.controlloop.actorserviceprovider.OperationFinalResult; +import org.onap.policy.controlloop.actorserviceprovider.OperationProperties; +import org.onap.policy.controlloop.actorserviceprovider.OperationResult; +import org.onap.policy.controlloop.drl.legacy.ControlLoopParams; +import org.onap.policy.controlloop.eventmanager.ActorConstants; +import org.onap.policy.controlloop.eventmanager.Step; import org.onap.policy.controlloop.utils.ControlLoopUtils; -import org.onap.policy.controlloop.actor.so.SoActorServiceProvider; -import org.onap.policy.controlloop.actor.cds.CdsActorServiceProvider; -import org.onap.policy.controlloop.actor.cds.CdsActorServiceProvider.CdsActorServiceManager; -import org.onap.policy.controlloop.actor.cds.constants.CdsActorConstants; -import org.onap.policy.aai.AaiCqResponse; -import org.onap.policy.appc.Request; -import org.onap.policy.appc.Response; -import org.onap.policy.appc.CommonHeader; -import org.onap.policy.appclcm.AppcLcmDmaapWrapper; -import org.onap.policy.cds.CdsResponse; -import org.onap.policy.cds.client.CdsProcessorGrpcClient; -import org.onap.policy.cds.properties.CdsServerProperties; -import org.onap.policy.drools.utils.Pair; -import org.onap.policy.sdnr.PciRequestWrapper; -import org.onap.policy.sdnr.PciResponseWrapper; -import org.onap.policy.sdnr.PciRequest; -import org.onap.policy.sdnr.PciResponse; -import org.onap.policy.vfc.VfcRequest; -import org.onap.policy.vfc.VfcResponse; -import org.onap.policy.vfc.VfcManager; -import org.onap.policy.so.SoManager; -import org.onap.policy.so.SoRequest; -import org.onap.policy.so.SoResponseWrapper; -import org.onap.policy.sdnc.SdncRequest; -import org.onap.policy.sdnc.SdncManager; -import org.onap.policy.sdnc.SdncResponse; -import org.onap.policy.drools.core.lock.Lock; -import org.onap.policy.guard.PolicyGuardRequest; -import org.onap.policy.guard.PolicyGuardResponse; -import org.onap.policy.guard.PolicyGuardXacmlRequestAttributes; -import org.onap.policy.guard.PolicyGuardXacmlHelper; +import org.onap.policy.drools.apps.controller.usecases.UsecasesEventManager; +import org.onap.policy.controlloop.eventmanager.ClEventManagerWithSteps.State; +import org.onap.policy.controlloop.eventmanager.ClEventManagerWithOutcome.OperationOutcome2; +import org.onap.policy.controlloop.eventmanager.ClEventManagerWithEvent.NewEventStatus; +import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager; +import org.onap.policy.controlloop.eventmanager.EventManagerServices; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.Constructor; - import org.slf4j.LoggerFactory; import org.slf4j.Logger; -import java.time.Instant; -import java.util.LinkedList; -import java.util.Iterator; - - import org.onap.policy.drools.system.PolicyEngineConstants; + /* - * This object is to provide support for timeouts - * due to a bug in drools' built-in timers - */ -declare ControlLoopTimer - closedLoopControlName : String - requestId : String - delay : String - expired : boolean - //timerType is the type of timer: either "ClosedLoop" or "Operation" - timerType : String +* Called at initial start-up, to create the event services that will be used by all +* event managers in this rule engine instance. +*/ +rule "CREATE.EVENT.SERVICES" + when + not (EventManagerServices()) + then + + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}", drools.getRule().getName()); + + try { + insert(new EventManagerServices("event-manager")); + + } catch(RuntimeException e) { + logger.warn("{}: cannot create event services", drools.getRule().getName(), e); + } end /* * -* Called when the ControlLoopParams object has been inserted into working memory from the BRMSGW. +* Called when the ControlLoopParams object has been inserted into working memory from the PAP. * */ rule "INSERT.PARAMS" @@ -107,7 +82,7 @@ rule "INSERT.PARAMS" $params : ControlLoopParams() then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); logger.info("{}: {} : TOSCA-POLICY=[{}]", $params.getClosedLoopControlName(), $params.getPolicyName() + "." + drools.getRule().getName(), $params.getToscaPolicy()); end @@ -122,7 +97,7 @@ rule "NEW.TOSCA.POLICY" $policy : ToscaPolicy() then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); logger.info("{}: [{}|{}|{}|{}]: CONTENT: {}", drools.getRule().getName(), $policy.getType(), $policy.getTypeVersion(), $policy.getName(), $policy.getVersion(), $policy); @@ -142,7 +117,7 @@ rule "REMOVE.PARAMS" not ( ToscaPolicy( getName() == $policyName, getVersion() == $policyVersion ) ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); logger.info("{}: [{}|{}|{}]", drools.getRule().getName(), $params.getPolicyScope(), $params.getPolicyName(), $params.getPolicyVersion()); @@ -159,1552 +134,976 @@ rule "EVENT" when $params : ControlLoopParams( $clName : getClosedLoopControlName() ) $event : CanonicalOnset( closedLoopControlName == $clName ) - not ( ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) ) + $services : EventManagerServices() + not ( UsecasesEventManager( closedLoopControlName == $event.getClosedLoopControlName(), + getEvent() == $event ) ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName()); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: event={}", + $clName, $params.getPolicyName(), drools.getRule().getName(), + $event); + // + // Retract the event from memory; it will be managed by the manager from now on + // + retract($event); - try { + VirtualControlLoopNotification notification = null; + try { // // Check the event, because we need it to not be null when - // we create the ControlLoopEventManager. The ControlLoopEventManager - // will do extra syntax checking as well check if the closed loop is disabled. + // we create the UsecasesEventManager. The UsecasesEventManager + // will do extra syntax checking as well as check if the closed loop is disabled. // if ($event.getRequestId() == null) { - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); + notification = new VirtualControlLoopNotification($event); notification.setNotification(ControlLoopNotificationType.REJECTED); notification.setFrom("policy"); notification.setMessage("Missing requestId"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); + notification.setPolicyScope(drools.getRule().getName()); + notification.setPolicyName($params.getPolicyName()); notification.setPolicyVersion($params.getPolicyVersion()); - // - // Let interested parties know - // - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - - // - // Retract it from memory - // - retract($event); } else { - // - // Create an EventManager - // - ControlLoopEventManager manager = new ControlLoopEventManager($clName, $event.getRequestId()); - // - // Determine if EventManager can actively process the event - // (i.e. syntax, is_closed_loop_disabled checks etc.) - // - VirtualControlLoopNotification notification = manager.activate($params.getToscaPolicy(), $event); - notification.setFrom("pdp-0001-controller=controlloop"); // Engine.getInstanceName() - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - // - // Are we actively pursuing this event? - // - if (notification.getNotification() == ControlLoopNotificationType.ACTIVE) { - // - // Insert Event Manager into memory, this will now kick off processing. - // - insert(manager); - // - // Let interested parties know - // - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - // - // Setup the Overall Control Loop timer - // - ControlLoopTimer clTimer = new ControlLoopTimer(); - clTimer.setTimerType("ClosedLoop"); - clTimer.setClosedLoopControlName($event.getClosedLoopControlName()); - clTimer.setRequestId($event.getRequestId().toString()); - clTimer.setDelay(manager.getControlLoopTimeout(1500) + "s"); - // - // Insert it - // - insert(clTimer); - } else { - // - // Let interested parties know - // - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - // - // Retract it from memory - // - retract($event); - } + UsecasesEventManager manager = + new UsecasesEventManager($services, $params, $event, drools.getWorkingMemory()); + insert(manager); + try { + // load the first policy/step + manager.start(); - // - // Now that the manager is inserted into Drools working memory, we'll wait for - // another rule to fire in order to continue processing. This way we can also - // then screen for additional ONSET and ABATED events for this RequestId. - // + if (manager.getSteps().isEmpty()) { + // no steps implies no policies, thus go straight to DONE state + manager.setState(State.DONE); + + manager.setAccepted(true); + + notification = manager.makeNotification(); + notification.setNotification(ControlLoopNotificationType.ACTIVE); + notification.setPolicyScope(drools.getRule().getName()); + notification.setPolicyName($params.getPolicyName()); + notification.setPolicyVersion($params.getPolicyVersion()); + + } else { + // Note: the notification will be generated lazily + manager.setState(State.POLICY_LOADED); + } + + } catch(Exception e) { + retract(manager); + throw e; + } } } catch (Exception e) { - logger.warn("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName(), e); - - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); + logger.warn("{}: {}.{}: error starting manager", $clName, $params.getPolicyName(), + drools.getRule().getName(), e); + notification = new VirtualControlLoopNotification($event); notification.setNotification(ControlLoopNotificationType.REJECTED); notification.setMessage("Exception occurred: " + e.getMessage()); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); + notification.setPolicyScope(drools.getRule().getName()); + notification.setPolicyName($params.getPolicyName()); notification.setPolicyVersion($params.getPolicyVersion()); - // - // - // - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - // - // Retract the event - // - retract($event); + } + // + // Generate notification + // + try { + if (notification != null) { + PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); + } + + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: event={} exception generating notification", + $clName, $params.getPolicyName(), drools.getRule().getName(), + $event, e); } end /* * -* This rule happens when we got a valid ONSET, closed loop is enabled and an Event Manager -* is now created. We can start processing the yaml specification via the Event Manager. +* This rule fires when we get a subsequent event. * */ -rule "EVENT.MANAGER" +rule "EVENT.MANAGER.NEW.EVENT" when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : VirtualControlLoopEvent( closedLoopControlName == $clName ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) - $clTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId().toString(), timerType == "ClosedLoop", !expired ) + $event : VirtualControlLoopEvent( ) + $manager : UsecasesEventManager( closedLoopControlName == $event.getClosedLoopControlName(), + getEvent() == $event ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}: event={} manager={} clTimer={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event, $manager, $clTimer); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: event={} manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $event, $manager); + // + // Remove the event from memory + // + retract($event); - try { - // - // Check which event this is. - // - ControlLoopEventManager.NewEventStatus eventStatus = $manager.onNewEvent($event); - // - // Check what kind of event this is - // - if (eventStatus == NewEventStatus.SUBSEQUENT_ONSET) { - // - // We don't care about subsequent onsets - // - logger.info("{}: {}: subsequent onset", - $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - retract($event); - return; - } - if (eventStatus == NewEventStatus.SYNTAX_ERROR) { + // + // Check what kind of event this is + // + switch($manager.onNewEvent($event)) { + case SYNTAX_ERROR: // // Ignore any bad syntax events // - logger.warn("{}: {}: syntax error", - $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - retract($event); - return; - } - // - // We only want the initial ONSET event in memory, - // all the other events need to be retracted to support - // cleanup and avoid the other rules being fired for this event. - // - if (eventStatus != NewEventStatus.FIRST_ONSET) { - logger.warn("{}: {}: no first onset", - $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - retract($event); - } + logger.warn("{}: {}.{}: syntax error", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName()); + break; - logger.debug("{}: {}: target={}", $clName, - $params.getPolicyName() + "." + drools.getRule().getName(), $event.getTarget()); - // - // Now start seeing if we need to process this event - // - - // - // Check if this is a Final Event - // - VirtualControlLoopNotification notification = $manager.isControlLoopFinal(); - - - if (notification != null) { + case FIRST_ABATEMENT: + case SUBSEQUENT_ABATEMENT: // - // Its final, but are we waiting for abatement? + // TODO: handle the abatement. Currently, it's just discarded. // - if ($manager.getNumAbatements() > 0) { - logger.info("{}: {}: abatement received for {}. Closing the control loop", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event.getRequestId()); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - // - // In this case, we are done - // - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - // - // Unlock the target - // - Lock lock = $manager.unlockCurrentOperation(); - if(lock != null) { - logger.debug("{}: {}.{}: retracting lock={}", $clName, - $params.getPolicyName(), drools.getRule().getName(), lock); - retract(lock); - } - // - // Retract everything from memory - // - logger.info("{}: {}: retracting onset, manager, and timer", - $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - - retract($manager.getOnsetEvent()); - - // 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. - // Or, we could track this and then subsequently ignore the events - // - } else { - // - // Check whether we need to wait for abatement - // - if ($manager.getProcessor().getControlLoop().getAbatement() == true && notification.getNotification() == ControlLoopNotificationType.FINAL_SUCCESS) { - logger.info("{}: {}: waiting for abatement ..", - $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - } else { - logger.info("{}: {}: no abatement expect for {}. Closing the control loop", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event.getRequestId()); - - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - - // - // In this case, we are done - // - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - // - // Unlock the target - // - Lock lock = $manager.unlockCurrentOperation(); - if(lock != null) { - logger.debug("{}: {}.{}: retracting lock={}", $clName, - $params.getPolicyName(), drools.getRule().getName(), lock); - retract(lock); - } - // - // Retract everything from memory - // - logger.info("{}: {}: retracting onset, manager, and timer", - $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - - retract($manager.getOnsetEvent()); - - // don't retract manager, etc. - a clean-up rule will do that - } - } - } else { + logger.info("{}: {}.{}: abatement", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName()); + break; + + case FIRST_ONSET: + case SUBSEQUENT_ONSET: + default: // - // NOT final, so let's ask for the next operation + // We don't care about subsequent onsets // - ControlLoopOperationManager operation = $manager.processControlLoop(); - if (operation != null) { - // - // Let's ask for a lock right away - // - logger.info("{}: {}.{}: requesting lock for operation={}", - $clName, $params.getPolicyName(), drools.getRule().getName(), - operation); - - Pair oldNew = $manager.lockCurrentOperation( - new LockCallbackWorkingMemory($params.getClosedLoopControlName(), drools.getWorkingMemory())); - if(oldNew.first() != null) { - logger.debug("{}: {}.{}: retracting lock={}", $clName, - $params.getPolicyName(), drools.getRule().getName(), oldNew.first()); - retract(oldNew.first()); - } - if(oldNew.second() != null) { - logger.debug("{}: {}.{}: inserting lock={}", $clName, - $params.getPolicyName(), drools.getRule().getName(), oldNew.second()); - insert(oldNew.second()); - } - - // - // insert the operation into memory - // - insert(operation); - - // - // insert operation timeout object - // - ControlLoopTimer opTimer = new ControlLoopTimer(); - opTimer.setTimerType("Operation"); - opTimer.setClosedLoopControlName($event.getClosedLoopControlName()); - opTimer.setRequestId($event.getRequestId().toString()); - Integer timeout = operation.getOperationTimeout(); - opTimer.setDelay(timeout > 0 ? timeout.toString() + "s" : $clTimer.getDelay()); - insert(opTimer); - - } else { - // - // Probably waiting for abatement - // - logger.info("{}: {}: no operation, probably waiting for abatement", - $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - } - } - } catch (Exception e) { - logger.warn("{}: {}: unexpected", - $clName, - $params.getPolicyName() + "." + drools.getRule().getName(), e); - - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE); - notification.setMessage(e.getMessage()); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - - retract($event); - - // don't retract manager, etc. - a clean-up rule will do that + logger.warn("{}: {}.{}: subsequent onset", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName()); + break; } - end /* * -* Lock denied +* All steps have been executed, load the next policy. * */ -rule "EVENT.MANAGER.OPERATION.LOCK.DENIED" +rule "EVENT.MANAGER.LOAD.NEXT.POLICY" when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : VirtualControlLoopEvent( closedLoopControlName == $clName ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) - $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), - onset.getRequestId() == $event.getRequestId(), "None".equalsIgnoreCase(getGuardApprovalStatus()) ) - $lock : Lock (ownerKey == $event.getRequestId().toString(), isUnavailable()) + $manager : UsecasesEventManager( + isActive(), + getState() == State.POLICY_LOADED, + getSteps().isEmpty() ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}.{}: event={} manager={} operation={} lock={}", - $clName, $params.getPolicyName(), drools.getRule().getName(), - $event, $manager, $operation, $lock); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager); - logger.debug("The target resource {} is already processing", $event.getAai().get($event.getTarget())); - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setNotification(ControlLoopNotificationType.REJECTED); - notification.setMessage("The target " + $event.getAai().get($event.getTarget()) + " is already locked"); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); + try { + $manager.loadNextPolicy($manager.getResult()); - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); + if ($manager.getSteps().isEmpty()) { + // no steps - must be the final policy + $manager.setState(State.DONE); + } - retract($event); + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception loading next policy", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); + $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to load next policy"); + } - // don't retract manager, etc. - a clean-up rule will do that + update($manager); end /* * -* Guard Permitted, let's send request to the actor. +* Policy loaded, identify any preprocessor steps that need to be run first. * */ -rule "EVENT.MANAGER.OPERATION.LOCKED.GUARD_PERMITTED" +rule "EVENT.MANAGER.PREPROCESS" when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : VirtualControlLoopEvent( closedLoopControlName == $clName ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) - $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), - onset.getRequestId() == $event.getRequestId(), "Permit".equalsIgnoreCase(getGuardApprovalStatus()) ) - $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId().toString(), timerType == "Operation", !expired ) + $manager : UsecasesEventManager( + isActive(), + getState() == State.POLICY_LOADED, + $step : getSteps().peek(), + $step != null, + !$step.isPreprocessed() ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}: event={} manager={} operation={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event, $manager, $operation); - - Object request = null; - boolean caughtException = false; + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: {} manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $step, $manager); try { - request = $operation.startOperation($event); - - if (request != null) { - logger.debug("{}: {}: starting operation ..", - $clName, - $params.getPolicyName() + "." + drools.getRule().getName()); - // - // Tell interested parties we are performing this Operation - // - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setNotification(ControlLoopNotificationType.OPERATION); - notification.setMessage($operation.getOperationMessage()); - notification.setHistory($operation.getHistory()); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - - switch ($operation.policy.getActor()){ - - case "APPC": - - if (request instanceof Request) { - PolicyEngineConstants.getManager().deliver("APPC-CL", request); - } - else if (request instanceof AppcLcmDmaapWrapper) { - PolicyEngineConstants.getManager().deliver("APPC-LCM-READ", request); - } - break; - case "SO": - // at this point the AAI named query request should have already been made, the response - // recieved and used in the construction of the SO Request which is stored in operationRequest - - if(request instanceof SoRequest) { - // Call SO. The response will be inserted into memory once it's received - class mySoCallback implements SoManager.SoCallback { - public void onSoResponseWrapper(SoResponseWrapper wrapper) { - drools.getWorkingMemory().insert(wrapper); - } - } - SoActorServiceProvider.sendRequest($event.getRequestId().toString(), - new mySoCallback(), - request, - PolicyEngineConstants.getManager().getEnvironmentProperty("so.url"), - PolicyEngineConstants.getManager().getEnvironmentProperty("so.username"), - PolicyEngineConstants.getManager().getEnvironmentProperty("so.password")); - } - break; - case "VFC": - if (request instanceof VfcRequest) { - class myVfcCallback implements VfcManager.VfcCallback { - - public void onResponse(VfcResponse responseError) { - drools.getWorkingMemory().insert(responseError); - } - }; - // Start VFC thread - Thread t = new Thread(new VfcManager(new myVfcCallback(), - (VfcRequest)request, - PolicyEngineConstants.getManager().getEnvironmentProperty("vfc.url"), - PolicyEngineConstants.getManager().getEnvironmentProperty("vfc.username"), - PolicyEngineConstants.getManager().getEnvironmentProperty("vfc.password"))); - t.start(); - } - break; - case "SDNR": - if (request instanceof PciRequestWrapper) { - PolicyEngineConstants.getManager().deliver("SDNR-CL", request); - } - break; - - case "SDNC": - if (request instanceof SdncRequest) { - class mySdncCallback implements SdncManager.SdncCallback { - public void onCallback(SdncResponse response) { - drools.getWorkingMemory().insert(response); - } - } - // Start SDNC thread - Thread t = new Thread(new SdncManager(new mySdncCallback(), - (SdncRequest)request, - PolicyEngineConstants.getManager().getEnvironmentProperty("sdnc.url"), - PolicyEngineConstants.getManager().getEnvironmentProperty("sdnc.username"), - PolicyEngineConstants.getManager().getEnvironmentProperty("sdnc.password"))); - t.start(); - } - break; - - case "CDS": - - if(request instanceof ExecutionServiceInput) { - - // Instantiate cds actor, service manager and grpc properties - - CdsActorServiceProvider cdsActor = new CdsActorServiceProvider(); - CdsActorServiceProvider.CdsActorServiceManager cdsActorServiceManager = cdsActor.new CdsActorServiceManager(); - - CdsServerProperties props = new CdsServerProperties(); - props.setHost(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcHost")); - props.setPort(Integer.parseInt(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcPort"))); - props.setUsername(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcUsername")); - props.setPassword(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcPassword")); - props.setTimeout(Integer.parseInt(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcTimeout"))); - - // Send cds grpc request - try (CdsProcessorGrpcClient client = new CdsProcessorGrpcClient(cdsActorServiceManager, props)) { - CdsResponse response = - cdsActorServiceManager.sendRequestToCds(client, props, (ExecutionServiceInput) request); - logger.info("CDS response: {}", response); - insert(response); - } - } - break; - } - } else { - // - // What happens if its null? - // - logger.warn("{}: {}: unexpected null operation request", - $clName, - $params.getPolicyName() + "." + drools.getRule().getName()); - if ("SO".equals($operation.policy.getActor())) { - retract($opTimer); - retract($operation); - modify($manager) {finishOperation($operation)}; - } - else if ("vfc".equalsIgnoreCase($operation.policy.getActor())) { - retract($opTimer); - retract($operation); - modify($manager) {finishOperation($operation)}; - } - else if ("sdnc".equalsIgnoreCase($operation.policy.getActor())) { - retract($opTimer); - retract($operation); - modify($manager) {finishOperation($operation)}; - } - } - - } catch (Exception e) { - String msg = e.getMessage(); - logger.warn("{}: {}: operation={}: AAI failure: {}", - $clName, - $params.getPolicyName() + "." + drools.getRule().getName(), - $operation, msg, e); - $operation.setOperationHasException(msg); - - if(request != null) { - // - // Create a notification for it ("DB Write - end operation") - // - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE); - notification.setMessage($operation.getOperationHistory()); - notification.setHistory($operation.getHistory()); - - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - } - - retract($opTimer); - retract($operation); - caughtException = true; + /* + * Load any preprocessor steps. + * + * Note: this will not change the state of the manager, but it may change the + * state of the step. + */ + $step.setPreprocessed(true); + $manager.loadPreprocessorSteps(); + + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception loading preprocessor steps", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); + $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to load preprocessing steps"); } - // Having the modify statement in the catch clause doesn't work for whatever reason - if (caughtException) { - modify($manager) {finishOperation($operation)}; - } + update($manager); end - /* * -* We were able to acquire a lock so now let's ask Xacml Guard whether -* we are allowed to proceed with the request to the actor. +* Accepts the event, when ready to execute a step of interest. Does not change the +* state of the manager, leaving that to be done by rule "EVENT.MANAGER.EXECUTE.STEP". * */ -rule "EVENT.MANAGER.OPERATION.LOCKED.GUARD_NOT_YET_QUERIED" +rule "EVENT.MANAGER.ACCEPT" + salience 200 when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : VirtualControlLoopEvent( closedLoopControlName == $clName ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) - $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), - onset.getRequestId() == $event.getRequestId(), "None".equalsIgnoreCase(getGuardApprovalStatus()) ) - $lock : Lock (ownerKey == $event.getRequestId().toString(), isActive()) + $manager : UsecasesEventManager( + isActive(), + !isAccepted(), + getState() == State.POLICY_LOADED, + $step : getSteps().peek(), + $step != null, + $step.isPreprocessed(), + $step.acceptsEvent() ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}: event={} manager={} operation={} lock={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event, $manager, $operation, $lock); - - // - // Sending notification that we are about to query Guard ("DB write - start operation") - // - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setNotification(ControlLoopNotificationType.OPERATION); - notification.setMessage("Sending guard query for " + $operation.policy.getActor() + " " - + $operation.policy.getRecipe()); - notification.setHistory($operation.getHistory()); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $step, $manager); - // - // Now send Guard Request to XACML Guard. In order to bypass the call to Guard, - // just change guardEnabled to false. - // - // In order to use REST XACML, provide a URL instead of "" as a second argument - // to the CallGuardTask() and set the first argument to null - // (instead of XacmlPdpEngine). - // - - // NOTE: The environment properties uses "guard.disabled" but the boolean is guardEnabled - boolean guardEnabled = "false".equalsIgnoreCase(PolicyEngineConstants.getManager().getEnvironmentProperty("guard.disabled")); - - if(guardEnabled){ - - Thread t = new Thread(new org.onap.policy.guard.CallGuardTask( - drools.getWorkingMemory(), - $event.getClosedLoopControlName(), - $operation.policy.getActor().toString(), - $operation.policy.getRecipe(), - $operation.getTargetEntity(), - $event.getRequestId().toString(), - () -> { - try { - AaiCqResponse resp_cq = $manager.getCqResponse($event); - if (resp_cq == null){ - return null; - } else { - String custId = $operation.policy.getTarget().getModelCustomizationId(); - String invId = $operation.policy.getTarget().getModelInvariantId(); - String verId = $operation.policy.getTarget().getModelVersionId(); - return resp_cq.getVfModuleCount(custId, invId, verId); - } - } catch (Exception e){ - logger.warn("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName(), e); - } - return null; - })); - t.start(); - } - else{ - insert(new PolicyGuardResponse("Permit", $event.getRequestId(), $operation.policy.getRecipe())); + try { + $manager.setAccepted(true); + + VirtualControlLoopNotification notification = $manager.makeNotification(); + notification.setNotification(ControlLoopNotificationType.ACTIVE); + notification.setPolicyScope(drools.getRule().getName()); + notification.setPolicyName($manager.getPolicyName()); + notification.setPolicyVersion($manager.getPolicyVersion()); + $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName()); + + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception processing operation outcome", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); + $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to accept the event"); } + update($manager); end -// -// This rule will be triggered when a thread talking to the XACML Guard inserts a -// guardResponse object into the working memory -// -rule "GUARD.RESPONSE" +/* +* +* Ready to execute the step. +* +*/ +rule "EVENT.MANAGER.EXECUTE.STEP" when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : CanonicalOnset( closedLoopControlName == $clName ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) - $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), - onset.getRequestId() == $event.getRequestId() ) - $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId().toString(), timerType == "Operation", !expired ) - $guardResponse : PolicyGuardResponse(requestId == $event.getRequestId(), $operation.policy.recipe == operation) + $manager : UsecasesEventManager( + isActive(), + getState() == State.POLICY_LOADED, + $step : getSteps().peek(), + $step != null, + $step.isPreprocessed() ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}: event={} manager={} operation={} opTimer={} guardResponse={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event, $manager, $operation, $opTimer, $guardResponse); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: {} manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $step, $manager); + try { + $step.init(); + $step.setProperties(); - //we will permit the operation if there was no Guard for it - if("Indeterminate".equalsIgnoreCase($guardResponse.getResult())){ - $guardResponse.setResult("Permit"); - } + boolean guardDisabled = "true".equalsIgnoreCase( + PolicyEngineConstants.getManager().getEnvironmentProperty( + ControlLoopEventManager.GUARD_DISABLED_PROPERTY)); - // - // This notification has Guard result in "message". ("DB write - end operation in case of Guard Deny") - // - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setNotification(ControlLoopNotificationType.OPERATION); - notification.setMessage("Guard result for " + $operation.policy.getActor() + " " + $operation.policy.getRecipe() - + " is " + $guardResponse.getResult()); - notification.setHistory($operation.getHistory()); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); + if (guardDisabled && XacmlActor.NAME.equals($step.getActorName())) { + // guard is disabled - just enqueue a "SUCCESS" (i.e., "Permit") + OperationOutcome outcome = $step.getParams().makeOutcome(); + outcome.setStart(Instant.now()); + outcome.setEnd(outcome.getStart()); - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); + $manager.getOutcomes().add(outcome); + $manager.setState(State.AWAITING_OUTCOME); - if("Permit".equalsIgnoreCase($guardResponse.getResult())){ + } else if ($manager.executeStep()) { + $manager.setState(State.AWAITING_OUTCOME); - modify($operation){setGuardApprovalStatus($guardResponse.getResult())}; - } - else { - //This is the Deny case - $operation.startOperation($event); - $operation.setOperationHasGuardDeny(); - retract($opTimer); - retract($operation); - modify($manager) {finishOperation($operation)}; - } + } else { + // this step is no longer necessary - try the next one + $manager.nextStep(); + } - retract($guardResponse); + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception executing a step", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); + $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to execute the next step"); + } + update($manager); end /* * -* This rule responds to APPC Response Events -* -* I would have like to be consistent and write the Response like this: -* $response : Response( CommonHeader.RequestId == $onset.getRequestId() ) -* -* However, no compile error was given. But a runtime error was given. I think -* because drools is confused between the classname CommonHeader vs the property CommonHeader. +* Generate SDNR notification. Does not discard the outcome from the queue, leaving it to be +* handled by rule "EVENT.MANAGER.PROCESS.OUTCOME". * */ -rule "APPC.RESPONSE" +rule "EVENT.MANAGER.GENERATE.SDNR.NOTIFICATION" + // this should fire BEFORE the "EVENT.MANAGER.PROCESS.OUTCOME" rule + salience 100 when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : CanonicalOnset( closedLoopControlName == $clName ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) - $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), - onset.getRequestId() == $event.getRequestId() ) - $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId().toString(), timerType == "Operation", !expired ) - $response : Response( getCommonHeader().RequestId == $event.getRequestId() ) + $manager : UsecasesEventManager( + isActive(), + getState() == State.AWAITING_OUTCOME, + $outcome : getOutcomes().peek(), + $outcome != null, + $outcome.getEnd() != null, + !isAbort($outcome), + $step : getSteps().peek(), + "SDNR".equals($step.getActorName()), + $outcome.isFor("SDNR", $step.getOperationName()) ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event, $manager, $operation, $opTimer, $response); - // - // Get the result of the operation - // - PolicyResult policyResult = $operation.onResponse($response); - if (policyResult != null) { - logger.debug("{}: {}: operation finished - result={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - policyResult); - // - // This Operation has completed, construct a notification showing our results. (DB write - end operation) - // - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - notification.setMessage($operation.getOperationHistory()); - notification.setHistory($operation.getHistory()); - if (policyResult.equals(PolicyResult.SUCCESS)) { - notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS); - // - // Let interested parties know - // - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - } else { - notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE); - // - // Let interested parties know - // - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - } - // - // Ensure the operation is complete - // - if ($operation.isOperationComplete() == true) { - // - // It is complete, remove it from memory - // - retract($operation); - // - // We must also retract the timer object - // NOTE: We could write a Rule to do this - // - retract($opTimer); - // - // Complete the operation - // - modify($manager) {finishOperation($operation)}; - } else { - // - // Just doing this will kick off the LOCKED rule again - // - modify($operation) {}; - } - } else { - // - // Its not finished yet (i.e. expecting more Response objects) - // - // Or possibly it is a leftover response that we timed the request out previously - // + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: {} manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $step, $manager); + + try { + ControlLoopResponse clResponse = $manager.makeControlLoopResponse($outcome); + $manager.deliver("DCAE_CL_RSP", clResponse, "SDNR notification", drools.getRule().getName()); + + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception generating SDNR Response notification", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); } - // - // We are going to retract these objects from memory - // - retract($response); end /* * -* The problem with Responses is that they don't have a controlLoopControlName -* field in them, so the only way to attach them is via RequestId. If we have multiple -* control loop .drl's loaded in the same container, we need to be sure the cleanup -* rules don't remove Responses for other control loops. +* Process a GUARD outcome. Does not discard the outcome from the queue, leaving it to be +* handled by rule "EVENT.MANAGER.PROCESS.OUTCOME". * */ -rule "APPC.RESPONSE.CLEANUP" +rule "EVENT.MANAGER.PROCESS.GUARD.OUTCOME" + salience 100 when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $response : Response($id : getCommonHeader().RequestId ) - not ( CanonicalOnset( requestId == $id ) ) + $manager : UsecasesEventManager( + isActive(), + getState() == State.AWAITING_OUTCOME, + $outcome : getOutcomes().peek(), + $outcome != null, + !isAbort($outcome), + $step : getSteps().peek(), + XacmlActor.NAME.equals($step.getActorName()), + $outcome.isFor($step.getActorName(), $step.getOperationName()) ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - logger.debug("{}: {}: orphan appc response={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), $id); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: {} manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $step, $manager); - // - // Retract it - // - retract($response); + try { + VirtualControlLoopNotification notification = $manager.makeNotification(); + notification.setNotification(ControlLoopNotificationType.OPERATION); + notification.setPolicyScope(drools.getRule().getName()); + notification.setPolicyName($manager.getPolicyName()); + notification.setPolicyVersion($manager.getPolicyVersion()); + notification.setHistory(Collections.emptyList()); + + // get actor/operation name from the policy step, not from the guard step + Step step2 = $step.getParentStep(); + + if ($outcome.getEnd() == null) { + // it's a "start" operation + notification.setMessage("Sending guard query for " + step2.getActorName() + " " + step2.getOperationName()); + + } else if ($outcome.getResult() == OperationResult.SUCCESS) { + notification.setMessage("Guard result for " + step2.getActorName() + " " + step2.getOperationName() + + " is Permit"); + } else { + // it's a failure + notification.setMessage("Guard result for " + step2.getActorName() + " " + step2.getOperationName() + + " is Deny"); + } + + $manager.deliver("POLICY-CL-MGT", notification, "GUARD notification", drools.getRule().getName()); + + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception generating GUARD notification", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); + } end /* * -* This rule responds to APPC Response Events using the new LCM interface provided by appc +* Process an outcome when the policy's operation starts. * */ -rule "APPC.LCM.RESPONSE" +rule "EVENT.MANAGER.PROCESS.POLICY.STARTED" when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : CanonicalOnset( closedLoopControlName == $clName ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) - $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), - onset.getRequestId() == $event.getRequestId() ) - $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId().toString(), timerType == "Operation", !expired ) - $response : AppcLcmDmaapWrapper( getBody().getOutput().getCommonHeader().getRequestId() == $event.getRequestId() ) + $manager : UsecasesEventManager( + isActive(), + getState() == State.AWAITING_OUTCOME, + $outcome : getOutcomes().peek(), + $outcome != null, + $outcome.getEnd() == null, + $step : getSteps().peek(), + $step.isPolicyStep(), + $outcome.isFor($step.getActorName(), $step.getOperationName()) ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event, $manager, $operation, $operation, $opTimer, $response); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: {} manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $step, $manager); - // - // Get the result of the operation - // - PolicyResult policyResult = $operation.onResponse($response); - if (policyResult != null) { - logger.debug("{}: {}: operation finished - result={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - policyResult); - - // - // This Operation has completed, construct a notification showing our results. (DB write - end operation) - // - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - notification.setMessage($operation.getOperationHistory()); - notification.setHistory($operation.getHistory()); - if (policyResult.equals(PolicyResult.SUCCESS)) { - notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS); - } else { - notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE); - } - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - // - // Ensure the operation is complete - // - if ($operation.isOperationComplete() == true) { - // - // It is complete, remove it from memory - // - retract($operation); - // - // We must also retract the timer object - // NOTE: We could write a Rule to do this - // - retract($opTimer); - // - // Complete the operation - // - modify($manager) {finishOperation($operation)}; - } else { - // - // Just doing this will kick off the LOCKED rule again - // - modify($operation) {}; - } - } else { - // - // Its not finished yet (i.e. expecting more Response objects) - // - // Or possibly it is a leftover response that we timed the request out previously - // + try { + $manager.getOutcomes().remove(); + + // it's a "start" operation for the step + $manager.bumpAttempts(); + + $manager.addToHistory($outcome); + $manager.storeInDataBase($manager.getPartialHistory().peekLast()); + + VirtualControlLoopNotification notification = $manager.makeNotification(); + notification.setNotification(ControlLoopNotificationType.OPERATION); + notification.setPolicyScope(drools.getRule().getName()); + notification.setPolicyName($manager.getPolicyName()); + notification.setPolicyVersion($manager.getPolicyVersion()); + notification.setHistory(Collections.emptyList()); + notification.setMessage($manager.getOperationMessage()); + + $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName()); + + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception processing operation outcome", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); + $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle policy 'start' outcome"); } - // - // We are going to retract these objects from memory - // - retract($response); + + update($manager); end /* * -* Clean Up any lingering LCM reponses +* Process an outcome when an arbitrary Preprocessor step starts. * */ -rule "APPC.LCM.RESPONSE.CLEANUP" +rule "EVENT.MANAGER.PROCESS.PREPROCESSOR.STARTED" when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $response : AppcLcmDmaapWrapper($id : getBody().getOutput().getCommonHeader().getRequestId() ) - not ( CanonicalOnset( requestId == $id ) ) + $manager : UsecasesEventManager( + isActive(), + getState() == State.AWAITING_OUTCOME, + $outcome : getOutcomes().peek(), + $outcome != null, + $outcome.getEnd() == null, + $step : getSteps().peek(), + !$step.isPolicyStep(), + $outcome.isFor($step.getActorName(), $step.getOperationName()) ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - logger.debug("{}: {}: orphan appc response={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), $id); - // - // Retract it - // - retract($response); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: {} manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $step, $manager); + + try { + $manager.getOutcomes().remove(); + + // it's a "start" operation for the step + $manager.bumpAttempts(); + + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception processing operation outcome", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); + $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle 'start' outcome"); + } + + update($manager); end /* * -* This rule responds to SO Response Events +* Process an outcome when the policy's operation succeeds. * */ -rule "SO.RESPONSE" +rule "EVENT.MANAGER.PROCESS.POLICY.SUCCESS" when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : CanonicalOnset( closedLoopControlName == $clName ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) - $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), - onset.getRequestId() == $event.getRequestId() ) - $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId().toString(), timerType == "Operation", !expired ) - $response : SoResponseWrapper(requestId.toString() == $event.getRequestId().toString() ) + $manager : UsecasesEventManager( + isActive(), + getState() == State.AWAITING_OUTCOME, + $outcome : getOutcomes().peek(), + $outcome != null, + $outcome.getEnd() != null, + $outcome.getResult() == OperationResult.SUCCESS, + $step : getSteps().peek(), + $step.isPolicyStep(), + $outcome.isFor($step.getActorName(), $step.getOperationName()) ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event, $manager, $operation, $operation, $opTimer, $response); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: {} manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $step, $manager); - // Get the result of the operation - // - PolicyResult policyResult = $operation.onResponse($response); - if (policyResult != null) { - logger.debug("{}: {}: operation finished - result={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - policyResult); + try { + $manager.getOutcomes().remove(); - // - // This Operation has completed, construct a notification showing our results - // - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - notification.setMessage($operation.getOperationHistory()); - notification.setHistory($operation.getHistory()); - if (policyResult.equals(PolicyResult.SUCCESS)) { - notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS); - } else { - notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE); + // let the step record the response that's contained within the outcome + $step.success($outcome); - } - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - // - // Ensure the operation is complete - // - if ($operation.isOperationComplete() == true) { - // - // It is complete, remove it from memory - // - retract($operation); - // - // We must also retract the timer object - // NOTE: We could write a Rule to do this - // - retract($opTimer); - // - // Complete the operation - // - modify($manager) {finishOperation($operation)}; - } else { - // - // Just doing this will kick off the LOCKED rule again - // - modify($operation) {}; - } - } else { - // - // Its not finished yet (i.e. expecting more Response objects) - // - // Or possibly it is a leftover response that we timed the request out previously - // + $manager.addToHistory($outcome); + $manager.storeInDataBase($manager.getPartialHistory().peekLast()); + + $manager.setResult($outcome.getResult()); + + VirtualControlLoopNotification notification = $manager.makeNotification(); + notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS); + notification.setPolicyScope(drools.getRule().getName()); + notification.setPolicyName($manager.getPolicyName()); + notification.setPolicyVersion($manager.getPolicyVersion()); + notification.setHistory($manager.getPartialHistory().stream().map(OperationOutcome2::getClOperation) + .collect(Collectors.toList())); + + // this step is complete - discard it + $manager.getSteps().remove(); + + $manager.setState(State.POLICY_LOADED); + + $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName()); + + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception processing operation outcome", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); + $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle policy 'success' outcome"); } - // - // We are going to retract these objects from memory - // - retract($response); + update($manager); end /* * -* This rule responds to VFC Response Events +* Process a final failure outcome, when the event has been accepted. * */ -rule "VFC.RESPONSE" +rule "EVENT.MANAGER.PROCESS.FINAL.FAILURE.ACCEPTED" when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : CanonicalOnset( closedLoopControlName == $clName ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) - $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), - onset.getRequestId() == $event.getRequestId() ) - $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId().toString(), timerType == "Operation", !expired ) - $response : VfcResponse( requestId.toString() == $event.getRequestId().toString() ) + $manager : UsecasesEventManager( + isActive(), + isAccepted(), + getState() == State.AWAITING_OUTCOME, + $outcome : getOutcomes().peek(), + $outcome != null, + !isAbort($outcome), + $outcome.getEnd() != null, + $outcome.isFinalOutcome(), + $outcome.getResult() != OperationResult.SUCCESS, + $step : getSteps().peek() ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event, $manager, $operation, $operation, $opTimer, $response); - // Get the result of the operation - // - PolicyResult policyResult = $operation.onResponse($response); - if (policyResult != null) { - // - // This Operation has completed, construct a notification showing our results - // - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - notification.setMessage($operation.getOperationHistory()); - notification.setHistory($operation.getHistory()); - // - // Ensure the operation is complete - // - if ($operation.isOperationComplete() == true) { - // - // It is complete, remove it from memory - // - retract($operation); - // - // We must also retract the timer object - // NOTE: We could write a Rule to do this - // - retract($opTimer); - // - // Complete the operation - // - modify($manager) {finishOperation($operation)}; - } else { - // - // Just doing this will kick off the LOCKED rule again - // - modify($operation) {}; - } - } else { - // - // Its not finished yet (i.e. expecting more Response objects) - // - // Or possibly it is a leftover response that we timed the request out previously - // + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: {} manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $step, $manager); + + try { + $manager.getOutcomes().remove(); + + if (!$outcome.isFor($step.getActorName(), $step.getOperationName())) { + $outcome.setResult(OperationResult.FAILURE_GUARD); + $outcome.setMessage("Operation denied by " + $outcome.getActor()); } - // - // We are going to retract these objects from memory - // - retract($response); + // final failure for this policy + $manager.addToHistory($outcome); + $manager.storeInDataBase($manager.getPartialHistory().peekLast()); + + $manager.setResult($outcome.getResult()); + + VirtualControlLoopNotification notification = $manager.makeNotification(); + notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE); + notification.setPolicyScope(drools.getRule().getName()); + notification.setPolicyName($manager.getPolicyName()); + notification.setPolicyVersion($manager.getPolicyVersion()); + notification.setHistory($manager.getPartialHistory().stream().map(OperationOutcome2::getClOperation) + .toList()); + + // trigger move to the next policy - clear all steps + $manager.getSteps().clear(); + $manager.setState(State.POLICY_LOADED); + + $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName()); + + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception processing operation outcome", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); + $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle policy 'failure' outcome"); + } + + update($manager); end /* * -* This rule responds to SDNC Response Events +* Process a final failure outcome, when the event has NOT been accepted. This typically +* occurs when an A&AI query fails BEFORE the first lock has been requested (and thus +* before the first policy's operation has been started). * */ -rule "SDNC.RESPONSE" +rule "EVENT.MANAGER.PROCESS.FINAL.FAILURE.REJECTED" when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : CanonicalOnset( closedLoopControlName == $clName ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestId == $event.getRequestId() ) - $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() ) - $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId().toString(), timerType == "Operation", !expired ) - $response : SdncResponse( requestId.toString() == $event.getRequestId().toString() ) + $manager : UsecasesEventManager( + isActive(), + !isAccepted(), + getState() == State.AWAITING_OUTCOME, + $outcome : getOutcomes().peek(), + $outcome != null, + !isAbort($outcome), + $outcome.getEnd() != null, + $outcome.isFinalOutcome(), + $outcome.getResult() != OperationResult.SUCCESS, + $step : getSteps().peek() ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event, $manager, $operation, $operation, $opTimer, $response); - - // Get the result of the operation - // - PolicyResult policyResult = $operation.onResponse($response); - if (policyResult != null) { - // - // This Operation has completed, construct a notification showing our results - // - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - notification.setMessage($operation.getOperationHistory()); - notification.setHistory($operation.getHistory()); - // - // Ensure the operation is complete - // - if ($operation.isOperationComplete()) { - // - // It is complete, remove it from memory - // - retract($operation); - // - // We must also retract the timer object - // NOTE: We could write a Rule to do this - // - retract($opTimer); - // - // Complete the operation - // - modify($manager) {finishOperation($operation)}; - } else { - // - // Just doing this will kick off the LOCKED rule again - // - modify($operation) {}; - } - } else { - // - // Its not finished yet (i.e. expecting more Response objects) - // - // Or possibly it is a leftover response that we timed the request out previously - // - } - // - // We are going to retract these objects from memory - // - retract($response); -end + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: {} manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $step, $manager); + retract($manager); -/** - * This rule responds to CDS Response Events - */ -rule "${policyName}.CDS.RESPONSE" - when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : CanonicalOnset( closedLoopControlName == $params.getClosedLoopControlName() ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) - $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), - onset.getRequestId() == $event.getRequestId() ) - $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId().toString(), timerType == "Operation", !expired ) - $response : CdsResponse( requestId == $event.getRequestId().toString() ) + try { + // final failure for this policy + $manager.addToHistory($outcome); - then + $manager.setResult($outcome.getResult()); - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName()); - logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}", - $params.getClosedLoopControlName(), drools.getRule().getName(), - $event, $manager, $operation, $operation, $opTimer, $response); + VirtualControlLoopNotification notification = $manager.makeNotification(); + notification.setNotification(ControlLoopNotificationType.REJECTED); + notification.setPolicyScope(drools.getRule().getName()); + notification.setPolicyName($manager.getPolicyName()); + notification.setPolicyVersion($manager.getPolicyVersion()); + notification.setHistory($manager.getPartialHistory().stream().map(OperationOutcome2::getClOperation) + .toList()); + + $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName()); + + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception processing operation outcome", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); + $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to reject event"); + } - // Get the result of the operation - PolicyResult policyResult = $operation.onResponse($response); + $manager.destroy(); +end - if (policyResult != null) { - logger.debug("{}: {}: operation finished - result={}", - $params.getClosedLoopControlName(), drools.getRule().getName(), - policyResult); +/* +* +* Process an outcome when the policy's operation fails. +* +*/ +rule "EVENT.MANAGER.PROCESS.POLICY.FAILURE" + when + $manager : UsecasesEventManager( + isActive(), + getState() == State.AWAITING_OUTCOME, + $outcome : getOutcomes().peek(), + $outcome != null, + !isAbort($outcome), + $outcome.getEnd() != null, + !$outcome.isFinalOutcome(), + $outcome.getResult() != OperationResult.SUCCESS, + $step : getSteps().peek(), + $step.isPolicyStep(), + $outcome.isFor($step.getActorName(), $step.getOperationName()) ) + then - // The operation has completed, construct a notification showing our results - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setFrom("policy"); - notification.setPolicyName(drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - notification.setMessage($operation.getOperationHistory()); - notification.setHistory($operation.getHistory()); - notification.setNotification( - ($response != null && CdsActorConstants.SUCCESS.equals($response.getStatus())) - ? ControlLoopNotificationType.OPERATION_SUCCESS : ControlLoopNotificationType.OPERATION_FAILURE); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: {} manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $step, $manager); - // Send the notification - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); + try { + // not a final failure, thus it will be retried automatically - // Ensure the operation is complete - if ($operation.isOperationComplete()) { + $manager.getOutcomes().remove(); - // It is complete, remove it from memory - retract($operation); + // do NOT invoke manager.setResult() - // We must also retract the timer object - // NOTE: We could write a Rule to do this - retract($opTimer); + $manager.addToHistory($outcome); + $manager.storeInDataBase($manager.getPartialHistory().peekLast()); - // Complete the operation - modify($manager) {finishOperation($operation)}; + VirtualControlLoopNotification notification = $manager.makeNotification(); + notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE); + notification.setPolicyScope(drools.getRule().getName()); + notification.setPolicyName($manager.getPolicyName()); + notification.setPolicyVersion($manager.getPolicyVersion()); - } else { - // Just doing this will kick off the LOCKED rule again - modify($operation) {}; - } - } else { - // Its not finished yet (i.e. expecting more Response objects) - // Or possibly it is a leftover response that we timed the request out previously - logger.info( - "policyResult is null" - + "\nIt's not finished yet (i.e. expecting more Response objects)" - + "\nOr possibly it is a leftover response that we timed the request out previously"); - } + $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName()); - // We are going to retract these objects from memory - retract($response); + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception processing operation outcome", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); + $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle policy 'failure' outcome"); + } + update($manager); end -/* /* * -* This manages a single timer. -* Due to a bug in the drools code, the drools timer needed to be split from most of the objects in the when clause +* Discard an outcome that was not handled by any other rule. * */ -rule "TIMER.FIRED" - timer (expr: $timeout) +rule "EVENT.MANAGER.DISCARD.OUTCOME" + salience -10 when - $timer : ControlLoopTimer($timeout : delay, !expired) + $manager : UsecasesEventManager( + isActive(), + getState() == State.AWAITING_OUTCOME, + $outcome : getOutcomes().peek(), + $outcome != null, + $step : getSteps().peek() ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("This is TIMER.FIRED"); - modify($timer){setExpired(true)}; - end -/* -* -* This is the timer that manages the timeout for an individual operation. -* -*/ -rule "EVENT.MANAGER.OPERATION.TIMEOUT" - when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : VirtualControlLoopEvent( closedLoopControlName == $clName ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) - $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), - onset.getRequestId() == $event.getRequestId() ) - $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId().toString(), timerType == "Operation", expired ) - then + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: {} {} manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $step, $outcome.getResult(), $manager); - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - logger.debug("{}: {}: event={} manager={} operation={} opTimer={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event, $manager, $operation, $operation, $opTimer); + try { + $manager.getOutcomes().remove(); - // - // Tell it its timed out - // - $operation.setOperationHasTimedOut(); - // - // Create a notification for it ("DB Write - end operation") - // - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE); - notification.setMessage($operation.getOperationHistory()); - notification.setHistory($operation.getHistory()); - // - // Let interested parties know - // - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - // - // Get rid of the timer - // - retract($opTimer); - // - // Ensure the operation is complete - // - if ($operation.isOperationComplete() == true) { - // - // It is complete, remove it from memory - // - retract($operation); - // - // Complete the operation - // - modify($manager) {finishOperation($operation)}; - } else { - // - // Just doing this will kick off the LOCKED rule again - // - modify($operation) {}; + if ($outcome.getEnd() != null && $outcome.isFor($step.getActorName(), $step.getOperationName())) { + // it's a completion for the step + + // let the step record the response that's contained within the outcome + if ($outcome.getResult() == OperationResult.SUCCESS) { + $step.success($outcome); + } + + // this step is complete - discard it + $manager.getSteps().remove(); + + $manager.setState(State.POLICY_LOADED); + } + + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception processing operation outcome", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); + $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle outcome"); } + + update($manager); end /* * -* This is the timer that manages the overall control loop timeout. +* Abort processing. This can happen in any state (once the manager has been started). * */ -rule "EVENT.MANAGER.TIMEOUT" +rule "EVENT.MANAGER.ABORT" when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : VirtualControlLoopEvent( closedLoopControlName == $clName ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) - $clTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId().toString(), timerType == "ClosedLoop", expired ) + $manager : UsecasesEventManager( + isActive(), + getState() != State.DONE, + $outcome : getOutcomes().peek(), + $outcome != null, + isAbort($outcome), + $step : getSteps().peek() ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName()); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: {} manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $step, $manager); - logger.debug("{}: {}: event={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event); - // - // Tell the Event Manager it has timed out - // - VirtualControlLoopNotification notification = $manager.setControlLoopTimedOut(); - if (notification != null) { - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - // - // Let interested parties know - // - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); + try { + // determine the final message + String msg; + switch ($outcome.getActor()) { + case ActorConstants.CL_TIMEOUT_ACTOR: + msg = "Control Loop timed out"; + break; + case ActorConstants.LOCK_ACTOR: + msg = "Target Lock was lost"; + break; + default: + msg = "Processing aborted by " + $outcome.getActor(); + break; + } + + $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE, msg); + + if ($step != null && "SDNR".equals($step.getActorName()) + && $outcome.isFor($step.getActorName(), $step.getOperationName())) { + + // aborted while processing the SDNR step - generate a notification + ControlLoopResponse clResponse = $manager.makeControlLoopResponse($outcome); + $manager.deliver("DCAE_CL_RSP", clResponse, "SDNR notification", drools.getRule().getName()); + } + + if ($step != null) { + $outcome.setActor($step.getActorName()); + $outcome.setOperation($step.getOperationName()); + + $manager.addToHistory($outcome); + $manager.storeInDataBase($manager.getPartialHistory().peekLast()); + } + + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception handling ABORT outcome", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); + $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle ABORT"); } - // - // Retract the event - // - retract($event); + + update($manager); end /* * -* This rule cleans up the manager and other objects after an event has -* been retracted. +* Done processing. Arriving here implies that the event has been accepted. * */ -rule "EVENT.MANAGER.CLEANUP" +rule "EVENT.MANAGER.FINAL" when - $manager : ControlLoopEventManager( $clName : getClosedLoopControlName(), $requestId : getRequestId() ) - $operations : LinkedList() - from collect( ControlLoopOperationManager( onset.closedLoopControlName == $clName, - onset.getRequestId() == $requestId ) ) - $timers : LinkedList() - from collect( ControlLoopTimer( closedLoopControlName == $clName, - requestId == $requestId.toString() ) ) - not( VirtualControlLoopEvent( closedLoopControlName == $clName, requestId == $requestId ) ) + $manager : UsecasesEventManager( + !isActive() || getState() == State.DONE ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $clName, drools.getRule().getName()); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); + logger.info("{}: {}.{}: manager={}", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager); - logger.debug("{}: {}: manager={} timers={} operations={}", - $clName, drools.getRule().getName(), - $manager, $timers.size(), $operations.size()); + retract($manager); - // - // Retract lock by invoking unlock() - // - Lock lock = $manager.unlockCurrentOperation(); - if(lock != null) { - retract(lock); - } + try { + VirtualControlLoopNotification notification = $manager.makeNotification(); + notification.setPolicyScope(drools.getRule().getName()); + notification.setPolicyName($manager.getPolicyName()); + notification.setPolicyVersion($manager.getPolicyVersion()); + notification.setHistory($manager.getFullHistory().stream().map(OperationOutcome2::getClOperation) + .toList()); + + OperationFinalResult finalResult = $manager.getFinalResult(); + if (finalResult == null) { + finalResult = ($manager.isActive() ? OperationFinalResult.FINAL_SUCCESS : OperationFinalResult.FINAL_FAILURE); + } - // - // Retract EVERYTHING - // - retract($manager); + switch (finalResult) { + case FINAL_FAILURE_EXCEPTION: + notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE); + notification.setMessage("Exception in processing closed loop"); + break; + case FINAL_SUCCESS: + notification.setNotification(ControlLoopNotificationType.FINAL_SUCCESS); + break; + case FINAL_OPENLOOP: + notification.setNotification(ControlLoopNotificationType.FINAL_OPENLOOP); + break; + case FINAL_FAILURE: + default: + notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE); + break; + } - for(Object manager: $operations) { - retract((ControlLoopOperationManager) manager); - } - for(Object timer: $timers) { - retract((ControlLoopTimer) timer); + if ($manager.getFinalMessage() != null) { + notification.setMessage($manager.getFinalMessage()); + } + + $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName()); + + } catch(RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception generating final notification", + $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(), + $manager, e); } + + $manager.destroy(); end /* * -* This rule will clean up any rogue onsets where there is no +* This rule will clean up any rogue events where there is no * ControlLoopParams object corresponding to the onset event. * */ rule "EVENT.CLEANUP" + salience -100 when $event : VirtualControlLoopEvent( $clName: closedLoopControlName ) - not ( ControlLoopParams( getClosedLoopControlName() == $clName) ) then - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); + Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName()); logger.info("{}: {}", $clName, drools.getRule().getName()); - logger.debug("{}: {}: orphan onset event={}", + logger.debug("{}: {}: orphan event={}", $clName, drools.getRule().getName(), $event); - + // + // Retract the event + // retract($event); end /* * -* This rule responds to SDNR Response Events. +* At this point, it appears that if we prevent the rules from getting messages from +* topics, then that will also prevent the actors from getting them. So the following +* rules are here just to discard those messages. +* +* These have a higher salience so the objects are removed before the "FINAL" message +* is processed, so that the junit test can assume things are done once they see the +* "FINAL" message. Otherwise, tests might fail sporadically. * */ -rule "SDNR.RESPONSE" +rule "APPC.Response.CLEANUP" + salience 1 when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $event : CanonicalOnset( closedLoopControlName == $clName ) - $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId() ) - $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), - onset.getRequestId() == $event.getRequestId() ) - $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), - requestId == $event.getRequestId().toString(), timerType == "Operation", !expired ) - $response : PciResponseWrapper( getBody().getCommonHeader().getRequestId() == $event.getRequestId() ) + $msg : org.onap.policy.appc.Response( ) then + retract($msg); +end - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - $event, $manager, $operation, $operation, $opTimer, $response); - - // - // Get the result of the operation - // - PolicyResult policyResult = $operation.onResponse($response); - if (policyResult != null) { - logger.debug("{}: {}: operation finished - result={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), - policyResult); - - // - // This Operation has completed, construct a notification showing our results. (DB write - end operation) - // - VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event); - notification.setFrom("policy"); - notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName()); - notification.setPolicyScope($params.getPolicyScope()); - notification.setPolicyVersion($params.getPolicyVersion()); - notification.setMessage($operation.getOperationHistory()); - notification.setHistory($operation.getHistory()); - if (policyResult.equals(PolicyResult.SUCCESS)) { - notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS); - } else { - notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE); - } - PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); - - ControlLoopResponse clResponse = $operation.getControlLoopResponse($response, $event); - PolicyEngineConstants.getManager().deliver("DCAE_CL_RSP", clResponse); - // - // Ensure the operation is complete - // - if ($operation.isOperationComplete() == true) { - // - // It is complete, remove it from memory - // - retract($operation); - // - // We must also retract the timer object - // NOTE: We could write a Rule to do this - // - retract($opTimer); - // - // Complete the operation - // - modify($manager) {finishOperation($operation)}; - } else { - // - // Just doing this will kick off the LOCKED rule again - // - modify($operation) {}; - } - } else { - // - // Its not finished yet (i.e. expecting more Response objects) - // - // Or possibly it is a leftover response that we timed the request out previously - // - } - // - // We are going to retract these objects from memory - // - retract($response); +rule "APPC.Request.CLEANUP" + salience 1 + when + $msg : org.onap.policy.appc.Request( ) + then + retract($msg); end -/* -* -* Clean Up any lingering SDNR reponses. -* -*/ -rule "SDNR.RESPONSE.CLEANUP" +rule "APPC-LCM.Response.CLEANUP" + salience 1 when - $params : ControlLoopParams( $clName : getClosedLoopControlName() ) - $response : PciResponseWrapper($id : getBody().getCommonHeader().getRequestId ) - not ( CanonicalOnset( requestId == $id ) ) + $msg : org.onap.policy.appclcm.AppcLcmDmaapWrapper( ) then + retract($msg); +end - Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage()); - logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName()); - logger.debug("{}: {}: orphan sdnr response={}", - $clName, $params.getPolicyName() + "." + drools.getRule().getName(), $id); - // - // Retract it - // - retract($response); +rule "SDNR.Response.CLEANUP" + salience 1 + when + $msg : org.onap.policy.sdnr.PciResponseWrapper( ) + then + retract($msg); end