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