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