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