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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.policy.controlloop.eventmanager;
23 import java.io.Serializable;
24 import java.io.UnsupportedEncodingException;
25 import java.net.URLDecoder;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.LinkedList;
33 import java.util.List;
35 import java.util.NoSuchElementException;
37 import java.util.UUID;
38 import java.util.stream.Collectors;
39 import org.apache.commons.lang3.StringUtils;
40 import org.onap.policy.aai.AaiCqResponse;
41 import org.onap.policy.aai.AaiGetVnfResponse;
42 import org.onap.policy.aai.AaiGetVserverResponse;
43 import org.onap.policy.aai.AaiManager;
44 import org.onap.policy.aai.AaiNqInstanceFilters;
45 import org.onap.policy.aai.AaiNqNamedQuery;
46 import org.onap.policy.aai.AaiNqQueryParameters;
47 import org.onap.policy.aai.AaiNqRequest;
48 import org.onap.policy.aai.AaiNqResponse;
49 import org.onap.policy.aai.AaiNqResponseWrapper;
50 import org.onap.policy.aai.AaiNqVServer;
51 import org.onap.policy.aai.util.AaiException;
52 import org.onap.policy.controlloop.ControlLoopEventStatus;
53 import org.onap.policy.controlloop.ControlLoopException;
54 import org.onap.policy.controlloop.ControlLoopNotificationType;
55 import org.onap.policy.controlloop.ControlLoopOperation;
56 import org.onap.policy.controlloop.VirtualControlLoopEvent;
57 import org.onap.policy.controlloop.VirtualControlLoopNotification;
58 import org.onap.policy.controlloop.policy.FinalResult;
59 import org.onap.policy.controlloop.policy.Policy;
60 import org.onap.policy.controlloop.processor.ControlLoopProcessor;
61 import org.onap.policy.drools.core.lock.Lock;
62 import org.onap.policy.drools.core.lock.LockCallback;
63 import org.onap.policy.drools.core.lock.LockImpl;
64 import org.onap.policy.drools.core.lock.LockState;
65 import org.onap.policy.drools.system.PolicyEngineConstants;
66 import org.onap.policy.drools.utils.Pair;
67 import org.onap.policy.rest.RestManager;
68 import org.onap.policy.so.util.Serialization;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
72 public class ControlLoopEventManager implements Serializable {
73 public static final String PROV_STATUS_ACTIVE = "ACTIVE";
74 private static final String VM_NAME = "VM_NAME";
75 private static final String VNF_NAME = "VNF_NAME";
76 public static final String GENERIC_VNF_VNF_ID = "generic-vnf.vnf-id";
77 public static final String GENERIC_VNF_VNF_NAME = "generic-vnf.vnf-name";
78 public static final String VSERVER_VSERVER_NAME = "vserver.vserver-name";
79 public static final String GENERIC_VNF_IS_CLOSED_LOOP_DISABLED = "generic-vnf.is-closed-loop-disabled";
80 public static final String VSERVER_IS_CLOSED_LOOP_DISABLED = "vserver.is-closed-loop-disabled";
81 public static final String GENERIC_VNF_PROV_STATUS = "generic-vnf.prov-status";
82 public static final String VSERVER_PROV_STATUS = "vserver.prov-status";
84 public static final String AAI_URL = "aai.url";
85 public static final String AAI_USERNAME_PROPERTY = "aai.username";
86 public static final String AAI_PASS_PROPERTY = "aai.password";
88 private static final String QUERY_AAI_ERROR_MSG = "Exception from queryAai: ";
91 * Additional time, in seconds, to add to a "lock" request. This ensures that the lock won't expire right before an
92 * operation completes.
94 private static final int ADDITIONAL_LOCK_SEC = 60;
96 private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventManager.class);
98 private static final long serialVersionUID = -1216568161322872641L;
100 private static final Set<String> VALID_TARGETS;
103 VALID_TARGETS = Collections.unmodifiableSet(new HashSet<>(
104 Arrays.asList(VM_NAME, VNF_NAME, VSERVER_VSERVER_NAME,
105 GENERIC_VNF_VNF_ID, GENERIC_VNF_VNF_NAME)
106 .stream().map(String::toLowerCase).collect(Collectors.toList())));
109 public final String closedLoopControlName;
110 private final UUID requestId;
112 private String controlLoopResult;
113 private ControlLoopProcessor processor = null;
114 private VirtualControlLoopEvent onset;
115 private Integer numOnsets = 0;
116 private Integer numAbatements = 0;
117 private VirtualControlLoopEvent abatement;
118 private FinalResult controlLoopTimedOut = null;
120 private boolean isActivated = false;
121 private LinkedList<ControlLoopOperation> controlLoopHistory = new LinkedList<>();
122 private ControlLoopOperationManager currentOperation = null;
123 private ControlLoopOperationManager lastOperationManager = null;
124 private transient Lock targetLock = null;
125 private AaiGetVnfResponse vnfResponse = null;
126 private AaiGetVserverResponse vserverResponse = null;
127 private boolean useTargetLock = true;
130 * Wrapper for AAI vserver named-query response. This is initialized in a lazy fashion.
132 private AaiNqResponseWrapper nqVserverResponse = null;
134 private static Collection<String> requiredAAIKeys = new ArrayList<>();
137 requiredAAIKeys.add("AICVServerSelfLink");
138 requiredAAIKeys.add("AICIdentity");
139 requiredAAIKeys.add("is_closed_loop_disabled");
140 requiredAAIKeys.add(VM_NAME);
144 * Constructs the object.
146 * @param closedLoopControlName name of the control loop
147 * @param requestId ID of the request with which this manager is associated
149 public ControlLoopEventManager(String closedLoopControlName, UUID requestId) {
150 this.closedLoopControlName = closedLoopControlName;
151 this.requestId = requestId;
154 public String getClosedLoopControlName() {
155 return closedLoopControlName;
158 public String getControlLoopResult() {
159 return controlLoopResult;
162 public void setControlLoopResult(String controlLoopResult) {
163 this.controlLoopResult = controlLoopResult;
166 public Integer getNumOnsets() {
170 public void setNumOnsets(Integer numOnsets) {
171 this.numOnsets = numOnsets;
174 public Integer getNumAbatements() {
175 return numAbatements;
178 public void setNumAbatements(Integer numAbatements) {
179 this.numAbatements = numAbatements;
182 public boolean isActivated() {
186 public void setActivated(boolean isActivated) {
187 this.isActivated = isActivated;
190 public boolean useTargetLock() {
191 return useTargetLock();
194 public void setUseTargetLock(boolean useTargetLock) {
195 this.useTargetLock = useTargetLock;
198 public VirtualControlLoopEvent getOnsetEvent() {
202 public VirtualControlLoopEvent getAbatementEvent() {
203 return this.abatement;
206 public ControlLoopProcessor getProcessor() {
207 return this.processor;
210 public UUID getRequestId() {
215 * Activate a control loop event.
217 * @param event the event
218 * @return the VirtualControlLoopNotification
220 public VirtualControlLoopNotification activate(VirtualControlLoopEvent event) {
221 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
224 // This method should ONLY be called ONCE
226 if (this.isActivated) {
227 throw new ControlLoopException("ControlLoopEventManager has already been activated.");
230 // Syntax check the event
232 checkEventSyntax(event);
235 // At this point we are good to go with this event
240 notification.setNotification(ControlLoopNotificationType.ACTIVE);
242 // Set ourselves as active
244 this.isActivated = true;
245 } catch (ControlLoopException e) {
246 logger.error("{}: activate by event threw: ", this, e);
247 notification.setNotification(ControlLoopNotificationType.REJECTED);
248 notification.setMessage(e.getMessage());
254 * Activate a control loop event.
256 * @param yamlSpecification the yaml specification
257 * @param event the event
258 * @return the VirtualControlLoopNotification
260 public VirtualControlLoopNotification activate(String yamlSpecification, VirtualControlLoopEvent event) {
261 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
264 // This method should ONLY be called ONCE
266 if (this.isActivated) {
267 throw new ControlLoopException("ControlLoopEventManager has already been activated.");
270 // Syntax check the event
272 checkEventSyntax(event);
277 if (yamlSpecification == null || yamlSpecification.length() < 1) {
278 throw new ControlLoopException("yaml specification is null or 0 length");
280 } catch (ControlLoopException e) {
281 logger.error("{}: activate by YAML specification and event threw: ", this, e);
282 notification.setNotification(ControlLoopNotificationType.REJECTED);
283 notification.setMessage(e.getMessage());
287 String decodedYaml = null;
289 decodedYaml = URLDecoder.decode(yamlSpecification, "UTF-8");
290 if (decodedYaml != null && decodedYaml.length() > 0) {
291 yamlSpecification = decodedYaml;
293 } catch (UnsupportedEncodingException e) {
294 logger.error("{}: YAML decode in activate by YAML specification and event threw: ", this, e);
295 notification.setNotification(ControlLoopNotificationType.REJECTED);
296 notification.setMessage(e.getMessage());
302 // Parse the YAML specification
304 this.processor = new ControlLoopProcessor(yamlSpecification);
306 // At this point we are good to go with this event
313 notification.setNotification(ControlLoopNotificationType.ACTIVE);
315 // Set ourselves as active
317 this.isActivated = true;
318 } catch (ControlLoopException e) {
319 logger.error("{}: activate by YAML specification and event threw: ", this, e);
320 notification.setNotification(ControlLoopNotificationType.REJECTED);
321 notification.setMessage(e.getMessage());
327 * Check if the control loop is final.
329 * @return a VirtualControlLoopNotification if the control loop is final, otherwise <code>null</code> is returned
330 * @throws ControlLoopException if an error occurs
332 public VirtualControlLoopNotification isControlLoopFinal() throws ControlLoopException {
333 validateFinalControlLoop();
335 // Ok, start creating the notification
337 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
339 // Check if the overall control loop has timed out
341 if (this.isControlLoopTimedOut()) {
343 // Yes we have timed out
345 notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
346 notification.setMessage("Control Loop timed out");
347 notification.getHistory().addAll(this.controlLoopHistory);
351 // Check if the current policy is Final
353 FinalResult result = this.processor.checkIsCurrentPolicyFinal();
354 if (result == null) {
356 // we are not at a final result
362 case FINAL_FAILURE_EXCEPTION:
363 notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
364 notification.setMessage("Exception in processing closed loop");
367 case FINAL_FAILURE_RETRIES:
368 case FINAL_FAILURE_TIMEOUT:
369 case FINAL_FAILURE_GUARD:
370 notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
373 notification.setNotification(ControlLoopNotificationType.FINAL_OPENLOOP);
376 notification.setNotification(ControlLoopNotificationType.FINAL_SUCCESS);
382 // Be sure to add all the history
384 notification.getHistory().addAll(this.controlLoopHistory);
388 private void validateFinalControlLoop() throws ControlLoopException {
390 // Check if they activated us
392 if (!this.isActivated) {
393 throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
396 // Make sure we are expecting this call.
398 if (this.onset == null) {
399 throw new ControlLoopException("No onset event for ControlLoopEventManager.");
404 * Process the control loop.
406 * @return a ControlLoopOperationManager
407 * @throws ControlLoopException if an error occurs
409 public ControlLoopOperationManager processControlLoop() throws ControlLoopException {
410 validateFinalControlLoop();
412 // Is there a current operation?
414 if (this.currentOperation != null) {
416 // Throw an exception, or simply return the current operation?
418 throw new ControlLoopException("Already working an Operation, do not call this method.");
421 // Ensure we are not FINAL
423 VirtualControlLoopNotification notification = this.isControlLoopFinal();
424 if (notification != null) {
426 // This is weird, we require them to call the isControlLoopFinal() method first
428 // We should really abstract this and avoid throwing an exception, because it really
429 // isn't an exception.
431 throw new ControlLoopException("Control Loop is in FINAL state, do not call this method.");
434 // Not final so get the policy that needs to be worked on.
436 Policy policy = this.processor.getCurrentPolicy();
437 if (policy == null) {
438 throw new ControlLoopException("ControlLoopEventManager: processor came upon null Policy.");
441 // And setup an operation
443 this.lastOperationManager = this.currentOperation;
444 this.currentOperation = new ControlLoopOperationManager(this.onset, policy, this);
448 return this.currentOperation;
452 * Finish an operation.
454 * @param operation the operation
456 public void finishOperation(ControlLoopOperationManager operation) throws ControlLoopException {
458 // Verify we have a current operation
460 if (this.currentOperation != null) {
462 // Validate they are finishing the current operation
463 // PLD - this is simply comparing the policy. Do we want to equals the whole object?
465 if (this.currentOperation.policy.equals(operation.policy)) {
466 logger.debug("Finishing {} result is {}", this.currentOperation.policy.getRecipe(),
467 this.currentOperation.getOperationResult());
471 this.controlLoopHistory.addAll(this.currentOperation.getHistory());
473 // Move to the next Policy
475 this.processor.nextPolicyForResult(this.currentOperation.getOperationResult());
477 // Just null this out
479 this.lastOperationManager = this.currentOperation;
480 this.currentOperation = null;
483 // Don't release the lock - it may be re-used by the next operation
488 logger.debug("Cannot finish current operation {} does not match given operation {}",
489 this.currentOperation.policy, operation.policy);
492 throw new ControlLoopException("No operation to finish.");
496 * Obtain a lock for the current operation.
498 * @param callback call-back to be invoked when the lock state changes
499 * @return a pair containing the old lock and the new lock, either of which may be null
500 * @throws ControlLoopException if an error occurs
502 public synchronized Pair<Lock, Lock> lockCurrentOperation(LockCallback callback) throws ControlLoopException {
506 if (this.currentOperation == null) {
507 throw new ControlLoopException("Do not have a current operation.");
511 // Release the old lock if it's for a different resource.
514 if (this.targetLock != null
515 && !this.targetLock.getResourceId().equals(this.currentOperation.getTargetEntity())) {
516 logger.debug("{}: different resource - releasing old lock", getClosedLoopControlName());
517 oldLock = this.targetLock;
518 this.targetLock = null;
521 // keep the lock a little longer than the operation, including retries
522 int optimeout = Math.max(1, this.currentOperation.getOperationTimeout());
523 int nattempts = 1 + Math.max(0, this.currentOperation.getMaxRetries());
524 int holdSec = optimeout * nattempts + ADDITIONAL_LOCK_SEC;
527 // Have we acquired it already?
529 if (this.targetLock != null) {
530 // we have the lock - just extend it
531 this.targetLock.extend(holdSec, callback);
532 return new Pair<>(oldLock, null);
534 } else if (this.useTargetLock) {
535 this.targetLock = createRealLock(this.currentOperation.getTargetEntity(), this.onset.getRequestId(),
537 return new Pair<>(oldLock, this.targetLock);
540 // Not using target locks - create a lock w/o actually locking.
541 logger.debug("{}: not using target locking; using pseudo locks", getClosedLoopControlName());
542 this.targetLock = createPseudoLock(this.currentOperation.getTargetEntity(), this.onset.getRequestId(),
545 // Note: no need to invoke callback, as the lock is already ACTIVE
547 return new Pair<>(oldLock, this.targetLock);
552 * Releases the lock for the current operation, deleting it from working memory.
554 * @return the lock, if the operation was locked, {@code null} otherwise
556 public synchronized Lock unlockCurrentOperation() {
557 if (this.targetLock == null) {
561 Lock lock = this.targetLock;
562 this.targetLock = null;
569 public enum NewEventStatus {
570 FIRST_ONSET, SUBSEQUENT_ONSET, FIRST_ABATEMENT, SUBSEQUENT_ABATEMENT, SYNTAX_ERROR;
574 * An event onset/abatement.
576 * @param event the event
578 * @throws AaiException if an error occurs retrieving information from A&AI
580 public NewEventStatus onNewEvent(VirtualControlLoopEvent event) throws AaiException {
582 this.checkEventSyntax(event);
583 if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ONSET) {
585 // Check if this is our original ONSET
587 if (event.equals(this.onset)) {
589 // Query A&AI if needed
596 return NewEventStatus.FIRST_ONSET;
599 // Log that we got an onset
602 return NewEventStatus.SUBSEQUENT_ONSET;
603 } else if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ABATED) {
605 // Have we already got an abatement?
607 if (this.abatement == null) {
611 this.abatement = event;
613 // Keep track that we received another
615 this.numAbatements++;
619 return NewEventStatus.FIRST_ABATEMENT;
622 // Keep track that we received another
624 this.numAbatements++;
628 return NewEventStatus.SUBSEQUENT_ABATEMENT;
631 } catch (ControlLoopException e) {
632 logger.error("{}: onNewEvent threw: ", this, e);
634 return NewEventStatus.SYNTAX_ERROR;
639 * Commit the abatement to the history database.
641 * @param message the abatement message
642 * @param outcome the abatement outcome
644 public void commitAbatement(String message, String outcome) {
645 if (this.lastOperationManager == null) {
646 logger.error("{}: commitAbatement: no operation manager", this);
650 this.lastOperationManager.commitAbatement(message, outcome);
651 } catch (NoSuchElementException e) {
652 logger.error("{}: commitAbatement threw an exception ", this, e);
658 * Set the control loop time out.
660 * @return a VirtualControlLoopNotification
662 public VirtualControlLoopNotification setControlLoopTimedOut() {
663 this.controlLoopTimedOut = FinalResult.FINAL_FAILURE_TIMEOUT;
664 VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
665 notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
666 notification.setMessage("Control Loop timed out");
667 notification.getHistory().addAll(this.controlLoopHistory);
671 public boolean isControlLoopTimedOut() {
672 return (this.controlLoopTimedOut == FinalResult.FINAL_FAILURE_TIMEOUT);
676 * Get the control loop timeout.
678 * @param defaultTimeout the default timeout
679 * @return the timeout
681 public int getControlLoopTimeout(Integer defaultTimeout) {
682 if (this.processor != null && this.processor.getControlLoop() != null) {
683 Integer timeout = this.processor.getControlLoop().getTimeout();
684 if (timeout != null && timeout > 0) {
688 if (defaultTimeout != null) {
689 return defaultTimeout;
694 public AaiGetVnfResponse getVnfResponse() {
698 public AaiGetVserverResponse getVserverResponse() {
699 return vserverResponse;
703 * Check an event syntax.
705 * @param event the event syntax
706 * @throws ControlLoopException if an error occurs
708 public void checkEventSyntax(VirtualControlLoopEvent event) throws ControlLoopException {
709 validateStatus(event);
710 if (StringUtils.isBlank(event.getClosedLoopControlName())) {
711 throw new ControlLoopException("No control loop name");
713 if (event.getRequestId() == null) {
714 throw new ControlLoopException("No request ID");
716 if (event.getClosedLoopEventStatus() == ControlLoopEventStatus.ABATED) {
719 if (StringUtils.isBlank(event.getTarget())) {
720 throw new ControlLoopException("No target field");
721 } else if (!VALID_TARGETS.contains(event.getTarget().toLowerCase())) {
722 throw new ControlLoopException("target field invalid - expecting VM_NAME or VNF_NAME");
724 validateAaiData(event);
727 private void validateStatus(VirtualControlLoopEvent event) throws ControlLoopException {
728 if (event.getClosedLoopEventStatus() == null
729 || (event.getClosedLoopEventStatus() != ControlLoopEventStatus.ONSET
730 && event.getClosedLoopEventStatus() != ControlLoopEventStatus.ABATED)) {
731 throw new ControlLoopException("Invalid value in closedLoopEventStatus");
735 private void validateAaiData(VirtualControlLoopEvent event) throws ControlLoopException {
736 if (event.getAai() == null) {
737 throw new ControlLoopException("AAI is null");
739 if (event.getAai().get(GENERIC_VNF_VNF_ID) == null && event.getAai().get(VSERVER_VSERVER_NAME) == null
740 && event.getAai().get(GENERIC_VNF_VNF_NAME) == null) {
741 throw new ControlLoopException(
742 "generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
747 * Query A&AI for an event.
749 * @param event the event
750 * @throws AaiException if an error occurs retrieving information from A&AI
752 public void queryAai(VirtualControlLoopEvent event) throws AaiException {
754 Map<String, String> aai = event.getAai();
756 if (aai.containsKey(VSERVER_IS_CLOSED_LOOP_DISABLED) || aai.containsKey(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)) {
758 if (isClosedLoopDisabled(event)) {
759 throw new AaiException("is-closed-loop-disabled is set to true on VServer or VNF");
762 if (isProvStatusInactive(event)) {
763 throw new AaiException("prov-status is not ACTIVE on VServer or VNF");
766 // no need to query, as we already have the data
770 if (vnfResponse != null || vserverResponse != null) {
771 // query has already been performed
776 if (aai.containsKey(GENERIC_VNF_VNF_ID) || aai.containsKey(GENERIC_VNF_VNF_NAME)) {
777 vnfResponse = getAaiVnfInfo(event);
778 processVnfResponse(vnfResponse, aai.containsKey(GENERIC_VNF_VNF_ID));
779 } else if (aai.containsKey(VSERVER_VSERVER_NAME)) {
780 vserverResponse = getAaiVserverInfo(event);
781 processVServerResponse(vserverResponse);
783 } catch (AaiException e) {
784 logger.error(QUERY_AAI_ERROR_MSG, e);
786 } catch (Exception e) {
787 logger.error(QUERY_AAI_ERROR_MSG, e);
788 throw new AaiException(QUERY_AAI_ERROR_MSG + e.toString());
793 * Process a response from A&AI for a VNF.
795 * @param aaiResponse the response from A&AI
796 * @param queryByVnfId <code>true</code> if the query was based on vnf-id, <code>false</code> if the query was based
798 * @throws AaiException if an error occurs processing the response
800 private static void processVnfResponse(AaiGetVnfResponse aaiResponse, boolean queryByVnfId) throws AaiException {
801 String queryTypeString = (queryByVnfId ? "vnf-id" : "vnf-name");
803 if (aaiResponse == null) {
804 throw new AaiException("AAI Response is null (query by " + queryTypeString + ")");
806 if (aaiResponse.getRequestError() != null) {
807 throw new AaiException("AAI Responded with a request error (query by " + queryTypeString + ")");
810 if (aaiResponse.getIsClosedLoopDisabled()) {
811 throw new AaiException("is-closed-loop-disabled is set to true (query by " + queryTypeString + ")");
814 if (!PROV_STATUS_ACTIVE.equals(aaiResponse.getProvStatus())) {
815 throw new AaiException("prov-status is not ACTIVE (query by " + queryTypeString + ")");
820 * Process a response from A&AI for a VServer.
822 * @param aaiResponse the response from A&AI
823 * @throws AaiException if an error occurs processing the response
825 private static void processVServerResponse(AaiGetVserverResponse aaiResponse) throws AaiException {
826 if (aaiResponse == null) {
827 throw new AaiException("AAI Response is null (query by vserver-name)");
829 if (aaiResponse.getRequestError() != null) {
830 throw new AaiException("AAI Responded with a request error (query by vserver-name)");
833 List<AaiNqVServer> lst = aaiResponse.getVserver();
838 AaiNqVServer svr = lst.get(0);
839 if (svr.getIsClosedLoopDisabled()) {
840 throw new AaiException("is-closed-loop-disabled is set to true (query by vserver-name)");
843 if (!PROV_STATUS_ACTIVE.equals(svr.getProvStatus())) {
844 throw new AaiException("prov-status is not ACTIVE (query by vserver-name)");
849 * Is closed loop disabled for an event.
851 * @param event the event
852 * @return <code>true</code> if the control loop is disabled, <code>false</code> otherwise
854 public static boolean isClosedLoopDisabled(VirtualControlLoopEvent event) {
855 Map<String, String> aai = event.getAai();
856 return (isAaiTrue(aai.get(VSERVER_IS_CLOSED_LOOP_DISABLED))
857 || isAaiTrue(aai.get(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)));
861 * Does provisioning status, for an event, have a value other than ACTIVE.
863 * @param event the event
864 * @return {@code true} if the provisioning status is neither ACTIVE nor {@code null}, {@code false} otherwise
866 protected static boolean isProvStatusInactive(VirtualControlLoopEvent event) {
867 Map<String, String> aai = event.getAai();
868 return (!PROV_STATUS_ACTIVE.equals(aai.getOrDefault(VSERVER_PROV_STATUS, PROV_STATUS_ACTIVE))
869 || !PROV_STATUS_ACTIVE.equals(aai.getOrDefault(GENERIC_VNF_PROV_STATUS, PROV_STATUS_ACTIVE)));
873 * Determines the boolean value represented by the given AAI field value.
875 * @param aaiValue value to be examined
876 * @return the boolean value represented by the field value, or {@code false} if the value is {@code null}
878 protected static boolean isAaiTrue(String aaiValue) {
879 return ("true".equalsIgnoreCase(aaiValue) || "T".equalsIgnoreCase(aaiValue) || "yes".equalsIgnoreCase(aaiValue)
880 || "Y".equalsIgnoreCase(aaiValue));
884 * Get the A&AI VService information for an event.
886 * @param event the event
887 * @return a AaiGetVserverResponse
888 * @throws ControlLoopException if an error occurs
890 public static AaiGetVserverResponse getAaiVserverInfo(VirtualControlLoopEvent event) throws ControlLoopException {
891 UUID requestId = event.getRequestId();
892 AaiGetVserverResponse response = null;
893 String vserverName = event.getAai().get(VSERVER_VSERVER_NAME);
896 if (vserverName != null) {
897 String aaiHostUrl = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_URL);
898 String aaiUser = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_USERNAME_PROPERTY);
899 String aaiPassword = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_PASS_PROPERTY);
900 String aaiGetQueryByVserver = "/aai/v11/nodes/vservers?vserver-name=";
901 String url = aaiHostUrl + aaiGetQueryByVserver;
902 logger.info("AAI Host URL by VServer: {}", url);
903 response = new AaiManager(new RestManager()).getQueryByVserverName(url, aaiUser, aaiPassword, requestId,
906 } catch (Exception e) {
907 logger.error("getAaiVserverInfo exception: ", e);
908 throw new ControlLoopException("Exception in getAaiVserverInfo: ", e);
915 * Get A&AI VNF information for an event.
917 * @param event the event
918 * @return a AaiGetVnfResponse
919 * @throws ControlLoopException if an error occurs
921 public static AaiGetVnfResponse getAaiVnfInfo(VirtualControlLoopEvent event) throws ControlLoopException {
922 UUID requestId = event.getRequestId();
923 AaiGetVnfResponse response = null;
924 String vnfName = event.getAai().get(GENERIC_VNF_VNF_NAME);
925 String vnfId = event.getAai().get(GENERIC_VNF_VNF_ID);
927 String aaiHostUrl = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_URL);
928 String aaiUser = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_USERNAME_PROPERTY);
929 String aaiPassword = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_PASS_PROPERTY);
932 if (vnfName != null) {
933 String aaiGetQueryByVnfName = "/aai/v11/network/generic-vnfs/generic-vnf?vnf-name=";
934 String url = aaiHostUrl + aaiGetQueryByVnfName;
935 logger.info("AAI Host URL by VNF name: {}", url);
936 response = new AaiManager(new RestManager()).getQueryByVnfName(url, aaiUser, aaiPassword, requestId,
938 } else if (vnfId != null) {
939 String aaiGetQueryByVnfId = "/aai/v11/network/generic-vnfs/generic-vnf/";
940 String url = aaiHostUrl + aaiGetQueryByVnfId;
941 logger.info("AAI Host URL by VNF ID: {}", url);
943 new AaiManager(new RestManager()).getQueryByVnfId(url, aaiUser, aaiPassword, requestId, vnfId);
945 } catch (Exception e) {
946 logger.error("getAaiVnfInfo exception: ", e);
947 throw new ControlLoopException("Exception in getAaiVnfInfo: ", e);
954 * Gets the output from the AAI vserver named-query, using the cache, if appropriate.
956 * @return output from the AAI vserver named-query
958 public AaiNqResponseWrapper getNqVserverFromAai() {
959 if (nqVserverResponse != null) {
961 return nqVserverResponse;
964 String vserverName = onset.getAai().get(VSERVER_VSERVER_NAME);
965 if (vserverName == null) {
966 logger.warn("Missing vserver-name for AAI request {}", onset.getRequestId());
969 AaiNqRequest aaiNqRequest = getAaiNqRequest(vserverName);
971 if (logger.isDebugEnabled()) {
972 logger.debug("AAI Request sent: {}", Serialization.gsonPretty.toJson(aaiNqRequest));
975 AaiNqResponse aaiNqResponse = new AaiManager(new RestManager()).postQuery(getPeManagerEnvProperty(AAI_URL),
976 getPeManagerEnvProperty(AAI_USERNAME_PROPERTY), getPeManagerEnvProperty(AAI_PASS_PROPERTY),
977 aaiNqRequest, onset.getRequestId());
979 // Check AAI response
980 if (aaiNqResponse == null) {
981 logger.warn("No response received from AAI for request {}", aaiNqRequest);
985 // Create AAINQResponseWrapper
986 nqVserverResponse = new AaiNqResponseWrapper(onset.getRequestId(), aaiNqResponse);
988 if (logger.isDebugEnabled()) {
989 logger.debug("AAI Named Query Response: ");
990 logger.debug(Serialization.gsonPretty.toJson(nqVserverResponse.getAaiNqResponse()));
993 return nqVserverResponse;
997 * Gets an AAI Named Query Request object.
999 * @param vserverName vserver name.
1000 * @return the AAI Named Query Request object.
1002 public static AaiNqRequest getAaiNqRequest(String vserverName) {
1003 // create AAI named-query request with UUID started with ""
1004 AaiNqRequest aaiNqRequest = new AaiNqRequest();
1005 AaiNqQueryParameters aaiNqQueryParam = new AaiNqQueryParameters();
1006 AaiNqNamedQuery aaiNqNamedQuery = new AaiNqNamedQuery();
1007 final AaiNqInstanceFilters aaiNqInstanceFilter = new AaiNqInstanceFilters();
1010 aaiNqNamedQuery.setNamedQueryUuid(UUID.fromString("4ff56a54-9e3f-46b7-a337-07a1d3c6b469"));
1011 aaiNqQueryParam.setNamedQuery(aaiNqNamedQuery);
1012 aaiNqRequest.setQueryParameters(aaiNqQueryParam);
1016 Map<String, Map<String, String>> aaiNqInstanceFilterMap = new HashMap<>();
1017 Map<String, String> aaiNqInstanceFilterMapItem = new HashMap<>();
1018 aaiNqInstanceFilterMapItem.put("vserver-name", vserverName);
1019 aaiNqInstanceFilterMap.put("vserver", aaiNqInstanceFilterMapItem);
1020 aaiNqInstanceFilter.getInstanceFilter().add(aaiNqInstanceFilterMap);
1021 aaiNqRequest.setInstanceFilters(aaiNqInstanceFilter);
1022 return aaiNqRequest;
1026 * This method reads and validates environmental properties coming from the policy engine. Null properties cause an
1027 * {@link IllegalArgumentException} runtime exception to be thrown
1029 * @param enginePropertyName the name of the parameter to retrieve
1030 * @return the property value
1032 private static String getPeManagerEnvProperty(String enginePropertyName) {
1033 String enginePropertyValue = PolicyEngineConstants.getManager().getEnvironmentProperty(enginePropertyName);
1034 if (enginePropertyValue == null) {
1035 throw new IllegalArgumentException("The value of policy engine manager environment property \""
1036 + enginePropertyName + "\" may not be null");
1038 return enginePropertyValue;
1042 public String toString() {
1043 return "ControlLoopEventManager [closedLoopControlName=" + closedLoopControlName + ", requestId=" + requestId
1044 + ", processor=" + processor + ", onset=" + (onset != null ? onset.getRequestId() : "null")
1045 + ", numOnsets=" + numOnsets + ", numAbatements=" + numAbatements + ", isActivated=" + isActivated
1046 + ", currentOperation=" + currentOperation + ", targetLock=" + targetLock + "]";
1050 * This function calls Aai Custom Query and responds with the AaiCqResponse.
1052 * @param event input event
1053 * @return AaiCqResponse Response from Aai for custom query. Can not be null.
1054 * @throws AaiException if error occurs
1056 public AaiCqResponse getCqResponse(VirtualControlLoopEvent event) throws AaiException {
1058 Map<String, String> aai = event.getAai();
1060 if (aai.containsKey(VSERVER_IS_CLOSED_LOOP_DISABLED) || aai.containsKey(GENERIC_VNF_IS_CLOSED_LOOP_DISABLED)) {
1062 if (isClosedLoopDisabled(event)) {
1063 throw new AaiException("is-closed-loop-disabled is set to true on VServer or VNF");
1066 if (isProvStatusInactive(event)) {
1067 throw new AaiException("prov-status is not ACTIVE on VServer or VNF");
1071 if (!aai.containsKey(VSERVER_VSERVER_NAME)) {
1072 throw new AaiException("Vserver name is missing");
1075 UUID reqId = event.getRequestId();
1076 AaiCqResponse response = null;
1077 String vserverId = event.getAai().get(VSERVER_VSERVER_NAME);
1079 String aaiHostUrl = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_URL);
1080 String aaiUser = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_USERNAME_PROPERTY);
1081 String aaiPassword = PolicyEngineConstants.getManager().getEnvironmentProperty(AAI_PASS_PROPERTY);
1083 response = new AaiManager(new RestManager()).getCustomQueryResponse(aaiHostUrl, aaiUser, aaiPassword, reqId,
1086 if (response == null) {
1087 throw new AaiException("Aai response is undefined");
1095 // the following methods may be overridden by junit tests
1097 protected Lock createRealLock(String targetEntity, UUID requestId, int holdSec, LockCallback callback) {
1098 return PolicyEngineConstants.getManager().createLock(targetEntity, requestId.toString(), holdSec, callback,
1102 // note: the "callback" is required, because it will be invoked when lock.extend() is
1104 protected Lock createPseudoLock(String targetEntity, UUID requestId, int holdSec, LockCallback callback) {
1105 return new LockImpl(LockState.ACTIVE, targetEntity, requestId.toString(), holdSec, callback);