2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.controlloop;
23 import org.onap.policy.controlloop.VirtualControlLoopEvent;
24 import org.onap.policy.controlloop.VirtualControlLoopNotification;
25 import org.onap.policy.controlloop.ControlLoopEventStatus;
26 import org.onap.policy.controlloop.ControlLoopNotificationType;
27 import org.onap.policy.controlloop.ControlLoopLogger;
28 import org.onap.policy.controlloop.policy.PolicyResult;
29 import org.onap.policy.controlloop.policy.ControlLoopPolicy;
30 import org.onap.policy.controlloop.policy.Policy;
31 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager;
32 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager.NEW_EVENT_STATUS;
33 import org.onap.policy.controlloop.eventmanager.ControlLoopOperationManager;
34 import org.onap.policy.controlloop.actor.so.SOActorServiceProvider;
35 import org.onap.policy.aai.AaiNqResponseWrapper;
36 import org.onap.policy.appc.Request;
37 import org.onap.policy.appc.Response;
38 import org.onap.policy.appc.CommonHeader;
39 import org.onap.policy.appclcm.LcmRequestWrapper;
40 import org.onap.policy.appclcm.LcmResponseWrapper;
41 import org.onap.policy.appclcm.LcmRequest;
42 import org.onap.policy.appclcm.LcmResponse;
43 import org.onap.policy.appclcm.LcmCommonHeader;
44 import org.onap.policy.vfc.VFCRequest;
45 import org.onap.policy.vfc.VFCResponse;
46 import org.onap.policy.vfc.VFCManager;
47 import org.onap.policy.so.SOManager;
48 import org.onap.policy.so.SORequest;
49 import org.onap.policy.so.SORequestStatus;
50 import org.onap.policy.so.SORequestDetails;
51 import org.onap.policy.so.SOModelInfo;
52 import org.onap.policy.so.SOCloudConfiguration;
53 import org.onap.policy.so.SORequestInfo;
54 import org.onap.policy.so.SORequestParameters;
55 import org.onap.policy.so.SORelatedInstanceListElement;
56 import org.onap.policy.so.SORelatedInstance;
57 import org.onap.policy.so.SOResponse;
58 import org.onap.policy.so.SOResponseWrapper;
59 import org.onap.policy.guard.PolicyGuard;
60 import org.onap.policy.guard.PolicyGuard.LockResult;
61 import org.onap.policy.guard.TargetLock;
62 import org.onap.policy.guard.GuardResult;
63 import org.onap.policy.guard.PolicyGuardRequest;
64 import org.onap.policy.guard.PolicyGuardResponse;
65 import org.onap.policy.guard.PolicyGuardXacmlRequestAttributes;
66 import org.onap.policy.guard.PolicyGuardXacmlHelper;
68 import org.yaml.snakeyaml.Yaml;
69 import org.yaml.snakeyaml.constructor.Constructor;
71 import org.slf4j.LoggerFactory;
72 import org.slf4j.Logger;
74 import java.time.Instant;
75 import java.util.LinkedList;
76 import java.util.Iterator;
78 import org.onap.policy.drools.system.PolicyEngine;
81 * This structure mimics the Params structure.
82 * Its only purpose is to allow management of
83 * rules by the PAP component..
84 * It has no use at runtime since the rules go by
85 * Params for matching purposes.
88 closedLoopControlName : String
89 controlLoopYaml : String
93 * Control Loop Identity
96 closedLoopControlName : String
97 controlLoopYaml : String
101 * Used to clean up Params that no longer have associated rules.
103 declare ParamsCleaner
104 closedLoopControlName : String
105 controlLoopYaml : String
109 * This object is to provide support for timeouts
110 * due to a bug in drools' built-in timers
112 declare ControlLoopTimer
113 closedLoopControlName : String
117 //timerType is the type of timer: either "ClosedLoop" or "Operation"
123 * Called once and only once to insert the parameters into working memory for this Closed Loop policy.
124 * This has a higher salience so we can ensure that the Params is created before we have a chance to
125 * discard any events.
128 rule "${policyName}.SETUP"
131 not( Params( getClosedLoopControlName() == "${closedLoopControlName}", getControlLoopYaml() == "${controlLoopYaml}" ) )
134 Params params = new Params();
135 params.setClosedLoopControlName("${closedLoopControlName}");
136 params.setControlLoopYaml("${controlLoopYaml}");
139 // Note: globals have bad behavior when persistence is used,
140 // hence explicitly getting the logger vs using a global
142 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
143 logger.info("{}: {} : YAML=[{}]", params.getClosedLoopControlName(), drools.getRule().getName(), params.getControlLoopYaml());
148 * This rule responds to DCAE Events where there is no manager yet. Either it is
149 * the first ONSET, or a subsequent badly formed Event (i.e. Syntax error, or is-closed-loop-disabled)
152 rule "${policyName}.EVENT"
154 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
155 $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
156 not ( ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() ) )
159 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
160 logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
165 // Check the event, because we need it to not be null when
166 // we create the ControlLoopEventManager. The ControlLoopEventManager
167 // will do extra syntax checking as well check if the closed loop is disabled.
169 if ($event.getRequestId() == null) {
170 VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
171 notification.setNotification(ControlLoopNotificationType.REJECTED);
172 notification.setFrom("policy");
173 notification.setMessage("Missing requestId");
174 notification.setPolicyName(drools.getRule().getName());
175 notification.setPolicyScope("${policyScope}");
176 notification.setPolicyVersion("${policyVersion}");
179 // Let interested parties know
181 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
184 // Retract it from memory
187 } else if ($event.getClosedLoopEventStatus() != ControlLoopEventStatus.ONSET) {
188 throw new ControlLoopException($event.getClosedLoopEventStatus() + " received with no prior onset");
191 // Create an EventManager
193 ControlLoopEventManager manager = new ControlLoopEventManager($params.getClosedLoopControlName(), $event.getRequestId());
195 // Determine if EventManager can actively process the event (i.e. syntax, is_closed_loop_disabled checks etc.)
197 VirtualControlLoopNotification notification = manager.activate($params.getControlLoopYaml(), $event);
198 notification.setFrom("pdp-0001-controller=controlloop"); // Engine.getInstanceName()
199 notification.setPolicyName(drools.getRule().getName());
200 notification.setPolicyScope("${policyScope}");
201 notification.setPolicyVersion("${policyVersion}");
203 // Are we actively pursuing this event?
205 if (notification.getNotification() == ControlLoopNotificationType.ACTIVE) {
207 // Insert Event Manager into memory, this will now kick off processing.
211 // Let interested parties know
213 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
215 // Setup the Overall Control Loop timer
217 ControlLoopTimer clTimer = new ControlLoopTimer();
218 clTimer.setTimerType("ClosedLoop");
219 clTimer.setClosedLoopControlName($event.getClosedLoopControlName());
220 clTimer.setRequestID($event.getRequestId().toString());
221 clTimer.setDelay(manager.getControlLoopTimeout(1500) + "s");
228 // Let interested parties know
230 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
232 // Retract it from memory
238 // Now that the manager is inserted into Drools working memory, we'll wait for
239 // another rule to fire in order to continue processing. This way we can also
240 // then screen for additional ONSET and ABATED events for this RequestId.
243 } catch (Exception e) {
244 logger.warn("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName(), e);
246 VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
247 notification.setNotification(ControlLoopNotificationType.REJECTED);
248 notification.setMessage("Exception occurred: " + e.getMessage());
249 notification.setPolicyName(drools.getRule().getName());
250 notification.setPolicyScope("${policyScope}");
251 notification.setPolicyVersion("${policyVersion}");
255 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
265 * This rule happens when we got a valid ONSET, closed loop is enabled and an Event Manager
266 * is now created. We can start processing the yaml specification via the Event Manager.
269 rule "${policyName}.EVENT.MANAGER"
271 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
272 $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
273 $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
274 $clTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "ClosedLoop", !expired )
277 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
278 logger.info("{}: {}: event={} manager={} clTimer={}",
279 $params.getClosedLoopControlName(), drools.getRule().getName(),
280 $event, $manager, $clTimer);
284 // Check which event this is.
286 ControlLoopEventManager.NEW_EVENT_STATUS eventStatus = $manager.onNewEvent($event);
288 // Check what kind of event this is
290 if (eventStatus == NEW_EVENT_STATUS.SUBSEQUENT_ONSET) {
292 // We don't care about subsequent onsets
294 logger.info("{}: {}: subsequent onset",
295 $params.getClosedLoopControlName(), drools.getRule().getName());
299 if (eventStatus == NEW_EVENT_STATUS.SYNTAX_ERROR) {
301 // Ignore any bad syntax events
303 logger.warn("{}: {}: syntax error",
304 $params.getClosedLoopControlName(), drools.getRule().getName());
309 // We only want the initial ONSET event in memory,
310 // all the other events need to be retracted to support
311 // cleanup and avoid the other rules being fired for this event.
313 if (eventStatus != NEW_EVENT_STATUS.FIRST_ONSET) {
314 logger.warn("{}: {}: not first onset",
315 $params.getClosedLoopControlName(), drools.getRule().getName());
319 logger.debug("{}: {}: target={}", $params.getClosedLoopControlName(),
320 drools.getRule().getName(), $event.getTarget());
322 // Now start seeing if we need to process this event
326 // Check if this is a Final Event
328 VirtualControlLoopNotification notification = $manager.isControlLoopFinal();
331 if (notification != null) {
333 // Its final, but are we waiting for abatement?
335 if ($manager.getNumAbatements() > 0) {
336 logger.info("{}: {}: abatement received for {}. Closing the control loop",
337 $params.getClosedLoopControlName(), drools.getRule().getName(),
338 $event.getRequestId());
340 /// DB Write---end event processing for this RequestId()
341 $manager.commitAbatement("Event Abated","Closed");
343 notification.setFrom("policy");
344 notification.setPolicyName(drools.getRule().getName());
345 notification.setPolicyScope("${policyScope}");
346 notification.setPolicyVersion("${policyVersion}");
348 // In this case, we are done
350 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
354 TargetLock lock = $manager.unlockCurrentOperation();
356 logger.debug("{}: {}: retracting lock=", $params.getClosedLoopControlName(),
357 drools.getRule().getName(), lock);
361 // Retract everything from memory
363 logger.info("{}: {}: retracting onset, manager, and timer",
364 $params.getClosedLoopControlName(), drools.getRule().getName());
366 retract($manager.getOnsetEvent());
370 // TODO - what if we get subsequent Events for this RequestId?
371 // By default, it will all start over again. May be confusing for Ruby.
372 // Or, we could track this and then subsequently ignore the events
376 // Check whether we need to wait for abatement
378 if ($manager.getProcessor().getControlLoop().getAbatement() == true && notification.getNotification() == ControlLoopNotificationType.FINAL_SUCCESS) {
379 logger.info("{}: {}: waiting for abatement ..",
380 $params.getClosedLoopControlName(), drools.getRule().getName());
382 logger.info("{}: {}: no abatement expect for {}. Closing the control loop",
383 $params.getClosedLoopControlName(), drools.getRule().getName(),
384 $event.getRequestId());
386 notification.setFrom("policy");
387 notification.setPolicyName(drools.getRule().getName());
388 notification.setPolicyScope("${policyScope}");
389 notification.setPolicyVersion("${policyVersion}");
392 // In this case, we are done
394 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
398 TargetLock lock = $manager.unlockCurrentOperation();
400 logger.debug("{}: {}: retracting lock=", $params.getClosedLoopControlName(),
401 drools.getRule().getName(), lock);
405 // Retract everything from memory
407 logger.info("{}: {}: retracting onset, manager, and timer",
408 $params.getClosedLoopControlName(), drools.getRule().getName());
410 retract($manager.getOnsetEvent());
417 // NOT final, so let's ask for the next operation
419 ControlLoopOperationManager operation = $manager.processControlLoop();
420 if (operation != null) {
422 // Let's ask for a lock right away
424 LockResult<GuardResult, TargetLock> result = $manager.lockCurrentOperation();
425 logger.info("{}: {}: guard lock acquired={}",
426 $params.getClosedLoopControlName(), drools.getRule().getName(),
428 if (result.getA().equals(GuardResult.LOCK_ACQUIRED)) {
430 // insert the operation into memory
435 // insert operation timeout object
437 ControlLoopTimer opTimer = new ControlLoopTimer();
438 opTimer.setTimerType("Operation");
439 opTimer.setClosedLoopControlName($event.getClosedLoopControlName());
440 opTimer.setRequestID($event.getRequestId().toString());
441 opTimer.setDelay(operation.getOperationTimeout().toString() + "s");
445 // Insert lock into memory
447 insert(result.getB());
450 logger.debug("The target resource {} is already processing",
451 $event.getAai().get($event.getTarget()));
452 notification = new VirtualControlLoopNotification($event);
453 notification.setNotification(ControlLoopNotificationType.REJECTED);
454 notification.setMessage("The target " + $event.getAai().get($event.getTarget()) + " is already locked");
455 notification.setFrom("policy");
456 notification.setPolicyName(drools.getRule().getName());
457 notification.setPolicyScope("${policyScope}");
458 notification.setPolicyVersion("${policyVersion}");
460 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
466 if(result.getB() != null) {
467 retract(result.getB());
470 logger.info("{}: {}: starting operation={}",
471 $params.getClosedLoopControlName(), drools.getRule().getName(),
475 // Probably waiting for abatement
477 logger.info("{}: {}: no operation, probably waiting for abatement",
478 $params.getClosedLoopControlName(), drools.getRule().getName());
481 } catch (Exception e) {
482 logger.warn("{}: {}: unexpected",
483 $params.getClosedLoopControlName(),
484 drools.getRule().getName(), e);
486 VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
487 notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
488 notification.setMessage(e.getMessage());
489 notification.setFrom("policy");
490 notification.setPolicyName(drools.getRule().getName());
491 notification.setPolicyScope("${policyScope}");
492 notification.setPolicyVersion("${policyVersion}");
494 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
505 * Guard Permitted, let's send request to the actor.
508 rule "${policyName}.EVENT.MANAGER.OPERATION.LOCKED.GUARD_PERMITTED"
510 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
511 $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
512 $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
513 $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId(), "Permit".equalsIgnoreCase(getGuardApprovalStatus()) )
514 $lock : TargetLock (requestID == $event.getRequestId())
515 $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
518 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
519 logger.info("{}: {}: event={} manager={} operation={} lock={}",
520 $params.getClosedLoopControlName(), drools.getRule().getName(),
521 $event, $manager, $operation, $lock);
523 Object request = null;
524 boolean caughtException = false;
527 request = $operation.startOperation($event);
529 if (request != null) {
530 logger.debug("{}: {}: starting operation ..",
531 $params.getClosedLoopControlName(), drools.getRule().getName());
533 // Tell interested parties we are performing this Operation
535 VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
536 notification.setNotification(ControlLoopNotificationType.OPERATION);
537 notification.setMessage($operation.getOperationMessage());
538 notification.setHistory($operation.getHistory());
539 notification.setFrom("policy");
540 notification.setPolicyName(drools.getRule().getName());
541 notification.setPolicyScope("${policyScope}");
542 notification.setPolicyVersion("${policyVersion}");
544 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
546 switch ($operation.policy.getActor()){
550 if (request instanceof Request) {
551 PolicyEngine.manager.deliver("APPC-CL", request);
553 else if (request instanceof LcmRequestWrapper) {
554 PolicyEngine.manager.deliver("APPC-LCM-READ", request);
558 // at this point the AAI named query request should have already been made, the response recieved and used
559 // in the construction of the SO Request which is stored in operationRequest
561 if(request instanceof SORequest) {
562 // Call SO. The response will be inserted into memory once it's received
563 SOActorServiceProvider.sendRequest($event.getRequestId().toString(), drools.getWorkingMemory(), request);
567 if (request instanceof VFCRequest) {
569 Thread t = new Thread(new VFCManager(drools.getWorkingMemory(), (VFCRequest)request));
576 // What happens if its null?
578 logger.warn("{}: {}: unexpected null operation request",
579 $params.getClosedLoopControlName(),
580 drools.getRule().getName());
581 if ("SO".equals($operation.policy.getActor())) {
584 modify($manager) {finishOperation($operation)};
586 else if ("vfc".equalsIgnoreCase($operation.policy.getActor())) {
589 modify($manager) {finishOperation($operation)};
593 } catch (Exception e) {
594 String msg = e.getMessage();
595 logger.warn("{}: {}: operation={}: AAI failure: {}",
596 $params.getClosedLoopControlName(), drools.getRule().getName(),
598 $operation.setOperationHasException(msg);
600 if(request != null) {
602 // Create a notification for it ("DB Write - end operation")
604 VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
605 notification.setFrom("policy");
606 notification.setPolicyName(drools.getRule().getName());
607 notification.setPolicyScope("${policyScope}");
608 notification.setPolicyVersion("${policyVersion}");
609 notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
610 notification.setMessage($operation.getOperationHistory());
611 notification.setHistory($operation.getHistory());
613 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
618 caughtException = true;
621 // Having the modify statement in the catch clause doesn't work for whatever reason
622 if (caughtException) {
623 modify($manager) {finishOperation($operation)};
630 * We were able to acquire a lock so now let's ask Xacml Guard whether
631 * we are allowed to proceed with the request to the actor.
634 rule "${policyName}.EVENT.MANAGER.OPERATION.LOCKED.GUARD_NOT_YET_QUERIED"
636 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
637 $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
638 $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
639 $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId(), getGuardApprovalStatus() == "NONE" )
640 $lock : TargetLock (requestID == $event.getRequestId())
643 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
644 logger.info("{}: {}: event={} manager={} operation={} lock={}",
645 $params.getClosedLoopControlName(), drools.getRule().getName(),
646 $event, $manager, $operation, $lock);
649 // Sending notification that we are about to query Guard ("DB write - start operation")
651 VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
652 notification.setNotification(ControlLoopNotificationType.OPERATION);
653 notification.setMessage("Sending guard query for " + $operation.policy.getActor() + " " + $operation.policy.getRecipe());
654 notification.setHistory($operation.getHistory());
655 notification.setFrom("policy");
656 notification.setPolicyName(drools.getRule().getName());
657 notification.setPolicyScope("${policyScope}");
658 notification.setPolicyVersion("${policyVersion}");
660 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
663 // Now send Guard Request to XACML Guard. In order to bypass the call to Guard,
664 // just change guardEnabled to false.
666 // In order to use REST XACML, provide a URL instead of "" as a second argument
667 // to the CallGuardTask() and set the first argument to null
668 // (instead of XacmlPdpEngine).
671 // NOTE: The environment properties uses "guard.disabled" but the boolean is guardEnabled
672 boolean guardEnabled = "false".equalsIgnoreCase(PolicyEngine.manager.getEnvironmentProperty("guard.disabled"));
676 Thread t = new Thread(new org.onap.policy.guard.CallGuardTask(
677 drools.getWorkingMemory(),
678 $event.getClosedLoopControlName(),
679 $operation.policy.getActor().toString(),
680 $operation.policy.getRecipe(),
681 $operation.getTargetEntity(),
682 $event.getRequestId().toString(),
684 AaiNqResponseWrapper resp = $manager.getNqVserverFromAai();
685 return(resp == null ? null : resp.countVfModules());
690 insert(new PolicyGuardResponse("Permit", $event.getRequestId(), $operation.policy.getRecipe()));
696 // This rule will be triggered when a thread talking to the XACML Guard inserts a
697 // guardResponse object into the working memory
699 rule "${policyName}.GUARD.RESPONSE"
701 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
702 $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
703 $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
704 $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
705 $lock : TargetLock (requestID == $event.getRequestId())
706 $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
707 $guardResponse : PolicyGuardResponse(requestID == $event.getRequestId(), $operation.policy.recipe == operation)
710 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
711 logger.info("{}: {}: event={} manager={} operation={} lock={} opTimer={} guardResponse={}",
712 $params.getClosedLoopControlName(), drools.getRule().getName(),
713 $event, $manager, $operation, $lock, $opTimer, $guardResponse);
716 //we will permit the operation if there was no Guard for it
717 if("Indeterminate".equalsIgnoreCase($guardResponse.getResult())){
718 $guardResponse.setResult("Permit");
722 // This notification has Guard result in "message". ("DB write - end operation in case of Guard Deny")
724 VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
725 notification.setNotification(ControlLoopNotificationType.OPERATION);
726 notification.setMessage("Guard result for " + $operation.policy.getActor() + " " + $operation.policy.getRecipe() + " is " + $guardResponse.getResult());
727 notification.setHistory($operation.getHistory());
728 notification.setFrom("policy");
729 notification.setPolicyName(drools.getRule().getName());
730 notification.setPolicyScope("${policyScope}");
731 notification.setPolicyVersion("${policyVersion}");
733 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
735 if("Permit".equalsIgnoreCase($guardResponse.getResult())){
737 modify($operation){setGuardApprovalStatus($guardResponse.getResult())};
740 //This is the Deny case
741 $operation.startOperation($event);
742 $operation.setOperationHasGuardDeny();
745 modify($manager) {finishOperation($operation)};
748 retract($guardResponse);
754 * This rule responds to APPC Response Events
756 * I would have like to be consistent and write the Response like this:
757 * $response : Response( CommonHeader.RequestId == $onset.getRequestId() )
759 * However, no compile error was given. But a runtime error was given. I think
760 * because drools is confused between the classname CommonHeader vs the property CommonHeader.
763 rule "${policyName}.APPC.RESPONSE"
765 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
766 $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
767 $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
768 $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
769 $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
770 $lock : TargetLock (requestID == $event.getRequestId())
771 $response : Response( getCommonHeader().RequestId == $event.getRequestId() )
774 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
775 logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
776 logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}",
777 $params.getClosedLoopControlName(), drools.getRule().getName(),
778 $event, $manager, $operation, $lock, $opTimer, $response);
780 // Get the result of the operation
782 PolicyResult policyResult = $operation.onResponse($response);
783 if (policyResult != null) {
784 logger.debug("{}: {}: operation finished - result={}",
785 $params.getClosedLoopControlName(), drools.getRule().getName(),
788 // This Operation has completed, construct a notification showing our results. (DB write - end operation)
790 VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
791 notification.setFrom("policy");
792 notification.setPolicyName(drools.getRule().getName());
793 notification.setPolicyScope("${policyScope}");
794 notification.setPolicyVersion("${policyVersion}");
795 notification.setMessage($operation.getOperationHistory());
796 notification.setHistory($operation.getHistory());
797 if (policyResult.equals(PolicyResult.SUCCESS)) {
798 notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
800 // Let interested parties know
802 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
804 notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
806 // Let interested parties know
808 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
811 // Ensure the operation is complete
813 if ($operation.isOperationComplete() == true) {
815 // It is complete, remove it from memory
819 // We must also retract the timer object
820 // NOTE: We could write a Rule to do this
824 // Complete the operation
826 modify($manager) {finishOperation($operation)};
829 // Just doing this will kick off the LOCKED rule again
831 modify($operation) {};
835 // Its not finished yet (i.e. expecting more Response objects)
837 // Or possibly it is a leftover response that we timed the request out previously
841 // We are going to retract these objects from memory
848 * The problem with Responses is that they don't have a controlLoopControlName
849 * field in them, so the only way to attach them is via RequestId. If we have multiple
850 * control loop .drl's loaded in the same container, we need to be sure the cleanup
851 * rules don't remove Responses for other control loops.
854 rule "${policyName}.APPC.RESPONSE.CLEANUP"
856 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
857 $response : Response($id : getCommonHeader().RequestId )
858 not ( VirtualControlLoopEvent( requestId == $id, closedLoopEventStatus == ControlLoopEventStatus.ONSET ) )
861 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
862 logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
863 logger.debug("{}: {}: orphan appc response={}",
864 $params.getClosedLoopControlName(), drools.getRule().getName(), $id);
874 * This rule responds to APPC Response Events using the new LCM interface provided by appc
877 rule "${policyName}.APPC.LCM.RESPONSE"
879 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
880 $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
881 $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
882 $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
883 $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
884 $lock : TargetLock (requestID == $event.getRequestId())
885 $response : LcmResponseWrapper( getBody().getCommonHeader().getRequestId() == $event.getRequestId() )
888 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
889 logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
890 logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}",
891 $params.getClosedLoopControlName(), drools.getRule().getName(),
892 $event, $manager, $operation, $lock, $operation, $opTimer, $response);
895 // Get the result of the operation
897 PolicyResult policyResult = $operation.onResponse($response);
898 if (policyResult != null) {
899 logger.debug("{}: {}: operation finished - result={}",
900 $params.getClosedLoopControlName(), drools.getRule().getName(),
904 // This Operation has completed, construct a notification showing our results. (DB write - end operation)
906 VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
907 notification.setFrom("policy");
908 notification.setPolicyName(drools.getRule().getName());
909 notification.setPolicyScope("${policyScope}");
910 notification.setPolicyVersion("${policyVersion}");
911 notification.setMessage($operation.getOperationHistory());
912 notification.setHistory($operation.getHistory());
913 if (policyResult.equals(PolicyResult.SUCCESS)) {
914 notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
916 notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
918 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
920 // Ensure the operation is complete
922 if ($operation.isOperationComplete() == true) {
924 // It is complete, remove it from memory
928 // We must also retract the timer object
929 // NOTE: We could write a Rule to do this
933 // Complete the operation
935 modify($manager) {finishOperation($operation)};
938 // Just doing this will kick off the LOCKED rule again
940 modify($operation) {};
944 // Its not finished yet (i.e. expecting more Response objects)
946 // Or possibly it is a leftover response that we timed the request out previously
950 // We are going to retract these objects from memory
957 * Clean Up any lingering LCM reponses
960 rule "${policyName}.APPC.LCM.RESPONSE.CLEANUP"
962 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
963 $response : LcmResponseWrapper($id : getBody().getCommonHeader().getRequestId )
964 not ( VirtualControlLoopEvent( requestId == $id, closedLoopEventStatus == ControlLoopEventStatus.ONSET ) )
967 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
968 logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
969 logger.debug("{}: {}: orphan appc response={}",
970 $params.getClosedLoopControlName(), drools.getRule().getName(), $id);
979 * This rule responds to SO Response Events
982 rule "${policyName}.SO.RESPONSE"
984 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
985 $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
986 $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
987 $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
988 $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
989 $lock : TargetLock (requestID == $event.getRequestId())
990 $response : SOResponseWrapper(requestID.toString() == $event.getRequestId().toString() )
993 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
994 logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
995 logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}",
996 $params.getClosedLoopControlName(), drools.getRule().getName(),
997 $event, $manager, $operation, $lock, $operation, $opTimer, $response);
999 // Get the result of the operation
1001 PolicyResult policyResult = $operation.onResponse($response);
1002 if (policyResult != null) {
1003 logger.debug("{}: {}: operation finished - result={}",
1004 $params.getClosedLoopControlName(), drools.getRule().getName(),
1008 // This Operation has completed, construct a notification showing our results
1010 VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
1011 notification.setFrom("policy");
1012 notification.setPolicyName(drools.getRule().getName());
1013 notification.setPolicyScope("${policyScope}");
1014 notification.setPolicyVersion("${policyVersion}");
1015 notification.setMessage($operation.getOperationHistory());
1016 notification.setHistory($operation.getHistory());
1017 if (policyResult.equals(PolicyResult.SUCCESS)) {
1018 notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
1020 notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
1023 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
1025 // Ensure the operation is complete
1027 if ($operation.isOperationComplete() == true) {
1029 // It is complete, remove it from memory
1031 retract($operation);
1033 // We must also retract the timer object
1034 // NOTE: We could write a Rule to do this
1038 // Complete the operation
1040 modify($manager) {finishOperation($operation)};
1043 // Just doing this will kick off the LOCKED rule again
1045 modify($operation) {};
1049 // Its not finished yet (i.e. expecting more Response objects)
1051 // Or possibly it is a leftover response that we timed the request out previously
1055 // We are going to retract these objects from memory
1063 * This rule responds to VFC Response Events
1066 rule "${policyName}.VFC.RESPONSE"
1068 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
1069 $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
1070 $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
1071 $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
1072 $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
1073 $lock : TargetLock (requestID == $event.getRequestId())
1074 $response : VFCResponse( requestId.toString() == $event.getRequestId().toString() )
1076 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1077 logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
1078 logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}",
1079 $params.getClosedLoopControlName(), drools.getRule().getName(),
1080 $event, $manager, $operation, $lock, $operation, $opTimer, $response);
1082 // Get the result of the operation
1084 PolicyResult policyResult = $operation.onResponse($response);
1085 if (policyResult != null) {
1087 // This Operation has completed, construct a notification showing our results
1089 VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
1090 notification.setFrom("policy");
1091 notification.setPolicyName(drools.getRule().getName());
1092 notification.setPolicyScope("${policyScope}");
1093 notification.setPolicyVersion("${policyVersion}");
1094 notification.setMessage($operation.getOperationHistory());
1095 notification.setHistory($operation.getHistory());
1097 // Ensure the operation is complete
1099 if ($operation.isOperationComplete() == true) {
1101 // It is complete, remove it from memory
1103 retract($operation);
1105 // We must also retract the timer object
1106 // NOTE: We could write a Rule to do this
1110 // Complete the operation
1112 modify($manager) {finishOperation($operation)};
1115 // Just doing this will kick off the LOCKED rule again
1117 modify($operation) {};
1121 // Its not finished yet (i.e. expecting more Response objects)
1123 // Or possibly it is a leftover response that we timed the request out previously
1127 // We are going to retract these objects from memory
1135 * This manages a single timer.
1136 * Due to a bug in the drools code, the drools timer needed to be split from most of the objects in the when clause
1139 rule "${policyName}.TIMER.FIRED"
1140 timer (expr: $timeout)
1142 $timer : ControlLoopTimer($timeout : delay, !expired)
1144 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1145 logger.info("This is ${policyName}.TIMER.FIRED");
1146 modify($timer){setExpired(true)};
1151 * This is the timer that manages the timeout for an individual operation.
1154 rule "${policyName}.EVENT.MANAGER.OPERATION.TIMEOUT"
1156 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
1157 $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
1158 $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
1159 $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
1160 $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), expired, timerType == "Operation" )
1161 $lock : TargetLock (requestID == $event.getRequestId())
1164 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1165 logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
1166 logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={}",
1167 $params.getClosedLoopControlName(), drools.getRule().getName(),
1168 $event, $manager, $operation, $lock, $operation, $opTimer);
1171 // Tell it its timed out
1173 $operation.setOperationHasTimedOut();
1175 // Create a notification for it ("DB Write - end operation")
1177 VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
1178 notification.setFrom("policy");
1179 notification.setPolicyName(drools.getRule().getName());
1180 notification.setPolicyScope("${policyScope}");
1181 notification.setPolicyVersion("${policyVersion}");
1182 notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
1183 notification.setMessage($operation.getOperationHistory());
1184 notification.setHistory($operation.getHistory());
1186 // Let interested parties know
1188 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
1190 // Get rid of the timer
1194 // Ensure the operation is complete
1196 if ($operation.isOperationComplete() == true) {
1198 // It is complete, remove it from memory
1200 retract($operation);
1202 // Complete the operation
1204 modify($manager) {finishOperation($operation)};
1207 // Just doing this will kick off the LOCKED rule again
1209 modify($operation) {};
1215 * This is the timer that manages the overall control loop timeout.
1218 rule "${policyName}.EVENT.MANAGER.TIMEOUT"
1220 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
1221 $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
1222 $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
1223 $clTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), expired, timerType == "ClosedLoop" )
1226 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1227 logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
1229 logger.debug("{}: {}: event={}",
1230 $params.getClosedLoopControlName(), drools.getRule().getName(),
1233 // Tell the Event Manager it has timed out
1235 VirtualControlLoopNotification notification = $manager.setControlLoopTimedOut();
1236 if (notification != null) {
1237 notification.setFrom("policy");
1238 notification.setPolicyName(drools.getRule().getName());
1239 notification.setPolicyScope("${policyScope}");
1240 notification.setPolicyVersion("${policyVersion}");
1242 // Let interested parties know
1244 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
1247 // Retract the event
1254 * This rule cleans up the manager and other objects after an event has
1258 rule "${policyName}.EVENT.MANAGER.CLEANUP"
1260 $manager : ControlLoopEventManager( $clName : getClosedLoopControlName(), $requestId : getRequestID() )
1261 $operations : LinkedList()
1262 from collect( ControlLoopOperationManager( onset.closedLoopControlName == $clName, onset.getRequestId() == $requestId ) )
1263 $timers : LinkedList()
1264 from collect( ControlLoopTimer( closedLoopControlName == $clName, requestID == $requestId.toString() ) )
1265 $locks : LinkedList()
1266 from collect( TargetLock (requestID == $requestId) )
1267 not( VirtualControlLoopEvent( closedLoopControlName == $clName, requestId == $requestId ) )
1270 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1271 logger.info("{}: {}", $clName, drools.getRule().getName());
1273 logger.debug("{}: {}: manager={} timers={} operations={}",
1274 $clName, drools.getRule().getName(),
1275 $manager, $timers.size(), $operations.size());
1278 // Retract EVERYTHING
1282 for(Object manager: $operations) {
1283 retract((ControlLoopOperationManager) manager);
1285 for(Object timer: $timers) {
1286 retract((ControlLoopTimer) timer);
1288 for(Object lock: $locks) {
1289 TargetLock tgt = (TargetLock) lock;
1291 // Ensure we release the lock
1293 PolicyGuard.unlockTarget(tgt);
1300 * This rule will clean up any rogue onsets where there is no
1301 * ControlLoopParams object corresponding to the onset event.
1304 rule "${policyName}.EVENT.CLEANUP"
1306 $event : VirtualControlLoopEvent( $clName: closedLoopControlName )
1307 not ( Params( getClosedLoopControlName() == $clName) )
1310 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1311 logger.info("{}: {}", $clName, drools.getRule().getName());
1312 logger.debug("{}: {}: orphan onset event={}",
1313 $clName, drools.getRule().getName(), $event);
1320 * When rules are deleted, the associated Params (and its subordinate objects)
1321 * remain in working memory, because there are no longer any rules to clean
1322 * them up. However, ANY time new rules are loaded, this rule will trigger
1323 * a clean-up of ALL Params, regardless of their name & yaml, thus removing
1324 * any that no longer have associated rules.
1325 * This has a higher salience so that we immediately check Params when the
1326 * rules change, before processing any events.
1329 rule "${policyName}.PARAMS.CHECKUP"
1332 Params( $clName: closedLoopControlName, $yaml: controlLoopYaml )
1335 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1336 logger.info("{}: {} : YAML=[{}]", $clName, drools.getRule().getName(), $yaml);
1338 ParamsCleaner cleaner = new ParamsCleaner();
1339 cleaner.setClosedLoopControlName($clName);
1340 cleaner.setControlLoopYaml($yaml);
1347 * This rule removes "cleaner" objects for rules that are still active, thus
1348 * preventing the associated Params objects from being removed. Any cleaners
1349 * that are left after this rule has fired will cause their corresponding Params
1351 * This has a higher salience so that we discard the cleaner before it has
1352 * a chance to force the removal of the associated Params.
1355 rule "${policyName}.CLEANER.ACTIVE"
1358 $cleaner: ParamsCleaner( getClosedLoopControlName() == "${closedLoopControlName}", getControlLoopYaml() == "${controlLoopYaml}" )
1361 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1362 logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(), $cleaner.getControlLoopYaml());
1369 * This rule removes Params objects that no longer have associated rules; if a
1370 * Params still had associated rules, then the cleaner would have been removed
1371 * by those rules and thus this rule would not fire.
1372 * This has a higher salience so that we remove old Params before it causes any
1373 * events to be processed.
1376 rule "${policyName}.PARAMS.CLEANUP"
1379 $params: Params( $clName: closedLoopControlName, $yaml: controlLoopYaml )
1380 ParamsCleaner( getClosedLoopControlName() == $clName, getControlLoopYaml() == $yaml )
1383 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1384 logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(), $params.getControlLoopYaml());
1388 // Note: the cleaner may be needed for cleaning additional params, thus
1389 // we do not retract it here - we'll leave that to another rule
1394 * This rule removes "cleaner" objects when they're no longer needed.
1397 rule "${policyName}.CLEANER.CLEANUP"
1399 $cleaner: ParamsCleaner( )
1402 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1403 logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(), $cleaner.getControlLoopYaml());