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