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