9a1a29239fd76dbcc751c0192f5aba9e871e4516
[policy/drools-applications.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
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
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
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=========================================================
19  */
20
21 package org.onap.policy.controlloop;
22
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;
67
68 import org.yaml.snakeyaml.Yaml;
69 import org.yaml.snakeyaml.constructor.Constructor;
70
71 import org.slf4j.LoggerFactory;
72 import org.slf4j.Logger;
73
74 import java.time.Instant;
75 import java.util.LinkedList;
76 import java.util.Iterator;
77
78 import org.onap.policy.drools.system.PolicyEngine;
79
80 /*
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.
86  */
87 declare PapParams
88   closedLoopControlName : String
89   controlLoopYaml : String
90 end
91
92 /*
93  * Control Loop Identity
94  */
95 declare Params
96   closedLoopControlName : String
97   controlLoopYaml : String
98 end
99
100 /*
101  * Used to clean up Params that no longer have associated rules.
102  */
103 declare ParamsCleaner
104   closedLoopControlName : String
105   controlLoopYaml : String
106 end
107
108 /*
109  * This object is to provide support for timeouts
110  * due to a bug in drools' built-in timers
111  */
112 declare ControlLoopTimer
113     closedLoopControlName : String
114     requestID : String
115     delay : String
116     expired : boolean
117     //timerType is the type of timer: either "ClosedLoop" or "Operation"
118     timerType : String
119 end
120
121 /*
122 *
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.
126 *
127 */
128 rule "${policyName}.SETUP"
129     salience 1
130     when
131         not( Params( getClosedLoopControlName() == "${closedLoopControlName}", 
132             getControlLoopYaml() == "${controlLoopYaml}" ) )
133     then
134     
135     Params params = new Params();
136     params.setClosedLoopControlName("${closedLoopControlName}");
137     params.setControlLoopYaml("${controlLoopYaml}");
138     insert(params);
139
140     // Note: globals have bad behavior when persistence is used,
141     //       hence explicitly getting the logger vs using a global
142     
143     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
144     logger.info("{}: {} : YAML=[{}]", params.getClosedLoopControlName(), drools.getRule().getName(), 
145         params.getControlLoopYaml());
146 end
147
148 /*
149 *
150 * This rule responds to DCAE Events where there is no manager yet. Either it is
151 * the first ONSET, or a subsequent badly formed Event (i.e. Syntax error, or is-closed-loop-disabled)
152 *
153 */
154 rule "${policyName}.EVENT"
155     when
156         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
157         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
158         not ( ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), 
159             requestID == $event.getRequestId() ) )
160     then
161  
162     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
163     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
164     
165     try {
166       
167         //
168         // Check the event, because we need it to not be null when
169         // we create the ControlLoopEventManager. The ControlLoopEventManager
170         // will do extra syntax checking as well check if the closed loop is disabled.
171         //
172         if ($event.getRequestId() == null) {
173             VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
174             notification.setNotification(ControlLoopNotificationType.REJECTED);
175             notification.setFrom("policy");
176             notification.setMessage("Missing requestId");
177             notification.setPolicyName(drools.getRule().getName());
178             notification.setPolicyScope("${policyScope}");
179             notification.setPolicyVersion("${policyVersion}");
180             
181             //
182             // Let interested parties know
183             //
184             PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
185             
186             //
187             // Retract it from memory
188             //
189             retract($event);
190         } else if ($event.getClosedLoopEventStatus() != ControlLoopEventStatus.ONSET) {
191             throw new ControlLoopException($event.getClosedLoopEventStatus() + " received with no prior onset");
192         } else {
193             //
194             // Create an EventManager
195             //
196             ControlLoopEventManager manager = new ControlLoopEventManager($params.getClosedLoopControlName(), 
197                 $event.getRequestId());
198             //
199             // Determine if EventManager can actively process the event 
200             // (i.e. syntax, is_closed_loop_disabled checks etc.)
201             //
202             VirtualControlLoopNotification notification = manager.activate($params.getControlLoopYaml(), $event);
203             notification.setFrom("pdp-0001-controller=controlloop"); // Engine.getInstanceName()
204             notification.setPolicyName(drools.getRule().getName());
205             notification.setPolicyScope("${policyScope}");
206             notification.setPolicyVersion("${policyVersion}");
207             //
208             // Are we actively pursuing this event?
209             //
210             if (notification.getNotification() == ControlLoopNotificationType.ACTIVE) {
211                 //
212                 // Insert Event Manager into memory, this will now kick off processing.
213                 //
214                 insert(manager);
215                 //
216                 // Let interested parties know
217                 //
218                 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
219                 //
220                 // Setup the Overall Control Loop timer
221                 //
222                 ControlLoopTimer clTimer = new ControlLoopTimer();
223                 clTimer.setTimerType("ClosedLoop");
224                 clTimer.setClosedLoopControlName($event.getClosedLoopControlName());
225                 clTimer.setRequestID($event.getRequestId().toString());
226                 clTimer.setDelay(manager.getControlLoopTimeout(1500) + "s");
227                 //
228                 // Insert it
229                 //
230                 insert(clTimer);
231             } else {
232                 //
233                 // Let interested parties know
234                 //
235                 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
236                 //
237                 // Retract it from memory
238                 //
239                 retract($event);
240             }
241             
242             //
243             // Now that the manager is inserted into Drools working memory, we'll wait for
244             // another rule to fire in order to continue processing. This way we can also
245             // then screen for additional ONSET and ABATED events for this RequestId.
246             //
247         }
248     } catch (Exception e) {
249         logger.warn("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName(), e);
250         
251         VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
252         notification.setNotification(ControlLoopNotificationType.REJECTED);
253         notification.setMessage("Exception occurred: " + e.getMessage());
254         notification.setPolicyName(drools.getRule().getName());
255         notification.setPolicyScope("${policyScope}");
256         notification.setPolicyVersion("${policyVersion}");
257         //
258         //
259         //
260         PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
261         //
262         // Retract the event
263         //
264         retract($event);
265     }
266 end
267
268 /*
269 *
270 * This rule happens when we got a valid ONSET, closed loop is enabled and an Event Manager
271 * is now created. We can start processing the yaml specification via the Event Manager.
272 *
273 */
274 rule "${policyName}.EVENT.MANAGER"
275     when
276         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
277         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
278         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), 
279             requestID == $event.getRequestId() )
280         $clTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), 
281             requestID == $event.getRequestId().toString(), timerType == "ClosedLoop", !expired )
282     then
283
284     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
285     logger.info("{}: {}: event={} manager={} clTimer={}", 
286                 $params.getClosedLoopControlName(), drools.getRule().getName(),
287                 $event, $manager, $clTimer);
288     
289     try {
290         //
291         // Check which event this is.
292         //
293         ControlLoopEventManager.NEW_EVENT_STATUS eventStatus = $manager.onNewEvent($event);
294         //
295         // Check what kind of event this is
296         //
297         if (eventStatus == NEW_EVENT_STATUS.SUBSEQUENT_ONSET) {
298             //
299             // We don't care about subsequent onsets
300             //
301             logger.info("{}: {}: subsequent onset", 
302                         $params.getClosedLoopControlName(), drools.getRule().getName());
303             retract($event);
304             return;
305         }
306         if (eventStatus == NEW_EVENT_STATUS.SYNTAX_ERROR) {
307             //
308             // Ignore any bad syntax events
309             //
310             logger.warn("{}: {}: syntax error", 
311                         $params.getClosedLoopControlName(), drools.getRule().getName());
312             retract($event);
313             return;
314         }
315         //
316         // We only want the initial ONSET event in memory,
317         // all the other events need to be retracted to support
318         // cleanup and avoid the other rules being fired for this event.
319         //
320         if (eventStatus != NEW_EVENT_STATUS.FIRST_ONSET) {
321             logger.warn("{}: {}: not first onset", 
322                         $params.getClosedLoopControlName(), drools.getRule().getName());
323             retract($event);
324         }
325         
326         logger.debug("{}: {}: target={}", $params.getClosedLoopControlName(), 
327                      drools.getRule().getName(), $event.getTarget());
328         //
329         // Now start seeing if we need to process this event
330         //
331
332         //
333         // Check if this is a Final Event
334         //
335         VirtualControlLoopNotification notification = $manager.isControlLoopFinal();
336     
337     
338         if (notification != null) {
339             //
340             // Its final, but are we waiting for abatement?
341             //
342             if ($manager.getNumAbatements() > 0) {
343                 logger.info("{}: {}: abatement received for {}.  Closing the control loop", 
344                             $params.getClosedLoopControlName(), drools.getRule().getName(), 
345                             $event.getRequestId());
346                 
347                 /// DB Write---end event processing for this RequestId()
348                 $manager.commitAbatement("Event Abated","Closed");
349                 
350                 notification.setFrom("policy");
351                 notification.setPolicyName(drools.getRule().getName());
352                 notification.setPolicyScope("${policyScope}");
353                 notification.setPolicyVersion("${policyVersion}");
354                 //
355                 // In this case, we are done
356                 //
357                 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
358                 //
359                 // Unlock the target
360                 //
361                 TargetLock lock = $manager.unlockCurrentOperation();
362                 if (lock != null) {
363                     logger.debug("{}: {}: retracting lock=", $params.getClosedLoopControlName(), 
364                                  drools.getRule().getName(), lock);
365                     retract(lock);
366                 }
367                 //
368                 // Retract everything from memory
369                 //
370                 logger.info("{}: {}: retracting onset, manager, and timer", 
371                             $params.getClosedLoopControlName(), drools.getRule().getName());
372                 
373                 retract($manager.getOnsetEvent());
374                 retract($manager);
375                 retract($clTimer);
376                 //
377                 // TODO - what if we get subsequent Events for this RequestId?
378                 // By default, it will all start over again. May be confusing for Ruby.
379                 // Or, we could track this and then subsequently ignore the events
380                 //
381             } else {
382                 //
383                 // Check whether we need to wait for abatement
384                 //
385                 if ($manager.getProcessor().getControlLoop().getAbatement() == true && notification.getNotification() == ControlLoopNotificationType.FINAL_SUCCESS) {
386                   logger.info("{}: {}: waiting for abatement ..", 
387                               $params.getClosedLoopControlName(), drools.getRule().getName());
388                 } else {
389                   logger.info("{}: {}: no abatement expect for {}.  Closing the control loop", 
390                               $params.getClosedLoopControlName(), drools.getRule().getName(), 
391                               $event.getRequestId());
392                   
393                   notification.setFrom("policy");
394                   notification.setPolicyName(drools.getRule().getName());
395                   notification.setPolicyScope("${policyScope}");
396                   notification.setPolicyVersion("${policyVersion}");
397                   
398                   //
399                   // In this case, we are done
400                   //
401                   PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
402                   //
403                   // Unlock the target
404                   //
405                   TargetLock lock = $manager.unlockCurrentOperation();
406                   if (lock != null) {
407                       logger.debug("{}: {}: retracting lock=", $params.getClosedLoopControlName(), 
408                                   drools.getRule().getName(), lock);
409                       retract(lock);
410                   }
411                   //
412                   // Retract everything from memory
413                   //
414                   logger.info("{}: {}: retracting onset, manager, and timer", 
415                               $params.getClosedLoopControlName(), drools.getRule().getName());
416                   
417                   retract($manager.getOnsetEvent());
418                   retract($manager);
419                   retract($clTimer);
420                 }
421             }
422         } else {
423             //
424             // NOT final, so let's ask for the next operation
425             //
426             ControlLoopOperationManager operation = $manager.processControlLoop();
427             if (operation != null) {
428               //
429               // Let's ask for a lock right away
430               //
431               LockResult<GuardResult, TargetLock> result = $manager.lockCurrentOperation();
432               logger.info("{}: {}: guard lock acquired={}", 
433                             $params.getClosedLoopControlName(), drools.getRule().getName(), 
434                             result.getB());
435               if (result.getA().equals(GuardResult.LOCK_ACQUIRED)) {
436                   //
437                   // insert the operation into memory
438                   //
439                   insert(operation);
440                   //
441                   // insert operation timeout object
442                   //
443                    ControlLoopTimer opTimer = new ControlLoopTimer();
444                    opTimer.setTimerType("Operation");
445                    opTimer.setClosedLoopControlName($event.getClosedLoopControlName());
446                    opTimer.setRequestID($event.getRequestId().toString());
447                    opTimer.setDelay(operation.getOperationTimeout().toString() + "s");
448                   insert(opTimer);
449               
450                   //
451                   // Insert lock into memory
452                   //
453                   insert(result.getB());
454               } else {
455                    logger.debug("The target resource {} is already processing",
456                             $event.getAai().get($event.getTarget()));
457                   notification = new VirtualControlLoopNotification($event);
458                   notification.setNotification(ControlLoopNotificationType.REJECTED);
459                   notification.setMessage("The target " + $event.getAai().get($event.getTarget()) 
460                       + " is already locked");
461                   notification.setFrom("policy");
462                   notification.setPolicyName(drools.getRule().getName());
463                   notification.setPolicyScope("${policyScope}");
464                   notification.setPolicyVersion("${policyVersion}");
465       
466                   PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
467
468                   retract($event);
469                   retract($manager);
470                   retract($clTimer);
471
472                   if (result.getB() != null) {
473                       retract(result.getB());
474                   }
475               }
476               logger.info("{}: {}: starting operation={}", 
477                           $params.getClosedLoopControlName(), drools.getRule().getName(), 
478                           operation);
479             } else {
480                 //
481                 // Probably waiting for abatement
482                 //
483               logger.info("{}: {}: no operation, probably waiting for abatement", 
484                           $params.getClosedLoopControlName(), drools.getRule().getName());
485             }
486         }
487     } catch (Exception e) {
488          logger.warn("{}: {}: unexpected", 
489                   $params.getClosedLoopControlName(), 
490                   drools.getRule().getName(), e);
491
492          VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
493          notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
494          notification.setMessage(e.getMessage());
495          notification.setFrom("policy");
496          notification.setPolicyName(drools.getRule().getName());
497          notification.setPolicyScope("${policyScope}");
498          notification.setPolicyVersion("${policyVersion}");
499   
500          PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
501
502          retract($event);
503          retract($manager);
504          retract($clTimer);
505     }
506         
507 end
508
509 /*
510 *
511 * Guard Permitted, let's send request to the actor.
512 *
513 */
514 rule "${policyName}.EVENT.MANAGER.OPERATION.LOCKED.GUARD_PERMITTED"
515     when
516         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
517         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
518         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), 
519             requestID == $event.getRequestId() )
520         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), 
521             onset.getRequestId() == $event.getRequestId(), "Permit".equalsIgnoreCase(getGuardApprovalStatus()) )
522         $lock : TargetLock (requestID == $event.getRequestId())
523         $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), 
524             requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
525     then
526
527     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
528     logger.info("{}: {}: event={} manager={} operation={} lock={}", 
529                 $params.getClosedLoopControlName(), drools.getRule().getName(),
530                 $event, $manager, $operation, $lock);    
531
532     Object request = null;
533     boolean caughtException = false;
534     
535     try {
536         request = $operation.startOperation($event);
537         
538         if (request != null) {
539           logger.debug("{}: {}: starting operation ..", 
540                        $params.getClosedLoopControlName(), drools.getRule().getName());
541           //
542           // Tell interested parties we are performing this Operation
543           //
544           VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
545           notification.setNotification(ControlLoopNotificationType.OPERATION);
546           notification.setMessage($operation.getOperationMessage());
547           notification.setHistory($operation.getHistory());
548           notification.setFrom("policy");
549           notification.setPolicyName(drools.getRule().getName());
550           notification.setPolicyScope("${policyScope}");
551           notification.setPolicyVersion("${policyVersion}");
552
553           PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
554
555           switch ($operation.policy.getActor()){
556
557               case "APPC":
558                   if (request instanceof Request) {
559                       PolicyEngine.manager.deliver("APPC-CL", request);
560                   }
561                   else if (request instanceof LcmRequestWrapper) {
562                       PolicyEngine.manager.deliver("APPC-LCM-READ", request);
563                   }
564                   break;
565               case "SO":
566                   // at this point the AAI named query request should have already been made, 
567                   // the response recieved and used
568                   // in the construction of the SO Request which is stored in operationRequest
569
570                   if(request instanceof SORequest) {
571                       // Call SO. The response will be inserted into memory once it's received 
572                       SoActorServiceProvider.sendRequest($event.getRequestId().toString(), drools.getWorkingMemory(),
573                           request);                        
574                   }
575                   break;
576               case "VFC":
577                   if (request instanceof VFCRequest) {
578                       // Start VFC thread
579                       Thread t = new Thread(new VFCManager(drools.getWorkingMemory(), (VFCRequest)request));
580                       t.start();
581                   }          
582                   break;
583           }
584         } else {
585           //
586           // What happens if its null?
587           //
588             logger.warn("{}: {}: unexpected null operation request", 
589                       $params.getClosedLoopControlName(), 
590                       drools.getRule().getName());
591             if ("SO".equals($operation.policy.getActor())) {
592                 retract($opTimer);
593                 retract($operation);
594                 modify($manager) {finishOperation($operation)};
595             }
596             else if ("vfc".equalsIgnoreCase($operation.policy.getActor())) {
597                 retract($opTimer);
598                 retract($operation);
599                 modify($manager) {finishOperation($operation)};
600             }
601         }
602         
603     } catch (Exception e) {
604         String msg = e.getMessage();
605         logger.warn("{}: {}: operation={}:  AAI failure: {}", 
606                     $params.getClosedLoopControlName(), drools.getRule().getName(),
607                     $operation, msg, e);
608         $operation.setOperationHasException(msg);
609         
610         if(request != null) {
611             //
612             // Create a notification for it ("DB Write - end operation")
613             //
614             VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
615             notification.setFrom("policy");
616             notification.setPolicyName(drools.getRule().getName());
617             notification.setPolicyScope("${policyScope}");
618             notification.setPolicyVersion("${policyVersion}");
619             notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
620             notification.setMessage($operation.getOperationHistory());
621             notification.setHistory($operation.getHistory());
622           
623             PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
624         }
625     
626         retract($opTimer);
627         retract($operation);
628         caughtException = true;
629     }
630     
631     // Having the modify statement in the catch clause doesn't work for whatever reason
632     if (caughtException) {
633         modify($manager) {finishOperation($operation)};
634     }
635 end
636
637
638 /*
639 *
640 * We were able to acquire a lock so now let's ask Xacml Guard whether 
641 * we are allowed to proceed with the request to the actor.
642 *
643 */
644 rule "${policyName}.EVENT.MANAGER.OPERATION.LOCKED.GUARD_NOT_YET_QUERIED"
645     when
646         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
647         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
648         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), 
649             requestID == $event.getRequestId() )
650         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), 
651             onset.getRequestId() == $event.getRequestId(), getGuardApprovalStatus() == "NONE" )
652         $lock : TargetLock (requestID == $event.getRequestId())
653     then
654
655     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
656     logger.info("{}: {}: event={} manager={} operation={} lock={}", 
657                 $params.getClosedLoopControlName(), drools.getRule().getName(),
658                 $event, $manager, $operation, $lock);
659     
660     //
661     // Sending notification that we are about to query Guard ("DB write - start operation")
662     //
663     VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
664     notification.setNotification(ControlLoopNotificationType.OPERATION);
665     notification.setMessage("Sending guard query for " + $operation.policy.getActor() + " " 
666         + $operation.policy.getRecipe());
667     notification.setHistory($operation.getHistory());
668     notification.setFrom("policy");
669     notification.setPolicyName(drools.getRule().getName());
670     notification.setPolicyScope("${policyScope}");
671     notification.setPolicyVersion("${policyVersion}");
672     
673     PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
674         
675     //
676     // Now send Guard Request to XACML Guard. In order to bypass the call to Guard, 
677     // just change guardEnabled to false.
678     // 
679     // In order to use REST XACML, provide a URL instead of "" as a second argument 
680     // to the CallGuardTask() and set the first argument to null 
681     // (instead of XacmlPdpEngine).
682     //
683     
684     // NOTE: The environment properties uses "guard.disabled" but the boolean is guardEnabled
685     boolean guardEnabled = "false".equalsIgnoreCase(PolicyEngine.manager.getEnvironmentProperty("guard.disabled"));
686     
687     if(guardEnabled){
688     
689         Thread t = new Thread(new org.onap.policy.guard.CallGuardTask(
690                                                         drools.getWorkingMemory(),
691                                                         $event.getClosedLoopControlName(),
692                                                         $operation.policy.getActor().toString(),
693                                                         $operation.policy.getRecipe(),
694                                                         $operation.getTargetEntity(),
695                                                         $event.getRequestId().toString(),
696                                                         () -> {
697                                                             AaiNqResponseWrapper resp = $manager.getNqVserverFromAai();
698                                                             return(resp == null ? null : resp.countVfModules());
699                                                         }));
700         t.start();
701     }
702     else{
703         insert(new PolicyGuardResponse("Permit", $event.getRequestId(), $operation.policy.getRecipe()));
704     }
705
706 end
707
708 //
709 // This rule will be triggered when a thread talking to the XACML Guard inserts a 
710 // guardResponse object into the working memory
711 //
712 rule "${policyName}.GUARD.RESPONSE"
713     when
714         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
715         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), 
716             closedLoopEventStatus == ControlLoopEventStatus.ONSET )
717         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), 
718             requestID == $event.getRequestId() ) 
719         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), 
720             onset.getRequestId() == $event.getRequestId() )
721         $lock : TargetLock (requestID == $event.getRequestId())
722         $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), 
723             requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
724         $guardResponse : PolicyGuardResponse(requestID == $event.getRequestId(), $operation.policy.recipe == operation)
725     then
726
727     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
728     logger.info("{}: {}: event={} manager={} operation={} lock={} opTimer={} guardResponse={}", 
729                  $params.getClosedLoopControlName(), drools.getRule().getName(),
730                  $event, $manager, $operation, $lock, $opTimer, $guardResponse);
731         
732         
733     //we will permit the operation if there was no Guard for it
734     if("Indeterminate".equalsIgnoreCase($guardResponse.getResult())){
735         $guardResponse.setResult("Permit");
736     }
737     
738     //
739     // This notification has Guard result in "message". ("DB write - end operation in case of Guard Deny")
740     //
741     VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
742     notification.setNotification(ControlLoopNotificationType.OPERATION);
743     notification.setMessage("Guard result for " + $operation.policy.getActor() + " " + $operation.policy.getRecipe()
744         + " is " + $guardResponse.getResult());
745     notification.setHistory($operation.getHistory());
746     notification.setFrom("policy");
747     notification.setPolicyName(drools.getRule().getName());
748     notification.setPolicyScope("${policyScope}");
749     notification.setPolicyVersion("${policyVersion}");
750     
751     PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
752     
753     if("Permit".equalsIgnoreCase($guardResponse.getResult())){
754     
755         modify($operation){setGuardApprovalStatus($guardResponse.getResult())};
756     }
757     else {
758         //This is the Deny case
759         $operation.startOperation($event);
760         $operation.setOperationHasGuardDeny();
761         retract($opTimer);
762         retract($operation);
763         modify($manager) {finishOperation($operation)};
764     }
765     
766     retract($guardResponse);
767             
768 end
769
770 /*
771 *
772 * This rule responds to APPC Response Events
773 *
774 * I would have like to be consistent and write the Response like this:
775 * $response : Response( CommonHeader.RequestId == $onset.getRequestId() )
776 *
777 * However, no compile error was given. But a runtime error was given. I think
778 * because drools is confused between the classname CommonHeader vs the property CommonHeader.
779 *
780 */
781 rule "${policyName}.APPC.RESPONSE"
782     when
783         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
784         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), 
785             closedLoopEventStatus == ControlLoopEventStatus.ONSET ) 
786         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), 
787             requestID == $event.getRequestId() )
788         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), 
789             onset.getRequestId() == $event.getRequestId() )
790         $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), 
791             requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
792         $lock : TargetLock (requestID == $event.getRequestId())
793         $response : Response( getCommonHeader().RequestId == $event.getRequestId() )
794     then
795
796     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
797     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
798     logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}", 
799                  $params.getClosedLoopControlName(), drools.getRule().getName(),
800                  $event, $manager, $operation, $lock, $opTimer, $response);
801     //
802     // Get the result of the operation
803     //
804     PolicyResult policyResult = $operation.onResponse($response);
805     if (policyResult != null) {
806         logger.debug("{}: {}: operation finished - result={}", 
807                     $params.getClosedLoopControlName(), drools.getRule().getName(),
808                     policyResult);
809         //
810         // This Operation has completed, construct a notification showing our results. (DB write - end operation)
811         //
812         VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
813         notification.setFrom("policy");
814         notification.setPolicyName(drools.getRule().getName());
815         notification.setPolicyScope("${policyScope}");
816         notification.setPolicyVersion("${policyVersion}");
817         notification.setMessage($operation.getOperationHistory());
818         notification.setHistory($operation.getHistory());
819         if (policyResult.equals(PolicyResult.SUCCESS)) {
820             notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
821             //
822             // Let interested parties know
823             //
824             PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
825         } else {
826             notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
827             //
828             // Let interested parties know
829             //
830             PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
831         }
832         //
833         // Ensure the operation is complete
834         //
835         if ($operation.isOperationComplete() == true) {
836             //
837             // It is complete, remove it from memory
838             //
839             retract($operation);
840             //
841             // We must also retract the timer object
842             // NOTE: We could write a Rule to do this
843             //
844             retract($opTimer);
845             //
846             // Complete the operation
847             //
848             modify($manager) {finishOperation($operation)};
849         } else {
850             //
851             // Just doing this will kick off the LOCKED rule again
852             //
853             modify($operation) {};
854         }
855     } else {
856         //
857         // Its not finished yet (i.e. expecting more Response objects)
858         //
859         // Or possibly it is a leftover response that we timed the request out previously
860         //
861     }
862     //
863     // We are going to retract these objects from memory
864     //
865     retract($response);
866 end
867
868 /*
869 *
870 * The problem with Responses is that they don't have a controlLoopControlName
871 * field in them, so the only way to attach them is via RequestId. If we have multiple
872 * control loop .drl's loaded in the same container, we need to be sure the cleanup
873 * rules don't remove Responses for other control loops.
874 *
875 */
876 rule "${policyName}.APPC.RESPONSE.CLEANUP"
877     when
878         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
879         $response : Response($id : getCommonHeader().RequestId )
880         not ( VirtualControlLoopEvent( requestId == $id, closedLoopEventStatus == ControlLoopEventStatus.ONSET ) ) 
881     then
882
883     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
884     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
885     logger.debug("{}: {}: orphan appc response={}", 
886                 $params.getClosedLoopControlName(), drools.getRule().getName(), $id);
887         
888     //
889     // Retract it
890     //
891     retract($response);
892 end
893
894 /*
895 *
896 * This rule responds to APPC Response Events using the new LCM interface provided by appc
897 *
898 */
899 rule "${policyName}.APPC.LCM.RESPONSE"
900     when
901         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
902         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), 
903             closedLoopEventStatus == ControlLoopEventStatus.ONSET ) 
904         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), 
905             requestID == $event.getRequestId() )
906         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), 
907             onset.getRequestId() == $event.getRequestId() )
908         $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), 
909             requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
910         $lock : TargetLock (requestID == $event.getRequestId())
911         $response : LcmResponseWrapper( getBody().getCommonHeader().getRequestId() == $event.getRequestId() )
912     then
913
914     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
915     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
916     logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}", 
917                 $params.getClosedLoopControlName(), drools.getRule().getName(),
918                 $event, $manager, $operation, $lock, $operation, $opTimer, $response);
919     
920     //
921     // Get the result of the operation
922     //
923     PolicyResult policyResult = $operation.onResponse($response);
924     if (policyResult != null) {
925       logger.debug("{}: {}: operation finished - result={}", 
926                   $params.getClosedLoopControlName(), drools.getRule().getName(),
927                   policyResult);
928       
929       //
930       // This Operation has completed, construct a notification showing our results. (DB write - end operation)
931       //
932       VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
933       notification.setFrom("policy");
934       notification.setPolicyName(drools.getRule().getName());
935       notification.setPolicyScope("${policyScope}");
936       notification.setPolicyVersion("${policyVersion}");
937       notification.setMessage($operation.getOperationHistory());
938       notification.setHistory($operation.getHistory());
939       if (policyResult.equals(PolicyResult.SUCCESS)) {
940           notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
941       } else {
942           notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
943       }
944       PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
945       //
946       // Ensure the operation is complete
947       //
948       if ($operation.isOperationComplete() == true) {
949           //
950           // It is complete, remove it from memory
951           //
952           retract($operation);
953           //
954           // We must also retract the timer object
955           // NOTE: We could write a Rule to do this
956           //
957           retract($opTimer);
958           //
959           // Complete the operation
960           //
961           modify($manager) {finishOperation($operation)};
962       } else {
963           //
964           // Just doing this will kick off the LOCKED rule again
965           //
966           modify($operation) {};
967       }
968     } else {
969         //
970         // Its not finished yet (i.e. expecting more Response objects)
971         //
972         // Or possibly it is a leftover response that we timed the request out previously
973         //
974     }
975     //
976     // We are going to retract these objects from memory
977     //
978     retract($response);
979 end
980
981 /*
982 *
983 * Clean Up any lingering LCM reponses
984 *
985 */
986 rule "${policyName}.APPC.LCM.RESPONSE.CLEANUP"
987     when
988         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
989         $response : LcmResponseWrapper($id : getBody().getCommonHeader().getRequestId )
990         not ( VirtualControlLoopEvent( requestId == $id, closedLoopEventStatus == ControlLoopEventStatus.ONSET ) ) 
991     then
992     
993     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
994     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
995     logger.debug("{}: {}: orphan appc response={}", 
996                 $params.getClosedLoopControlName(), drools.getRule().getName(), $id);
997     //
998     // Retract it
999     //
1000     retract($response);
1001 end
1002
1003 /*
1004 *
1005 * This rule responds to SO Response Events
1006 *
1007 */
1008 rule "${policyName}.SO.RESPONSE"
1009     when
1010         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
1011         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), 
1012             closedLoopEventStatus == ControlLoopEventStatus.ONSET )
1013         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), 
1014             requestID == $event.getRequestId() )
1015         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), 
1016             onset.getRequestId() == $event.getRequestId() )
1017         $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), 
1018             requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
1019         $lock : TargetLock (requestID == $event.getRequestId())
1020         $response : SOResponseWrapper(requestID.toString() == $event.getRequestId().toString() )
1021     then
1022
1023     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1024     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
1025     logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}", 
1026                 $params.getClosedLoopControlName(), drools.getRule().getName(),
1027                 $event, $manager, $operation, $lock, $operation, $opTimer, $response);
1028         
1029     // Get the result of the operation
1030     //
1031     PolicyResult policyResult = $operation.onResponse($response);
1032     if (policyResult != null) {
1033         logger.debug("{}: {}: operation finished - result={}", 
1034                     $params.getClosedLoopControlName(), drools.getRule().getName(),
1035                     policyResult);
1036       
1037         //
1038         // This Operation has completed, construct a notification showing our results
1039         //
1040         VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
1041         notification.setFrom("policy");
1042         notification.setPolicyName(drools.getRule().getName());
1043         notification.setPolicyScope("${policyScope}");
1044         notification.setPolicyVersion("${policyVersion}");
1045         notification.setMessage($operation.getOperationHistory());
1046         notification.setHistory($operation.getHistory());
1047         if (policyResult.equals(PolicyResult.SUCCESS)) {
1048             notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
1049         } else {
1050             notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
1051
1052         }
1053         PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
1054         //
1055         // Ensure the operation is complete
1056         //
1057         if ($operation.isOperationComplete() == true) {
1058             //
1059             // It is complete, remove it from memory
1060             //
1061             retract($operation);
1062             //
1063             // We must also retract the timer object
1064             // NOTE: We could write a Rule to do this
1065             //
1066             retract($opTimer);
1067             //
1068             // Complete the operation
1069             //
1070             modify($manager) {finishOperation($operation)};
1071         } else {
1072             //
1073             // Just doing this will kick off the LOCKED rule again
1074             //
1075             modify($operation) {};
1076         }
1077     } else {
1078         //
1079         // Its not finished yet (i.e. expecting more Response objects)
1080         //
1081         // Or possibly it is a leftover response that we timed the request out previously
1082         //
1083     }
1084     //
1085     // We are going to retract these objects from memory
1086     //
1087     retract($response);
1088
1089 end
1090
1091 /*
1092 *
1093 * This rule responds to VFC Response Events
1094 *
1095 */
1096 rule "${policyName}.VFC.RESPONSE"
1097     when
1098         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
1099         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), 
1100             closedLoopEventStatus == ControlLoopEventStatus.ONSET )
1101         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), 
1102             requestID == $event.getRequestId() )
1103         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), 
1104             onset.getRequestId() == $event.getRequestId() )
1105         $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), 
1106             requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
1107         $lock : TargetLock (requestID == $event.getRequestId())
1108         $response : VFCResponse( requestId.toString() == $event.getRequestId().toString() )    
1109     then
1110         Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1111         logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
1112         logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}", 
1113                     $params.getClosedLoopControlName(), drools.getRule().getName(),
1114                     $event, $manager, $operation, $lock, $operation, $opTimer, $response);
1115
1116         // Get the result of the operation
1117         //
1118         PolicyResult policyResult = $operation.onResponse($response);
1119         if (policyResult != null) {
1120             //
1121             // This Operation has completed, construct a notification showing our results
1122             //
1123             VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
1124             notification.setFrom("policy");
1125             notification.setPolicyName(drools.getRule().getName());
1126             notification.setPolicyScope("${policyScope}");
1127             notification.setPolicyVersion("${policyVersion}");
1128             notification.setMessage($operation.getOperationHistory());
1129             notification.setHistory($operation.getHistory());
1130             //
1131             // Ensure the operation is complete
1132             //
1133             if ($operation.isOperationComplete() == true) {
1134                 //
1135                 // It is complete, remove it from memory
1136                 //
1137                 retract($operation);
1138                 //
1139                 // We must also retract the timer object
1140                 // NOTE: We could write a Rule to do this
1141                 //
1142                 retract($opTimer);
1143                 //
1144                 // Complete the operation
1145                 //
1146                 modify($manager) {finishOperation($operation)};
1147             } else {
1148                 //
1149                 // Just doing this will kick off the LOCKED rule again
1150                 //
1151                 modify($operation) {};
1152             }
1153         } else {
1154             //
1155             // Its not finished yet (i.e. expecting more Response objects)
1156             //
1157             // Or possibly it is a leftover response that we timed the request out previously
1158             //
1159         }
1160         //
1161         // We are going to retract these objects from memory
1162         //
1163         retract($response);
1164
1165 end
1166
1167 /*
1168 *
1169 * This manages a single timer.
1170 * Due to a bug in the drools code, the drools timer needed to be split from most of the objects in the when clause
1171 *
1172 */
1173 rule "${policyName}.TIMER.FIRED"
1174     timer (expr: $timeout)
1175     when
1176         $timer : ControlLoopTimer($timeout : delay, !expired)
1177     then
1178         Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1179         logger.info("This is ${policyName}.TIMER.FIRED");
1180         modify($timer){setExpired(true)};
1181     end
1182
1183 /*
1184 *
1185 * This is the timer that manages the timeout for an individual operation.
1186 *
1187 */
1188 rule "${policyName}.EVENT.MANAGER.OPERATION.TIMEOUT"
1189     when
1190         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
1191         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
1192         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), 
1193             requestID == $event.getRequestId() )
1194         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), 
1195             onset.getRequestId() == $event.getRequestId() )
1196         $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), 
1197             requestID == $event.getRequestId().toString(), expired, timerType == "Operation" )
1198         $lock : TargetLock (requestID == $event.getRequestId())
1199     then
1200     
1201     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1202     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
1203     logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={}", 
1204                 $params.getClosedLoopControlName(), drools.getRule().getName(),
1205                 $event, $manager, $operation, $lock, $operation, $opTimer);
1206     
1207     //
1208     // Tell it its timed out
1209     //
1210     $operation.setOperationHasTimedOut();
1211     //
1212     // Create a notification for it ("DB Write - end operation")
1213     //
1214     VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
1215     notification.setFrom("policy");
1216     notification.setPolicyName(drools.getRule().getName());
1217     notification.setPolicyScope("${policyScope}");
1218     notification.setPolicyVersion("${policyVersion}");
1219     notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
1220     notification.setMessage($operation.getOperationHistory());
1221     notification.setHistory($operation.getHistory());
1222     //
1223     // Let interested parties know
1224     //
1225     PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
1226     //
1227     // Get rid of the timer
1228     //
1229     retract($opTimer);
1230     //
1231     // Ensure the operation is complete
1232     //
1233     if ($operation.isOperationComplete() == true) {
1234         //
1235         // It is complete, remove it from memory
1236         //
1237         retract($operation);
1238         //
1239         // Complete the operation
1240         //
1241         modify($manager) {finishOperation($operation)};
1242     } else {
1243         //
1244         // Just doing this will kick off the LOCKED rule again
1245         //
1246         modify($operation) {};
1247     }
1248 end
1249
1250 /*
1251 *
1252 * This is the timer that manages the overall control loop timeout.
1253 *
1254 */
1255 rule "${policyName}.EVENT.MANAGER.TIMEOUT"
1256     when
1257         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
1258         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
1259         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), 
1260             requestID == $event.getRequestId() )
1261         $clTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), 
1262             requestID == $event.getRequestId().toString(), expired, timerType == "ClosedLoop" )
1263     then
1264     
1265     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1266     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
1267
1268     logger.debug("{}: {}: event={}", 
1269               $params.getClosedLoopControlName(), drools.getRule().getName(),
1270               $event);
1271     //
1272     // Tell the Event Manager it has timed out
1273     //
1274     VirtualControlLoopNotification notification = $manager.setControlLoopTimedOut();
1275     if (notification != null) {
1276         notification.setFrom("policy");
1277         notification.setPolicyName(drools.getRule().getName());
1278         notification.setPolicyScope("${policyScope}");
1279         notification.setPolicyVersion("${policyVersion}");
1280         //
1281         // Let interested parties know
1282         //
1283         PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
1284     }
1285     //
1286     // Retract the event
1287     //
1288     retract($event);
1289 end
1290
1291 /*
1292 *
1293 * This rule cleans up the manager and other objects after an event has
1294 * been retracted.
1295 *
1296 */
1297 rule "${policyName}.EVENT.MANAGER.CLEANUP"
1298     when
1299         $manager : ControlLoopEventManager( $clName : getClosedLoopControlName(), $requestId : getRequestID() )
1300         $operations : LinkedList()
1301                         from collect( ControlLoopOperationManager( onset.closedLoopControlName == $clName, 
1302                             onset.getRequestId() == $requestId ) )
1303         $timers : LinkedList()
1304                         from collect( ControlLoopTimer( closedLoopControlName == $clName, 
1305                             requestID == $requestId.toString() ) )
1306         $locks : LinkedList()
1307                         from collect( TargetLock (requestID == $requestId) )
1308         not( VirtualControlLoopEvent( closedLoopControlName == $clName, requestId == $requestId ) )
1309     then
1310     
1311     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1312     logger.info("{}: {}", $clName, drools.getRule().getName());
1313
1314     logger.debug("{}: {}: manager={} timers={} operations={}", 
1315               $clName, drools.getRule().getName(),
1316               $manager, $timers.size(), $operations.size());
1317     
1318     //
1319     // Retract EVERYTHING
1320     //
1321     retract($manager);
1322     
1323     for(Object manager: $operations) {
1324         retract((ControlLoopOperationManager) manager);
1325     }
1326     for(Object timer: $timers) {
1327         retract((ControlLoopTimer) timer);
1328     }
1329     for(Object lock: $locks) {
1330         TargetLock tgt = (TargetLock) lock;
1331         //
1332         // Ensure we release the lock
1333         //
1334         PolicyGuard.unlockTarget(tgt);
1335         retract(tgt);
1336     }
1337 end
1338
1339 /*
1340 *
1341 * This rule will clean up any rogue onsets where there is no 
1342 * ControlLoopParams object corresponding to the onset event.
1343 *
1344 */
1345 rule "${policyName}.EVENT.CLEANUP"
1346     when
1347         $event : VirtualControlLoopEvent( $clName: closedLoopControlName )
1348         not ( Params( getClosedLoopControlName() == $clName) )
1349     then
1350  
1351     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1352     logger.info("{}: {}", $clName, drools.getRule().getName());
1353     logger.debug("{}: {}: orphan onset event={}", 
1354                 $clName, drools.getRule().getName(), $event);
1355
1356     retract($event);
1357 end
1358
1359 /*
1360 *
1361 * When rules are deleted, the associated Params (and its subordinate objects)
1362 * remain in working memory, because there are no longer any rules to clean
1363 * them up.  However, ANY time new rules are loaded, this rule will trigger
1364 * a clean-up of ALL Params, regardless of their name & yaml, thus removing
1365 * any that no longer have associated rules.
1366 * This has a higher salience so that we immediately check Params when the
1367 * rules change, before processing any events.
1368 *
1369 */
1370 rule "${policyName}.PARAMS.CHECKUP"
1371     salience 2
1372     when
1373         Params( $clName: closedLoopControlName, $yaml: controlLoopYaml )
1374     then
1375  
1376     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1377     logger.info("{}: {} : YAML=[{}]", $clName, drools.getRule().getName(), $yaml);
1378     
1379     ParamsCleaner cleaner = new ParamsCleaner();
1380     cleaner.setClosedLoopControlName($clName);
1381     cleaner.setControlLoopYaml($yaml);
1382     
1383     insert(cleaner);
1384 end
1385
1386 /*
1387 *
1388 * This rule removes "cleaner" objects for rules that are still active, thus
1389 * preventing the associated Params objects from being removed.  Any cleaners
1390 * that are left after this rule has fired will cause their corresponding Params
1391 * to be removed.
1392 * This has a higher salience so that we discard the cleaner before it has
1393 * a chance to force the removal of the associated Params.
1394 *
1395 */
1396 rule "${policyName}.CLEANER.ACTIVE"
1397     salience 2
1398     when
1399         $cleaner: ParamsCleaner( getClosedLoopControlName() == "${closedLoopControlName}", 
1400             getControlLoopYaml() == "${controlLoopYaml}" )
1401     then
1402  
1403     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1404     logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(), 
1405         $cleaner.getControlLoopYaml());
1406     
1407     retract($cleaner);
1408 end
1409
1410 /*
1411 *
1412 * This rule removes Params objects that no longer have associated rules; if a
1413 * Params still had associated rules, then the cleaner would have been removed
1414 * by those rules and thus this rule would not fire.
1415 * This has a higher salience so that we remove old Params before it causes any
1416 * events to be processed.
1417 *
1418 */
1419 rule "${policyName}.PARAMS.CLEANUP"
1420     salience 1
1421     when
1422         $params: Params( $clName: closedLoopControlName, $yaml: controlLoopYaml )
1423         ParamsCleaner( getClosedLoopControlName() == $clName, getControlLoopYaml() == $yaml )
1424     then
1425  
1426     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1427     logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(), 
1428         $params.getControlLoopYaml());
1429     
1430     retract($params);
1431     
1432     // Note: the cleaner may be needed for cleaning additional params, thus
1433     // we do not retract it here - we'll leave that to another rule
1434 end
1435
1436 /*
1437 *
1438 * This rule removes "cleaner" objects when they're no longer needed.
1439 *
1440 */
1441 rule "${policyName}.CLEANER.CLEANUP"
1442     when
1443         $cleaner: ParamsCleaner( )
1444     then
1445  
1446     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1447     logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(), 
1448         $cleaner.getControlLoopYaml());
1449     
1450     retract($cleaner);
1451 end