87a477406249cdf033bf3b0ab215fcc4b0c7cfed
[policy/drools-applications.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.controlloop;
22
23 import org.onap.policy.controlloop.VirtualControlLoopEvent;
24 import org.onap.policy.controlloop.VirtualControlLoopNotification;
25 import org.onap.policy.controlloop.ControlLoopEventStatus;
26 import org.onap.policy.controlloop.ControlLoopNotificationType;
27 import org.onap.policy.controlloop.ControlLoopLogger;
28 import org.onap.policy.controlloop.policy.PolicyResult;
29 import org.onap.policy.controlloop.policy.ControlLoopPolicy;
30 import org.onap.policy.controlloop.policy.Policy;
31 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager;
32 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager.NEW_EVENT_STATUS;
33 import org.onap.policy.controlloop.eventmanager.ControlLoopOperationManager;
34 import org.onap.policy.controlloop.actor.so.SOActorServiceProvider;
35 import org.onap.policy.aai.AaiNqResponseWrapper;
36 import org.onap.policy.appc.Request;
37 import org.onap.policy.appc.Response;
38 import org.onap.policy.appc.CommonHeader;
39 import org.onap.policy.appclcm.LcmRequestWrapper;
40 import org.onap.policy.appclcm.LcmResponseWrapper;
41 import org.onap.policy.appclcm.LcmRequest;
42 import org.onap.policy.appclcm.LcmResponse;
43 import org.onap.policy.appclcm.LcmCommonHeader;
44 import org.onap.policy.vfc.VFCRequest;
45 import org.onap.policy.vfc.VFCResponse;
46 import org.onap.policy.vfc.VFCManager;
47 import org.onap.policy.so.SOManager;
48 import org.onap.policy.so.SORequest;
49 import org.onap.policy.so.SORequestStatus;
50 import org.onap.policy.so.SORequestDetails;
51 import org.onap.policy.so.SOModelInfo;
52 import org.onap.policy.so.SOCloudConfiguration;
53 import org.onap.policy.so.SORequestInfo;
54 import org.onap.policy.so.SORequestParameters;
55 import org.onap.policy.so.SORelatedInstanceListElement;
56 import org.onap.policy.so.SORelatedInstance;
57 import org.onap.policy.so.SOResponse;
58 import org.onap.policy.so.SOResponseWrapper;
59 import org.onap.policy.guard.PolicyGuard;
60 import org.onap.policy.guard.PolicyGuard.LockResult;
61 import org.onap.policy.guard.TargetLock;
62 import org.onap.policy.guard.GuardResult;
63 import org.onap.policy.guard.PolicyGuardRequest;
64 import org.onap.policy.guard.PolicyGuardResponse;
65 import org.onap.policy.guard.PolicyGuardXacmlRequestAttributes;
66 import org.onap.policy.guard.PolicyGuardXacmlHelper;
67
68 import org.yaml.snakeyaml.Yaml;
69 import org.yaml.snakeyaml.constructor.Constructor;
70
71 import org.slf4j.LoggerFactory;
72 import org.slf4j.Logger;
73
74 import java.time.Instant;
75 import java.util.LinkedList;
76 import java.util.Iterator;
77
78 import org.onap.policy.drools.system.PolicyEngine;
79
80 /*
81  * This structure mimics the Params structure.
82  * Its only purpose is to allow management of
83  * rules by the PAP component..
84  * It has no use at runtime since the rules go by
85  * Params for matching purposes.
86  */
87 declare PapParams
88   closedLoopControlName : String
89   controlLoopYaml : String
90 end
91
92 /*
93  * Control Loop Identity
94  */
95 declare Params
96   closedLoopControlName : String
97   controlLoopYaml : String
98 end
99
100 /*
101  * Used to clean up Params that no longer have associated rules.
102  */
103 declare ParamsCleaner
104   closedLoopControlName : String
105   controlLoopYaml : String
106 end
107
108 /*
109  * This object is to provide support for timeouts
110  * due to a bug in drools' built-in timers
111  */
112 declare ControlLoopTimer
113     closedLoopControlName : String
114     requestID : String
115     delay : String
116     expired : boolean
117     //timerType is the type of timer: either "ClosedLoop" or "Operation"
118     timerType : String
119 end
120
121 /*
122 *
123 * Called once and only once to insert the parameters into working memory for this Closed Loop policy.
124 * This has a higher salience so we can ensure that the Params is created before we have a chance to
125 * discard any events.
126 *
127 */
128 rule "${policyName}.SETUP"
129     salience 1
130     when
131         not( Params( getClosedLoopControlName() == "${closedLoopControlName}", getControlLoopYaml() == "${controlLoopYaml}" ) )
132     then
133     
134     Params params = new Params();
135     params.setClosedLoopControlName("${closedLoopControlName}");
136     params.setControlLoopYaml("${controlLoopYaml}");
137     insert(params);
138
139     // Note: globals have bad behavior when persistence is used,
140     //       hence explicitly getting the logger vs using a global
141     
142     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
143     logger.info("{}: {} : YAML=[{}]", params.getClosedLoopControlName(), drools.getRule().getName(), params.getControlLoopYaml());
144 end
145
146 /*
147 *
148 * This rule responds to DCAE Events where there is no manager yet. Either it is
149 * the first ONSET, or a subsequent badly formed Event (i.e. Syntax error, or is-closed-loop-disabled)
150 *
151 */
152 rule "${policyName}.EVENT"
153     when
154         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
155         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
156         not ( ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() ) )
157     then
158  
159     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
160     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
161     
162     try {
163       
164         //
165         // Check the event, because we need it to not be null when
166         // we create the ControlLoopEventManager. The ControlLoopEventManager
167         // will do extra syntax checking as well check if the closed loop is disabled.
168         //
169         if ($event.getRequestId() == null) {
170             VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
171             notification.setNotification(ControlLoopNotificationType.REJECTED);
172             notification.setFrom("policy");
173             notification.setMessage("Missing requestId");
174             notification.setPolicyName(drools.getRule().getName());
175             notification.setPolicyScope("${policyScope}");
176             notification.setPolicyVersion("${policyVersion}");
177             
178             //
179             // Let interested parties know
180             //
181             PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
182             
183             //
184             // Retract it from memory
185             //
186             retract($event);
187         } else if ($event.getClosedLoopEventStatus() != ControlLoopEventStatus.ONSET) {
188                 throw new ControlLoopException($event.getClosedLoopEventStatus() + " received with no prior onset");
189         } else {
190             //
191             // Create an EventManager
192             //
193             ControlLoopEventManager manager = new ControlLoopEventManager($params.getClosedLoopControlName(), $event.getRequestId());
194             //
195             // Determine if EventManager can actively process the event (i.e. syntax, is_closed_loop_disabled checks etc.)
196             //
197             VirtualControlLoopNotification notification = manager.activate($params.getControlLoopYaml(), $event);
198             notification.setFrom("pdp-0001-controller=controlloop"); // Engine.getInstanceName()
199             notification.setPolicyName(drools.getRule().getName());
200             notification.setPolicyScope("${policyScope}");
201             notification.setPolicyVersion("${policyVersion}");
202             //
203             // Are we actively pursuing this event?
204             //
205             if (notification.getNotification() == ControlLoopNotificationType.ACTIVE) {
206                 //
207                 // Insert Event Manager into memory, this will now kick off processing.
208                 //
209                 insert(manager);
210                 //
211                 // Let interested parties know
212                 //
213                 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
214                 //
215                 // Setup the Overall Control Loop timer
216                 //
217                 ControlLoopTimer clTimer = new ControlLoopTimer();
218                 clTimer.setTimerType("ClosedLoop");
219                 clTimer.setClosedLoopControlName($event.getClosedLoopControlName());
220                 clTimer.setRequestID($event.getRequestId().toString());
221                 clTimer.setDelay(manager.getControlLoopTimeout(1500) + "s");
222                 //
223                 // Insert it
224                 //
225                 insert(clTimer);
226             } else {
227                 //
228                 // Let interested parties know
229                 //
230                 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
231                 //
232                 // Retract it from memory
233                 //
234                 retract($event);
235             }
236             
237             //
238             // Now that the manager is inserted into Drools working memory, we'll wait for
239             // another rule to fire in order to continue processing. This way we can also
240             // then screen for additional ONSET and ABATED events for this RequestId.
241             //
242         }
243     } catch (Exception e) {
244         logger.warn("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName(), e);
245         
246         VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
247         notification.setNotification(ControlLoopNotificationType.REJECTED);
248         notification.setMessage("Exception occurred: " + e.getMessage());
249         notification.setPolicyName(drools.getRule().getName());
250         notification.setPolicyScope("${policyScope}");
251         notification.setPolicyVersion("${policyVersion}");
252         //
253         //
254         //
255         PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
256         //
257         // Retract the event
258         //
259         retract($event);
260     }
261 end
262
263 /*
264 *
265 * This rule happens when we got a valid ONSET, closed loop is enabled and an Event Manager
266 * is now created. We can start processing the yaml specification via the Event Manager.
267 *
268 */
269 rule "${policyName}.EVENT.MANAGER"
270     when
271         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
272         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
273         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
274         $clTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "ClosedLoop", !expired )
275     then
276
277     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
278     logger.info("{}: {}: event={} manager={} clTimer={}", 
279                 $params.getClosedLoopControlName(), drools.getRule().getName(),
280                 $event, $manager, $clTimer);
281     
282     try {
283             //
284             // Check which event this is.
285             //
286             ControlLoopEventManager.NEW_EVENT_STATUS eventStatus = $manager.onNewEvent($event);
287             //
288             // Check what kind of event this is
289             //
290             if (eventStatus == NEW_EVENT_STATUS.SUBSEQUENT_ONSET) {
291                 //
292                 // We don't care about subsequent onsets
293                 //
294                 logger.info("{}: {}: subsequent onset", 
295                             $params.getClosedLoopControlName(), drools.getRule().getName());
296                 retract($event);
297                 return;
298             }
299             if (eventStatus == NEW_EVENT_STATUS.SYNTAX_ERROR) {
300                 //
301                 // Ignore any bad syntax events
302                 //
303                 logger.warn("{}: {}: syntax error", 
304                             $params.getClosedLoopControlName(), drools.getRule().getName());
305                 retract($event);
306                 return;
307             }
308             //
309             // We only want the initial ONSET event in memory,
310             // all the other events need to be retracted to support
311             // cleanup and avoid the other rules being fired for this event.
312             //
313             if (eventStatus != NEW_EVENT_STATUS.FIRST_ONSET) {
314                 logger.warn("{}: {}: not first onset", 
315                             $params.getClosedLoopControlName(), drools.getRule().getName());
316                 retract($event);
317             }
318             
319             logger.debug("{}: {}: target={}", $params.getClosedLoopControlName(), 
320                          drools.getRule().getName(), $event.getTarget());
321             //
322             // Now start seeing if we need to process this event
323             //
324
325         //
326         // Check if this is a Final Event
327         //
328         VirtualControlLoopNotification notification = $manager.isControlLoopFinal();
329     
330     
331         if (notification != null) {
332             //
333             // Its final, but are we waiting for abatement?
334             //
335             if ($manager.getNumAbatements() > 0) {
336                 logger.info("{}: {}: abatement received for {}.  Closing the control loop", 
337                             $params.getClosedLoopControlName(), drools.getRule().getName(), 
338                             $event.getRequestId());
339                 
340                 /// DB Write---end event processing for this RequestId()
341                 $manager.commitAbatement("Event Abated","Closed");
342                 
343                 notification.setFrom("policy");
344                 notification.setPolicyName(drools.getRule().getName());
345                 notification.setPolicyScope("${policyScope}");
346                 notification.setPolicyVersion("${policyVersion}");
347                 //
348                 // In this case, we are done
349                 //
350                 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
351                 //
352                 // Unlock the target
353                 //
354                 TargetLock lock = $manager.unlockCurrentOperation();
355                 if (lock != null) {
356                     logger.debug("{}: {}: retracting lock=", $params.getClosedLoopControlName(), 
357                                  drools.getRule().getName(), lock);
358                     retract(lock);
359                 }
360                 //
361                 // Retract everything from memory
362                 //
363                 logger.info("{}: {}: retracting onset, manager, and timer", 
364                             $params.getClosedLoopControlName(), drools.getRule().getName());
365                 
366                 retract($manager.getOnsetEvent());
367                 retract($manager);
368                 retract($clTimer);
369                 //
370                 // TODO - what if we get subsequent Events for this RequestId?
371                 // By default, it will all start over again. May be confusing for Ruby.
372                 // Or, we could track this and then subsequently ignore the events
373                 //
374             } else {
375                 //
376                 // Check whether we need to wait for abatement
377                 //
378                 if ($manager.getProcessor().getControlLoop().getAbatement() == true && notification.getNotification() == ControlLoopNotificationType.FINAL_SUCCESS) {
379                   logger.info("{}: {}: waiting for abatement ..", 
380                               $params.getClosedLoopControlName(), drools.getRule().getName());
381                 } else {
382                   logger.info("{}: {}: no abatement expect for {}.  Closing the control loop", 
383                               $params.getClosedLoopControlName(), drools.getRule().getName(), 
384                               $event.getRequestId());
385                   
386                   notification.setFrom("policy");
387                   notification.setPolicyName(drools.getRule().getName());
388                   notification.setPolicyScope("${policyScope}");
389                   notification.setPolicyVersion("${policyVersion}");
390                   
391                   //
392                   // In this case, we are done
393                   //
394                   PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
395                   //
396                   // Unlock the target
397                   //
398                   TargetLock lock = $manager.unlockCurrentOperation();
399                   if (lock != null) {
400                       logger.debug("{}: {}: retracting lock=", $params.getClosedLoopControlName(), 
401                                   drools.getRule().getName(), lock);
402                       retract(lock);
403                   }
404                   //
405                   // Retract everything from memory
406                   //
407                   logger.info("{}: {}: retracting onset, manager, and timer", 
408                               $params.getClosedLoopControlName(), drools.getRule().getName());
409                   
410                   retract($manager.getOnsetEvent());
411                   retract($manager);
412                   retract($clTimer);
413                 }
414             }
415         } else {
416             //
417             // NOT final, so let's ask for the next operation
418             //
419             ControlLoopOperationManager operation = $manager.processControlLoop();
420             if (operation != null) {
421               //
422               // Let's ask for a lock right away
423               //
424               LockResult<GuardResult, TargetLock> result = $manager.lockCurrentOperation();
425               logger.info("{}: {}: guard lock acquired={}", 
426                             $params.getClosedLoopControlName(), drools.getRule().getName(), 
427                             result.getB());
428               if (result.getA().equals(GuardResult.LOCK_ACQUIRED)) {
429                 //
430                 // insert the operation into memory
431                 //
432                 insert(operation);
433                 
434                 //
435                 // insert operation timeout object
436                 //
437                 ControlLoopTimer opTimer = new ControlLoopTimer();
438                 opTimer.setTimerType("Operation");
439                 opTimer.setClosedLoopControlName($event.getClosedLoopControlName());
440                 opTimer.setRequestID($event.getRequestId().toString());
441                 opTimer.setDelay(operation.getOperationTimeout().toString() + "s");
442                 insert(opTimer);
443               
444                 //
445                 // Insert lock into memory
446                 //
447                 insert(result.getB());
448               }
449               else {
450                 logger.debug("The target resource {} is already processing",
451                                           $event.getAai().get($event.getTarget()));
452                 notification = new VirtualControlLoopNotification($event);
453                         notification.setNotification(ControlLoopNotificationType.REJECTED);
454                         notification.setMessage("The target " + $event.getAai().get($event.getTarget()) + " is already locked");
455                         notification.setFrom("policy");
456                         notification.setPolicyName(drools.getRule().getName());
457                         notification.setPolicyScope("${policyScope}");
458                         notification.setPolicyVersion("${policyVersion}");
459       
460                         PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);                      
461                 
462                 retract($event);
463                 retract($manager);
464                 retract($clTimer);
465                 
466                 if(result.getB() != null) {
467                      retract(result.getB());
468                 }
469               }
470               logger.info("{}: {}: starting operation={}", 
471                           $params.getClosedLoopControlName(), drools.getRule().getName(), 
472                           operation);
473             } else {
474                 //
475                 // Probably waiting for abatement
476                 //
477               logger.info("{}: {}: no operation, probably waiting for abatement", 
478                           $params.getClosedLoopControlName(), drools.getRule().getName());
479             }
480         }
481     } catch (Exception e) {
482         logger.warn("{}: {}: unexpected", 
483                   $params.getClosedLoopControlName(), 
484                   drools.getRule().getName(), e);
485
486         VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
487             notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
488             notification.setMessage(e.getMessage());
489             notification.setFrom("policy");
490             notification.setPolicyName(drools.getRule().getName());
491             notification.setPolicyScope("${policyScope}");
492             notification.setPolicyVersion("${policyVersion}");
493   
494                 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);                      
495                 
496         retract($event);
497         retract($manager);
498         retract($clTimer);
499     }
500         
501 end
502
503 /*
504 *
505 * Guard Permitted, let's send request to the actor.
506 *
507 */
508 rule "${policyName}.EVENT.MANAGER.OPERATION.LOCKED.GUARD_PERMITTED"
509     when
510         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
511         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
512         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
513         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId(), "Permit".equalsIgnoreCase(getGuardApprovalStatus()) )
514         $lock : TargetLock (requestID == $event.getRequestId())
515         $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
516     then
517
518     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
519     logger.info("{}: {}: event={} manager={} operation={} lock={}", 
520                 $params.getClosedLoopControlName(), drools.getRule().getName(),
521                 $event, $manager, $operation, $lock);    
522
523     Object request = null;
524     boolean caughtException = false;
525     
526     try {
527         request = $operation.startOperation($event);
528         
529             if (request != null) {
530               logger.debug("{}: {}: starting operation ..", 
531                            $params.getClosedLoopControlName(), drools.getRule().getName());
532               //
533               // Tell interested parties we are performing this Operation
534               //
535               VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
536               notification.setNotification(ControlLoopNotificationType.OPERATION);
537               notification.setMessage($operation.getOperationMessage());
538               notification.setHistory($operation.getHistory());
539               notification.setFrom("policy");
540               notification.setPolicyName(drools.getRule().getName());
541               notification.setPolicyScope("${policyScope}");
542               notification.setPolicyVersion("${policyVersion}");
543               
544               PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
545               
546               switch ($operation.policy.getActor()){
547                   
548                   case "APPC":
549               
550                       if (request instanceof Request) {
551                           PolicyEngine.manager.deliver("APPC-CL", request);
552                       }
553                       else if (request instanceof LcmRequestWrapper) {
554                           PolicyEngine.manager.deliver("APPC-LCM-READ", request);
555                       }
556                       break;
557                   case "SO":
558                       // at this point the AAI named query request should have already been made, the response recieved and used
559                       // in the construction of the SO Request which is stored in operationRequest
560                       
561                       if(request instanceof SORequest) {
562                           // Call SO. The response will be inserted into memory once it's received 
563                           SOActorServiceProvider.sendRequest($event.getRequestId().toString(), drools.getWorkingMemory(), request);                        
564                       }
565                       break;
566                   case "VFC":
567                       if (request instanceof VFCRequest) {
568                           // Start VFC thread
569                           Thread t = new Thread(new VFCManager(drools.getWorkingMemory(), (VFCRequest)request));
570                           t.start();
571                       }          
572                       break;
573               }
574             } else {
575               //
576               // What happens if its null?
577               //
578                 logger.warn("{}: {}: unexpected null operation request", 
579                           $params.getClosedLoopControlName(), 
580                           drools.getRule().getName());
581                 if ("SO".equals($operation.policy.getActor())) {
582                     retract($opTimer);
583                     retract($operation);
584                     modify($manager) {finishOperation($operation)};
585                 }
586                 else if ("vfc".equalsIgnoreCase($operation.policy.getActor())) {
587                     retract($opTimer);
588                     retract($operation);
589                     modify($manager) {finishOperation($operation)};
590                 }
591             }
592             
593     } catch (Exception e) {
594         String msg = e.getMessage();
595         logger.warn("{}: {}: operation={}:  AAI failure: {}", 
596                     $params.getClosedLoopControlName(), drools.getRule().getName(),
597                     $operation, msg, e);
598         $operation.setOperationHasException(msg);
599         
600         if(request != null) {
601                 //
602                 // Create a notification for it ("DB Write - end operation")
603                 //
604                 VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
605                 notification.setFrom("policy");
606                 notification.setPolicyName(drools.getRule().getName());
607                 notification.setPolicyScope("${policyScope}");
608                 notification.setPolicyVersion("${policyVersion}");
609                 notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
610                 notification.setMessage($operation.getOperationHistory());
611                 notification.setHistory($operation.getHistory());
612           
613             PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
614         }
615     
616         retract($opTimer);
617         retract($operation);
618         caughtException = true;
619     }
620     
621     // Having the modify statement in the catch clause doesn't work for whatever reason
622     if (caughtException) {
623         modify($manager) {finishOperation($operation)};
624     }
625 end
626
627
628 /*
629 *
630 * We were able to acquire a lock so now let's ask Xacml Guard whether 
631 * we are allowed to proceed with the request to the actor.
632 *
633 */
634 rule "${policyName}.EVENT.MANAGER.OPERATION.LOCKED.GUARD_NOT_YET_QUERIED"
635     when
636         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
637         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
638         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
639         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId(), getGuardApprovalStatus() == "NONE" )
640         $lock : TargetLock (requestID == $event.getRequestId())
641     then
642
643     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
644     logger.info("{}: {}: event={} manager={} operation={} lock={}", 
645                 $params.getClosedLoopControlName(), drools.getRule().getName(),
646                 $event, $manager, $operation, $lock);
647     
648     //
649     // Sending notification that we are about to query Guard ("DB write - start operation")
650     //
651     VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
652     notification.setNotification(ControlLoopNotificationType.OPERATION);
653     notification.setMessage("Sending guard query for " + $operation.policy.getActor() + " " + $operation.policy.getRecipe());
654     notification.setHistory($operation.getHistory());
655     notification.setFrom("policy");
656     notification.setPolicyName(drools.getRule().getName());
657     notification.setPolicyScope("${policyScope}");
658     notification.setPolicyVersion("${policyVersion}");
659     
660     PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
661         
662     //
663     // Now send Guard Request to XACML Guard. In order to bypass the call to Guard, 
664     // just change guardEnabled to false.
665     // 
666     // In order to use REST XACML, provide a URL instead of "" as a second argument 
667     // to the CallGuardTask() and set the first argument to null 
668     // (instead of XacmlPdpEngine).
669     //
670     
671     // NOTE: The environment properties uses "guard.disabled" but the boolean is guardEnabled
672     boolean guardEnabled = "false".equalsIgnoreCase(PolicyEngine.manager.getEnvironmentProperty("guard.disabled"));
673     
674     if(guardEnabled){
675     
676         Thread t = new Thread(new org.onap.policy.guard.CallGuardTask(
677                                                         drools.getWorkingMemory(),
678                                                         $event.getClosedLoopControlName(),
679                                                         $operation.policy.getActor().toString(),
680                                                         $operation.policy.getRecipe(),
681                                                         $operation.getTargetEntity(),
682                                                         $event.getRequestId().toString(),
683                                                         () -> {
684                                                             AaiNqResponseWrapper resp = $manager.getNqVserverFromAai();
685                                                             return(resp == null ? null : resp.countVfModules());
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 : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
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 : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
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 : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
884         $lock : TargetLock (requestID == $event.getRequestId())
885         $response : LcmResponseWrapper( getBody().getCommonHeader().getRequestId() == $event.getRequestId() )
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 : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
989         $lock : TargetLock (requestID == $event.getRequestId())
990         $response : SOResponseWrapper(requestID.toString() == $event.getRequestId().toString() )
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 : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), timerType == "Operation", !expired )
1073         $lock : TargetLock (requestID == $event.getRequestId())
1074                 $response : VFCResponse( requestId.toString() == $event.getRequestId().toString() )     
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 manages a single timer.
1136 * Due to a bug in the drools code, the drools timer needed to be split from most of the objects in the when clause
1137 *
1138 */
1139 rule "${policyName}.TIMER.FIRED"
1140     timer (expr: $timeout)
1141     when
1142         $timer : ControlLoopTimer($timeout : delay, !expired)
1143     then
1144         Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1145         logger.info("This is ${policyName}.TIMER.FIRED");
1146         modify($timer){setExpired(true)};
1147     end
1148
1149 /*
1150 *
1151 * This is the timer that manages the timeout for an individual operation.
1152 *
1153 */
1154 rule "${policyName}.EVENT.MANAGER.OPERATION.TIMEOUT"
1155     when
1156         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
1157         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
1158         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
1159         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
1160         $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), expired, timerType == "Operation" )
1161         $lock : TargetLock (requestID == $event.getRequestId())
1162     then
1163     
1164     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1165     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
1166     logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={}", 
1167                 $params.getClosedLoopControlName(), drools.getRule().getName(),
1168                 $event, $manager, $operation, $lock, $operation, $opTimer);
1169     
1170     //
1171     // Tell it its timed out
1172     //
1173     $operation.setOperationHasTimedOut();
1174     //
1175     // Create a notification for it ("DB Write - end operation")
1176     //
1177     VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
1178     notification.setFrom("policy");
1179     notification.setPolicyName(drools.getRule().getName());
1180     notification.setPolicyScope("${policyScope}");
1181     notification.setPolicyVersion("${policyVersion}");
1182     notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
1183     notification.setMessage($operation.getOperationHistory());
1184     notification.setHistory($operation.getHistory());
1185     //
1186     // Let interested parties know
1187     //
1188     PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
1189     //
1190     // Get rid of the timer
1191     //
1192     retract($opTimer);
1193     //
1194     // Ensure the operation is complete
1195     //
1196     if ($operation.isOperationComplete() == true) {
1197         //
1198         // It is complete, remove it from memory
1199         //
1200         retract($operation);
1201         //
1202         // Complete the operation
1203         //
1204         modify($manager) {finishOperation($operation)};
1205     } else {
1206         //
1207         // Just doing this will kick off the LOCKED rule again
1208         //
1209         modify($operation) {};
1210     }
1211 end
1212
1213 /*
1214 *
1215 * This is the timer that manages the overall control loop timeout.
1216 *
1217 */
1218 rule "${policyName}.EVENT.MANAGER.TIMEOUT"
1219     when
1220         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
1221         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
1222         $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId() )
1223         $clTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(), requestID == $event.getRequestId().toString(), expired, timerType == "ClosedLoop" )
1224     then
1225     
1226     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1227     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
1228
1229     logger.debug("{}: {}: event={}", 
1230               $params.getClosedLoopControlName(), drools.getRule().getName(),
1231               $event);
1232     //
1233     // Tell the Event Manager it has timed out
1234     //
1235     VirtualControlLoopNotification notification = $manager.setControlLoopTimedOut();
1236     if (notification != null) {
1237         notification.setFrom("policy");
1238         notification.setPolicyName(drools.getRule().getName());
1239         notification.setPolicyScope("${policyScope}");
1240         notification.setPolicyVersion("${policyVersion}");
1241         //
1242         // Let interested parties know
1243         //
1244         PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
1245     }
1246     //
1247     // Retract the event
1248     //
1249     retract($event);
1250 end
1251
1252 /*
1253 *
1254 * This rule cleans up the manager and other objects after an event has
1255 * been retracted.
1256 *
1257 */
1258 rule "${policyName}.EVENT.MANAGER.CLEANUP"
1259     when
1260         $manager : ControlLoopEventManager( $clName : getClosedLoopControlName(), $requestId : getRequestID() )
1261         $operations : LinkedList()
1262                         from collect( ControlLoopOperationManager( onset.closedLoopControlName == $clName, onset.getRequestId() == $requestId ) )
1263         $timers : LinkedList()
1264                         from collect( ControlLoopTimer( closedLoopControlName == $clName, requestID == $requestId.toString() ) )
1265         $locks : LinkedList()
1266                         from collect( TargetLock (requestID == $requestId) )
1267         not( VirtualControlLoopEvent( closedLoopControlName == $clName, requestId == $requestId ) )
1268     then
1269     
1270     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1271     logger.info("{}: {}", $clName, drools.getRule().getName());
1272
1273     logger.debug("{}: {}: manager={} timers={} operations={}", 
1274               $clName, drools.getRule().getName(),
1275               $manager, $timers.size(), $operations.size());
1276     
1277     //
1278     // Retract EVERYTHING
1279     //
1280     retract($manager);
1281     
1282     for(Object manager: $operations) {
1283         retract((ControlLoopOperationManager) manager);
1284     }
1285     for(Object timer: $timers) {
1286         retract((ControlLoopTimer) timer);
1287     }
1288     for(Object lock: $locks) {
1289         TargetLock tgt = (TargetLock) lock;
1290         //
1291         // Ensure we release the lock
1292         //
1293         PolicyGuard.unlockTarget(tgt);
1294         retract(tgt);
1295     }
1296 end
1297
1298 /*
1299 *
1300 * This rule will clean up any rogue onsets where there is no 
1301 * ControlLoopParams object corresponding to the onset event.
1302 *
1303 */
1304 rule "${policyName}.EVENT.CLEANUP"
1305     when
1306         $event : VirtualControlLoopEvent( $clName: closedLoopControlName )
1307         not ( Params( getClosedLoopControlName() == $clName) )
1308     then
1309  
1310     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1311     logger.info("{}: {}", $clName, drools.getRule().getName());
1312     logger.debug("{}: {}: orphan onset event={}", 
1313                 $clName, drools.getRule().getName(), $event);
1314
1315     retract($event);
1316 end
1317
1318 /*
1319 *
1320 * When rules are deleted, the associated Params (and its subordinate objects)
1321 * remain in working memory, because there are no longer any rules to clean
1322 * them up.  However, ANY time new rules are loaded, this rule will trigger
1323 * a clean-up of ALL Params, regardless of their name & yaml, thus removing
1324 * any that no longer have associated rules.
1325 * This has a higher salience so that we immediately check Params when the
1326 * rules change, before processing any events.
1327 *
1328 */
1329 rule "${policyName}.PARAMS.CHECKUP"
1330     salience 2
1331     when
1332         Params( $clName: closedLoopControlName, $yaml: controlLoopYaml )
1333     then
1334  
1335     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1336     logger.info("{}: {} : YAML=[{}]", $clName, drools.getRule().getName(), $yaml);
1337     
1338     ParamsCleaner cleaner = new ParamsCleaner();
1339     cleaner.setClosedLoopControlName($clName);
1340     cleaner.setControlLoopYaml($yaml);
1341     
1342     insert(cleaner);
1343 end
1344
1345 /*
1346 *
1347 * This rule removes "cleaner" objects for rules that are still active, thus
1348 * preventing the associated Params objects from being removed.  Any cleaners
1349 * that are left after this rule has fired will cause their corresponding Params
1350 * to be removed.
1351 * This has a higher salience so that we discard the cleaner before it has
1352 * a chance to force the removal of the associated Params.
1353 *
1354 */
1355 rule "${policyName}.CLEANER.ACTIVE"
1356     salience 2
1357     when
1358         $cleaner: ParamsCleaner( getClosedLoopControlName() == "${closedLoopControlName}", getControlLoopYaml() == "${controlLoopYaml}" )
1359     then
1360  
1361     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1362     logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(), $cleaner.getControlLoopYaml());
1363     
1364     retract($cleaner);
1365 end
1366
1367 /*
1368 *
1369 * This rule removes Params objects that no longer have associated rules; if a
1370 * Params still had associated rules, then the cleaner would have been removed
1371 * by those rules and thus this rule would not fire.
1372 * This has a higher salience so that we remove old Params before it causes any
1373 * events to be processed.
1374 *
1375 */
1376 rule "${policyName}.PARAMS.CLEANUP"
1377     salience 1
1378     when
1379         $params: Params( $clName: closedLoopControlName, $yaml: controlLoopYaml )
1380         ParamsCleaner( getClosedLoopControlName() == $clName, getControlLoopYaml() == $yaml )
1381     then
1382  
1383     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1384     logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(), $params.getControlLoopYaml());
1385     
1386     retract($params);
1387     
1388     // Note: the cleaner may be needed for cleaning additional params, thus
1389     // we do not retract it here - we'll leave that to another rule
1390 end
1391
1392 /*
1393 *
1394 * This rule removes "cleaner" objects when they're no longer needed.
1395 *
1396 */
1397 rule "${policyName}.CLEANER.CLEANUP"
1398     when
1399         $cleaner: ParamsCleaner( )
1400     then
1401  
1402     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1403     logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(), $cleaner.getControlLoopYaml());
1404     
1405     retract($cleaner);
1406 end