07c7e2086fff04ad83991f49fbbea8df211cf4e9
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2017 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.util.AAIException;
36 import org.onap.policy.appc.Request;
37 import org.onap.policy.appc.Response;
38 import org.onap.policy.appc.CommonHeader;
39 import org.onap.policy.appclcm.LCMRequestWrapper;
40 import org.onap.policy.appclcm.LCMResponseWrapper;
41 import org.onap.policy.appclcm.LCMRequest;
42 import org.onap.policy.appclcm.LCMResponse;
43 import org.onap.policy.appclcm.LCMCommonHeader;
44 import org.onap.policy.vfc.VFCRequest;
45 import org.onap.policy.vfc.VFCResponse;
46 import org.onap.policy.vfc.VFCManager;
47 import org.onap.policy.so.SOManager;
48 import org.onap.policy.so.SORequest;
49 import org.onap.policy.so.SORequestStatus;
50 import org.onap.policy.so.SORequestDetails;
51 import org.onap.policy.so.SOModelInfo;
52 import org.onap.policy.so.SOCloudConfiguration;
53 import org.onap.policy.so.SORequestInfo;
54 import org.onap.policy.so.SORequestParameters;
55 import org.onap.policy.so.SORelatedInstanceListElement;
56 import org.onap.policy.so.SORelatedInstance;
57 import org.onap.policy.so.SOResponse;
58 import org.onap.policy.so.SOResponseWrapper;
59 import org.onap.policy.guard.PolicyGuard;
60 import org.onap.policy.guard.PolicyGuard.LockResult;
61 import org.onap.policy.guard.TargetLock;
62 import org.onap.policy.guard.GuardResult;
63 import org.onap.policy.guard.PolicyGuardRequest;
64 import org.onap.policy.guard.PolicyGuardResponse;
65 import org.onap.policy.guard.PolicyGuardXacmlRequestAttributes;
66 import org.onap.policy.guard.PolicyGuardXacmlHelper;
67
68 import org.yaml.snakeyaml.Yaml;
69 import org.yaml.snakeyaml.constructor.Constructor;
70
71 import org.slf4j.LoggerFactory;
72 import org.slf4j.Logger;
73
74 import java.time.Instant;
75 import java.util.LinkedList;
76 import java.util.Iterator;
77
78 import org.onap.policy.drools.system.PolicyEngine;
79
80 /*
81  * This structure mimics the Params structure.
82  * Its only purpose is to allow management of
83  * rules by the PAP component..
84  * It has no use at runtime since the rules go by
85  * Params for matching purposes.
86  */
87 declare PapParams
88   closedLoopControlName : String
89   controlLoopYaml : String
90 end
91
92 /*
93  * Control Loop Identity
94  */
95 declare Params
96   closedLoopControlName : String
97   controlLoopYaml : String
98 end
99
100
101 /*
102  * Operation Timer
103  */
104 declare OperationTimer
105   closedLoopControlName : String
106   requestID : String
107   delay : String
108 end
109
110 /*
111  * Control Loop Timer
112  */
113 declare ControlLoopTimer
114   closedLoopControlName : String
115   requestID : String
116   delay : String
117 end
118
119 /*
120 *
121 * Called once and only once to insert the parameters into working memory for this Closed Loop policy.
122 *
123 */
124 rule "${policyName}.SETUP"
125     when
126     then
127     
128     Params params = new Params();
129     params.setClosedLoopControlName("${closedLoopControlName}");
130     params.setControlLoopYaml("${controlLoopYaml}");
131     insert(params);
132
133     // Note: globals have bad behavior when persistence is used,
134     //       hence explicitly getting the logger vs using a global
135     
136     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
137     logger.info("{}: {} : YAML=[{}]", params.getClosedLoopControlName(), drools.getRule().getName(), params.getControlLoopYaml());
138     
139     String sqlDbUsername = PolicyEngine.manager.getEnvironmentProperty("sql.db.username");
140     
141     String aaiUrl = PolicyEngine.manager.getEnvironmentProperty("aai.url");
142     String aaiUsername = PolicyEngine.manager.getEnvironmentProperty("aai.username");
143     String aaiPassword = PolicyEngine.manager.getEnvironmentProperty("aai.password");
144     
145     String soUrl =PolicyEngine.manager.getEnvironmentProperty("so.url");
146     String soUsername = PolicyEngine.manager.getEnvironmentProperty("so.username");
147     String soPassword = PolicyEngine.manager.getEnvironmentProperty("so.password");
148     
149     String vfcUrl =PolicyEngine.manager.getEnvironmentProperty("vfc.url");
150     String vfcUsername = PolicyEngine.manager.getEnvironmentProperty("vfc.username");
151     String vfcPassword = PolicyEngine.manager.getEnvironmentProperty("vfc.password");
152     
153     String guardUrl = PolicyEngine.manager.getEnvironmentProperty("guard.url");
154     String guardUsername = PolicyEngine.manager.getEnvironmentProperty("pdpx.username");
155     String guardPassword = PolicyEngine.manager.getEnvironmentProperty("pdpx.password");
156     String guardJdbcUrl = PolicyEngine.manager.getEnvironmentProperty("guard.jdbc.url");
157     String guardDisabled = PolicyEngine.manager.getEnvironmentProperty("guard.disabled");
158     
159     logger.info("{}: {} : AAI=[{}:{}]", params.getClosedLoopControlName(), drools.getRule().getName(), aaiUrl, aaiUsername);
160     logger.info("{}: {} : SO=[{}:{}]", params.getClosedLoopControlName(), drools.getRule().getName(), soUrl, soUsername);
161     logger.info("{}: {} : VFC=[{}:{}]", params.getClosedLoopControlName(), drools.getRule().getName(), vfcUrl, vfcUsername);
162     logger.info("{}: {} : GUARD=[{}:{}:{}:{}]", params.getClosedLoopControlName(), drools.getRule().getName(), guardUrl, guardUsername, guardJdbcUrl, guardDisabled);
163     logger.info("{}: {} : DB=[{}]", params.getClosedLoopControlName(), drools.getRule().getName(), sqlDbUsername);
164 end
165
166 /*
167 *
168 * This rule responds to DCAE Events where there is no manager yet. Either it is
169 * the first ONSET, or a subsequent badly formed Event (i.e. Syntax error, or is-closed-loop-disabled)
170 *
171 */
172 rule "${policyName}.EVENT"
173     when
174         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
175         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
176         not ( ControlLoopEventManager( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID ) )
177     then
178  
179     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
180     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
181     
182     try {
183       
184         //
185         // Check the event, because we need it to not be null when
186         // we create the ControlLoopEventManager. The ControlLoopEventManager
187         // will do extra syntax checking as well check if the closed loop is disabled.
188         //
189         if ($event.requestID == null) {
190             VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
191             notification.notification = ControlLoopNotificationType.REJECTED;
192             notification.from = "policy";
193             notification.message = "Missing requestID";
194             notification.policyName = drools.getRule().getName();
195             notification.policyScope = "${policyScope}";
196             notification.policyVersion = "${policyVersion}";
197             
198             //
199             // Let interested parties know
200             //
201             PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
202             
203             //
204             // Retract it from memory
205             //
206             retract($event);
207         } else if ($event.closedLoopEventStatus != ControlLoopEventStatus.ONSET) {
208                 throw new ControlLoopException($event.closedLoopEventStatus + " received with no prior onset");
209         } else {
210             //
211             // Create an EventManager
212             //
213             ControlLoopEventManager manager = new ControlLoopEventManager($params.getClosedLoopControlName(), $event.requestID);
214             //
215             // Determine if EventManager can actively process the event (i.e. syntax, is_closed_loop_disabled checks etc.)
216             //
217             VirtualControlLoopNotification notification = manager.activate($params.getControlLoopYaml(), $event);
218             notification.from = "pdp-0001-controller=controlloop"; // Engine.getInstanceName()
219             notification.policyName = drools.getRule().getName();
220             notification.policyScope = "${policyScope}";
221             notification.policyVersion = "${policyVersion}";
222             //
223             // Are we actively pursuing this event?
224             //
225             if (notification.notification == ControlLoopNotificationType.ACTIVE) {
226                 //
227                 // Insert Event Manager into memory, this will now kick off processing.
228                 //
229                 insert(manager);
230                 //
231                 // Let interested parties know
232                 //
233                 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
234                 //
235                 // Setup the Overall Control Loop timer
236                 //
237                 ControlLoopTimer clTimer = new ControlLoopTimer();
238                 clTimer.setClosedLoopControlName($event.closedLoopControlName);
239                 clTimer.setRequestID($event.requestID.toString());
240                 clTimer.setDelay(manager.getControlLoopTimeout(1500) + "s");
241                 //
242                 // Insert it
243                 //
244                 insert(clTimer);
245             } else {
246                 //
247                 // Let interested parties know
248                 //
249                 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
250                 //
251                 // Retract it from memory
252                 //
253                 retract($event);
254             }
255             
256             //
257             // Now that the manager is inserted into Drools working memory, we'll wait for
258             // another rule to fire in order to continue processing. This way we can also
259             // then screen for additional ONSET and ABATED events for this RequestID.
260             //
261         }
262     } catch (Exception e) {
263         logger.warn("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName(), e);
264         
265         VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
266         notification.notification = ControlLoopNotificationType.REJECTED;
267         notification.message = "Exception occurred: " + e.getMessage();
268         notification.policyName = drools.getRule().getName();
269         notification.policyScope = "${policyScope}";
270         notification.policyVersion = "${policyVersion}";
271         //
272         //
273         //
274         PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
275         //
276         // Retract the event
277         //
278         retract($event);
279     }
280 end
281
282 /*
283 *
284 * This rule happens when we got a valid ONSET, closed loop is enabled and an Event Manager
285 * is now created. We can start processing the yaml specification via the Event Manager.
286 *
287 */
288 rule "${policyName}.EVENT.MANAGER"
289     when
290         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
291         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
292         $manager : ControlLoopEventManager( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID )
293         $clTimer : ControlLoopTimer ( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID.toString() )
294     then
295
296     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
297     logger.info("{}: {}: event={} manager={} clTimer={}", 
298                 $params.getClosedLoopControlName(), drools.getRule().getName(),
299                 $event, $manager, $clTimer);
300     
301     try {
302             //
303             // Check which event this is.
304             //
305             ControlLoopEventManager.NEW_EVENT_STATUS eventStatus = $manager.onNewEvent($event);
306             //
307             // Check what kind of event this is
308             //
309             if (eventStatus == NEW_EVENT_STATUS.SUBSEQUENT_ONSET) {
310                 //
311                 // We don't care about subsequent onsets
312                 //
313                 logger.info("{}: {}: subsequent onset", 
314                             $params.getClosedLoopControlName(), drools.getRule().getName());
315                 retract($event);
316                 return;
317             }
318             if (eventStatus == NEW_EVENT_STATUS.SYNTAX_ERROR) {
319                 //
320                 // Ignore any bad syntax events
321                 //
322                 logger.warn("{}: {}: syntax error", 
323                             $params.getClosedLoopControlName(), drools.getRule().getName());
324                 retract($event);
325                 return;
326             }
327             //
328             // We only want the initial ONSET event in memory,
329             // all the other events need to be retracted to support
330             // cleanup and avoid the other rules being fired for this event.
331             //
332             if (eventStatus != NEW_EVENT_STATUS.FIRST_ONSET) {
333                 logger.warn("{}: {}: no first onset", 
334                             $params.getClosedLoopControlName(), drools.getRule().getName());
335                 retract($event);
336             }
337             
338             logger.debug("{}: {}: target={}", $params.getClosedLoopControlName(), 
339                          drools.getRule().getName(), $event.target);
340             //
341             // Now start seeing if we need to process this event
342             //
343
344         //
345         // Check if this is a Final Event
346         //
347         VirtualControlLoopNotification notification = $manager.isControlLoopFinal();
348     
349     
350         if (notification != null) {
351             //
352             // Its final, but are we waiting for abatement?
353             //
354             if ($manager.getNumAbatements() > 0) {
355                 logger.info("{}: {}: abatement received for {}.  Closing the control loop", 
356                             $params.getClosedLoopControlName(), drools.getRule().getName(), 
357                             $event.requestID);
358                 notification.from = "policy";
359                 notification.policyName = drools.getRule().getName();
360                 notification.policyScope = "${policyScope}";
361                 notification.policyVersion = "${policyVersion}";
362                 //
363                 // In this case, we are done
364                 //
365                 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
366                 //
367                 // Unlock the target
368                 //
369                 TargetLock lock = $manager.unlockCurrentOperation();
370                 if (lock != null) {
371                     logger.debug("{}: {}: retracting lock=", $params.getClosedLoopControlName(), 
372                                  drools.getRule().getName(), lock);
373                     retract(lock);
374                 }
375                 //
376                 // Retract everything from memory
377                 //
378                 logger.info("{}: {}: retracting onset, manager, and timer", 
379                             $params.getClosedLoopControlName(), drools.getRule().getName());
380                 
381                 retract($manager.getOnsetEvent());
382                 retract($manager);
383                 retract($clTimer);
384                 //
385                 // TODO - what if we get subsequent Events for this RequestID?
386                 // By default, it will all start over again. May be confusing for Ruby.
387                 // Or, we could track this and then subsequently ignore the events
388                 //
389             } else {
390                 //
391                 // Check whether we need to wait for abatement
392                 //
393                 if ($manager.getProcessor().getControlLoop().getAbatement() == true && notification.notification == ControlLoopNotificationType.FINAL_SUCCESS) {
394                   logger.info("{}: {}: waiting for abatement ..", 
395                               $params.getClosedLoopControlName(), drools.getRule().getName());
396                 } else {
397                   logger.info("{}: {}: no abatement expect for {}.  Closing the control loop", 
398                               $params.getClosedLoopControlName(), drools.getRule().getName(), 
399                               $event.requestID);
400                   
401                   notification.from = "policy";
402                   notification.policyName = drools.getRule().getName();
403                   notification.policyScope = "${policyScope}";
404                   notification.policyVersion = "${policyVersion}";
405                   
406                   //
407                   // In this case, we are done
408                   //
409                   PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
410                   //
411                   // Unlock the target
412                   //
413                   TargetLock lock = $manager.unlockCurrentOperation();
414                   if (lock != null) {
415                       logger.debug("{}: {}: retracting lock=", $params.getClosedLoopControlName(), 
416                                   drools.getRule().getName(), lock);
417                       retract(lock);
418                   }
419                   //
420                   // Retract everything from memory
421                   //
422                   logger.info("{}: {}: retracting onset, manager, and timer", 
423                               $params.getClosedLoopControlName(), drools.getRule().getName());
424                   
425                   retract($manager.getOnsetEvent());
426                   retract($manager);
427                   retract($clTimer);
428                 }
429             }
430         } else {
431             //
432             // NOT final, so let's ask for the next operation
433             //
434             ControlLoopOperationManager operation = $manager.processControlLoop();
435             if (operation != null) {
436               //
437               // Let's ask for a lock right away
438               //
439               LockResult<GuardResult, TargetLock> result = $manager.lockCurrentOperation();
440               logger.info("{}: {}: guard lock acquired={}", 
441                             $params.getClosedLoopControlName(), drools.getRule().getName(), 
442                             result.getB());
443               if (result.getA().equals(GuardResult.LOCK_ACQUIRED)) {
444                 //
445                 // insert the operation into memory
446                 //
447                 insert(operation);
448                 
449                 //
450                 // insert operation timeout object
451                 //
452                 OperationTimer opTimer = new OperationTimer();
453                 opTimer.setClosedLoopControlName($event.closedLoopControlName);
454                 opTimer.setRequestID($event.requestID.toString());
455                 opTimer.setDelay(operation.getOperationTimeout().toString() + "s");
456                 insert(opTimer);
457               
458                 //
459                 // Insert lock into memory
460                 //
461                 insert(result.getB());
462               }
463               else {
464                 logger.debug("The target resource {} is already processing",
465                                           $event.AAI.get($event.target));
466                 notification = new VirtualControlLoopNotification($event);
467                         notification.notification = ControlLoopNotificationType.REJECTED;
468                         notification.message = "The target " + $event.AAI.get($event.target) + " is already locked";
469                         notification.from = "policy";
470                         notification.policyName = drools.getRule().getName();
471                         notification.policyScope = "${policyScope}";
472                         notification.policyVersion = "${policyVersion}";
473       
474                         PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);                      
475                 
476                 retract($event);
477                 retract($manager);
478                 retract($clTimer);
479               }
480               logger.info("{}: {}: starting operation={}", 
481                           $params.getClosedLoopControlName(), drools.getRule().getName(), 
482                           operation);
483             } else {
484                 //
485                 // Probably waiting for abatement
486                 //
487               logger.info("{}: {}: no operation, probably waiting for abatement", 
488                           $params.getClosedLoopControlName(), drools.getRule().getName());
489             }
490         }
491     } catch (Exception e) {
492         logger.warn("{}: {}: unexpected", 
493                   $params.getClosedLoopControlName(), 
494                   drools.getRule().getName(), e);
495
496         VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
497             notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
498             notification.message = e.getMessage();
499             notification.from = "policy";
500             notification.policyName = drools.getRule().getName();
501             notification.policyScope = "${policyScope}";
502             notification.policyVersion = "${policyVersion}";
503   
504                 PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);                      
505                 
506         retract($event);
507         retract($manager);
508         retract($clTimer);
509     }
510         
511 end
512
513 /*
514 *
515 * Guard Permitted, let's send request to the actor.
516 *
517 */
518 rule "${policyName}.EVENT.MANAGER.OPERATION.LOCKED.GUARD_PERMITTED"
519     when
520         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
521         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
522         $manager : ControlLoopEventManager( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID )
523         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.closedLoopControlName, onset.requestID == $event.requestID, "Permit".equalsIgnoreCase(getGuardApprovalStatus()) )
524         $lock : TargetLock (requestID == $event.requestID)
525         $opTimer : OperationTimer( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID.toString() )
526     then
527
528     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
529     logger.info("{}: {}: event={} manager={} operation={} lock={}", 
530                 $params.getClosedLoopControlName(), drools.getRule().getName(),
531                 $event, $manager, $operation, $lock);    
532
533     Object request = null;
534     boolean caughtException = false;
535     try {
536         request = $operation.startOperation($event);
537     }
538     catch (AAIException e) {
539         String msg = e.getMessage();
540         logger.warn("{}: {}: operation={}:  AAI failure: {}", 
541                     $params.getClosedLoopControlName(), drools.getRule().getName(),
542                     $operation, msg, e);
543         $operation.setOperationHasException(msg);
544         retract($opTimer);
545         retract($operation);
546         caughtException = true;
547     }
548     
549     // Having the modify statement in the catch clause doesn't work for whatever reason
550     if (caughtException) {
551         modify($manager) {finishOperation($operation)};
552     }
553     else if (request != null) {
554       logger.debug("{}: {}: starting operation ..", 
555                    $params.getClosedLoopControlName(), drools.getRule().getName());
556       //
557       // Tell interested parties we are performing this Operation
558       //
559       VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
560       notification.notification = ControlLoopNotificationType.OPERATION;
561       notification.message = $operation.getOperationMessage();
562       notification.history = $operation.getHistory();
563       notification.from = "policy";
564       notification.policyName = drools.getRule().getName();
565       notification.policyScope = "${policyScope}";
566       notification.policyVersion = "${policyVersion}";
567       
568       PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
569       
570       switch ($operation.policy.getActor()){
571           
572           case "APPC":
573       
574               if (request instanceof Request) {
575                   PolicyEngine.manager.deliver("APPC-CL", request);
576               }
577               else if (request instanceof LCMRequestWrapper) {
578                   PolicyEngine.manager.deliver("APPC-LCM-READ", request);
579               }
580               break;
581           case "SO":
582               // at this point the AAI named query request should have already been made, the response recieved and used
583               // in the construction of the SO Request which is stored in operationRequest
584               
585               if(request instanceof SORequest) {
586                   // Call SO. The response will be inserted into memory once it's received 
587                   SOActorServiceProvider.sendRequest($event.requestID.toString(), drools.getWorkingMemory(), request);                        
588               }
589               break;
590           case "VFC":
591               if (request instanceof VFCRequest) {
592                   // Start VFC thread
593                   Thread t = new Thread(new VFCManager(drools.getWorkingMemory(), (VFCRequest)request));
594                   t.start();
595               }          
596               break;
597       }
598     } else {
599       //
600       // What happens if its null?
601       //
602                 logger.warn("{}: {}: unexpected null operation request", 
603                   $params.getClosedLoopControlName(), 
604                   drools.getRule().getName());
605                 if ("SO".equals($operation.policy.getActor())) {
606                     retract($opTimer);
607                         retract($operation);
608                         modify($manager) {finishOperation($operation)};
609                 }
610                 else if ("vfc".equalsIgnoreCase($operation.policy.getActor())) {
611                     retract($opTimer);
612                         retract($operation);
613                         modify($manager) {finishOperation($operation)};
614                 }
615
616     }
617 end
618
619
620 /*
621 *
622 * We were able to acquire a lock so now let's ask Xacml Guard whether 
623 * we are allowed to proceed with the request to the actor.
624 *
625 */
626 rule "${policyName}.EVENT.MANAGER.OPERATION.LOCKED.GUARD_NOT_YET_QUERIED"
627     when
628         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
629         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
630         $manager : ControlLoopEventManager( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID )
631         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.closedLoopControlName, onset.requestID == $event.requestID, getGuardApprovalStatus() == "NONE" )
632         $lock : TargetLock (requestID == $event.requestID)
633     then
634
635     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
636     logger.info("{}: {}: event={} manager={} operation={} lock={}", 
637                 $params.getClosedLoopControlName(), drools.getRule().getName(),
638                 $event, $manager, $operation, $lock);
639     
640     //
641     // Sending notification that we are about to query Guard ("DB write - start operation")
642     //
643     VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
644     notification.notification = ControlLoopNotificationType.OPERATION;
645     notification.message = "Sending guard query for " + $operation.policy.getActor() + " " + $operation.policy.getRecipe();
646     notification.history = $operation.getHistory();
647     notification.from = "policy";
648     notification.policyName = drools.getRule().getName();
649     notification.policyScope = "${policyScope}";
650     notification.policyVersion = "${policyVersion}";
651     
652     PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
653         
654     //
655     // Now send Guard Request to XACML Guard. In order to bypass the call to Guard, 
656     // just change guardEnabled to false.
657     // 
658     // In order to use REST XACML, provide a URL instead of "" as a second argument 
659     // to the CallGuardTask() and set the first argument to null 
660     // (instead of XacmlPdpEngine).
661     //
662     
663     // NOTE: The environment properties uses "guard.disabled" but the boolean is guardEnabled
664     boolean guardEnabled = "false".equalsIgnoreCase(PolicyEngine.manager.getEnvironmentProperty("guard.disabled"));
665     
666     if(guardEnabled){
667     
668         Thread t = new Thread(new org.onap.policy.guard.CallGuardTask(
669                                                         drools.getWorkingMemory(),
670                                                         $event.closedLoopControlName,
671                                                         $operation.policy.getActor().toString(),
672                                                         $operation.policy.getRecipe(),
673                                                         $operation.getTargetEntity(),
674                                                         $event.requestID.toString()
675                                                         ));
676         t.start();
677     }
678     else{
679         insert(new PolicyGuardResponse("Permit", $event.requestID, $operation.policy.getRecipe()));
680     }
681
682 end
683
684 //
685 // This rule will be triggered when a thread talking to the XACML Guard inserts a 
686 // guardResponse object into the working memory
687 //
688 rule "${policyName}.GUARD.RESPONSE"
689     when
690         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
691         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
692         $manager : ControlLoopEventManager( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID ) 
693         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.closedLoopControlName, onset.requestID == $event.requestID )
694         $lock : TargetLock (requestID == $event.requestID)
695         $opTimer : OperationTimer( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID.toString() )
696         $guardResponse : PolicyGuardResponse(requestID == $event.requestID, $operation.policy.recipe == operation)
697     then
698
699     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
700     logger.info("{}: {}: event={} manager={} operation={} lock={} opTimer={} guardResponse={}", 
701                  $params.getClosedLoopControlName(), drools.getRule().getName(),
702                  $event, $manager, $operation, $lock, $opTimer, $guardResponse);
703         
704         
705     //we will permit the operation if there was no Guard for it
706     if("Indeterminate".equalsIgnoreCase($guardResponse.result)){
707         $guardResponse.result = "Permit";
708     }
709     
710     //
711     // This notification has Guard result in "message". ("DB write - end operation in case of Guard Deny")
712     //
713     VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
714     notification.notification = ControlLoopNotificationType.OPERATION;
715     notification.message = "Guard result for " + $operation.policy.getActor() + " " + $operation.policy.getRecipe() + " is " + $guardResponse.result;
716     notification.history = $operation.getHistory();
717     notification.from = "policy";
718     notification.policyName = drools.getRule().getName();
719     notification.policyScope = "${policyScope}";
720     notification.policyVersion = "${policyVersion}";
721     
722     PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
723     
724     if("Permit".equalsIgnoreCase($guardResponse.result)){
725     
726         modify($operation){setGuardApprovalStatus($guardResponse.result)};
727     }
728     else {
729         //This is the Deny case
730         $operation.startOperation($event);
731         $operation.setOperationHasGuardDeny();
732         retract($opTimer);
733         retract($operation);
734         modify($manager) {finishOperation($operation)};
735     }
736     
737     retract($guardResponse);
738             
739 end
740
741 /*
742 *
743 * This rule responds to APPC Response Events
744 *
745 * I would have like to be consistent and write the Response like this:
746 * $response : Response( CommonHeader.RequestID == $onset.requestID )
747 *
748 * However, no compile error was given. But a runtime error was given. I think
749 * because drools is confused between the classname CommonHeader vs the property CommonHeader.
750 *
751 */
752 rule "${policyName}.APPC.RESPONSE"
753     when
754         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
755         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET ) 
756         $manager : ControlLoopEventManager( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID )
757         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.closedLoopControlName, onset.requestID == $event.requestID )
758         $opTimer : OperationTimer( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID.toString() )
759         $lock : TargetLock (requestID == $event.requestID)
760         $response : Response( getCommonHeader().RequestID == $event.requestID )
761     then
762
763     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
764     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
765     logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}", 
766                  $params.getClosedLoopControlName(), drools.getRule().getName(),
767                  $event, $manager, $operation, $lock, $opTimer, $response);
768     //
769     // Get the result of the operation
770     //
771     PolicyResult policyResult = $operation.onResponse($response);
772     if (policyResult != null) {
773         logger.debug("{}: {}: operation finished - result={}", 
774                     $params.getClosedLoopControlName(), drools.getRule().getName(),
775                     policyResult);
776         //
777         // This Operation has completed, construct a notification showing our results. (DB write - end operation)
778         //
779         VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
780         notification.from = "policy";
781         notification.policyName = drools.getRule().getName();
782         notification.policyScope = "${policyScope}";
783         notification.policyVersion = "${policyVersion}";
784         notification.message = $operation.getOperationHistory();
785         notification.history = $operation.getHistory();
786         if (policyResult.equals(PolicyResult.SUCCESS)) {
787             notification.notification = ControlLoopNotificationType.OPERATION_SUCCESS;
788             //
789             // Let interested parties know
790             //
791             PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
792         } else {
793             notification.notification = ControlLoopNotificationType.OPERATION_FAILURE;
794             //
795             // Let interested parties know
796             //
797             PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
798         }
799         //
800         // Ensure the operation is complete
801         //
802         if ($operation.isOperationComplete() == true) {
803             //
804             // It is complete, remove it from memory
805             //
806             retract($operation);
807             //
808             // We must also retract the timer object
809             // NOTE: We could write a Rule to do this
810             //
811             retract($opTimer);
812             //
813             // Complete the operation
814             //
815             modify($manager) {finishOperation($operation)};
816         } else {
817             //
818             // Just doing this will kick off the LOCKED rule again
819             //
820             modify($operation) {};
821         }
822     } else {
823         //
824         // Its not finished yet (i.e. expecting more Response objects)
825         //
826         // Or possibly it is a leftover response that we timed the request out previously
827         //
828     }
829     //
830     // We are going to retract these objects from memory
831     //
832     retract($response);
833 end
834
835 /*
836 *
837 * The problem with Responses is that they don't have a controlLoopControlName
838 * field in them, so the only way to attach them is via RequestID. If we have multiple
839 * control loop .drl's loaded in the same container, we need to be sure the cleanup
840 * rules don't remove Responses for other control loops.
841 *
842 */
843 rule "${policyName}.APPC.RESPONSE.CLEANUP"
844     when
845         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
846         $response : Response($id : getCommonHeader().RequestID )
847         not ( VirtualControlLoopEvent( requestID == $id, closedLoopEventStatus == ControlLoopEventStatus.ONSET ) ) 
848     then
849
850     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
851     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
852     logger.debug("{}: {}: orphan appc response={}", 
853                 $params.getClosedLoopControlName(), drools.getRule().getName(), $id);
854         
855     //
856     // Retract it
857     //
858     retract($response);
859 end
860
861 /*
862 *
863 * This rule responds to APPC Response Events using the new LCM interface provided by appc
864 *
865 */
866 rule "${policyName}.APPC.LCM.RESPONSE"
867     when
868         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
869         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET ) 
870         $manager : ControlLoopEventManager( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID )
871         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.closedLoopControlName, onset.requestID == $event.requestID )
872         $opTimer : OperationTimer( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID.toString() )
873         $lock : TargetLock (requestID == $event.requestID)
874         $response : LCMResponseWrapper( getBody().getCommonHeader().getRequestId() == $event.requestID )
875     then
876
877     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
878     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
879     logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}", 
880                 $params.getClosedLoopControlName(), drools.getRule().getName(),
881                 $event, $manager, $operation, $lock, $operation, $opTimer, $response);
882     
883     //
884     // Get the result of the operation
885     //
886     PolicyResult policyResult = $operation.onResponse($response);
887     if (policyResult != null) {
888       logger.debug("{}: {}: operation finished - result={}", 
889                   $params.getClosedLoopControlName(), drools.getRule().getName(),
890                   policyResult);
891       
892       //
893       // This Operation has completed, construct a notification showing our results. (DB write - end operation)
894       //
895       VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
896       notification.from = "policy";
897       notification.policyName = drools.getRule().getName();
898       notification.policyScope = "${policyScope}";
899       notification.policyVersion = "${policyVersion}";
900       notification.message = $operation.getOperationHistory();
901       notification.history = $operation.getHistory();
902       if (policyResult.equals(PolicyResult.SUCCESS)) {
903           notification.notification = ControlLoopNotificationType.OPERATION_SUCCESS;
904       } else {
905           notification.notification = ControlLoopNotificationType.OPERATION_FAILURE;
906       }
907       PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
908       //
909       // Ensure the operation is complete
910       //
911       if ($operation.isOperationComplete() == true) {
912           //
913           // It is complete, remove it from memory
914           //
915           retract($operation);
916           //
917           // We must also retract the timer object
918           // NOTE: We could write a Rule to do this
919           //
920           retract($opTimer);
921           //
922           // Complete the operation
923           //
924           modify($manager) {finishOperation($operation)};
925       } else {
926           //
927           // Just doing this will kick off the LOCKED rule again
928           //
929           modify($operation) {};
930       }
931     } else {
932         //
933         // Its not finished yet (i.e. expecting more Response objects)
934         //
935         // Or possibly it is a leftover response that we timed the request out previously
936         //
937     }
938     //
939     // We are going to retract these objects from memory
940     //
941     retract($response);
942 end
943
944 /*
945 *
946 * Clean Up any lingering LCM reponses
947 *
948 */
949 rule "${policyName}.APPC.LCM.RESPONSE.CLEANUP"
950     when
951         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
952         $response : LCMResponseWrapper($id : getBody().getCommonHeader().getRequestId )
953         not ( VirtualControlLoopEvent( requestID == $id, closedLoopEventStatus == ControlLoopEventStatus.ONSET ) ) 
954     then
955     
956     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
957     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
958     logger.debug("{}: {}: orphan appc response={}", 
959                 $params.getClosedLoopControlName(), drools.getRule().getName(), $id);
960     //
961     // Retract it
962     //
963     retract($response);
964 end
965
966 /*
967 *
968 * This rule responds to SO Response Events
969 *
970 */
971 rule "${policyName}.SO.RESPONSE"
972     when
973         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
974         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
975         $manager : ControlLoopEventManager( closedLoopControlName == $event.closedLoopControlName )
976         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.closedLoopControlName, onset.requestID == $event.requestID )
977         $opTimer : OperationTimer( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID.toString() )
978         $lock : TargetLock (requestID == $event.requestID)
979         $response : SOResponseWrapper(requestID.toString() == $event.requestID.toString() )
980     then
981                         
982     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
983     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
984     logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}", 
985                 $params.getClosedLoopControlName(), drools.getRule().getName(),
986                 $event, $manager, $operation, $lock, $operation, $opTimer, $response);
987         
988     // Get the result of the operation
989     //
990     PolicyResult policyResult = $operation.onResponse($response);
991     if (policyResult != null) {
992         logger.debug("{}: {}: operation finished - result={}", 
993                     $params.getClosedLoopControlName(), drools.getRule().getName(),
994                     policyResult);
995       
996         //
997         // This Operation has completed, construct a notification showing our results
998         //
999         VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
1000         notification.from = "policy";
1001         notification.policyName = drools.getRule().getName();
1002         notification.policyScope = "${policyScope}";
1003         notification.policyVersion = "${policyVersion}";
1004         notification.message = $operation.getOperationHistory();
1005         notification.history = $operation.getHistory();
1006         if (policyResult.equals(PolicyResult.SUCCESS)) {
1007             notification.notification = ControlLoopNotificationType.OPERATION_SUCCESS;
1008         } else {
1009             notification.notification = ControlLoopNotificationType.OPERATION_FAILURE;
1010
1011         }
1012         PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
1013         //
1014         // Ensure the operation is complete
1015         //
1016         if ($operation.isOperationComplete() == true) {
1017             //
1018             // It is complete, remove it from memory
1019             //
1020             retract($operation);
1021             //
1022             // We must also retract the timer object
1023             // NOTE: We could write a Rule to do this
1024             //
1025             retract($opTimer);
1026             //
1027             // Complete the operation
1028             //
1029             modify($manager) {finishOperation($operation)};
1030         } else {
1031             //
1032             // Just doing this will kick off the LOCKED rule again
1033             //
1034             modify($operation) {};
1035         }
1036     } else {
1037         //
1038         // Its not finished yet (i.e. expecting more Response objects)
1039         //
1040         // Or possibly it is a leftover response that we timed the request out previously
1041         //
1042     }
1043     //
1044     // We are going to retract these objects from memory
1045     //
1046     retract($response);
1047
1048 end
1049
1050 /*
1051 *
1052 * This rule responds to VFC Response Events
1053 *
1054 */
1055 rule "${policyName}.VFC.RESPONSE"
1056         when
1057                 $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
1058                 $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(), closedLoopEventStatus == ControlLoopEventStatus.ONSET )
1059                 $manager : ControlLoopEventManager( closedLoopControlName == $event.closedLoopControlName )
1060                 $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.closedLoopControlName, onset.requestID == $event.requestID )
1061                 $opTimer : OperationTimer( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID.toString() )
1062         $lock : TargetLock (requestID == $event.requestID)
1063                 $response : VFCResponse( requestId.toString() == $event.requestID.toString() )  
1064         then
1065                 Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1066         logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
1067         logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={} response={}", 
1068                         $params.getClosedLoopControlName(), drools.getRule().getName(),
1069                         $event, $manager, $operation, $lock, $operation, $opTimer, $response);
1070                 
1071                 // Get the result of the operation
1072                 //
1073                 PolicyResult policyResult = $operation.onResponse($response);
1074                 if (policyResult != null) {
1075                         //
1076                         // This Operation has completed, construct a notification showing our results
1077                         //
1078                         VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
1079                         notification.from = "policy";
1080                         notification.policyName = drools.getRule().getName();
1081                         notification.policyScope = "${policyScope}";
1082                         notification.policyVersion = "${policyVersion}";
1083                         notification.message = $operation.getOperationHistory();
1084                         notification.history = $operation.getHistory();
1085                         //
1086                         // Ensure the operation is complete
1087                         //
1088                         if ($operation.isOperationComplete() == true) {
1089                                 //
1090                                 // It is complete, remove it from memory
1091                                 //
1092                                 retract($operation);
1093                                 //
1094                                 // We must also retract the timer object
1095                                 // NOTE: We could write a Rule to do this
1096                                 //
1097                                 retract($opTimer);
1098                                 //
1099                                 // Complete the operation
1100                                 //
1101                                 modify($manager) {finishOperation($operation)};
1102                         } else {
1103                                 //
1104                                 // Just doing this will kick off the LOCKED rule again
1105                                 //
1106                                 modify($operation) {};
1107                         }
1108                 } else {
1109                         //
1110                         // Its not finished yet (i.e. expecting more Response objects)
1111                         //
1112                         // Or possibly it is a leftover response that we timed the request out previously
1113                         //
1114                 }
1115                 //
1116                 // We are going to retract these objects from memory
1117                 //
1118                 retract($response);
1119
1120 end
1121
1122 /*
1123 *
1124 * This is the timer that manages the timeout for an individual operation.
1125 *
1126 */
1127 rule "${policyName}.EVENT.MANAGER.OPERATION.TIMEOUT"
1128     timer (expr: $to )
1129     when
1130         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
1131         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
1132         $manager : ControlLoopEventManager( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID )
1133         $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.closedLoopControlName, onset.requestID == $event.requestID )
1134         $opTimer : OperationTimer( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID.toString(), $to : getDelay() )
1135         $lock : TargetLock (requestID == $event.requestID)
1136     then
1137     
1138     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1139     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
1140     logger.debug("{}: {}: event={} manager={} operation={} lock={} opTimer={}", 
1141                 $params.getClosedLoopControlName(), drools.getRule().getName(),
1142                 $event, $manager, $operation, $lock, $operation, $opTimer);
1143     
1144     //
1145     // Tell it its timed out
1146     //
1147     $operation.setOperationHasTimedOut();
1148     //
1149     // Create a notification for it ("DB Write - end operation")
1150     //
1151     VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
1152     notification.from = "policy";
1153     notification.policyName = drools.getRule().getName();
1154     notification.policyScope = "${policyScope}";
1155     notification.policyVersion = "${policyVersion}";
1156     notification.notification = ControlLoopNotificationType.OPERATION_FAILURE;
1157     notification.message = $operation.getOperationHistory();
1158     notification.history = $operation.getHistory();
1159     //
1160     // Let interested parties know
1161     //
1162     PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
1163     //
1164     // Get rid of the timer
1165     //
1166     retract($opTimer);
1167     //
1168     // Ensure the operation is complete
1169     //
1170     if ($operation.isOperationComplete() == true) {
1171         //
1172         // It is complete, remove it from memory
1173         //
1174         retract($operation);
1175         //
1176         // Complete the operation
1177         //
1178         modify($manager) {finishOperation($operation)};
1179     } else {
1180         //
1181         // Just doing this will kick off the LOCKED rule again
1182         //
1183         modify($operation) {};
1184     }
1185 end
1186
1187 /*
1188 *
1189 * This is the timer that manages the overall control loop timeout.
1190 *
1191 */
1192 rule "${policyName}.EVENT.MANAGER.TIMEOUT"
1193     timer (expr: $to )
1194     when
1195         $params : Params( getClosedLoopControlName() == "${closedLoopControlName}" )
1196         $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName() )
1197         $manager : ControlLoopEventManager( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID )
1198         $clTimer : ControlLoopTimer ( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID.toString(), $to : getDelay() )
1199         $operations : LinkedList()
1200                         from collect( ControlLoopOperationManager( onset.closedLoopControlName == $event.closedLoopControlName, onset.requestID == $event.requestID ) )
1201         $opTimers : LinkedList()
1202                         from collect( OperationTimer( closedLoopControlName == $event.closedLoopControlName, requestID == $event.requestID.toString() ) )
1203         $locks : LinkedList()
1204                         from collect( TargetLock (requestID == $event.requestID) )
1205     then
1206     
1207     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
1208     logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
1209
1210     if ($operations == null) {
1211       logger.debug("{}: {}: event={} manager={} clTimer={} operations=0", 
1212                   $params.getClosedLoopControlName(), drools.getRule().getName(),
1213                   $event, $manager, $clTimer);
1214     } else {
1215       logger.debug("{}: {}: event={} manager={} clTimer={} operations={}", 
1216                   $params.getClosedLoopControlName(), drools.getRule().getName(),
1217                   $event, $manager, $clTimer, $operations.size());
1218     }
1219     //
1220     // Tell the Event Manager it has timed out
1221     //
1222     VirtualControlLoopNotification notification = $manager.setControlLoopTimedOut();
1223     if (notification != null) {
1224         notification.from = "policy";
1225         notification.policyName = drools.getRule().getName();
1226         notification.policyScope = "${policyScope}";
1227         notification.policyVersion = "${policyVersion}";
1228         //
1229         // Let interested parties know
1230         //
1231         PolicyEngine.manager.deliver("POLICY-CL-MGT", notification);
1232     }
1233     //
1234     // Retract EVERYTHING
1235     //
1236     retract($event);
1237     retract($manager);
1238     retract($clTimer);
1239     if ($operations != null && $operations.size() > 0) {
1240         Iterator<ControlLoopOperationManager> iter = $operations.iterator();
1241         while (iter.hasNext()) {
1242             ControlLoopOperationManager manager = iter.next();
1243             retract(manager);
1244         }
1245     }
1246     if ($opTimers != null && $opTimers.size() > 0) {
1247         Iterator<OperationTimer> iter = $opTimers.iterator();
1248         while (iter.hasNext()) {
1249             OperationTimer opTimer = iter.next();
1250             retract(opTimer);
1251         }
1252     }
1253     if ($locks != null && $locks.size() > 0) {
1254         Iterator<TargetLock> iter = $locks.iterator();
1255         while (iter.hasNext()) {
1256             TargetLock lock = iter.next();
1257             //
1258             // Ensure we release the lock
1259             //
1260             PolicyGuard.unlockTarget(lock);
1261             //
1262             //
1263             //
1264             retract(lock);
1265         }
1266     }
1267 end