b82a5f8e5d81023e62b6777ea4bf652344b4eff6
[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             Integer timeout = this.processor.getControlLoop().getTimeout();
670             if (timeout != null && timeout > 0) {
671                 return timeout;
672             }
673         }
674         if (defaultTimeout != null) {
675             return defaultTimeout;
676         }
677         return 0;
678     }
679
680     public AaiGetVnfResponse getVnfResponse() {
681         return vnfResponse;
682     }
683
684     public AaiGetVserverResponse getVserverResponse() {
685         return vserverResponse;
686     }
687
688     /**
689      * Check an event syntax.
690      *
691      * @param event the event syntax
692      * @throws ControlLoopException if an error occurs
693      */
694     public void checkEventSyntax(VirtualControlLoopEvent event) throws ControlLoopException {
695         if (event.getClosedLoopEventStatus() == null
696                 || (event.getClosedLoopEventStatus() != ControlLoopEventStatus.ONSET
697                         && event.getClosedLoopEventStatus() != ControlLoopEventStatus.ABATED)) {
698             throw new ControlLoopException("Invalid value in closedLoopEventStatus");
699         }
700         if (event.getClosedLoopControlName() == null || event.getClosedLoopControlName().length() < 1) {
701             throw new ControlLoopException("No control loop name");
702         }
703         if (event.getRequestId() == null) {
704             throw new ControlLoopException("No request ID");
705         }
706         if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ABATED) {
707             return;
708         }
709         if (event.getTarget() == null || event.getTarget().length() < 1) {
710             throw new ControlLoopException("No target field");
711         } else if (!VM_NAME.equalsIgnoreCase(event.getTarget()) && !VNF_NAME.equalsIgnoreCase(event.getTarget())
712                 && !VSERVER_VSERVER_NAME.equalsIgnoreCase(event.getTarget())
713                 && !GENERIC_VNF_VNF_ID.equalsIgnoreCase(event.getTarget())
714                 && !GENERIC_VNF_VNF_NAME.equalsIgnoreCase(event.getTarget())) {
715             throw new ControlLoopException("target field invalid - expecting VM_NAME or VNF_NAME");
716         }
717         if (event.getAai() == null) {
718             throw new ControlLoopException("AAI is null");
719         }
720         if (event.getAai().get(GENERIC_VNF_VNF_ID) == null && event.getAai().get(VSERVER_VSERVER_NAME) == null
721                 && event.getAai().get(GENERIC_VNF_VNF_NAME) == null) {
722             throw new ControlLoopException(
723                     "generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
724         }
725     }
726
727     /**
728      * Query A&AI for an event.
729      *
730      * @param event the event
731      * @throws AaiException if an error occurs retrieving information from A&AI
732      */
733     public void queryAai(VirtualControlLoopEvent event) throws AaiException {
734
735         Map<String, String> aai = event.getAai();
736
737         if (aai.containsKey(VSERVER_IS_CLOSED_LOOP_DISABLED) || aai.containsKey(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)) {
738
739             if (isClosedLoopDisabled(event)) {
740                 throw new AaiException("is-closed-loop-disabled is set to true on VServer or VNF");
741             }
742
743             if (isProvStatusInactive(event)) {
744                 throw new AaiException("prov-status is not ACTIVE on VServer or VNF");
745             }
746
747             // no need to query, as we already have the data
748             return;
749         }
750
751         if (vnfResponse != null || vserverResponse != null) {
752             // query has already been performed
753             return;
754         }
755
756         try {
757             if (aai.containsKey(GENERIC_VNF_VNF_ID) || aai.containsKey(GENERIC_VNF_VNF_NAME)) {
758                 vnfResponse = getAaiVnfInfo(event);
759                 processVnfResponse(vnfResponse, aai.containsKey(GENERIC_VNF_VNF_ID));
760             } else if (aai.containsKey(VSERVER_VSERVER_NAME)) {
761                 vserverResponse = getAaiVserverInfo(event);
762                 processVServerResponse(vserverResponse);
763             }
764         } catch (AaiException e) {
765             logger.error(QUERY_AAI_ERROR_MSG, e);
766             throw e;
767         } catch (Exception e) {
768             logger.error(QUERY_AAI_ERROR_MSG, e);
769             throw new AaiException(QUERY_AAI_ERROR_MSG + e.toString());
770         }
771     }
772
773     /**
774      * Process a response from A&AI for a VNF.
775      *
776      * @param aaiResponse the response from A&AI
777      * @param queryByVnfId <code>true</code> if the query was based on vnf-id, <code>false</code> if the query was based
778      *        on vnf-name
779      * @throws AaiException if an error occurs processing the response
780      */
781     private static void processVnfResponse(AaiGetVnfResponse aaiResponse, boolean queryByVnfId) throws AaiException {
782         String queryTypeString = (queryByVnfId ? "vnf-id" : "vnf-name");
783
784         if (aaiResponse == null) {
785             throw new AaiException("AAI Response is null (query by " + queryTypeString + ")");
786         }
787         if (aaiResponse.getRequestError() != null) {
788             throw new AaiException("AAI Responded with a request error (query by " + queryTypeString + ")");
789         }
790
791         if (aaiResponse.getIsClosedLoopDisabled()) {
792             throw new AaiException("is-closed-loop-disabled is set to true (query by " + queryTypeString + ")");
793         }
794
795         if (!PROV_STATUS_ACTIVE.equals(aaiResponse.getProvStatus())) {
796             throw new AaiException("prov-status is not ACTIVE (query by " + queryTypeString + ")");
797         }
798     }
799
800     /**
801      * Process a response from A&AI for a VServer.
802      *
803      * @param aaiResponse the response from A&AI
804      * @throws AaiException if an error occurs processing the response
805      */
806     private static void processVServerResponse(AaiGetVserverResponse aaiResponse) throws AaiException {
807         if (aaiResponse == null) {
808             throw new AaiException("AAI Response is null (query by vserver-name)");
809         }
810         if (aaiResponse.getRequestError() != null) {
811             throw new AaiException("AAI Responded with a request error (query by vserver-name)");
812         }
813
814         List<AaiNqVServer> lst = aaiResponse.getVserver();
815         if (lst.isEmpty()) {
816             return;
817         }
818
819         AaiNqVServer svr = lst.get(0);
820         if (svr.getIsClosedLoopDisabled()) {
821             throw new AaiException("is-closed-loop-disabled is set to true (query by vserver-name)");
822         }
823
824         if (!PROV_STATUS_ACTIVE.equals(svr.getProvStatus())) {
825             throw new AaiException("prov-status is not ACTIVE (query by vserver-name)");
826         }
827     }
828
829     /**
830      * Is closed loop disabled for an event.
831      *
832      * @param event the event
833      * @return <code>true</code> if the control loop is disabled, <code>false</code> otherwise
834      */
835     public static boolean isClosedLoopDisabled(VirtualControlLoopEvent event) {
836         Map<String, String> aai = event.getAai();
837         return (isAaiTrue(aai.get(VSERVER_IS_CLOSED_LOOP_DISABLED))
838                 || isAaiTrue(aai.get(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)));
839     }
840
841     /**
842      * Does provisioning status, for an event, have a value other than ACTIVE.
843      *
844      * @param event the event
845      * @return {@code true} if the provisioning status is neither ACTIVE nor {@code null}, {@code false} otherwise
846      */
847     protected static boolean isProvStatusInactive(VirtualControlLoopEvent event) {
848         Map<String, String> aai = event.getAai();
849         return (!PROV_STATUS_ACTIVE.equals(aai.getOrDefault(VSERVER_PROV_STATUS, PROV_STATUS_ACTIVE))
850                 || !PROV_STATUS_ACTIVE.equals(aai.getOrDefault(GENERIC_VNF_PROV_STATUS, PROV_STATUS_ACTIVE)));
851     }
852
853     /**
854      * Determines the boolean value represented by the given AAI field value.
855      *
856      * @param aaiValue value to be examined
857      * @return the boolean value represented by the field value, or {@code false} if the value is {@code null}
858      */
859     protected static boolean isAaiTrue(String aaiValue) {
860         return ("true".equalsIgnoreCase(aaiValue) || "T".equalsIgnoreCase(aaiValue) || "yes".equalsIgnoreCase(aaiValue)
861                 || "Y".equalsIgnoreCase(aaiValue));
862     }
863
864     /**
865      * Get the A&AI VService information for an event.
866      *
867      * @param event the event
868      * @return a AaiGetVserverResponse
869      * @throws ControlLoopException if an error occurs
870      */
871     public static AaiGetVserverResponse getAaiVserverInfo(VirtualControlLoopEvent event) throws ControlLoopException {
872         UUID requestId = event.getRequestId();
873         AaiGetVserverResponse response = null;
874         String vserverName = event.getAai().get(VSERVER_VSERVER_NAME);
875
876         try {
877             if (vserverName != null) {
878                 String aaiHostUrl = PolicyEngine.manager.getEnvironmentProperty(AAI_URL);
879                 String aaiUser = PolicyEngine.manager.getEnvironmentProperty(AAI_USERNAME_PROPERTY);
880                 String aaiPassword = PolicyEngine.manager.getEnvironmentProperty(AAI_PASS_PROPERTY);
881                 String aaiGetQueryByVserver = "/aai/v11/nodes/vservers?vserver-name=";
882                 String url = aaiHostUrl + aaiGetQueryByVserver;
883                 logger.info("AAI Host URL by VServer: {}", url);
884                 response = new AaiManager(new RestManager()).getQueryByVserverName(url, aaiUser, aaiPassword, requestId,
885                         vserverName);
886             }
887         } catch (Exception e) {
888             logger.error("getAaiVserverInfo exception: ", e);
889             throw new ControlLoopException("Exception in getAaiVserverInfo: ", e);
890         }
891
892         return response;
893     }
894
895     /**
896      * Get A&AI VNF information for an event.
897      *
898      * @param event the event
899      * @return a AaiGetVnfResponse
900      * @throws ControlLoopException if an error occurs
901      */
902     public static AaiGetVnfResponse getAaiVnfInfo(VirtualControlLoopEvent event) throws ControlLoopException {
903         UUID requestId = event.getRequestId();
904         AaiGetVnfResponse response = null;
905         String vnfName = event.getAai().get(GENERIC_VNF_VNF_NAME);
906         String vnfId = event.getAai().get(GENERIC_VNF_VNF_ID);
907
908         String aaiHostUrl = PolicyEngine.manager.getEnvironmentProperty(AAI_URL);
909         String aaiUser = PolicyEngine.manager.getEnvironmentProperty(AAI_USERNAME_PROPERTY);
910         String aaiPassword = PolicyEngine.manager.getEnvironmentProperty(AAI_PASS_PROPERTY);
911
912         try {
913             if (vnfName != null) {
914                 String aaiGetQueryByVnfName = "/aai/v11/network/generic-vnfs/generic-vnf?vnf-name=";
915                 String url = aaiHostUrl + aaiGetQueryByVnfName;
916                 logger.info("AAI Host URL by VNF name: {}", url);
917                 response = new AaiManager(new RestManager()).getQueryByVnfName(url, aaiUser, aaiPassword, requestId,
918                         vnfName);
919             } else if (vnfId != null) {
920                 String aaiGetQueryByVnfId = "/aai/v11/network/generic-vnfs/generic-vnf/";
921                 String url = aaiHostUrl + aaiGetQueryByVnfId;
922                 logger.info("AAI Host URL by VNF ID: {}", url);
923                 response =
924                         new AaiManager(new RestManager()).getQueryByVnfId(url, aaiUser, aaiPassword, requestId, vnfId);
925             }
926         } catch (Exception e) {
927             logger.error("getAaiVnfInfo exception: ", e);
928             throw new ControlLoopException("Exception in getAaiVnfInfo: ", e);
929         }
930
931         return response;
932     }
933
934     /**
935      * Gets the output from the AAI vserver named-query, using the cache, if appropriate.
936      *
937      * @return output from the AAI vserver named-query
938      */
939     public AaiNqResponseWrapper getNqVserverFromAai() {
940         if (nqVserverResponse != null) {
941             // already queried
942             return nqVserverResponse;
943         }
944
945         String vserverName = onset.getAai().get(VSERVER_VSERVER_NAME);
946         if (vserverName == null) {
947             logger.warn("Missing vserver-name for AAI request {}", onset.getRequestId());
948             return null;
949         }
950
951         // create AAI named-query request with UUID started with ""
952         AaiNqRequest aaiNqRequest = new AaiNqRequest();
953         AaiNqQueryParameters aaiNqQueryParam = new AaiNqQueryParameters();
954         AaiNqNamedQuery aaiNqNamedQuery = new AaiNqNamedQuery();
955         final AaiNqInstanceFilters aaiNqInstanceFilter = new AaiNqInstanceFilters();
956
957         // queryParameters
958         aaiNqNamedQuery.setNamedQueryUuid(UUID.fromString("4ff56a54-9e3f-46b7-a337-07a1d3c6b469"));
959         aaiNqQueryParam.setNamedQuery(aaiNqNamedQuery);
960         aaiNqRequest.setQueryParameters(aaiNqQueryParam);
961         //
962         // instanceFilters
963         //
964         Map<String, Map<String, String>> aaiNqInstanceFilterMap = new HashMap<>();
965         Map<String, String> aaiNqInstanceFilterMapItem = new HashMap<>();
966         aaiNqInstanceFilterMapItem.put("vserver-name", vserverName);
967         aaiNqInstanceFilterMap.put("vserver", aaiNqInstanceFilterMapItem);
968         aaiNqInstanceFilter.getInstanceFilter().add(aaiNqInstanceFilterMap);
969         aaiNqRequest.setInstanceFilters(aaiNqInstanceFilter);
970
971         if (logger.isDebugEnabled()) {
972             logger.debug("AAI Request sent: {}", Serialization.gsonPretty.toJson(aaiNqRequest));
973         }
974
975         AaiNqResponse aaiNqResponse = new AaiManager(new RestManager()).postQuery(getPeManagerEnvProperty(AAI_URL),
976                 getPeManagerEnvProperty(AAI_USERNAME_PROPERTY), getPeManagerEnvProperty(AAI_PASS_PROPERTY),
977                 aaiNqRequest, onset.getRequestId());
978
979         // Check AAI response
980         if (aaiNqResponse == null) {
981             logger.warn("No response received from AAI for request {}", aaiNqRequest);
982             return null;
983         }
984
985         // Create AAINQResponseWrapper
986         nqVserverResponse = new AaiNqResponseWrapper(onset.getRequestId(), aaiNqResponse);
987
988         if (logger.isDebugEnabled()) {
989             logger.debug("AAI Named Query Response: ");
990             logger.debug(Serialization.gsonPretty.toJson(nqVserverResponse.getAaiNqResponse()));
991         }
992
993         return nqVserverResponse;
994     }
995
996     /**
997      * This method reads and validates environmental properties coming from the policy engine. Null properties cause an
998      * {@link IllegalArgumentException} runtime exception to be thrown
999      *
1000      * @param enginePropertyName the name of the parameter to retrieve
1001      * @return the property value
1002      */
1003     private static String getPeManagerEnvProperty(String enginePropertyName) {
1004         String enginePropertyValue = PolicyEngine.manager.getEnvironmentProperty(enginePropertyName);
1005         if (enginePropertyValue == null) {
1006             throw new IllegalArgumentException("The value of policy engine manager environment property \""
1007                     + enginePropertyName + "\" may not be null");
1008         }
1009         return enginePropertyValue;
1010     }
1011
1012     @Override
1013     public boolean isActive() {
1014         // TODO
1015         return true;
1016     }
1017
1018     @Override
1019     public boolean releaseLock() {
1020         // TODO
1021         return false;
1022     }
1023
1024     @Override
1025     public String toString() {
1026         return "ControlLoopEventManager [closedLoopControlName=" + closedLoopControlName + ", requestId=" + requestId
1027                 + ", processor=" + processor + ", onset=" + (onset != null ? onset.getRequestId() : "null")
1028                 + ", numOnsets=" + numOnsets + ", numAbatements=" + numAbatements + ", isActivated=" + isActivated
1029                 + ", currentOperation=" + currentOperation + ", targetLock=" + targetLock + "]";
1030     }
1031
1032     /**
1033      * This function calls Aai Custom Query and responds with the AaiCqResponse.
1034      *
1035      * @param event input event
1036      * @return AaiCqResponse Response from Aai for custom query
1037      * @throws AaiException if error occurs
1038      */
1039     public AaiCqResponse getCqResponse(VirtualControlLoopEvent event) throws AaiException {
1040
1041         Map<String, String> aai = event.getAai();
1042
1043         if (aai.containsKey(VSERVER_IS_CLOSED_LOOP_DISABLED) || aai.containsKey(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)) {
1044
1045             if (isClosedLoopDisabled(event)) {
1046                 throw new AaiException("is-closed-loop-disabled is set to true on VServer or VNF");
1047             }
1048
1049             if (isProvStatusInactive(event)) {
1050                 throw new AaiException("prov-status is not ACTIVE on VServer or VNF");
1051             }
1052         }
1053
1054         if (!aai.containsKey(VSERVER_VSERVER_NAME)) {
1055             throw new AaiException("Vserver name is missing");
1056         }
1057
1058         UUID reqId = event.getRequestId();
1059         AaiCqResponse response = null;
1060         String vserverId = event.getAai().get(VSERVER_VSERVER_NAME);
1061
1062         String aaiHostUrl = PolicyEngine.manager.getEnvironmentProperty(AAI_URL);
1063         String aaiUser = PolicyEngine.manager.getEnvironmentProperty(AAI_USERNAME_PROPERTY);
1064         String aaiPassword = PolicyEngine.manager.getEnvironmentProperty(AAI_PASS_PROPERTY);
1065
1066         response = new AaiManager(new RestManager()).getCustomQueryResponse(aaiHostUrl, aaiUser, aaiPassword, reqId,
1067                 vserverId);
1068         return response;
1069
1070     }
1071
1072 }