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