7811b2d739345f8b05e4ef99b6a18c8015e319d4
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * controlloop event manager
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.eventmanager;
22
23 import java.io.Serializable;
24 import java.io.UnsupportedEncodingException;
25 import java.net.URLDecoder;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.HashMap;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.NoSuchElementException;
33 import java.util.UUID;
34 import org.onap.policy.aai.AaiCqResponse;
35 import org.onap.policy.aai.AaiGetVnfResponse;
36 import org.onap.policy.aai.AaiGetVserverResponse;
37 import org.onap.policy.aai.AaiManager;
38 import org.onap.policy.aai.AaiNqInstanceFilters;
39 import org.onap.policy.aai.AaiNqNamedQuery;
40 import org.onap.policy.aai.AaiNqQueryParameters;
41 import org.onap.policy.aai.AaiNqRequest;
42 import org.onap.policy.aai.AaiNqResponse;
43 import org.onap.policy.aai.AaiNqResponseWrapper;
44 import org.onap.policy.aai.AaiNqVServer;
45 import org.onap.policy.aai.util.AaiException;
46 import org.onap.policy.controlloop.ControlLoopEventStatus;
47 import org.onap.policy.controlloop.ControlLoopException;
48 import org.onap.policy.controlloop.ControlLoopNotificationType;
49 import org.onap.policy.controlloop.ControlLoopOperation;
50 import org.onap.policy.controlloop.VirtualControlLoopEvent;
51 import org.onap.policy.controlloop.VirtualControlLoopNotification;
52 import org.onap.policy.controlloop.policy.FinalResult;
53 import org.onap.policy.controlloop.policy.Policy;
54 import org.onap.policy.controlloop.processor.ControlLoopProcessor;
55 import org.onap.policy.drools.system.PolicyEngine;
56 import org.onap.policy.guard.GuardResult;
57 import org.onap.policy.guard.LockCallback;
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.rest.RestManager;
62 import org.onap.policy.so.util.Serialization;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66 public class ControlLoopEventManager implements LockCallback, Serializable {
67     public static final String PROV_STATUS_ACTIVE = "ACTIVE";
68     private static final String VM_NAME = "VM_NAME";
69     private static final String VNF_NAME = "VNF_NAME";
70     public static final String GENERIC_VNF_VNF_ID = "generic-vnf.vnf-id";
71     public static final String GENERIC_VNF_VNF_NAME = "generic-vnf.vnf-name";
72     public static final String VSERVER_VSERVER_NAME = "vserver.vserver-name";
73     public static final String GENERIC_VNF_IS_CLOSED_LOOP_DISABLED = "generic-vnf.is-closed-loop-disabled";
74     public static final String VSERVER_IS_CLOSED_LOOP_DISABLED = "vserver.is-closed-loop-disabled";
75     public static final String GENERIC_VNF_PROV_STATUS = "generic-vnf.prov-status";
76     public static final String VSERVER_PROV_STATUS = "vserver.prov-status";
77
78     private static final String AAI_URL = "aai.url";
79     private static final String AAI_USERNAME_PROPERTY = "aai.username";
80     private static final String AAI_PASS_PROPERTY = "aai.password";
81
82     private static final String QUERY_AAI_ERROR_MSG = "Exception from queryAai: ";
83
84     /**
85      * Additional time, in seconds, to add to a "lock" request. This ensures that the lock won't expire right before an
86      * operation completes.
87      */
88     private static final int ADDITIONAL_LOCK_SEC = 60;
89
90     private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventManager.class);
91
92     private static final long serialVersionUID = -1216568161322872641L;
93     public final String closedLoopControlName;
94     private final UUID requestId;
95
96     private String controlLoopResult;
97     private ControlLoopProcessor processor = null;
98     private VirtualControlLoopEvent onset;
99     private Integer numOnsets = 0;
100     private Integer numAbatements = 0;
101     private VirtualControlLoopEvent abatement;
102     private FinalResult controlLoopTimedOut = null;
103
104     private boolean isActivated = false;
105     private LinkedList<ControlLoopOperation> controlLoopHistory = new LinkedList<>();
106     private ControlLoopOperationManager currentOperation = null;
107     private ControlLoopOperationManager lastOperationManager = null;
108     private transient TargetLock targetLock = null;
109     private AaiGetVnfResponse vnfResponse = null;
110     private AaiGetVserverResponse vserverResponse = null;
111     private boolean useTargetLock = true;
112
113     /**
114      * Wrapper for AAI vserver named-query response. This is initialized in a lazy fashion.
115      */
116     private AaiNqResponseWrapper nqVserverResponse = null;
117
118     private static Collection<String> requiredAAIKeys = new ArrayList<>();
119
120     static {
121         requiredAAIKeys.add("AICVServerSelfLink");
122         requiredAAIKeys.add("AICIdentity");
123         requiredAAIKeys.add("is_closed_loop_disabled");
124         requiredAAIKeys.add(VM_NAME);
125     }
126
127     public ControlLoopEventManager(String closedLoopControlName, UUID requestId) {
128         this.closedLoopControlName = closedLoopControlName;
129         this.requestId = requestId;
130     }
131
132     public String getClosedLoopControlName() {
133         return closedLoopControlName;
134     }
135
136     public String getControlLoopResult() {
137         return controlLoopResult;
138     }
139
140     public void setControlLoopResult(String controlLoopResult) {
141         this.controlLoopResult = controlLoopResult;
142     }
143
144     public Integer getNumOnsets() {
145         return numOnsets;
146     }
147
148     public void setNumOnsets(Integer numOnsets) {
149         this.numOnsets = numOnsets;
150     }
151
152     public Integer getNumAbatements() {
153         return numAbatements;
154     }
155
156     public void setNumAbatements(Integer numAbatements) {
157         this.numAbatements = numAbatements;
158     }
159
160     public boolean isActivated() {
161         return isActivated;
162     }
163
164     public void setActivated(boolean isActivated) {
165         this.isActivated = isActivated;
166     }
167
168     public boolean useTargetLock() {
169         return useTargetLock();
170     }
171
172     public void setUseTargetLock(boolean useTargetLock) {
173         this.useTargetLock = useTargetLock;
174     }
175
176     public VirtualControlLoopEvent getOnsetEvent() {
177         return this.onset;
178     }
179
180     public VirtualControlLoopEvent getAbatementEvent() {
181         return this.abatement;
182     }
183
184     public ControlLoopProcessor getProcessor() {
185         return this.processor;
186     }
187
188     public UUID getRequestId() {
189         return requestId;
190     }
191
192     /**
193      * Activate a control loop event.
194      *
195      * @param event the event
196      * @return the VirtualControlLoopNotification
197      */
198     public VirtualControlLoopNotification activate(VirtualControlLoopEvent event) {
199         VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
200         try {
201             //
202             // This method should ONLY be called ONCE
203             //
204             if (this.isActivated) {
205                 throw new ControlLoopException("ControlLoopEventManager has already been activated.");
206             }
207             //
208             // Syntax check the event
209             //
210             checkEventSyntax(event);
211
212             //
213             // At this point we are good to go with this event
214             //
215             this.onset = event;
216             this.numOnsets = 1;
217             //
218             notification.setNotification(ControlLoopNotificationType.ACTIVE);
219             //
220             // Set ourselves as active
221             //
222             this.isActivated = true;
223         } catch (ControlLoopException e) {
224             logger.error("{}: activate by event threw: ", this, e);
225             notification.setNotification(ControlLoopNotificationType.REJECTED);
226             notification.setMessage(e.getMessage());
227         }
228         return notification;
229     }
230
231     /**
232      * Activate a control loop event.
233      *
234      * @param yamlSpecification the yaml specification
235      * @param event the event
236      * @return the VirtualControlLoopNotification
237      */
238     public VirtualControlLoopNotification activate(String yamlSpecification, VirtualControlLoopEvent event) {
239         VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
240         try {
241             //
242             // This method should ONLY be called ONCE
243             //
244             if (this.isActivated) {
245                 throw new ControlLoopException("ControlLoopEventManager has already been activated.");
246             }
247             //
248             // Syntax check the event
249             //
250             checkEventSyntax(event);
251
252             //
253             // Check the YAML
254             //
255             if (yamlSpecification == null || yamlSpecification.length() < 1) {
256                 throw new ControlLoopException("yaml specification is null or 0 length");
257             }
258         } catch (ControlLoopException e) {
259             logger.error("{}: activate by YAML specification and event threw: ", this, e);
260             notification.setNotification(ControlLoopNotificationType.REJECTED);
261             notification.setMessage(e.getMessage());
262             return notification;
263         }
264
265         String decodedYaml = null;
266         try {
267             decodedYaml = URLDecoder.decode(yamlSpecification, "UTF-8");
268             if (decodedYaml != null && decodedYaml.length() > 0) {
269                 yamlSpecification = decodedYaml;
270             }
271         } catch (UnsupportedEncodingException e) {
272             logger.error("{}: YAML decode in activate by YAML specification and event threw: ", this, e);
273             notification.setNotification(ControlLoopNotificationType.REJECTED);
274             notification.setMessage(e.getMessage());
275             return notification;
276         }
277
278         try {
279             //
280             // Parse the YAML specification
281             //
282             this.processor = new ControlLoopProcessor(yamlSpecification);
283             //
284             // At this point we are good to go with this event
285             //
286             this.onset = event;
287             this.numOnsets = 1;
288             //
289             //
290             //
291             notification.setNotification(ControlLoopNotificationType.ACTIVE);
292             //
293             // Set ourselves as active
294             //
295             this.isActivated = true;
296         } catch (ControlLoopException e) {
297             logger.error("{}: activate by YAML specification and event threw: ", this, e);
298             notification.setNotification(ControlLoopNotificationType.REJECTED);
299             notification.setMessage(e.getMessage());
300         }
301         return notification;
302     }
303
304     /**
305      * Check if the control loop is final.
306      *
307      * @return a VirtualControlLoopNotification if the control loop is final, otherwise <code>null</code> is returned
308      * @throws ControlLoopException if an error occurs
309      */
310     public VirtualControlLoopNotification isControlLoopFinal() throws ControlLoopException {
311         //
312         // Check if they activated us
313         //
314         if (!this.isActivated) {
315             throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
316         }
317         //
318         // Make sure we are expecting this call.
319         //
320         if (this.onset == null) {
321             throw new ControlLoopException("No onset event for ControlLoopEventManager.");
322         }
323         //
324         // Ok, start creating the notification
325         //
326         VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
327         //
328         // Check if the overall control loop has timed out
329         //
330         if (this.isControlLoopTimedOut()) {
331             //
332             // Yes we have timed out
333             //
334             notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
335             notification.setMessage("Control Loop timed out");
336             notification.getHistory().addAll(this.controlLoopHistory);
337             return notification;
338         }
339         //
340         // Check if the current policy is Final
341         //
342         FinalResult result = this.processor.checkIsCurrentPolicyFinal();
343         if (result == null) {
344             //
345             // we are not at a final result
346             //
347             return null;
348         }
349
350         switch (result) {
351             case FINAL_FAILURE_EXCEPTION:
352                 notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
353                 notification.setMessage("Exception in processing closed loop");
354                 break;
355             case FINAL_FAILURE:
356             case FINAL_FAILURE_RETRIES:
357             case FINAL_FAILURE_TIMEOUT:
358             case FINAL_FAILURE_GUARD:
359                 notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
360                 break;
361             case FINAL_OPENLOOP:
362                 notification.setNotification(ControlLoopNotificationType.FINAL_OPENLOOP);
363                 break;
364             case FINAL_SUCCESS:
365                 notification.setNotification(ControlLoopNotificationType.FINAL_SUCCESS);
366                 break;
367             default:
368                 return null;
369         }
370         //
371         // Be sure to add all the history
372         //
373         notification.getHistory().addAll(this.controlLoopHistory);
374         return notification;
375     }
376
377     /**
378      * Process the control loop.
379      *
380      * @return a ControlLoopOperationManager
381      * @throws ControlLoopException if an error occurs
382      * @throws AaiException if an error occurs retrieving information from A&AI
383      */
384     public ControlLoopOperationManager processControlLoop() throws ControlLoopException, AaiException {
385         //
386         // Check if they activated us
387         //
388         if (!this.isActivated) {
389             throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
390         }
391         //
392         // Make sure we are expecting this call.
393         //
394         if (this.onset == null) {
395             throw new ControlLoopException("No onset event for ControlLoopEventManager.");
396         }
397         //
398         // Is there a current operation?
399         //
400         if (this.currentOperation != null) {
401             //
402             // Throw an exception, or simply return the current operation?
403             //
404             throw new ControlLoopException("Already working an Operation, do not call this method.");
405         }
406         //
407         // Ensure we are not FINAL
408         //
409         VirtualControlLoopNotification notification = this.isControlLoopFinal();
410         if (notification != null) {
411             //
412             // This is weird, we require them to call the isControlLoopFinal() method first
413             //
414             // We should really abstract this and avoid throwing an exception, because it really
415             // isn't an exception.
416             //
417             throw new ControlLoopException("Control Loop is in FINAL state, do not call this method.");
418         }
419         //
420         // Not final so get the policy that needs to be worked on.
421         //
422         Policy policy = this.processor.getCurrentPolicy();
423         if (policy == null) {
424             throw new ControlLoopException("ControlLoopEventManager: processor came upon null Policy.");
425         }
426         //
427         // And setup an operation
428         //
429         this.lastOperationManager = this.currentOperation;
430         this.currentOperation = new ControlLoopOperationManager(this.onset, policy, this);
431         //
432         // Return it
433         //
434         return this.currentOperation;
435     }
436
437     /**
438      * Finish an operation.
439      *
440      * @param operation the operation
441      */
442     public void finishOperation(ControlLoopOperationManager operation) throws ControlLoopException {
443         //
444         // Verify we have a current operation
445         //
446         if (this.currentOperation != null) {
447             //
448             // Validate they are finishing the current operation
449             // PLD - this is simply comparing the policy. Do we want to equals the whole object?
450             //
451             if (this.currentOperation.policy.equals(operation.policy)) {
452                 logger.debug("Finishing {} result is {}", this.currentOperation.policy.getRecipe(),
453                         this.currentOperation.getOperationResult());
454                 //
455                 // Save history
456                 //
457                 this.controlLoopHistory.addAll(this.currentOperation.getHistory());
458                 //
459                 // Move to the next Policy
460                 //
461                 this.processor.nextPolicyForResult(this.currentOperation.getOperationResult());
462                 //
463                 // Just null this out
464                 //
465                 this.lastOperationManager = this.currentOperation;
466                 this.currentOperation = null;
467                 //
468                 // TODO: Release our lock
469                 //
470                 return;
471             }
472             logger.debug("Cannot finish current operation {} does not match given operation {}",
473                     this.currentOperation.policy, operation.policy);
474             return;
475         }
476         throw new ControlLoopException("No operation to finish.");
477     }
478
479     /**
480      * Obtain a lock for the current operation.
481      *
482      * @return the lock result
483      * @throws ControlLoopException if an error occurs
484      */
485     public synchronized LockResult<GuardResult, TargetLock> lockCurrentOperation() throws ControlLoopException {
486         //
487         // Sanity check
488         //
489         if (this.currentOperation == null) {
490             throw new ControlLoopException("Do not have a current operation.");
491         }
492         //
493         // Not using target locks? Create and return a lock w/o actually locking.
494         //
495         if (!this.useTargetLock) {
496             TargetLock lock = PolicyGuard.createTargetLock(this.currentOperation.policy.getTarget().getType(),
497                     this.currentOperation.getTargetEntity(), this.onset.getRequestId(), this);
498             this.targetLock = lock;
499             return LockResult.createLockResult(GuardResult.LOCK_ACQUIRED, lock);
500         }
501         //
502         // Have we acquired it already?
503         //
504         if (this.targetLock != null) {
505             //
506             // TODO: Make sure the current lock is for the same target.
507             // Currently, it should be. But in the future it may not.
508             //
509             GuardResult result = PolicyGuard.lockTarget(targetLock,
510                     this.currentOperation.getOperationTimeout() + ADDITIONAL_LOCK_SEC);
511             return new LockResult<>(result, this.targetLock);
512         } else {
513             //
514             // Ask the Guard
515             //
516             LockResult<GuardResult, TargetLock> lockResult = PolicyGuard.lockTarget(
517                     this.currentOperation.policy.getTarget().getType(), this.currentOperation.getTargetEntity(),
518                     this.onset.getRequestId(), this, this.currentOperation.getOperationTimeout() + ADDITIONAL_LOCK_SEC);
519             //
520             // Was it acquired?
521             //
522             if (lockResult.getA().equals(GuardResult.LOCK_ACQUIRED)) {
523                 //
524                 // Yes, let's save it
525                 //
526                 this.targetLock = lockResult.getB();
527             }
528             return lockResult;
529         }
530     }
531
532     /**
533      * Release the lock for the current operation.
534      *
535      * @return the target lock
536      */
537     public synchronized TargetLock unlockCurrentOperation() {
538         if (this.targetLock == null) {
539             return null;
540         }
541
542         TargetLock returnLock = this.targetLock;
543         this.targetLock = null;
544         //
545         // if using target locking unlock before returning
546         //
547         if (this.useTargetLock) {
548             PolicyGuard.unlockTarget(returnLock);
549         }
550
551         // always return the old target lock so rules can retract it
552         return returnLock;
553     }
554
555     public enum NewEventStatus {
556         FIRST_ONSET, SUBSEQUENT_ONSET, FIRST_ABATEMENT, SUBSEQUENT_ABATEMENT, SYNTAX_ERROR;
557     }
558
559     /**
560      * An event onset/abatement.
561      *
562      * @param event the event
563      * @return the status
564      * @throws AaiException if an error occurs retrieving information from A&AI
565      */
566     public NewEventStatus onNewEvent(VirtualControlLoopEvent event) throws AaiException {
567         try {
568             this.checkEventSyntax(event);
569             if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ONSET) {
570                 //
571                 // Check if this is our original ONSET
572                 //
573                 if (event.equals(this.onset)) {
574                     //
575                     // Query A&AI if needed
576                     //
577                     queryAai(event);
578
579                     //
580                     // DO NOT retract it
581                     //
582                     return NewEventStatus.FIRST_ONSET;
583                 }
584                 //
585                 // Log that we got an onset
586                 //
587                 this.numOnsets++;
588                 return NewEventStatus.SUBSEQUENT_ONSET;
589             } else if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ABATED) {
590                 //
591                 // Have we already got an abatement?
592                 //
593                 if (this.abatement == null) {
594                     //
595                     // Save this
596                     //
597                     this.abatement = event;
598                     //
599                     // Keep track that we received another
600                     //
601                     this.numAbatements++;
602                     //
603                     //
604                     //
605                     return NewEventStatus.FIRST_ABATEMENT;
606                 } else {
607                     //
608                     // Keep track that we received another
609                     //
610                     this.numAbatements++;
611                     //
612                     //
613                     //
614                     return NewEventStatus.SUBSEQUENT_ABATEMENT;
615                 }
616             }
617         } catch (ControlLoopException e) {
618             logger.error("{}: onNewEvent threw: ", this, e);
619         }
620         return NewEventStatus.SYNTAX_ERROR;
621     }
622
623
624     /**
625      * Commit the abatement to the history database.
626      *
627      * @param message the abatement message
628      * @param outcome the abatement outcome
629      */
630     public void commitAbatement(String message, String outcome) {
631         if (this.lastOperationManager == null) {
632             logger.error("{}: commitAbatement: no operation manager", this);
633             return;
634         }
635         try {
636             this.lastOperationManager.commitAbatement(message, outcome);
637         } catch (NoSuchElementException e) {
638             logger.error("{}: commitAbatement threw an exception ", this, e);
639         }
640     }
641
642
643     /**
644      * Set the control loop time out.
645      *
646      * @return a VirtualControlLoopNotification
647      */
648     public VirtualControlLoopNotification setControlLoopTimedOut() {
649         this.controlLoopTimedOut = FinalResult.FINAL_FAILURE_TIMEOUT;
650         VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
651         notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
652         notification.setMessage("Control Loop timed out");
653         notification.getHistory().addAll(this.controlLoopHistory);
654         return notification;
655     }
656
657     public boolean isControlLoopTimedOut() {
658         return (this.controlLoopTimedOut == FinalResult.FINAL_FAILURE_TIMEOUT);
659     }
660
661     /**
662      * Get the control loop timeout.
663      *
664      * @param defaultTimeout the default timeout
665      * @return the timeout
666      */
667     public int getControlLoopTimeout(Integer defaultTimeout) {
668         if (this.processor != null && this.processor.getControlLoop() != null) {
669             return this.processor.getControlLoop().getTimeout();
670         }
671         if (defaultTimeout != null) {
672             return defaultTimeout;
673         }
674         return 0;
675     }
676
677     public AaiGetVnfResponse getVnfResponse() {
678         return vnfResponse;
679     }
680
681     public AaiGetVserverResponse getVserverResponse() {
682         return vserverResponse;
683     }
684
685     /**
686      * Check an event syntax.
687      *
688      * @param event the event syntax
689      * @throws ControlLoopException if an error occurs
690      */
691     public void checkEventSyntax(VirtualControlLoopEvent event) throws ControlLoopException {
692         if (event.getClosedLoopEventStatus() == null
693                 || (event.getClosedLoopEventStatus() != ControlLoopEventStatus.ONSET
694                         && event.getClosedLoopEventStatus() != ControlLoopEventStatus.ABATED)) {
695             throw new ControlLoopException("Invalid value in closedLoopEventStatus");
696         }
697         if (event.getClosedLoopControlName() == null || event.getClosedLoopControlName().length() < 1) {
698             throw new ControlLoopException("No control loop name");
699         }
700         if (event.getRequestId() == null) {
701             throw new ControlLoopException("No request ID");
702         }
703         if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ABATED) {
704             return;
705         }
706         if (event.getTarget() == null || event.getTarget().length() < 1) {
707             throw new ControlLoopException("No target field");
708         } else if (!VM_NAME.equalsIgnoreCase(event.getTarget()) && !VNF_NAME.equalsIgnoreCase(event.getTarget())
709                 && !VSERVER_VSERVER_NAME.equalsIgnoreCase(event.getTarget())
710                 && !GENERIC_VNF_VNF_ID.equalsIgnoreCase(event.getTarget())
711                 && !GENERIC_VNF_VNF_NAME.equalsIgnoreCase(event.getTarget())) {
712             throw new ControlLoopException("target field invalid - expecting VM_NAME or VNF_NAME");
713         }
714         if (event.getAai() == null) {
715             throw new ControlLoopException("AAI is null");
716         }
717         if (event.getAai().get(GENERIC_VNF_VNF_ID) == null && event.getAai().get(VSERVER_VSERVER_NAME) == null
718                 && event.getAai().get(GENERIC_VNF_VNF_NAME) == null) {
719             throw new ControlLoopException(
720                     "generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
721         }
722     }
723
724     /**
725      * Query A&AI for an event.
726      *
727      * @param event the event
728      * @throws AaiException if an error occurs retrieving information from A&AI
729      */
730     public void queryAai(VirtualControlLoopEvent event) throws AaiException {
731
732         Map<String, String> aai = event.getAai();
733
734         if (aai.containsKey(VSERVER_IS_CLOSED_LOOP_DISABLED) || aai.containsKey(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)) {
735
736             if (isClosedLoopDisabled(event)) {
737                 throw new AaiException("is-closed-loop-disabled is set to true on VServer or VNF");
738             }
739
740             if (isProvStatusInactive(event)) {
741                 throw new AaiException("prov-status is not ACTIVE on VServer or VNF");
742             }
743
744             // no need to query, as we already have the data
745             return;
746         }
747
748         if (vnfResponse != null || vserverResponse != null) {
749             // query has already been performed
750             return;
751         }
752
753         try {
754             if (aai.containsKey(GENERIC_VNF_VNF_ID) || aai.containsKey(GENERIC_VNF_VNF_NAME)) {
755                 vnfResponse = getAaiVnfInfo(event);
756                 processVnfResponse(vnfResponse, aai.containsKey(GENERIC_VNF_VNF_ID));
757             } else if (aai.containsKey(VSERVER_VSERVER_NAME)) {
758                 vserverResponse = getAaiVserverInfo(event);
759                 processVServerResponse(vserverResponse);
760             }
761         } catch (AaiException e) {
762             logger.error(QUERY_AAI_ERROR_MSG, e);
763             throw e;
764         } catch (Exception e) {
765             logger.error(QUERY_AAI_ERROR_MSG, e);
766             throw new AaiException(QUERY_AAI_ERROR_MSG + e.toString());
767         }
768     }
769
770     /**
771      * Process a response from A&AI for a VNF.
772      *
773      * @param aaiResponse the response from A&AI
774      * @param queryByVnfId <code>true</code> if the query was based on vnf-id, <code>false</code> if the query was based
775      *        on vnf-name
776      * @throws AaiException if an error occurs processing the response
777      */
778     private static void processVnfResponse(AaiGetVnfResponse aaiResponse, boolean queryByVnfId) throws AaiException {
779         String queryTypeString = (queryByVnfId ? "vnf-id" : "vnf-name");
780
781         if (aaiResponse == null) {
782             throw new AaiException("AAI Response is null (query by " + queryTypeString + ")");
783         }
784         if (aaiResponse.getRequestError() != null) {
785             throw new AaiException("AAI Responded with a request error (query by " + queryTypeString + ")");
786         }
787
788         if (aaiResponse.getIsClosedLoopDisabled()) {
789             throw new AaiException("is-closed-loop-disabled is set to true (query by " + queryTypeString + ")");
790         }
791
792         if (!PROV_STATUS_ACTIVE.equals(aaiResponse.getProvStatus())) {
793             throw new AaiException("prov-status is not ACTIVE (query by " + queryTypeString + ")");
794         }
795     }
796
797     /**
798      * Process a response from A&AI for a VServer.
799      *
800      * @param aaiResponse the response from A&AI
801      * @throws AaiException if an error occurs processing the response
802      */
803     private static void processVServerResponse(AaiGetVserverResponse aaiResponse) throws AaiException {
804         if (aaiResponse == null) {
805             throw new AaiException("AAI Response is null (query by vserver-name)");
806         }
807         if (aaiResponse.getRequestError() != null) {
808             throw new AaiException("AAI Responded with a request error (query by vserver-name)");
809         }
810
811         List<AaiNqVServer> lst = aaiResponse.getVserver();
812         if (lst.isEmpty()) {
813             return;
814         }
815
816         AaiNqVServer svr = lst.get(0);
817         if (svr.getIsClosedLoopDisabled()) {
818             throw new AaiException("is-closed-loop-disabled is set to true (query by vserver-name)");
819         }
820
821         if (!PROV_STATUS_ACTIVE.equals(svr.getProvStatus())) {
822             throw new AaiException("prov-status is not ACTIVE (query by vserver-name)");
823         }
824     }
825
826     /**
827      * Is closed loop disabled for an event.
828      *
829      * @param event the event
830      * @return <code>true</code> if the control loop is disabled, <code>false</code> otherwise
831      */
832     public static boolean isClosedLoopDisabled(VirtualControlLoopEvent event) {
833         Map<String, String> aai = event.getAai();
834         return (isAaiTrue(aai.get(VSERVER_IS_CLOSED_LOOP_DISABLED))
835                 || isAaiTrue(aai.get(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)));
836     }
837
838     /**
839      * Does provisioning status, for an event, have a value other than ACTIVE.
840      *
841      * @param event the event
842      * @return {@code true} if the provisioning status is neither ACTIVE nor {@code null}, {@code false} otherwise
843      */
844     protected static boolean isProvStatusInactive(VirtualControlLoopEvent event) {
845         Map<String, String> aai = event.getAai();
846         return (!PROV_STATUS_ACTIVE.equals(aai.getOrDefault(VSERVER_PROV_STATUS, PROV_STATUS_ACTIVE))
847                 || !PROV_STATUS_ACTIVE.equals(aai.getOrDefault(GENERIC_VNF_PROV_STATUS, PROV_STATUS_ACTIVE)));
848     }
849
850     /**
851      * Determines the boolean value represented by the given AAI field value.
852      *
853      * @param aaiValue value to be examined
854      * @return the boolean value represented by the field value, or {@code false} if the value is {@code null}
855      */
856     protected static boolean isAaiTrue(String aaiValue) {
857         return ("true".equalsIgnoreCase(aaiValue) || "T".equalsIgnoreCase(aaiValue) || "yes".equalsIgnoreCase(aaiValue)
858                 || "Y".equalsIgnoreCase(aaiValue));
859     }
860
861     /**
862      * Get the A&AI VService information for an event.
863      *
864      * @param event the event
865      * @return a AaiGetVserverResponse
866      * @throws ControlLoopException if an error occurs
867      */
868     public static AaiGetVserverResponse getAaiVserverInfo(VirtualControlLoopEvent event) throws ControlLoopException {
869         UUID requestId = event.getRequestId();
870         AaiGetVserverResponse response = null;
871         String vserverName = event.getAai().get(VSERVER_VSERVER_NAME);
872
873         try {
874             if (vserverName != null) {
875                 String aaiHostUrl = PolicyEngine.manager.getEnvironmentProperty(AAI_URL);
876                 String aaiUser = PolicyEngine.manager.getEnvironmentProperty(AAI_USERNAME_PROPERTY);
877                 String aaiPassword = PolicyEngine.manager.getEnvironmentProperty(AAI_PASS_PROPERTY);
878                 String aaiGetQueryByVserver = "/aai/v11/nodes/vservers?vserver-name=";
879                 String url = aaiHostUrl + aaiGetQueryByVserver;
880                 logger.info("AAI Host URL by VServer: {}", url);
881                 response = new AaiManager(new RestManager()).getQueryByVserverName(url, aaiUser, aaiPassword, requestId,
882                         vserverName);
883             }
884         } catch (Exception e) {
885             logger.error("getAaiVserverInfo exception: ", e);
886             throw new ControlLoopException("Exception in getAaiVserverInfo: ", e);
887         }
888
889         return response;
890     }
891
892     /**
893      * Get A&AI VNF information for an event.
894      *
895      * @param event the event
896      * @return a AaiGetVnfResponse
897      * @throws ControlLoopException if an error occurs
898      */
899     public static AaiGetVnfResponse getAaiVnfInfo(VirtualControlLoopEvent event) throws ControlLoopException {
900         UUID requestId = event.getRequestId();
901         AaiGetVnfResponse response = null;
902         String vnfName = event.getAai().get(GENERIC_VNF_VNF_NAME);
903         String vnfId = event.getAai().get(GENERIC_VNF_VNF_ID);
904
905         String aaiHostUrl = PolicyEngine.manager.getEnvironmentProperty(AAI_URL);
906         String aaiUser = PolicyEngine.manager.getEnvironmentProperty(AAI_USERNAME_PROPERTY);
907         String aaiPassword = PolicyEngine.manager.getEnvironmentProperty(AAI_PASS_PROPERTY);
908
909         try {
910             if (vnfName != null) {
911                 String aaiGetQueryByVnfName = "/aai/v11/network/generic-vnfs/generic-vnf?vnf-name=";
912                 String url = aaiHostUrl + aaiGetQueryByVnfName;
913                 logger.info("AAI Host URL by VNF name: {}", url);
914                 response = new AaiManager(new RestManager()).getQueryByVnfName(url, aaiUser, aaiPassword, requestId,
915                         vnfName);
916             } else if (vnfId != null) {
917                 String aaiGetQueryByVnfId = "/aai/v11/network/generic-vnfs/generic-vnf/";
918                 String url = aaiHostUrl + aaiGetQueryByVnfId;
919                 logger.info("AAI Host URL by VNF ID: {}", url);
920                 response =
921                         new AaiManager(new RestManager()).getQueryByVnfId(url, aaiUser, aaiPassword, requestId, vnfId);
922             }
923         } catch (Exception e) {
924             logger.error("getAaiVnfInfo exception: ", e);
925             throw new ControlLoopException("Exception in getAaiVnfInfo: ", e);
926         }
927
928         return response;
929     }
930
931     /**
932      * Gets the output from the AAI vserver named-query, using the cache, if appropriate.
933      *
934      * @return output from the AAI vserver named-query
935      */
936     public AaiNqResponseWrapper getNqVserverFromAai() {
937         if (nqVserverResponse != null) {
938             // already queried
939             return nqVserverResponse;
940         }
941
942         String vserverName = onset.getAai().get(VSERVER_VSERVER_NAME);
943         if (vserverName == null) {
944             logger.warn("Missing vserver-name for AAI request {}", onset.getRequestId());
945             return null;
946         }
947
948         // create AAI named-query request with UUID started with ""
949         AaiNqRequest aaiNqRequest = new AaiNqRequest();
950         AaiNqQueryParameters aaiNqQueryParam = new AaiNqQueryParameters();
951         AaiNqNamedQuery aaiNqNamedQuery = new AaiNqNamedQuery();
952         final AaiNqInstanceFilters aaiNqInstanceFilter = new AaiNqInstanceFilters();
953
954         // queryParameters
955         aaiNqNamedQuery.setNamedQueryUuid(UUID.fromString("4ff56a54-9e3f-46b7-a337-07a1d3c6b469"));
956         aaiNqQueryParam.setNamedQuery(aaiNqNamedQuery);
957         aaiNqRequest.setQueryParameters(aaiNqQueryParam);
958         //
959         // instanceFilters
960         //
961         Map<String, Map<String, String>> aaiNqInstanceFilterMap = new HashMap<>();
962         Map<String, String> aaiNqInstanceFilterMapItem = new HashMap<>();
963         aaiNqInstanceFilterMapItem.put("vserver-name", vserverName);
964         aaiNqInstanceFilterMap.put("vserver", aaiNqInstanceFilterMapItem);
965         aaiNqInstanceFilter.getInstanceFilter().add(aaiNqInstanceFilterMap);
966         aaiNqRequest.setInstanceFilters(aaiNqInstanceFilter);
967
968         if (logger.isDebugEnabled()) {
969             logger.debug("AAI Request sent: {}", Serialization.gsonPretty.toJson(aaiNqRequest));
970         }
971
972         AaiNqResponse aaiNqResponse = new AaiManager(new RestManager()).postQuery(getPeManagerEnvProperty(AAI_URL),
973                 getPeManagerEnvProperty(AAI_USERNAME_PROPERTY), getPeManagerEnvProperty(AAI_PASS_PROPERTY),
974                 aaiNqRequest, onset.getRequestId());
975
976         // Check AAI response
977         if (aaiNqResponse == null) {
978             logger.warn("No response received from AAI for request {}", aaiNqRequest);
979             return null;
980         }
981
982         // Create AAINQResponseWrapper
983         nqVserverResponse = new AaiNqResponseWrapper(onset.getRequestId(), aaiNqResponse);
984
985         if (logger.isDebugEnabled()) {
986             logger.debug("AAI Named Query Response: ");
987             logger.debug(Serialization.gsonPretty.toJson(nqVserverResponse.getAaiNqResponse()));
988         }
989
990         return nqVserverResponse;
991     }
992
993     /**
994      * This method reads and validates environmental properties coming from the policy engine. Null properties cause an
995      * {@link IllegalArgumentException} runtime exception to be thrown
996      *
997      * @param enginePropertyName the name of the parameter to retrieve
998      * @return the property value
999      */
1000     private static String getPeManagerEnvProperty(String enginePropertyName) {
1001         String enginePropertyValue = PolicyEngine.manager.getEnvironmentProperty(enginePropertyName);
1002         if (enginePropertyValue == null) {
1003             throw new IllegalArgumentException("The value of policy engine manager environment property \""
1004                     + enginePropertyName + "\" may not be null");
1005         }
1006         return enginePropertyValue;
1007     }
1008
1009     @Override
1010     public boolean isActive() {
1011         // TODO
1012         return true;
1013     }
1014
1015     @Override
1016     public boolean releaseLock() {
1017         // TODO
1018         return false;
1019     }
1020
1021     @Override
1022     public String toString() {
1023         return "ControlLoopEventManager [closedLoopControlName=" + closedLoopControlName + ", requestId=" + requestId
1024                 + ", processor=" + processor + ", onset=" + (onset != null ? onset.getRequestId() : "null")
1025                 + ", numOnsets=" + numOnsets + ", numAbatements=" + numAbatements + ", isActivated=" + isActivated
1026                 + ", currentOperation=" + currentOperation + ", targetLock=" + targetLock + "]";
1027     }
1028
1029     /**
1030      * This function calls Aai Custom Query and responds with the AaiCqResponse.
1031      *
1032      * @param event input event
1033      * @return AaiCqResponse Response from Aai for custom query
1034      * @throws AaiException if error occurs
1035      */
1036     public AaiCqResponse getCqResponse(VirtualControlLoopEvent event) throws AaiException {
1037
1038         Map<String, String> aai = event.getAai();
1039
1040         if (aai.containsKey(VSERVER_IS_CLOSED_LOOP_DISABLED) || aai.containsKey(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)) {
1041
1042             if (isClosedLoopDisabled(event)) {
1043                 throw new AaiException("is-closed-loop-disabled is set to true on VServer or VNF");
1044             }
1045
1046             if (isProvStatusInactive(event)) {
1047                 throw new AaiException("prov-status is not ACTIVE on VServer or VNF");
1048             }
1049         }
1050
1051         if (!aai.containsKey(VSERVER_VSERVER_NAME)) {
1052             throw new AaiException("Vserver name is missing");
1053         }
1054
1055         UUID reqId = event.getRequestId();
1056         AaiCqResponse response = null;
1057         String vserverId = event.getAai().get(VSERVER_VSERVER_NAME);
1058
1059         String aaiHostUrl = PolicyEngine.manager.getEnvironmentProperty(AAI_URL);
1060         String aaiUser = PolicyEngine.manager.getEnvironmentProperty(AAI_USERNAME_PROPERTY);
1061         String aaiPassword = PolicyEngine.manager.getEnvironmentProperty(AAI_PASS_PROPERTY);
1062
1063         response = new AaiManager(new RestManager()).getCustomQueryResponse(aaiHostUrl, aaiUser, aaiPassword, reqId,
1064                 vserverId);
1065         return response;
1066
1067     }
1068
1069 }