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