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