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