7d42f8f766e481c01f396ac748eaa558fd8aa2c2
[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.PolicyEngine;
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      * @throws AaiException if an error occurs retrieving information from A&AI
403      */
404     public ControlLoopOperationManager processControlLoop() throws ControlLoopException, AaiException {
405         validateFinalControlLoop();
406         //
407         // Is there a current operation?
408         //
409         if (this.currentOperation != null) {
410             //
411             // Throw an exception, or simply return the current operation?
412             //
413             throw new ControlLoopException("Already working an Operation, do not call this method.");
414         }
415         //
416         // Ensure we are not FINAL
417         //
418         VirtualControlLoopNotification notification = this.isControlLoopFinal();
419         if (notification != null) {
420             //
421             // This is weird, we require them to call the isControlLoopFinal() method first
422             //
423             // We should really abstract this and avoid throwing an exception, because it really
424             // isn't an exception.
425             //
426             throw new ControlLoopException("Control Loop is in FINAL state, do not call this method.");
427         }
428         //
429         // Not final so get the policy that needs to be worked on.
430         //
431         Policy policy = this.processor.getCurrentPolicy();
432         if (policy == null) {
433             throw new ControlLoopException("ControlLoopEventManager: processor came upon null Policy.");
434         }
435         //
436         // And setup an operation
437         //
438         this.lastOperationManager = this.currentOperation;
439         this.currentOperation = new ControlLoopOperationManager(this.onset, policy, this);
440         //
441         // Return it
442         //
443         return this.currentOperation;
444     }
445
446     /**
447      * Finish an operation.
448      *
449      * @param operation the operation
450      */
451     public void finishOperation(ControlLoopOperationManager operation) throws ControlLoopException {
452         //
453         // Verify we have a current operation
454         //
455         if (this.currentOperation != null) {
456             //
457             // Validate they are finishing the current operation
458             // PLD - this is simply comparing the policy. Do we want to equals the whole object?
459             //
460             if (this.currentOperation.policy.equals(operation.policy)) {
461                 logger.debug("Finishing {} result is {}", this.currentOperation.policy.getRecipe(),
462                         this.currentOperation.getOperationResult());
463                 //
464                 // Save history
465                 //
466                 this.controlLoopHistory.addAll(this.currentOperation.getHistory());
467                 //
468                 // Move to the next Policy
469                 //
470                 this.processor.nextPolicyForResult(this.currentOperation.getOperationResult());
471                 //
472                 // Just null this out
473                 //
474                 this.lastOperationManager = this.currentOperation;
475                 this.currentOperation = null;
476                 //
477                 // TODO: Release our lock
478                 //
479                 return;
480             }
481             logger.debug("Cannot finish current operation {} does not match given operation {}",
482                     this.currentOperation.policy, operation.policy);
483             return;
484         }
485         throw new ControlLoopException("No operation to finish.");
486     }
487
488     /**
489      * Obtain a lock for the current operation.
490      *
491      * @return the lock result
492      * @throws ControlLoopException if an error occurs
493      */
494     public synchronized LockResult<GuardResult, TargetLock> lockCurrentOperation() throws ControlLoopException {
495         //
496         // Sanity check
497         //
498         if (this.currentOperation == null) {
499             throw new ControlLoopException("Do not have a current operation.");
500         }
501         //
502         // Not using target locks? Create and return a lock w/o actually locking.
503         //
504         if (!this.useTargetLock) {
505             TargetLock lock = PolicyGuard.createTargetLock(this.currentOperation.policy.getTarget().getType(),
506                     this.currentOperation.getTargetEntity(), this.onset.getRequestId(), this);
507             this.targetLock = lock;
508             return LockResult.createLockResult(GuardResult.LOCK_ACQUIRED, lock);
509         }
510         //
511         // Have we acquired it already?
512         //
513         if (this.targetLock != null) {
514             //
515             // TODO: Make sure the current lock is for the same target.
516             // Currently, it should be. But in the future it may not.
517             //
518             GuardResult result = PolicyGuard.lockTarget(targetLock,
519                     this.currentOperation.getOperationTimeout() + ADDITIONAL_LOCK_SEC);
520             return new LockResult<>(result, this.targetLock);
521         } else {
522             //
523             // Ask the Guard
524             //
525             LockResult<GuardResult, TargetLock> lockResult = PolicyGuard.lockTarget(
526                     this.currentOperation.policy.getTarget().getType(), this.currentOperation.getTargetEntity(),
527                     this.onset.getRequestId(), this, this.currentOperation.getOperationTimeout() + ADDITIONAL_LOCK_SEC);
528             //
529             // Was it acquired?
530             //
531             if (lockResult.getA().equals(GuardResult.LOCK_ACQUIRED)) {
532                 //
533                 // Yes, let's save it
534                 //
535                 this.targetLock = lockResult.getB();
536             }
537             return lockResult;
538         }
539     }
540
541     /**
542      * Release the lock for the current operation.
543      *
544      * @return the target lock
545      */
546     public synchronized TargetLock unlockCurrentOperation() {
547         if (this.targetLock == null) {
548             return null;
549         }
550
551         TargetLock returnLock = this.targetLock;
552         this.targetLock = null;
553         //
554         // if using target locking unlock before returning
555         //
556         if (this.useTargetLock) {
557             PolicyGuard.unlockTarget(returnLock);
558         }
559
560         // always return the old target lock so rules can retract it
561         return returnLock;
562     }
563
564     public enum NewEventStatus {
565         FIRST_ONSET, SUBSEQUENT_ONSET, FIRST_ABATEMENT, SUBSEQUENT_ABATEMENT, SYNTAX_ERROR;
566     }
567
568     /**
569      * An event onset/abatement.
570      *
571      * @param event the event
572      * @return the status
573      * @throws AaiException if an error occurs retrieving information from A&AI
574      */
575     public NewEventStatus onNewEvent(VirtualControlLoopEvent event) throws AaiException {
576         try {
577             this.checkEventSyntax(event);
578             if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ONSET) {
579                 //
580                 // Check if this is our original ONSET
581                 //
582                 if (event.equals(this.onset)) {
583                     //
584                     // Query A&AI if needed
585                     //
586                     queryAai(event);
587
588                     //
589                     // DO NOT retract it
590                     //
591                     return NewEventStatus.FIRST_ONSET;
592                 }
593                 //
594                 // Log that we got an onset
595                 //
596                 this.numOnsets++;
597                 return NewEventStatus.SUBSEQUENT_ONSET;
598             } else if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ABATED) {
599                 //
600                 // Have we already got an abatement?
601                 //
602                 if (this.abatement == null) {
603                     //
604                     // Save this
605                     //
606                     this.abatement = event;
607                     //
608                     // Keep track that we received another
609                     //
610                     this.numAbatements++;
611                     //
612                     //
613                     //
614                     return NewEventStatus.FIRST_ABATEMENT;
615                 } else {
616                     //
617                     // Keep track that we received another
618                     //
619                     this.numAbatements++;
620                     //
621                     //
622                     //
623                     return NewEventStatus.SUBSEQUENT_ABATEMENT;
624                 }
625             }
626         } catch (ControlLoopException e) {
627             logger.error("{}: onNewEvent threw: ", this, e);
628         }
629         return NewEventStatus.SYNTAX_ERROR;
630     }
631
632
633     /**
634      * Commit the abatement to the history database.
635      *
636      * @param message the abatement message
637      * @param outcome the abatement outcome
638      */
639     public void commitAbatement(String message, String outcome) {
640         if (this.lastOperationManager == null) {
641             logger.error("{}: commitAbatement: no operation manager", this);
642             return;
643         }
644         try {
645             this.lastOperationManager.commitAbatement(message, outcome);
646         } catch (NoSuchElementException e) {
647             logger.error("{}: commitAbatement threw an exception ", this, e);
648         }
649     }
650
651
652     /**
653      * Set the control loop time out.
654      *
655      * @return a VirtualControlLoopNotification
656      */
657     public VirtualControlLoopNotification setControlLoopTimedOut() {
658         this.controlLoopTimedOut = FinalResult.FINAL_FAILURE_TIMEOUT;
659         VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
660         notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
661         notification.setMessage("Control Loop timed out");
662         notification.getHistory().addAll(this.controlLoopHistory);
663         return notification;
664     }
665
666     public boolean isControlLoopTimedOut() {
667         return (this.controlLoopTimedOut == FinalResult.FINAL_FAILURE_TIMEOUT);
668     }
669
670     /**
671      * Get the control loop timeout.
672      *
673      * @param defaultTimeout the default timeout
674      * @return the timeout
675      */
676     public int getControlLoopTimeout(Integer defaultTimeout) {
677         if (this.processor != null && this.processor.getControlLoop() != null) {
678             Integer timeout = this.processor.getControlLoop().getTimeout();
679             if (timeout != null && timeout > 0) {
680                 return timeout;
681             }
682         }
683         if (defaultTimeout != null) {
684             return defaultTimeout;
685         }
686         return 0;
687     }
688
689     public AaiGetVnfResponse getVnfResponse() {
690         return vnfResponse;
691     }
692
693     public AaiGetVserverResponse getVserverResponse() {
694         return vserverResponse;
695     }
696
697     /**
698      * Check an event syntax.
699      *
700      * @param event the event syntax
701      * @throws ControlLoopException if an error occurs
702      */
703     public void checkEventSyntax(VirtualControlLoopEvent event) throws ControlLoopException {
704         validateStatus(event);
705         if (StringUtils.isBlank(event.getClosedLoopControlName())) {
706             throw new ControlLoopException("No control loop name");
707         }
708         if (event.getRequestId() == null) {
709             throw new ControlLoopException("No request ID");
710         }
711         if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ABATED) {
712             return;
713         }
714         if (StringUtils.isBlank(event.getTarget())) {
715             throw new ControlLoopException("No target field");
716         } else if (!VALID_TARGETS.contains(event.getTarget().toLowerCase())) {
717             throw new ControlLoopException("target field invalid - expecting VM_NAME or VNF_NAME");
718         }
719         validateAaiData(event);
720     }
721
722     private void validateStatus(VirtualControlLoopEvent event) throws ControlLoopException {
723         if (event.getClosedLoopEventStatus() == null
724                 || (event.getClosedLoopEventStatus() != ControlLoopEventStatus.ONSET
725                         && event.getClosedLoopEventStatus() != ControlLoopEventStatus.ABATED)) {
726             throw new ControlLoopException("Invalid value in closedLoopEventStatus");
727         }
728     }
729
730     private void validateAaiData(VirtualControlLoopEvent event) throws ControlLoopException {
731         if (event.getAai() == null) {
732             throw new ControlLoopException("AAI is null");
733         }
734         if (event.getAai().get(GENERIC_VNF_VNF_ID) == null && event.getAai().get(VSERVER_VSERVER_NAME) == null
735                 && event.getAai().get(GENERIC_VNF_VNF_NAME) == null) {
736             throw new ControlLoopException(
737                     "generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
738         }
739     }
740
741     /**
742      * Query A&AI for an event.
743      *
744      * @param event the event
745      * @throws AaiException if an error occurs retrieving information from A&AI
746      */
747     public void queryAai(VirtualControlLoopEvent event) throws AaiException {
748
749         Map<String, String> aai = event.getAai();
750
751         if (aai.containsKey(VSERVER_IS_CLOSED_LOOP_DISABLED) || aai.containsKey(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)) {
752
753             if (isClosedLoopDisabled(event)) {
754                 throw new AaiException("is-closed-loop-disabled is set to true on VServer or VNF");
755             }
756
757             if (isProvStatusInactive(event)) {
758                 throw new AaiException("prov-status is not ACTIVE on VServer or VNF");
759             }
760
761             // no need to query, as we already have the data
762             return;
763         }
764
765         if (vnfResponse != null || vserverResponse != null) {
766             // query has already been performed
767             return;
768         }
769
770         try {
771             if (aai.containsKey(GENERIC_VNF_VNF_ID) || aai.containsKey(GENERIC_VNF_VNF_NAME)) {
772                 vnfResponse = getAaiVnfInfo(event);
773                 processVnfResponse(vnfResponse, aai.containsKey(GENERIC_VNF_VNF_ID));
774             } else if (aai.containsKey(VSERVER_VSERVER_NAME)) {
775                 vserverResponse = getAaiVserverInfo(event);
776                 processVServerResponse(vserverResponse);
777             }
778         } catch (AaiException e) {
779             logger.error(QUERY_AAI_ERROR_MSG, e);
780             throw e;
781         } catch (Exception e) {
782             logger.error(QUERY_AAI_ERROR_MSG, e);
783             throw new AaiException(QUERY_AAI_ERROR_MSG + e.toString());
784         }
785     }
786
787     /**
788      * Process a response from A&AI for a VNF.
789      *
790      * @param aaiResponse the response from A&AI
791      * @param queryByVnfId <code>true</code> if the query was based on vnf-id, <code>false</code> if the query was based
792      *        on vnf-name
793      * @throws AaiException if an error occurs processing the response
794      */
795     private static void processVnfResponse(AaiGetVnfResponse aaiResponse, boolean queryByVnfId) throws AaiException {
796         String queryTypeString = (queryByVnfId ? "vnf-id" : "vnf-name");
797
798         if (aaiResponse == null) {
799             throw new AaiException("AAI Response is null (query by " + queryTypeString + ")");
800         }
801         if (aaiResponse.getRequestError() != null) {
802             throw new AaiException("AAI Responded with a request error (query by " + queryTypeString + ")");
803         }
804
805         if (aaiResponse.getIsClosedLoopDisabled()) {
806             throw new AaiException("is-closed-loop-disabled is set to true (query by " + queryTypeString + ")");
807         }
808
809         if (!PROV_STATUS_ACTIVE.equals(aaiResponse.getProvStatus())) {
810             throw new AaiException("prov-status is not ACTIVE (query by " + queryTypeString + ")");
811         }
812     }
813
814     /**
815      * Process a response from A&AI for a VServer.
816      *
817      * @param aaiResponse the response from A&AI
818      * @throws AaiException if an error occurs processing the response
819      */
820     private static void processVServerResponse(AaiGetVserverResponse aaiResponse) throws AaiException {
821         if (aaiResponse == null) {
822             throw new AaiException("AAI Response is null (query by vserver-name)");
823         }
824         if (aaiResponse.getRequestError() != null) {
825             throw new AaiException("AAI Responded with a request error (query by vserver-name)");
826         }
827
828         List<AaiNqVServer> lst = aaiResponse.getVserver();
829         if (lst.isEmpty()) {
830             return;
831         }
832
833         AaiNqVServer svr = lst.get(0);
834         if (svr.getIsClosedLoopDisabled()) {
835             throw new AaiException("is-closed-loop-disabled is set to true (query by vserver-name)");
836         }
837
838         if (!PROV_STATUS_ACTIVE.equals(svr.getProvStatus())) {
839             throw new AaiException("prov-status is not ACTIVE (query by vserver-name)");
840         }
841     }
842
843     /**
844      * Is closed loop disabled for an event.
845      *
846      * @param event the event
847      * @return <code>true</code> if the control loop is disabled, <code>false</code> otherwise
848      */
849     public static boolean isClosedLoopDisabled(VirtualControlLoopEvent event) {
850         Map<String, String> aai = event.getAai();
851         return (isAaiTrue(aai.get(VSERVER_IS_CLOSED_LOOP_DISABLED))
852                 || isAaiTrue(aai.get(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)));
853     }
854
855     /**
856      * Does provisioning status, for an event, have a value other than ACTIVE.
857      *
858      * @param event the event
859      * @return {@code true} if the provisioning status is neither ACTIVE nor {@code null}, {@code false} otherwise
860      */
861     protected static boolean isProvStatusInactive(VirtualControlLoopEvent event) {
862         Map<String, String> aai = event.getAai();
863         return (!PROV_STATUS_ACTIVE.equals(aai.getOrDefault(VSERVER_PROV_STATUS, PROV_STATUS_ACTIVE))
864                 || !PROV_STATUS_ACTIVE.equals(aai.getOrDefault(GENERIC_VNF_PROV_STATUS, PROV_STATUS_ACTIVE)));
865     }
866
867     /**
868      * Determines the boolean value represented by the given AAI field value.
869      *
870      * @param aaiValue value to be examined
871      * @return the boolean value represented by the field value, or {@code false} if the value is {@code null}
872      */
873     protected static boolean isAaiTrue(String aaiValue) {
874         return ("true".equalsIgnoreCase(aaiValue) || "T".equalsIgnoreCase(aaiValue) || "yes".equalsIgnoreCase(aaiValue)
875                 || "Y".equalsIgnoreCase(aaiValue));
876     }
877
878     /**
879      * Get the A&AI VService information for an event.
880      *
881      * @param event the event
882      * @return a AaiGetVserverResponse
883      * @throws ControlLoopException if an error occurs
884      */
885     public static AaiGetVserverResponse getAaiVserverInfo(VirtualControlLoopEvent event) throws ControlLoopException {
886         UUID requestId = event.getRequestId();
887         AaiGetVserverResponse response = null;
888         String vserverName = event.getAai().get(VSERVER_VSERVER_NAME);
889
890         try {
891             if (vserverName != null) {
892                 String aaiHostUrl = PolicyEngine.manager.getEnvironmentProperty(AAI_URL);
893                 String aaiUser = PolicyEngine.manager.getEnvironmentProperty(AAI_USERNAME_PROPERTY);
894                 String aaiPassword = PolicyEngine.manager.getEnvironmentProperty(AAI_PASS_PROPERTY);
895                 String aaiGetQueryByVserver = "/aai/v11/nodes/vservers?vserver-name=";
896                 String url = aaiHostUrl + aaiGetQueryByVserver;
897                 logger.info("AAI Host URL by VServer: {}", url);
898                 response = new AaiManager(new RestManager()).getQueryByVserverName(url, aaiUser, aaiPassword, requestId,
899                         vserverName);
900             }
901         } catch (Exception e) {
902             logger.error("getAaiVserverInfo exception: ", e);
903             throw new ControlLoopException("Exception in getAaiVserverInfo: ", e);
904         }
905
906         return response;
907     }
908
909     /**
910      * Get A&AI VNF information for an event.
911      *
912      * @param event the event
913      * @return a AaiGetVnfResponse
914      * @throws ControlLoopException if an error occurs
915      */
916     public static AaiGetVnfResponse getAaiVnfInfo(VirtualControlLoopEvent event) throws ControlLoopException {
917         UUID requestId = event.getRequestId();
918         AaiGetVnfResponse response = null;
919         String vnfName = event.getAai().get(GENERIC_VNF_VNF_NAME);
920         String vnfId = event.getAai().get(GENERIC_VNF_VNF_ID);
921
922         String aaiHostUrl = PolicyEngine.manager.getEnvironmentProperty(AAI_URL);
923         String aaiUser = PolicyEngine.manager.getEnvironmentProperty(AAI_USERNAME_PROPERTY);
924         String aaiPassword = PolicyEngine.manager.getEnvironmentProperty(AAI_PASS_PROPERTY);
925
926         try {
927             if (vnfName != null) {
928                 String aaiGetQueryByVnfName = "/aai/v11/network/generic-vnfs/generic-vnf?vnf-name=";
929                 String url = aaiHostUrl + aaiGetQueryByVnfName;
930                 logger.info("AAI Host URL by VNF name: {}", url);
931                 response = new AaiManager(new RestManager()).getQueryByVnfName(url, aaiUser, aaiPassword, requestId,
932                         vnfName);
933             } else if (vnfId != null) {
934                 String aaiGetQueryByVnfId = "/aai/v11/network/generic-vnfs/generic-vnf/";
935                 String url = aaiHostUrl + aaiGetQueryByVnfId;
936                 logger.info("AAI Host URL by VNF ID: {}", url);
937                 response =
938                         new AaiManager(new RestManager()).getQueryByVnfId(url, aaiUser, aaiPassword, requestId, vnfId);
939             }
940         } catch (Exception e) {
941             logger.error("getAaiVnfInfo exception: ", e);
942             throw new ControlLoopException("Exception in getAaiVnfInfo: ", e);
943         }
944
945         return response;
946     }
947
948     /**
949      * Gets the output from the AAI vserver named-query, using the cache, if appropriate.
950      *
951      * @return output from the AAI vserver named-query
952      */
953     public AaiNqResponseWrapper getNqVserverFromAai() {
954         if (nqVserverResponse != null) {
955             // already queried
956             return nqVserverResponse;
957         }
958
959         String vserverName = onset.getAai().get(VSERVER_VSERVER_NAME);
960         if (vserverName == null) {
961             logger.warn("Missing vserver-name for AAI request {}", onset.getRequestId());
962             return null;
963         }
964         AaiNqRequest aaiNqRequest = getAaiNqRequest(vserverName);
965
966         if (logger.isDebugEnabled()) {
967             logger.debug("AAI Request sent: {}", Serialization.gsonPretty.toJson(aaiNqRequest));
968         }
969
970         AaiNqResponse aaiNqResponse = new AaiManager(new RestManager()).postQuery(getPeManagerEnvProperty(AAI_URL),
971                 getPeManagerEnvProperty(AAI_USERNAME_PROPERTY), getPeManagerEnvProperty(AAI_PASS_PROPERTY),
972                 aaiNqRequest, onset.getRequestId());
973
974         // Check AAI response
975         if (aaiNqResponse == null) {
976             logger.warn("No response received from AAI for request {}", aaiNqRequest);
977             return null;
978         }
979
980         // Create AAINQResponseWrapper
981         nqVserverResponse = new AaiNqResponseWrapper(onset.getRequestId(), aaiNqResponse);
982
983         if (logger.isDebugEnabled()) {
984             logger.debug("AAI Named Query Response: ");
985             logger.debug(Serialization.gsonPretty.toJson(nqVserverResponse.getAaiNqResponse()));
986         }
987
988         return nqVserverResponse;
989     }
990
991     /**
992      * Gets an AAI Named Query Request object.
993      *
994      * @param vserverName vserver name.
995      * @return the AAI Named Query Request object.
996      */
997     public static AaiNqRequest getAaiNqRequest(String vserverName) {
998         // create AAI named-query request with UUID started with ""
999         AaiNqRequest aaiNqRequest = new AaiNqRequest();
1000         AaiNqQueryParameters aaiNqQueryParam = new AaiNqQueryParameters();
1001         AaiNqNamedQuery aaiNqNamedQuery = new AaiNqNamedQuery();
1002         final AaiNqInstanceFilters aaiNqInstanceFilter = new AaiNqInstanceFilters();
1003
1004         // queryParameters
1005         aaiNqNamedQuery.setNamedQueryUuid(UUID.fromString("4ff56a54-9e3f-46b7-a337-07a1d3c6b469"));
1006         aaiNqQueryParam.setNamedQuery(aaiNqNamedQuery);
1007         aaiNqRequest.setQueryParameters(aaiNqQueryParam);
1008         //
1009         // instanceFilters
1010         //
1011         Map<String, Map<String, String>> aaiNqInstanceFilterMap = new HashMap<>();
1012         Map<String, String> aaiNqInstanceFilterMapItem = new HashMap<>();
1013         aaiNqInstanceFilterMapItem.put("vserver-name", vserverName);
1014         aaiNqInstanceFilterMap.put("vserver", aaiNqInstanceFilterMapItem);
1015         aaiNqInstanceFilter.getInstanceFilter().add(aaiNqInstanceFilterMap);
1016         aaiNqRequest.setInstanceFilters(aaiNqInstanceFilter);
1017         return aaiNqRequest;
1018     }
1019
1020     /**
1021      * This method reads and validates environmental properties coming from the policy engine. Null properties cause an
1022      * {@link IllegalArgumentException} runtime exception to be thrown
1023      *
1024      * @param enginePropertyName the name of the parameter to retrieve
1025      * @return the property value
1026      */
1027     private static String getPeManagerEnvProperty(String enginePropertyName) {
1028         String enginePropertyValue = PolicyEngine.manager.getEnvironmentProperty(enginePropertyName);
1029         if (enginePropertyValue == null) {
1030             throw new IllegalArgumentException("The value of policy engine manager environment property \""
1031                     + enginePropertyName + "\" may not be null");
1032         }
1033         return enginePropertyValue;
1034     }
1035
1036     @Override
1037     public boolean isActive() {
1038         // TODO
1039         return true;
1040     }
1041
1042     @Override
1043     public boolean releaseLock() {
1044         // TODO
1045         return false;
1046     }
1047
1048     @Override
1049     public String toString() {
1050         return "ControlLoopEventManager [closedLoopControlName=" + closedLoopControlName + ", requestId=" + requestId
1051                 + ", processor=" + processor + ", onset=" + (onset != null ? onset.getRequestId() : "null")
1052                 + ", numOnsets=" + numOnsets + ", numAbatements=" + numAbatements + ", isActivated=" + isActivated
1053                 + ", currentOperation=" + currentOperation + ", targetLock=" + targetLock + "]";
1054     }
1055
1056     /**
1057      * This function calls Aai Custom Query and responds with the AaiCqResponse.
1058      *
1059      * @param event input event
1060      * @return AaiCqResponse Response from Aai for custom query
1061      * @throws AaiException if error occurs
1062      */
1063     public AaiCqResponse getCqResponse(VirtualControlLoopEvent event) throws AaiException {
1064
1065         Map<String, String> aai = event.getAai();
1066
1067         if (aai.containsKey(VSERVER_IS_CLOSED_LOOP_DISABLED) || aai.containsKey(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)) {
1068
1069             if (isClosedLoopDisabled(event)) {
1070                 throw new AaiException("is-closed-loop-disabled is set to true on VServer or VNF");
1071             }
1072
1073             if (isProvStatusInactive(event)) {
1074                 throw new AaiException("prov-status is not ACTIVE on VServer or VNF");
1075             }
1076         }
1077
1078         if (!aai.containsKey(VSERVER_VSERVER_NAME)) {
1079             throw new AaiException("Vserver name is missing");
1080         }
1081
1082         UUID reqId = event.getRequestId();
1083         AaiCqResponse response = null;
1084         String vserverId = event.getAai().get(VSERVER_VSERVER_NAME);
1085
1086         String aaiHostUrl = PolicyEngine.manager.getEnvironmentProperty(AAI_URL);
1087         String aaiUser = PolicyEngine.manager.getEnvironmentProperty(AAI_USERNAME_PROPERTY);
1088         String aaiPassword = PolicyEngine.manager.getEnvironmentProperty(AAI_PASS_PROPERTY);
1089
1090         response = new AaiManager(new RestManager()).getCustomQueryResponse(aaiHostUrl, aaiUser, aaiPassword, reqId,
1091                 vserverId);
1092         return response;
1093
1094     }
1095
1096 }