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