Merge "tosca compliant op policy support + vcpe test"
[policy/drools-applications.git] / controlloop / common / eventmanager / src / main / java / org / onap / policy / controlloop / eventmanager / ControlLoopEventManager.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * controlloop event manager
4  * ================================================================================
5  * Copyright (C) 2017-2020 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.Collections;
31 import java.util.HashSet;
32 import java.util.LinkedList;
33 import java.util.Map;
34 import java.util.NoSuchElementException;
35 import java.util.Set;
36 import java.util.UUID;
37 import java.util.stream.Collectors;
38 import java.util.stream.Stream;
39 import org.apache.commons.lang3.StringUtils;
40 import org.onap.policy.aai.AaiCqResponse;
41 import org.onap.policy.aai.AaiManager;
42 import org.onap.policy.aai.util.AaiException;
43 import org.onap.policy.controlloop.ControlLoopEventStatus;
44 import org.onap.policy.controlloop.ControlLoopException;
45 import org.onap.policy.controlloop.ControlLoopNotificationType;
46 import org.onap.policy.controlloop.ControlLoopOperation;
47 import org.onap.policy.controlloop.VirtualControlLoopEvent;
48 import org.onap.policy.controlloop.VirtualControlLoopNotification;
49 import org.onap.policy.controlloop.policy.FinalResult;
50 import org.onap.policy.controlloop.policy.Policy;
51 import org.onap.policy.controlloop.processor.ControlLoopProcessor;
52 import org.onap.policy.drools.core.lock.Lock;
53 import org.onap.policy.drools.core.lock.LockCallback;
54 import org.onap.policy.drools.core.lock.LockImpl;
55 import org.onap.policy.drools.core.lock.LockState;
56 import org.onap.policy.drools.system.PolicyEngineConstants;
57 import org.onap.policy.drools.utils.Pair;
58 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
59 import org.onap.policy.rest.RestManager;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63 public class ControlLoopEventManager implements Serializable {
64     public static final String PROV_STATUS_ACTIVE = "ACTIVE";
65     private static final String VM_NAME = "VM_NAME";
66     private static final String VNF_NAME = "VNF_NAME";
67     public static final String GENERIC_VNF_VNF_ID = "generic-vnf.vnf-id";
68     public static final String GENERIC_VNF_VNF_NAME = "generic-vnf.vnf-name";
69     public static final String VSERVER_VSERVER_NAME = "vserver.vserver-name";
70     public static final String GENERIC_VNF_IS_CLOSED_LOOP_DISABLED = "generic-vnf.is-closed-loop-disabled";
71     public static final String VSERVER_IS_CLOSED_LOOP_DISABLED = "vserver.is-closed-loop-disabled";
72     private static final String PNF_IS_IN_MAINT = "pnf.in-maint";
73     public static final String GENERIC_VNF_PROV_STATUS = "generic-vnf.prov-status";
74     public static final String VSERVER_PROV_STATUS = "vserver.prov-status";
75     public static final String PNF_ID = "pnf.pnf-id";
76     public static final String PNF_NAME = "pnf.pnf-name";
77
78     public static final String AAI_URL = "aai.url";
79     public static final String AAI_USERNAME_PROPERTY = "aai.username";
80     public static final String AAI_PASS_PROPERTY = "aai.password";
81
82
83     /**
84      * Additional time, in seconds, to add to a "lock" request. This ensures that the lock won't expire right before an
85      * operation completes.
86      */
87     private static final int ADDITIONAL_LOCK_SEC = 60;
88
89     private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventManager.class);
90
91     private static final long serialVersionUID = -1216568161322872641L;
92
93     private static final Set<String> VALID_TARGETS;
94
95     static {
96         VALID_TARGETS = Collections.unmodifiableSet(new HashSet<>(
97                 Stream.of(VM_NAME, VNF_NAME, VSERVER_VSERVER_NAME, GENERIC_VNF_VNF_ID, GENERIC_VNF_VNF_NAME, PNF_NAME)
98                         .map(String::toLowerCase).collect(Collectors.toList())));
99     }
100
101     public final String closedLoopControlName;
102     private final UUID requestId;
103
104     private String controlLoopResult;
105     private ControlLoopProcessor processor = null;
106     private VirtualControlLoopEvent onset;
107     private Integer numOnsets = 0;
108     private Integer numAbatements = 0;
109     private VirtualControlLoopEvent abatement;
110     private FinalResult controlLoopTimedOut = null;
111
112     private boolean isActivated = false;
113     private LinkedList<ControlLoopOperation> controlLoopHistory = new LinkedList<>();
114     private ControlLoopOperationManager currentOperation = null;
115     private ControlLoopOperationManager lastOperationManager = null;
116     private transient Lock targetLock = null;
117     private boolean useTargetLock = true;
118
119     /**
120      * Constructs the object.
121      *
122      * @param closedLoopControlName name of the control loop
123      * @param requestId ID of the request with which this manager is associated
124      */
125     public ControlLoopEventManager(String closedLoopControlName, UUID requestId) {
126         this.closedLoopControlName = closedLoopControlName;
127         this.requestId = requestId;
128     }
129
130     public String getClosedLoopControlName() {
131         return closedLoopControlName;
132     }
133
134     public String getControlLoopResult() {
135         return controlLoopResult;
136     }
137
138     public void setControlLoopResult(String controlLoopResult) {
139         this.controlLoopResult = controlLoopResult;
140     }
141
142     public Integer getNumOnsets() {
143         return numOnsets;
144     }
145
146     public void setNumOnsets(Integer numOnsets) {
147         this.numOnsets = numOnsets;
148     }
149
150     public Integer getNumAbatements() {
151         return numAbatements;
152     }
153
154     public void setNumAbatements(Integer numAbatements) {
155         this.numAbatements = numAbatements;
156     }
157
158     public boolean isActivated() {
159         return isActivated;
160     }
161
162     public void setActivated(boolean isActivated) {
163         this.isActivated = isActivated;
164     }
165
166     public boolean useTargetLock() {
167         return useTargetLock();
168     }
169
170     public void setUseTargetLock(boolean useTargetLock) {
171         this.useTargetLock = useTargetLock;
172     }
173
174     public VirtualControlLoopEvent getOnsetEvent() {
175         return this.onset;
176     }
177
178     public VirtualControlLoopEvent getAbatementEvent() {
179         return this.abatement;
180     }
181
182     public ControlLoopProcessor getProcessor() {
183         return this.processor;
184     }
185
186     public UUID getRequestId() {
187         return requestId;
188     }
189
190
191     private VirtualControlLoopNotification rejectNotification(VirtualControlLoopEvent event, String message) {
192         VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
193         notification.setNotification(ControlLoopNotificationType.REJECTED);
194         notification.setMessage(message);
195         return notification;
196     }
197
198     /**
199      * Preactivations check for an event.
200      *
201      * @param event the event
202      * @return the VirtualControlLoopNotification
203      */
204     private VirtualControlLoopNotification preActivationChecks(VirtualControlLoopEvent event) {
205         try {
206             //
207             // This method should ONLY be called ONCE
208             //
209             if (this.isActivated) {
210                 return rejectNotification(event, "ControlLoopEventManager has already been activated.");
211             }
212
213             //
214             // Syntax check the event
215             //
216             checkEventSyntax(event);
217         } catch (ControlLoopException e) {
218             logger.warn("{}: invalid event syntax: ", this, e);
219             return rejectNotification(event, e.getMessage());
220
221         }
222
223         return new VirtualControlLoopNotification(event);
224     }
225
226     /**
227      * Activate a control loop event.
228      *
229      * @param event the event
230      * @return the VirtualControlLoopNotification
231      */
232     public VirtualControlLoopNotification activate(VirtualControlLoopEvent event) {
233         VirtualControlLoopNotification notification = preActivationChecks(event);
234         if (notification.getNotification() == ControlLoopNotificationType.REJECTED) {
235             return notification;
236         }
237
238         return postActivate(event, notification);
239     }
240
241     /**
242      * Activate a control loop event.
243      *
244      * @param yamlSpecification the yaml specification
245      * @param event the event
246      * @return the VirtualControlLoopNotification
247      */
248     public VirtualControlLoopNotification activate(String yamlSpecification, VirtualControlLoopEvent event) {
249         VirtualControlLoopNotification notification = preActivationChecks(event);
250         if (notification.getNotification() == ControlLoopNotificationType.REJECTED) {
251             return notification;
252         }
253
254         if (yamlSpecification == null || yamlSpecification.length() < 1) {
255             return rejectNotification(event, "yaml specification is null or 0 length");
256         }
257
258         String decodedYaml = null;
259         try {
260             decodedYaml = URLDecoder.decode(yamlSpecification, "UTF-8");
261             if (decodedYaml != null && decodedYaml.length() > 0) {
262                 yamlSpecification = decodedYaml;
263             }
264         } catch (UnsupportedEncodingException e) {
265             logger.warn("{}: YAML decode in activate by YAML specification and event threw: ", this, e);
266             return rejectNotification(event, e.getMessage());
267         }
268
269         try {
270             //
271             // Parse the YAML specification
272             //
273             this.processor = new ControlLoopProcessor(yamlSpecification);
274         } catch (ControlLoopException e) {
275             logger.error("{}: activate by YAML specification and event threw: ", this, e);
276             return rejectNotification(event, e.getMessage());
277         }
278
279         return postActivate(event, notification);
280     }
281
282     /**
283      * Activate a control loop event.
284      *
285      * @param toscaPolicy the tosca policy
286      * @param event the event
287      * @return the VirtualControlLoopNotification
288      */
289     public VirtualControlLoopNotification activate(ToscaPolicy toscaPolicy, VirtualControlLoopEvent event) {
290         VirtualControlLoopNotification notification = preActivationChecks(event);
291         if (notification.getNotification() == ControlLoopNotificationType.REJECTED) {
292             return notification;
293         }
294
295         try {
296             this.processor = new ControlLoopProcessor(toscaPolicy);
297         } catch (ControlLoopException e) {
298             logger.error("{}: activate from Tosca Policy threw: ", this, e);
299             return rejectNotification(event, e.getMessage());
300         }
301
302         return postActivate(event, notification);
303     }
304
305     private VirtualControlLoopNotification postActivate(
306             VirtualControlLoopEvent event, VirtualControlLoopNotification notification) {
307         //
308         // At this point we are good to go with this event
309         //
310         this.onset = event;
311         this.numOnsets = 1;
312
313         //
314         // Set ourselves as active
315         //
316         this.isActivated = true;
317
318         notification.setNotification(ControlLoopNotificationType.ACTIVE);
319         return notification;
320     }
321
322     /**
323      * Check if the control loop is final.
324      *
325      * @return a VirtualControlLoopNotification if the control loop is final, otherwise <code>null</code> is returned
326      * @throws ControlLoopException if an error occurs
327      */
328     public VirtualControlLoopNotification isControlLoopFinal() throws ControlLoopException {
329         validateFinalControlLoop();
330         //
331         // Ok, start creating the notification
332         //
333         VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
334         //
335         // Check if the overall control loop has timed out
336         //
337         if (this.isControlLoopTimedOut()) {
338             //
339             // Yes we have timed out
340             //
341             notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
342             notification.setMessage("Control Loop timed out");
343             notification.getHistory().addAll(this.controlLoopHistory);
344             return notification;
345         }
346         //
347         // Check if the current policy is Final
348         //
349         FinalResult result = this.processor.checkIsCurrentPolicyFinal();
350         if (result == null) {
351             //
352             // we are not at a final result
353             //
354             return null;
355         }
356
357         switch (result) {
358             case FINAL_FAILURE_EXCEPTION:
359                 notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
360                 notification.setMessage("Exception in processing closed loop");
361                 break;
362             case FINAL_FAILURE:
363             case FINAL_FAILURE_RETRIES:
364             case FINAL_FAILURE_TIMEOUT:
365             case FINAL_FAILURE_GUARD:
366                 notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
367                 break;
368             case FINAL_OPENLOOP:
369                 notification.setNotification(ControlLoopNotificationType.FINAL_OPENLOOP);
370                 break;
371             case FINAL_SUCCESS:
372                 notification.setNotification(ControlLoopNotificationType.FINAL_SUCCESS);
373                 break;
374             default:
375                 return null;
376         }
377         //
378         // Be sure to add all the history
379         //
380         notification.getHistory().addAll(this.controlLoopHistory);
381         return notification;
382     }
383
384     private void validateFinalControlLoop() throws ControlLoopException {
385         //
386         // Check if they activated us
387         //
388         if (!this.isActivated) {
389             throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
390         }
391         //
392         // Make sure we are expecting this call.
393         //
394         if (this.onset == null) {
395             throw new ControlLoopException("No onset event for ControlLoopEventManager.");
396         }
397     }
398
399     /**
400      * Process the control loop.
401      *
402      * @return a ControlLoopOperationManager
403      * @throws ControlLoopException if an error occurs
404      */
405     public ControlLoopOperationManager processControlLoop() throws ControlLoopException {
406         validateFinalControlLoop();
407         //
408         // Is there a current operation?
409         //
410         if (this.currentOperation != null) {
411             //
412             // Throw an exception, or simply return the current operation?
413             //
414             throw new ControlLoopException("Already working an Operation, do not call this method.");
415         }
416         //
417         // Ensure we are not FINAL
418         //
419         VirtualControlLoopNotification notification = this.isControlLoopFinal();
420         if (notification != null) {
421             //
422             // This is weird, we require them to call the isControlLoopFinal() method first
423             //
424             // We should really abstract this and avoid throwing an exception, because it really
425             // isn't an exception.
426             //
427             throw new ControlLoopException("Control Loop is in FINAL state, do not call this method.");
428         }
429         //
430         // Not final so get the policy that needs to be worked on.
431         //
432         Policy policy = this.processor.getCurrentPolicy();
433         if (policy == null) {
434             throw new ControlLoopException("ControlLoopEventManager: processor came upon null Policy.");
435         }
436         //
437         // And setup an operation
438         //
439         this.lastOperationManager = this.currentOperation;
440         this.currentOperation = new ControlLoopOperationManager(this.onset, policy, this);
441         //
442         // Return it
443         //
444         return this.currentOperation;
445     }
446
447     /**
448      * Finish an operation.
449      *
450      * @param operation the operation
451      */
452     public void finishOperation(ControlLoopOperationManager operation) throws ControlLoopException {
453         //
454         // Verify we have a current operation
455         //
456         if (this.currentOperation != null) {
457             //
458             // Validate they are finishing the current operation
459             // PLD - this is simply comparing the policy. Do we want to equals the whole object?
460             //
461             if (this.currentOperation.policy.equals(operation.policy)) {
462                 logger.debug("Finishing {} result is {}", this.currentOperation.policy.getRecipe(),
463                         this.currentOperation.getOperationResult());
464                 //
465                 // Save history
466                 //
467                 this.controlLoopHistory.addAll(this.currentOperation.getHistory());
468                 //
469                 // Move to the next Policy
470                 //
471                 this.processor.nextPolicyForResult(this.currentOperation.getOperationResult());
472                 //
473                 // Just null this out
474                 //
475                 this.lastOperationManager = this.currentOperation;
476                 this.currentOperation = null;
477
478                 //
479                 // Don't release the lock - it may be re-used by the next operation
480                 //
481
482                 return;
483             }
484             logger.debug("Cannot finish current operation {} does not match given operation {}",
485                     this.currentOperation.policy, operation.policy);
486             return;
487         }
488         throw new ControlLoopException("No operation to finish.");
489     }
490
491     /**
492      * Obtain a lock for the current operation.
493      *
494      * @param callback call-back to be invoked when the lock state changes
495      * @return a pair containing the old lock and the new lock, either of which may be null
496      * @throws ControlLoopException if an error occurs
497      */
498     public synchronized Pair<Lock, Lock> lockCurrentOperation(LockCallback callback) throws ControlLoopException {
499         //
500         // Sanity check
501         //
502         if (this.currentOperation == null) {
503             throw new ControlLoopException("Do not have a current operation.");
504         }
505
506         //
507         // Release the old lock if it's for a different resource.
508         //
509         Lock oldLock = null;
510         if (this.targetLock != null
511                         && !this.targetLock.getResourceId().equals(this.currentOperation.getTargetEntity())) {
512             logger.debug("{}: different resource - releasing old lock", getClosedLoopControlName());
513             oldLock = this.targetLock;
514             this.targetLock = null;
515         }
516
517         // keep the lock a little longer than the operation, including retries
518         int optimeout = Math.max(1, this.currentOperation.getOperationTimeout());
519         int nattempts = 1 + Math.max(0, this.currentOperation.getMaxRetries());
520         int holdSec = optimeout * nattempts + ADDITIONAL_LOCK_SEC;
521
522         //
523         // Have we acquired it already?
524         //
525         if (this.targetLock != null) {
526             // we have the lock - just extend it
527             this.targetLock.extend(holdSec, callback);
528             return new Pair<>(oldLock, null);
529
530         } else if (this.useTargetLock) {
531             this.targetLock = createRealLock(this.currentOperation.getTargetEntity(), this.onset.getRequestId(),
532                             holdSec, callback);
533             return new Pair<>(oldLock, this.targetLock);
534
535         } else {
536             // Not using target locks - create a lock w/o actually locking.
537             logger.debug("{}: not using target locking; using pseudo locks", getClosedLoopControlName());
538             this.targetLock = createPseudoLock(this.currentOperation.getTargetEntity(), this.onset.getRequestId(),
539                             holdSec, callback);
540
541             // Note: no need to invoke callback, as the lock is already ACTIVE
542
543             return new Pair<>(oldLock, this.targetLock);
544         }
545     }
546
547     /**
548      * Releases the lock for the current operation, deleting it from working memory.
549      *
550      * @return the lock, if the operation was locked, {@code null} otherwise
551      */
552     public synchronized Lock unlockCurrentOperation() {
553         if (this.targetLock == null) {
554             return null;
555         }
556
557         Lock lock = this.targetLock;
558         this.targetLock = null;
559
560         lock.free();
561
562         return lock;
563     }
564
565     public enum NewEventStatus {
566         FIRST_ONSET, SUBSEQUENT_ONSET, FIRST_ABATEMENT, SUBSEQUENT_ABATEMENT, SYNTAX_ERROR;
567     }
568
569     /**
570      * An event onset/abatement.
571      *
572      * @param event the event
573      * @return the status
574      * @throws AaiException if an error occurs retrieving information from A&AI
575      */
576     public NewEventStatus onNewEvent(VirtualControlLoopEvent event) throws AaiException {
577         try {
578             this.checkEventSyntax(event);
579             if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ONSET) {
580                 //
581                 // Check if this is our original ONSET
582                 //
583                 if (event.equals(this.onset)) {
584                     //
585                     // DO NOT retract it
586                     //
587                     return NewEventStatus.FIRST_ONSET;
588                 }
589                 //
590                 // Log that we got an onset
591                 //
592                 this.numOnsets++;
593                 return NewEventStatus.SUBSEQUENT_ONSET;
594             } else if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ABATED) {
595                 //
596                 // Have we already got an abatement?
597                 //
598                 if (this.abatement == null) {
599                     //
600                     // Save this
601                     //
602                     this.abatement = event;
603                     //
604                     // Keep track that we received another
605                     //
606                     this.numAbatements++;
607                     //
608                     //
609                     //
610                     return NewEventStatus.FIRST_ABATEMENT;
611                 } else {
612                     //
613                     // Keep track that we received another
614                     //
615                     this.numAbatements++;
616                     //
617                     //
618                     //
619                     return NewEventStatus.SUBSEQUENT_ABATEMENT;
620                 }
621             }
622         } catch (ControlLoopException e) {
623             logger.error("{}: onNewEvent threw: ", this, e);
624         }
625         return NewEventStatus.SYNTAX_ERROR;
626     }
627
628
629     /**
630      * Commit the abatement to the history database.
631      *
632      * @param message the abatement message
633      * @param outcome the abatement outcome
634      */
635     public void commitAbatement(String message, String outcome) {
636         if (this.lastOperationManager == null) {
637             logger.error("{}: commitAbatement: no operation manager", this);
638             return;
639         }
640         try {
641             this.lastOperationManager.commitAbatement(message, outcome);
642         } catch (NoSuchElementException e) {
643             logger.error("{}: commitAbatement threw an exception ", this, e);
644         }
645     }
646
647
648     /**
649      * Set the control loop time out.
650      *
651      * @return a VirtualControlLoopNotification
652      */
653     public VirtualControlLoopNotification setControlLoopTimedOut() {
654         this.controlLoopTimedOut = FinalResult.FINAL_FAILURE_TIMEOUT;
655         VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
656         notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
657         notification.setMessage("Control Loop timed out");
658         notification.getHistory().addAll(this.controlLoopHistory);
659         return notification;
660     }
661
662     public boolean isControlLoopTimedOut() {
663         return (this.controlLoopTimedOut == FinalResult.FINAL_FAILURE_TIMEOUT);
664     }
665
666     /**
667      * Get the control loop timeout.
668      *
669      * @param defaultTimeout the default timeout
670      * @return the timeout
671      */
672     public int getControlLoopTimeout(Integer defaultTimeout) {
673         if (this.processor != null && this.processor.getControlLoop() != null) {
674             Integer timeout = this.processor.getControlLoop().getTimeout();
675             if (timeout != null && timeout > 0) {
676                 return timeout;
677             }
678         }
679         if (defaultTimeout != null) {
680             return defaultTimeout;
681         }
682         return 0;
683     }
684
685     /**
686      * Check an event syntax.
687      *
688      * @param event the event syntax
689      * @throws ControlLoopException if an error occurs
690      */
691     public void checkEventSyntax(VirtualControlLoopEvent event) throws ControlLoopException {
692         validateStatus(event);
693         if (StringUtils.isBlank(event.getClosedLoopControlName())) {
694             throw new ControlLoopException("No control loop name");
695         }
696         if (event.getRequestId() == null) {
697             throw new ControlLoopException("No request ID");
698         }
699         if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ABATED) {
700             return;
701         }
702         if (StringUtils.isBlank(event.getTarget())) {
703             throw new ControlLoopException("No target field");
704         } else if (!VALID_TARGETS.contains(event.getTarget().toLowerCase())) {
705             throw new ControlLoopException("target field invalid - expecting VM_NAME or VNF_NAME");
706         }
707         validateAaiData(event);
708     }
709
710     private void validateStatus(VirtualControlLoopEvent event) throws ControlLoopException {
711         if (event.getClosedLoopEventStatus() == null
712                 || (event.getClosedLoopEventStatus() != ControlLoopEventStatus.ONSET
713                         && event.getClosedLoopEventStatus() != ControlLoopEventStatus.ABATED)) {
714             throw new ControlLoopException("Invalid value in closedLoopEventStatus");
715         }
716     }
717
718     private void validateAaiData(VirtualControlLoopEvent event) throws ControlLoopException {
719         Map<String, String> eventAai = event.getAai();
720         if (eventAai == null) {
721             throw new ControlLoopException("AAI is null");
722         }
723         switch (event.getTargetType()) {
724             case VM:
725             case VNF:
726                 validateAaiVmVnfData(eventAai);
727                 return;
728             case PNF:
729                 validateAaiPnfData(eventAai);
730                 return;
731             default:
732                 throw new ControlLoopException("The target type is not supported");
733         }
734     }
735
736     private void validateAaiVmVnfData(Map<String, String> eventAai) throws ControlLoopException {
737         if (eventAai.get(GENERIC_VNF_VNF_ID) == null && eventAai.get(VSERVER_VSERVER_NAME) == null
738                     && eventAai.get(GENERIC_VNF_VNF_NAME) == null) {
739             throw new ControlLoopException(
740                     "generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
741         }
742     }
743
744     private void validateAaiPnfData(Map<String, String> eventAai) throws ControlLoopException {
745         if (eventAai.get(PNF_NAME) == null) {
746             throw new ControlLoopException("AAI PNF object key pnf-name is missing");
747         }
748     }
749
750     /**
751      * Is closed loop disabled for an event.
752      *
753      * @param event the event
754      * @return <code>true</code> if the control loop is disabled, <code>false</code> otherwise
755      */
756     public static boolean isClosedLoopDisabled(VirtualControlLoopEvent event) {
757         Map<String, String> aai = event.getAai();
758         return (isAaiTrue(aai.get(VSERVER_IS_CLOSED_LOOP_DISABLED))
759                 || isAaiTrue(aai.get(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED))
760                 || isAaiTrue(aai.get(PNF_IS_IN_MAINT)));
761     }
762
763     /**
764      * Does provisioning status, for an event, have a value other than ACTIVE.
765      *
766      * @param event the event
767      * @return {@code true} if the provisioning status is neither ACTIVE nor {@code null}, {@code false} otherwise
768      */
769     protected static boolean isProvStatusInactive(VirtualControlLoopEvent event) {
770         Map<String, String> aai = event.getAai();
771         return (!PROV_STATUS_ACTIVE.equals(aai.getOrDefault(VSERVER_PROV_STATUS, PROV_STATUS_ACTIVE))
772                 || !PROV_STATUS_ACTIVE.equals(aai.getOrDefault(GENERIC_VNF_PROV_STATUS, PROV_STATUS_ACTIVE)));
773     }
774
775     /**
776      * Determines the boolean value represented by the given AAI field value.
777      *
778      * @param aaiValue value to be examined
779      * @return the boolean value represented by the field value, or {@code false} if the value is {@code null}
780      */
781     protected static boolean isAaiTrue(String aaiValue) {
782         return ("true".equalsIgnoreCase(aaiValue) || "T".equalsIgnoreCase(aaiValue) || "yes".equalsIgnoreCase(aaiValue)
783                 || "Y".equalsIgnoreCase(aaiValue));
784     }
785
786     @Override
787     public String toString() {
788         return "ControlLoopEventManager [closedLoopControlName=" + closedLoopControlName + ", requestId=" + requestId
789                 + ", processor=" + processor + ", onset=" + (onset != null ? onset.getRequestId() : "null")
790                 + ", numOnsets=" + numOnsets + ", numAbatements=" + numAbatements + ", isActivated=" + isActivated
791                 + ", currentOperation=" + currentOperation + ", targetLock=" + targetLock + "]";
792     }
793
794     /**
795      * This function calls Aai Custom Query and responds with the AaiCqResponse.
796      *
797      * @param event input event
798      * @return AaiCqResponse Response from Aai for custom query. Can not be null.
799      * @throws AaiException if error occurs
800      */
801     public AaiCqResponse getCqResponse(VirtualControlLoopEvent event) throws AaiException {
802
803         Map<String, String> aai = event.getAai();
804
805         if (aai.containsKey(VSERVER_IS_CLOSED_LOOP_DISABLED) || aai.containsKey(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)) {
806
807             if (isClosedLoopDisabled(event)) {
808                 throw new AaiException("is-closed-loop-disabled is set to true on VServer or VNF");
809             }
810
811             if (isProvStatusInactive(event)) {
812                 throw new AaiException("prov-status is not ACTIVE on VServer or VNF");
813             }
814         }
815
816         if (!aai.containsKey(VSERVER_VSERVER_NAME)) {
817             throw new AaiException("Vserver name is missing");
818         }
819
820         UUID reqId = event.getRequestId();
821         AaiCqResponse response = null;
822         String vserverId = event.getAai().get(VSERVER_VSERVER_NAME);
823
824         String aaiHostUrl = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_URL);
825         String aaiUser = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_USERNAME_PROPERTY);
826         String aaiPassword = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_PASS_PROPERTY);
827
828         response = new AaiManager(new RestManager()).getCustomQueryResponse(aaiHostUrl, aaiUser, aaiPassword, reqId,
829                 vserverId);
830
831         if (response == null) {
832             throw new AaiException("Target vnf-id could not be found");
833         }
834
835         return response;
836
837     }
838
839     /**
840      * Get the specified pnf data from aai.
841      * @param event the event containing pnf id.
842      * @return pnf key value data.
843      * @throws AaiException if an aai error occurs.
844      */
845     public Map<String, String> getPnf(VirtualControlLoopEvent event) throws AaiException {
846         Map<String, String> aai = event.getAai();
847
848         if (!aai.containsKey(PNF_NAME)) {
849             throw new AaiException("Missing unique identifier for PNF AAI object in the event.");
850         }
851
852         UUID reqId = event.getRequestId();
853         String pnfName = event.getAai().get(PNF_NAME);
854         String aaiHostUrl = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_URL);
855         String aaiUser = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_USERNAME_PROPERTY);
856         String aaiPassword = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_PASS_PROPERTY);
857
858         Map<String, String> pnfParams =
859                 new AaiManager(new RestManager()).getPnf(aaiHostUrl, aaiUser, aaiPassword, reqId, pnfName);
860
861         if (pnfParams == null) {
862             throw new AaiException("Aai response is undefined");
863         }
864         return pnfParams;
865     }
866
867
868     // the following methods may be overridden by junit tests
869
870     protected Lock createRealLock(String targetEntity, UUID requestId, int holdSec, LockCallback callback) {
871         return PolicyEngineConstants.getManager().createLock(targetEntity, requestId.toString(), holdSec, callback,
872                         false);
873     }
874
875     // note: the "callback" is required, because it will be invoked when lock.extend() is
876     // invoked
877     protected Lock createPseudoLock(String targetEntity, UUID requestId, int holdSec, LockCallback callback) {
878         return new LockImpl(LockState.ACTIVE, targetEntity, requestId.toString(), holdSec, callback);
879     }
880 }