/*-
* ============LICENSE_START=======================================================
- * controlloop event manager
+ * ONAP
* ================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2021 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2024 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
package org.onap.policy.controlloop.eventmanager;
import java.io.Serializable;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
+import java.time.Instant;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Map;
import java.util.UUID;
-
-import org.onap.policy.aai.AAIGETVnfResponse;
-import org.onap.policy.aai.AAIGETVserverResponse;
-import org.onap.policy.aai.AAIManager;
-import org.onap.policy.controlloop.ControlLoopEventStatus;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.ToString;
import org.onap.policy.controlloop.ControlLoopException;
-import org.onap.policy.controlloop.ControlLoopNotificationType;
import org.onap.policy.controlloop.ControlLoopOperation;
-import org.onap.policy.controlloop.VirtualControlLoopEvent;
-import org.onap.policy.controlloop.VirtualControlLoopNotification;
-import org.onap.policy.controlloop.policy.FinalResult;
-import org.onap.policy.controlloop.policy.Policy;
+import org.onap.policy.controlloop.actorserviceprovider.ActorService;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
+import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
+import org.onap.policy.controlloop.ophistory.OperationHistoryDataManager;
+import org.onap.policy.controlloop.ophistory.OperationHistoryDataManagerStub;
import org.onap.policy.controlloop.processor.ControlLoopProcessor;
-import org.onap.policy.guard.GuardResult;
-import org.onap.policy.guard.LockCallback;
-import org.onap.policy.guard.PolicyGuard;
-import org.onap.policy.guard.PolicyGuard.LockResult;
-import org.onap.policy.guard.TargetLock;
-import org.onap.policy.drools.system.PolicyEngine;
+import org.onap.policy.drools.core.lock.LockCallback;
+import org.onap.policy.drools.system.PolicyEngineConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ControlLoopEventManager implements LockCallback, Serializable {
-
- /**
- *
- */
- private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventManager.class);
-
- private static final long serialVersionUID = -1216568161322872641L;
- public final String closedLoopControlName;
- public final UUID requestID;
-
- private String controlLoopResult;
- private transient ControlLoopProcessor processor = null;
- private VirtualControlLoopEvent onset;
- private Integer numOnsets = 0;
- private Integer numAbatements = 0;
- private VirtualControlLoopEvent abatement;
- private FinalResult controlLoopTimedOut = null;
-
- private boolean isActivated = false;
- private LinkedList<ControlLoopOperation> controlLoopHistory = new LinkedList<>();
- private ControlLoopOperationManager currentOperation = null;
- private transient TargetLock targetLock = null;
- private AAIGETVnfResponse vnfResponse = null;
- private AAIGETVserverResponse vserverResponse = null;
- private static String aaiHostURL;
- private static String aaiUser;
- private static String aaiPassword;
-
- private static Collection<String> requiredAAIKeys = new ArrayList<>();
- static {
- requiredAAIKeys.add("AICVServerSelfLink");
- requiredAAIKeys.add("AICIdentity");
- requiredAAIKeys.add("is_closed_loop_disabled");
- requiredAAIKeys.add("VM_NAME");
- }
-
- public ControlLoopEventManager(String closedLoopControlName, UUID requestID) {
- this.closedLoopControlName = closedLoopControlName;
- this.requestID = requestID;
- }
-
- public String getControlLoopResult() {
- return controlLoopResult;
- }
-
- public void setControlLoopResult(String controlLoopResult) {
- this.controlLoopResult = controlLoopResult;
- }
-
- public Integer getNumOnsets() {
- return numOnsets;
- }
-
- public void setNumOnsets(Integer numOnsets) {
- this.numOnsets = numOnsets;
- }
-
- public Integer getNumAbatements() {
- return numAbatements;
- }
-
- public void setNumAbatements(Integer numAbatements) {
- this.numAbatements = numAbatements;
- }
-
- public boolean isActivated() {
- return isActivated;
- }
-
- public void setActivated(boolean isActivated) {
- this.isActivated = isActivated;
- }
-
- public VirtualControlLoopEvent getOnsetEvent() {
- return this.onset;
- }
-
- public VirtualControlLoopEvent getAbatementEvent() {
- return this.abatement;
- }
-
- public ControlLoopProcessor getProcessor() {
- return this.processor;
- }
-
- public VirtualControlLoopNotification activate(VirtualControlLoopEvent event) {
- VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
- try {
- //
- // This method should ONLY be called ONCE
- //
- if (this.isActivated) {
- throw new ControlLoopException("ControlLoopEventManager has already been activated.");
- }
- //
- // Syntax check the event
- //
- checkEventSyntax(event);
- //
- // At this point we are good to go with this event
- //
- this.onset = event;
- this.numOnsets = 1;
- //
- notification.notification = ControlLoopNotificationType.ACTIVE;
- //
- // Set ourselves as active
- //
- this.isActivated = true;
- } catch (ControlLoopException e) {
- logger.error("{}: activate threw: ",this, e);
- notification.notification = ControlLoopNotificationType.REJECTED;
- notification.message = e.getMessage();
- }
- return notification;
- }
-
-
-
- public VirtualControlLoopNotification activate(String yamlSpecification, VirtualControlLoopEvent event) {
- VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
- try {
- //
- // This method should ONLY be called ONCE
- //
- if (this.isActivated) {
- throw new ControlLoopException("ControlLoopEventManager has already been activated.");
- }
- //
- // Syntax check the event
- //
- checkEventSyntax(event);
-
- //
- // Check the YAML
- //
- if (yamlSpecification == null || yamlSpecification.length() < 1) {
- throw new ControlLoopException("yaml specification is null or 0 length");
- }
- String decodedYaml = null;
- try {
- decodedYaml = URLDecoder.decode(yamlSpecification, "UTF-8");
- if (decodedYaml != null && decodedYaml.length() > 0) {
- yamlSpecification = decodedYaml;
- }
- } catch (UnsupportedEncodingException e) {
- logger.error("{}: activate threw: ",this, e);
- }
- //
- // Parse the YAML specification
- //
- this.processor = new ControlLoopProcessor(yamlSpecification);
-
- //
- // At this point we are good to go with this event
- //
- this.onset = event;
- this.numOnsets = 1;
- //
- //
- //
- notification.notification = ControlLoopNotificationType.ACTIVE;
- //
- // Set ourselves as active
- //
- this.isActivated = true;
- } catch (ControlLoopException e) {
- logger.error("{}: activate threw: ",this, e);
- notification.notification = ControlLoopNotificationType.REJECTED;
- notification.message = e.getMessage();
- }
- return notification;
- }
-
- public VirtualControlLoopNotification isControlLoopFinal() throws ControlLoopException {
- //
- // Check if they activated us
- //
- if (this.isActivated == false) {
- throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
- }
- //
- // Make sure we are expecting this call.
- //
- if (this.onset == null) {
- throw new ControlLoopException("No onset event for ControlLoopEventManager.");
- }
- //
- // Ok, start creating the notification
- //
- VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
- //
- // Check if the overall control loop has timed out
- //
- if (this.isControlLoopTimedOut()) {
- //
- // Yes we have timed out
- //
- notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
- notification.message = "Control Loop timed out";
- notification.history.addAll(this.controlLoopHistory);
- return notification;
- }
- //
- // Check if the current policy is Final
- //
- FinalResult result = this.processor.checkIsCurrentPolicyFinal();
- if (result == null) {
- //
- // we are not at a final result
- //
- return null;
- }
-
- switch (result) {
- case FINAL_FAILURE_EXCEPTION:
- notification.message = "Exception in processing closed loop";
- case FINAL_FAILURE:
- case FINAL_FAILURE_RETRIES:
- case FINAL_FAILURE_TIMEOUT:
- case FINAL_FAILURE_GUARD:
- notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
- break;
- case FINAL_OPENLOOP:
- notification.notification = ControlLoopNotificationType.FINAL_OPENLOOP;
- break;
- case FINAL_SUCCESS:
- notification.notification = ControlLoopNotificationType.FINAL_SUCCESS;
- break;
- default:
- return null;
- }
- //
- // Be sure to add all the history
- //
- notification.history.addAll(this.controlLoopHistory);
- return notification;
- }
-
- public ControlLoopOperationManager processControlLoop() throws ControlLoopException {
- //
- // Check if they activated us
- //
- if (this.isActivated == false) {
- throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
- }
- //
- // Make sure we are expecting this call.
- //
- if (this.onset == null) {
- throw new ControlLoopException("No onset event for ControlLoopEventManager.");
- }
- //
- // Is there a current operation?
- //
- if (this.currentOperation != null) {
- //
- // Throw an exception, or simply return the current operation?
- //
- throw new ControlLoopException("Already working an Operation, do not call this method.");
- }
- //
- // Ensure we are not FINAL
- //
- VirtualControlLoopNotification notification = this.isControlLoopFinal();
- if (notification != null) {
- //
- // This is weird, we require them to call the isControlLoopFinal() method first
- //
- // We should really abstract this and avoid throwing an exception, because it really
- // isn't an exception.
- //
- throw new ControlLoopException("Control Loop is in FINAL state, do not call this method.");
- }
- //
- // Not final so get the policy that needs to be worked on.
- //
- Policy policy = this.processor.getCurrentPolicy();
- if (policy == null) {
- throw new ControlLoopException("ControlLoopEventManager: processor came upon null Policy.");
- }
- //
- // And setup an operation
- //
- this.currentOperation = new ControlLoopOperationManager(this.onset, policy, this);
- //
- // Return it
- //
- return this.currentOperation;
- }
-
- public void finishOperation(ControlLoopOperationManager operation) throws ControlLoopException {
- //
- // Verify we have a current operation
- //
- if (this.currentOperation != null) {
- //
- // Validate they are finishing the current operation
- // PLD - this is simply comparing the policy. Do we want to equals the whole object?
- //
- if (this.currentOperation.policy.equals(operation.policy)) {
- logger.debug("Finishing {} result is {}", this.currentOperation.policy.getRecipe(), this.currentOperation.getOperationResult());
- //
- // Save history
- //
- this.controlLoopHistory.addAll(this.currentOperation.getHistory());
- //
- // Move to the next Policy
- //
- this.processor.nextPolicyForResult(this.currentOperation.getOperationResult());
- //
- // Just null this out
- //
- this.currentOperation = null;
- //
- // TODO: Release our lock
- //
- return;
- }
- logger.debug("Cannot finish current operation {} does not match given operation {}", this.currentOperation.policy, operation.policy);
- return;
- }
- throw new ControlLoopException("No operation to finish.");
- }
-
- public synchronized LockResult<GuardResult, TargetLock> lockCurrentOperation() throws ControlLoopException {
- //
- // Sanity check
- //
- if (this.currentOperation == null) {
- throw new ControlLoopException("Do not have a current operation.");
- }
- //
- // Have we acquired it already?
- //
- if (this.targetLock != null) {
- //
- // TODO: Make sure the current lock is for the same target.
- // Currently, it should be. But in the future it may not.
- //
- return new LockResult<GuardResult, TargetLock>(GuardResult.LOCK_ACQUIRED, this.targetLock);
- } else {
- //
- // Ask the Guard
- //
- LockResult<GuardResult, TargetLock> lockResult = PolicyGuard.lockTarget(
- this.currentOperation.policy.getTarget().getType(),
- this.getTargetInstance(this.currentOperation.policy),
- this.onset.requestID,
- this);
- //
- // Was it acquired?
- //
- if (lockResult.getA().equals(GuardResult.LOCK_ACQUIRED)) {
- //
- // Yes, let's save it
- //
- this.targetLock = lockResult.getB();
- }
- return lockResult;
- }
- }
-
- public synchronized TargetLock unlockCurrentOperation() {
- if (this.targetLock == null) {
- return null;
- }
- if (PolicyGuard.unlockTarget(this.targetLock) == true) {
- TargetLock returnLock = this.targetLock;
- this.targetLock = null;
- return returnLock;
- }
- return null;
- }
-
- public enum NEW_EVENT_STATUS {
- FIRST_ONSET,
- SUBSEQUENT_ONSET,
- FIRST_ABATEMENT,
- SUBSEQUENT_ABATEMENT,
- SYNTAX_ERROR
- ;
- }
-
- public NEW_EVENT_STATUS onNewEvent(VirtualControlLoopEvent event) {
- try {
- this.checkEventSyntax(event);
- if (event.closedLoopEventStatus == ControlLoopEventStatus.ONSET) {
- //
- // Check if this is our original ONSET
- //
- if (event.equals(this.onset)) {
- //
- // DO NOT retract it
- //
- return NEW_EVENT_STATUS.FIRST_ONSET;
- }
- //
- // Log that we got an onset
- //
- this.numOnsets++;
- return NEW_EVENT_STATUS.SUBSEQUENT_ONSET;
- } else if (event.closedLoopEventStatus == ControlLoopEventStatus.ABATED) {
- //
- // Have we already got an abatement?
- //
- if (this.abatement == null) {
- //
- // Save this
- //
- this.abatement = event;
- //
- // Keep track that we received another
- //
- this.numAbatements++;
- //
- //
- //
- return NEW_EVENT_STATUS.FIRST_ABATEMENT;
- } else {
- //
- // Keep track that we received another
- //
- this.numAbatements++;
- //
- //
- //
- return NEW_EVENT_STATUS.SUBSEQUENT_ABATEMENT;
- }
- } else {
- return NEW_EVENT_STATUS.SYNTAX_ERROR;
- }
- } catch (ControlLoopException e) {
- logger.error("{}: onNewEvent threw: ",this, e);
- return NEW_EVENT_STATUS.SYNTAX_ERROR;
- }
- }
-
- public VirtualControlLoopNotification setControlLoopTimedOut() {
- this.controlLoopTimedOut = FinalResult.FINAL_FAILURE_TIMEOUT;
- VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
- notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
- notification.message = "Control Loop timed out";
- notification.history.addAll(this.controlLoopHistory);
- return notification;
- }
-
- public boolean isControlLoopTimedOut() {
- return (this.controlLoopTimedOut == FinalResult.FINAL_FAILURE_TIMEOUT);
- }
-
- public int getControlLoopTimeout(Integer defaultTimeout) {
- if (this.processor != null && this.processor.getControlLoop() != null) {
- return this.processor.getControlLoop().getTimeout();
- }
- if (defaultTimeout != null) {
- return defaultTimeout;
- }
- return 0;
- }
-
- public AAIGETVnfResponse getVnfResponse() {
- return vnfResponse;
- }
-
- public AAIGETVserverResponse getVserverResponse() {
- return vserverResponse;
- }
-
- public void checkEventSyntax(VirtualControlLoopEvent event) throws ControlLoopException {
- if (event.closedLoopEventStatus == null ||
- (event.closedLoopEventStatus != ControlLoopEventStatus.ONSET &&
- event.closedLoopEventStatus != ControlLoopEventStatus.ABATED)) {
- throw new ControlLoopException("Invalid value in closedLoopEventStatus");
- }
- if (event.closedLoopControlName == null || event.closedLoopControlName.length() < 1) {
- throw new ControlLoopException("No control loop name");
- }
- if (event.requestID == null) {
- throw new ControlLoopException("No request ID");
- }
- if (event.AAI == null) {
- throw new ControlLoopException("AAI is null");
- }
- if (event.AAI.get("generic-vnf.vnf-id") == null && event.AAI.get("vserver.vserver-name") == null &&
- event.AAI.get("generic-vnf.vnf-name") == null) {
- throw new ControlLoopException("generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
- }
- if (event.AAI.get("vserver.is-closed-loop-disabled") == null) {
- try {
- if (event.AAI.get("generic-vnf.vnf-id") != null) {
- vnfResponse = getAAIVnfInfo(event);
- if (vnfResponse == null) {
- throw new ControlLoopException("AAI Response is null (query by vnf-id)");
- }
- if (isClosedLoopDisabled(vnfResponse) == true) {
- throw new ControlLoopException("is-closed-loop-disabled is set to true");
- }
- } else if (event.AAI.get("generic-vnf.vnf-name") != null) {
- vnfResponse = getAAIVnfInfo(event);
- if (vnfResponse == null) {
- throw new ControlLoopException("AAI Response is null (query by vnf-name)");
- }
- if (isClosedLoopDisabled(vnfResponse) == true) {
- throw new ControlLoopException("is-closed-loop-disabled is set to true");
- }
- } else if (event.AAI.get("vserver.vserver-name") != null) {
- vserverResponse = getAAIVserverInfo(event);
- if (vserverResponse == null) {
- throw new ControlLoopException("AAI Response is null (query by vserver-name)");
- }
- if (isClosedLoopDisabled(vserverResponse) == true) {
- throw new ControlLoopException("is-closed-loop-disabled is set to true");
- }
- }
- } catch (Exception e) {
- logger.error("Exception from getAAIInfo: ", e);
- throw new ControlLoopException("Exception from getAAIInfo: " + e.toString());
- }
- } else if (isClosedLoopDisabled(event)) {
- throw new ControlLoopException("is-closed-loop-disabled is set to true");
- }
- if (event.target == null || event.target.length() < 1) {
- throw new ControlLoopException("No target field");
- } else {
- if (! event.target.equalsIgnoreCase("VM_NAME") &&
- ! event.target.equalsIgnoreCase("VNF_NAME") &&
- ! event.target.equalsIgnoreCase("vserver.vserver-name") &&
- ! event.target.equalsIgnoreCase("generic-vnf.vnf-id") &&
- ! event.target.equalsIgnoreCase("generic-vnf.vnf-name") ) {
- throw new ControlLoopException("target field invalid - expecting VM_NAME or VNF_NAME");
- }
- }
- }
-
- public static boolean isClosedLoopDisabled(AAIGETVnfResponse aaiResponse) {
- if (aaiResponse != null && aaiResponse.isClosedLoopDisabled != null) {
- String value = aaiResponse.isClosedLoopDisabled;
- if ("true".equalsIgnoreCase(value) || "T".equalsIgnoreCase(value) ||
- "yes".equalsIgnoreCase(value) || "Y".equalsIgnoreCase(value)) {
- return true;
- }
- }
-
- return false;
- }
-
- public static boolean isClosedLoopDisabled(AAIGETVserverResponse aaiResponse) {
- if (aaiResponse != null && aaiResponse.isClosedLoopDisabled != null) {
- String value = aaiResponse.isClosedLoopDisabled;
- if ("true".equalsIgnoreCase(value) || "T".equalsIgnoreCase(value) ||
- "yes".equalsIgnoreCase(value) || "Y".equalsIgnoreCase(value)) {
- return true;
- }
- }
-
- return false;
- }
-
- public static boolean isClosedLoopDisabled(VirtualControlLoopEvent event) {
- if ("true".equalsIgnoreCase(event.AAI.get("vserver.is-closed-loop-disabled")) ||
- "T".equalsIgnoreCase(event.AAI.get("vserver.is-closed-loop-disabled")) ||
- "yes".equalsIgnoreCase(event.AAI.get("vserver.is-closed-loop-disabled")) ||
- "Y".equalsIgnoreCase(event.AAI.get("vserver.is-closed-loop-disabled"))) {
- return true;
- }
- return false;
- }
-
- public static AAIGETVserverResponse getAAIVserverInfo(VirtualControlLoopEvent event) throws ControlLoopException {
- UUID requestID = event.requestID;
- AAIGETVserverResponse response = null;
- String vserverName = event.AAI.get("vserver.vserver-name");
-
- try {
- if (vserverName != null) {
- aaiHostURL = PolicyEngine.manager.getEnvironmentProperty("aai.url");
- aaiUser = PolicyEngine.manager.getEnvironmentProperty("aai.username");
- aaiPassword = PolicyEngine.manager.getEnvironmentProperty("aai.password");
- String aaiGetQueryByVserver = "/aai/v11/nodes/vservers?vserver-name=";
- String url = aaiHostURL + aaiGetQueryByVserver;
- logger.info("url: " + url);
- response = AAIManager.getQueryByVserverName(url, aaiUser, aaiPassword, requestID, vserverName);
- }
- } catch (Exception e) {
- logger.error("getAAIVserverInfo exception: ", e);
- throw new ControlLoopException("Exception in getAAIVserverInfo: ", e);
+/**
+ * Manager for a single event. Once this has been created, the event can be retracted from
+ * working memory. Invoke {@link #isActive()} to determine if the manager is active (i.e.,
+ * hasn't been replicated from another server). When the manager is no longer needed,
+ * {@link #destroy()} should be invoked.
+ */
+@ToString(onlyExplicitlyIncluded = true)
+public class ControlLoopEventManager implements StepContext, Serializable {
+
+ private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventManager.class);
+ private static final long serialVersionUID = -1216568161322872641L;
+
+ /**
+ * Data manager used when the policy engine's guard.disabled property is "true".
+ */
+ private static final OperationHistoryDataManager STUB_DATA_MANAGER = new OperationHistoryDataManagerStub();
+
+ public static final String GUARD_DISABLED_PROPERTY = "guard.disabled";
+
+ /**
+ * Counts the number of these objects that have been created. This is used by junit
+ * tests.
+ */
+ private static final AtomicLong createCount = new AtomicLong(0);
+
+ /**
+ * {@code True} if this object was created by this JVM instance, {@code false}
+ * otherwise. This will be {@code false} if this object is reconstituted from a
+ * persistent store or by transfer from another server.
+ */
+ private final transient boolean createdByThisJvmInstance;
+
+ private final transient EventManagerServices services;
+
+ @Getter
+ @ToString.Include
+ public final String closedLoopControlName;
+ @Getter
+ @ToString.Include
+ private final UUID requestId;
+
+ /**
+ * Time, in milliseconds, when the control loop will time out.
+ */
+ @Getter
+ private final long endTimeMs;
+
+ // fields extracted from the ControlLoopParams
+ @Getter
+ private final String policyName;
+ @Getter
+ private final String policyVersion;
+
+ /**
+ * Maps a target entity to its lock.
+ */
+ private final transient Map<String, LockData> target2lock = new HashMap<>();
+
+ @Getter(AccessLevel.PROTECTED)
+ private final ControlLoopProcessor processor;
+
+ /**
+ * Set of properties used while processing the event.
+ */
+ private final Map<String, Serializable> properties = new ConcurrentHashMap<>();
+
+ /**
+ * Unprocessed outcomes from the operations. Outcomes are added to this each time the
+ * "start" or "complete" callback is invoked, typically by an operation running in a
+ * background thread, thus it must be thread safe.
+ */
+ @Getter
+ private final transient Deque<OperationOutcome> outcomes = new ConcurrentLinkedDeque<>();
+
+
+ /**
+ * Constructs the object.
+ *
+ * @param services services the manager should use when processing the event
+ * @param params control loop parameters
+ * @param requestId event request ID
+ * @throws ControlLoopException if the event is invalid or if a YAML processor cannot
+ * be created
+ */
+ public ControlLoopEventManager(EventManagerServices services, ControlLoopParams params, UUID requestId)
+ throws ControlLoopException {
+
+ createCount.incrementAndGet();
+
+ this.createdByThisJvmInstance = true;
+ this.services = services;
+ this.closedLoopControlName = params.getClosedLoopControlName();
+ this.requestId = requestId;
+ this.policyName = params.getPolicyName();
+ this.policyVersion = params.getPolicyVersion();
+ this.processor = new ControlLoopProcessor(params.getToscaPolicy());
+ this.endTimeMs = System.currentTimeMillis() + detmControlLoopTimeoutMs();
+ }
+
+ /**
+ * Gets the number of manager objects that have been created.
+ *
+ * @return the number of manager objects that have been created
+ */
+ public static long getCreateCount() {
+ return createCount.get();
+ }
+
+ /**
+ * Determines if the manager is still active.
+ *
+ * @return {@code true} if the manager is still active, {@code false} otherwise
+ */
+ public boolean isActive() {
+ return createdByThisJvmInstance;
+ }
+
+ /**
+ * Cancels the current operation and frees all locks.
+ */
+ public void destroy() {
+ if (isActive()) {
+ getBlockingExecutor().execute(this::freeAllLocks);
}
-
- return response;
- }
-
- public static AAIGETVnfResponse getAAIVnfInfo(VirtualControlLoopEvent event) throws ControlLoopException {
- UUID requestID = event.requestID;
- AAIGETVnfResponse response = null;
- String vnfName = event.AAI.get("generic-vnf.vnf-name");
- String vnfID = event.AAI.get("generic-vnf.vnf-id");
-
- aaiHostURL = PolicyEngine.manager.getEnvironmentProperty("aai.url");
- aaiUser = PolicyEngine.manager.getEnvironmentProperty("aai.username");
- aaiPassword = PolicyEngine.manager.getEnvironmentProperty("aai.password");
-
- try {
- if (vnfName != null) {
- String aaiGetQueryByVnfName = "/aai/v11/network/generic-vnfs/generic-vnf?vnf-name=";
- String url = aaiHostURL + aaiGetQueryByVnfName;
- logger.info("url: " + url);
- response = AAIManager.getQueryByVnfName(url, aaiUser, aaiPassword, requestID, vnfName);
- } else if (vnfID != null) {
- String aaiGetQueryByVnfID = "/aai/v11/network/generic-vnfs/generic-vnf/";
- String url = aaiHostURL + aaiGetQueryByVnfID;
- logger.info("url: " + url);
- response = AAIManager.getQueryByVnfID(url, aaiUser, aaiPassword, requestID, vnfID);
- }
- } catch (Exception e) {
- logger.error("getAAIVnfInfo exception: ", e);
- throw new ControlLoopException("Exception in getAAIVnfInfo: ", e);
+ }
+
+ /**
+ * Frees all locks.
+ */
+ private void freeAllLocks() {
+ target2lock.values().forEach(LockData::free);
+ }
+
+ /**
+ * Determines the overall control loop timeout.
+ *
+ * @return the policy timeout, in milliseconds, if specified, a default timeout
+ * otherwise
+ */
+ private long detmControlLoopTimeoutMs() {
+ // validation checks preclude null or 0 timeout values in the policy
+ int timeout = processor.getPolicy().getProperties().getTimeout();
+ return TimeUnit.MILLISECONDS.convert(timeout, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public synchronized CompletableFuture<OperationOutcome> requestLock(String targetEntity) {
+
+ long remainingMs = endTimeMs - System.currentTimeMillis();
+ int remainingSec = 15 + Math.max(0, (int) TimeUnit.SECONDS.convert(remainingMs, TimeUnit.MILLISECONDS));
+
+ LockData data = target2lock.computeIfAbsent(targetEntity, key -> {
+ var data2 = new LockData(key, requestId);
+ makeLock(targetEntity, requestId.toString(), remainingSec, data2);
+
+ data2.addUnavailableCallback(this::onComplete);
+
+ return data2;
+ });
+
+ return data.getFuture();
+ }
+
+ @Override
+ public synchronized CompletableFuture<OperationOutcome> releaseLock(String targetEntity) {
+ LockData data = target2lock.remove(targetEntity);
+
+ if (data == null) {
+ // lock did not exist - immediately return a success
+ OperationOutcome outcome = makeUnlockOutcome(targetEntity);
+ outcome.setEnd(outcome.getStart());
+ onComplete(outcome);
+
+ return CompletableFuture.completedFuture(outcome);
}
-
- return response;
- }
-
- @Override
- public boolean isActive() {
- // TODO
- return true;
- }
-
- @Override
- public boolean releaseLock() {
- // TODO
- return false;
- }
-
- public String getTargetInstance(Policy policy) {
- if (policy.getTarget() != null) {
- if (policy.getTarget().getType() != null) {
- switch(policy.getTarget().getType()) {
- case PNF:
- break;
- case VM:
- case VNF:
- if (this.onset.target.equalsIgnoreCase("vserver.vserver-name")) {
- return this.onset.AAI.get("vserver.vserver-name");
- }
- else if (this.onset.target.equalsIgnoreCase("generic-vnf.vnf-id")) {
- return this.onset.AAI.get("generic-vnf.vnf-id");
- }
- else if (this.onset.target.equalsIgnoreCase("generic-vnf.vnf-name")) {
- return this.onset.AAI.get("generic-vnf.vnf-name");
- }
- break;
- default:
- break;
- }
- }
- }
- return null;
- }
-
- @Override
- public String toString() {
- return "ControlLoopEventManager [closedLoopControlName=" + closedLoopControlName + ", requestID=" + requestID
- + ", processor=" + processor + ", onset=" + (onset != null ? onset.requestID : "null") + ", numOnsets=" + numOnsets + ", numAbatements="
- + numAbatements + ", isActivated="
- + isActivated + ", currentOperation=" + currentOperation + ", targetLock=" + targetLock + "]";
- }
-
+
+ /*
+ * previous lock operation may not have completed yet, thus we tack the unlock
+ * operation onto it.
+ *
+ * Note: we must invoke free(), asynchronously (i.e., using whenCompleteAsync()),
+ * as it may block
+ */
+
+ return data.getFuture().whenCompleteAsync((lockOutcome, thrown) -> {
+
+ OperationOutcome outcome = makeUnlockOutcome(targetEntity);
+
+ try {
+ data.free();
+
+ } catch (RuntimeException e) {
+ logger.warn("failed to unlock {}", targetEntity, e);
+ outcome.setResult(OperationResult.FAILURE_EXCEPTION);
+ outcome.setMessage(ControlLoopOperation.FAILED_MSG + ": " + e.getMessage());
+ }
+
+ outcome.setEnd(Instant.now());
+ onComplete(outcome);
+
+ }, getBlockingExecutor());
+ }
+
+ private OperationOutcome makeUnlockOutcome(String targetEntity) {
+ var outcome = new OperationOutcome();
+ outcome.setActor(ActorConstants.LOCK_ACTOR);
+ outcome.setOperation(ActorConstants.UNLOCK_OPERATION);
+ outcome.setTarget(targetEntity);
+ outcome.setResult(OperationResult.SUCCESS);
+ outcome.setMessage(ControlLoopOperation.SUCCESS_MSG);
+ outcome.setFinalOutcome(true);
+ outcome.setStart(Instant.now());
+ return outcome;
+ }
+
+ public void onStart(OperationOutcome outcome) {
+ outcomes.add(outcome);
+ }
+
+ public void onComplete(OperationOutcome outcome) {
+ outcomes.add(outcome);
+ }
+
+ /**
+ * Determines if the context contains a property.
+ *
+ * @param name name of the property of interest
+ * @return {@code true} if the context contains the property, {@code false} otherwise
+ */
+ @Override
+ public boolean contains(String name) {
+ return properties.containsKey(name);
+ }
+
+ /**
+ * Gets a property, casting it to the desired type.
+ *
+ * @param <T> desired type
+ * @param name name of the property whose value is to be retrieved
+ * @return the property's value, or {@code null} if it does not yet have a value
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getProperty(String name) {
+ return (T) properties.get(name);
+ }
+
+ /**
+ * Sets a property's value.
+ *
+ * @param name property name
+ * @param value new property value
+ */
+ @Override
+ public void setProperty(String name, Serializable value) {
+ logger.info("set property {}={} manager={}", name, value, this);
+ properties.put(name, value);
+ }
+
+ /**
+ * Removes a property.
+ *
+ * @param name property name
+ */
+ @Override
+ public void removeProperty(String name) {
+ properties.remove(name);
+ }
+
+ // the following methods may be overridden by junit tests
+
+ public Executor getExecutor() {
+ return ForkJoinPool.commonPool();
+ }
+
+ protected ExecutorService getBlockingExecutor() {
+ return PolicyEngineConstants.getManager().getExecutorService();
+ }
+
+ protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
+ PolicyEngineConstants.getManager().createLock(targetEntity, requestId, holdSec, callback, false);
+ }
+
+ public ActorService getActorService() {
+ return services.getActorService();
+ }
+
+ public OperationHistoryDataManager getDataManager() {
+ boolean guardDisabled = "true".equalsIgnoreCase(getEnvironmentProperty(GUARD_DISABLED_PROPERTY));
+ return (guardDisabled ? STUB_DATA_MANAGER : services.getDataManager());
+ }
+
+ protected String getEnvironmentProperty(String propName) {
+ return PolicyEngineConstants.getManager().getEnvironmentProperty(propName);
+ }
}