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