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