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