diff options
Diffstat (limited to 'controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java')
-rw-r--r-- | controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java | 1504 |
1 files changed, 793 insertions, 711 deletions
diff --git a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java index ba2df3358..7c900249c 100644 --- a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java +++ b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopOperationManager.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * controlloop operation manager * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,715 +54,797 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ControlLoopOperationManager implements Serializable { - private static final long serialVersionUID = -3773199283624595410L; - private static final Logger logger = LoggerFactory.getLogger(ControlLoopOperationManager.class); - - private static final String VSERVER_VSERVER_NAME = "vserver.vserver-name"; - private static final String GENERIC_VNF_VNF_NAME = "generic-vnf.vnf-name"; - private static final String GENERIC_VNF_VNF_ID = "generic-vnf.vnf-id"; - - @Override - public String toString() { - return "ControlLoopOperationManager [onset=" + (onset != null ? onset.getRequestID() : "null") + ", policy=" - + (policy != null ? policy.getId() : "null") + ", attempts=" + attempts - + ", policyResult=" + policyResult - + ", currentOperation=" + currentOperation + ", operationHistory=" + operationHistory - + "]"; - } - - // - // These properties are not changeable, but accessible - // for Drools Rule statements. - // - public final ControlLoopEvent onset; - public final transient Policy policy; - - // - // Properties used to track the Operation - // - private int attempts = 0; - private transient Operation currentOperation = null; - private LinkedList<Operation> operationHistory = new LinkedList<>(); - private PolicyResult policyResult = null; - private ControlLoopEventManager eventManager = null; - private String targetEntity; - - public ControlLoopEventManager getEventManager() { - return eventManager; - } - - public void setEventManager(ControlLoopEventManager eventManager) { - this.eventManager = eventManager; - } - - public String getTargetEntity() { - return this.targetEntity; - } - - // - // Internal class used for tracking - // - private class Operation { - private ControlLoopOperation clOperation = new ControlLoopOperation(); - private PolicyResult policyResult = null; - private int attempt = 0; - - @Override - public String toString() { - return "Operation [attempt=" + attempt + ", policyResult=" + policyResult + ", operation=" + clOperation - + "]"; - } - } - - private String guardApprovalStatus = "NONE";//"NONE", "PERMIT", "DENY" - private transient Object operationRequest; - - public Object getOperationRequest() { - return operationRequest; - } - - public String getGuardApprovalStatus() { - return guardApprovalStatus; - } - public void setGuardApprovalStatus(String guardApprovalStatus) { - this.guardApprovalStatus = guardApprovalStatus; - } - - public String getTarget(Policy policy) throws ControlLoopException, AAIException { - if (policy.getTarget() == null) { - throw new ControlLoopException("The target is null"); - } - - if (policy.getTarget().getType() == null) { - throw new ControlLoopException("The target type is null"); - } - - switch(policy.getTarget().getType()) { - case PNF: - throw new ControlLoopException("PNF target is not supported"); - case VM: - case VNF: - VirtualControlLoopEvent virtualOnset = (VirtualControlLoopEvent) this.onset; - if (this.onset.getTarget().equalsIgnoreCase(VSERVER_VSERVER_NAME)) { - return virtualOnset.getAAI().get(VSERVER_VSERVER_NAME); - } - else if (this.onset.getTarget().equalsIgnoreCase(GENERIC_VNF_VNF_ID)) { - return virtualOnset.getAAI().get(GENERIC_VNF_VNF_ID); - } - else if (this.onset.getTarget().equalsIgnoreCase(GENERIC_VNF_VNF_NAME)) { - /* - * If the onset is enriched with the vnf-id, - * we don't need an A&AI response - */ - if (virtualOnset.getAAI().containsKey(GENERIC_VNF_VNF_ID)) { - return virtualOnset.getAAI().get(GENERIC_VNF_VNF_ID); - } - - /* - * If the vnf-name was retrieved from the onset then the vnf-id - * must be obtained from the event manager's A&AI GET query - */ - String vnfId = this.eventManager.getVnfResponse().getVnfID(); - if (vnfId == null) { - throw new AAIException("No vnf-id found"); - } - return vnfId; - } - throw new ControlLoopException("Target does not match target type"); - default: - throw new ControlLoopException("The target type is not supported"); - } - } - - public ControlLoopOperationManager(ControlLoopEvent onset, Policy policy, ControlLoopEventManager em) throws ControlLoopException, AAIException { - this.onset = onset; - this.policy = policy; - this.guardApprovalStatus = "NONE"; - this.eventManager = em; - this.targetEntity = getTarget(policy); - - // - // Let's make a sanity check - // - switch (policy.getActor()) { - case "APPC": - if ("ModifyConfig".equalsIgnoreCase(policy.getRecipe())) { - /* - * The target vnf-id may not be the same as the source vnf-id - * specified in the yaml, the target vnf-id is retrieved by - * a named query to A&AI. - */ - String targetVnf = AppcLcmActorServiceProvider.vnfNamedQuery( policy.getTarget().getResourceID(), this.targetEntity); - this.targetEntity = targetVnf; - } - break; - case "SO": - break; - case "VFC": - break; - default: - throw new ControlLoopException("ControlLoopEventManager: policy has an unknown actor."); - } - } - - public Object startOperation(/*VirtualControlLoopEvent*/ControlLoopEvent onset) throws ControlLoopException{ - verifyOperatonCanRun(); - - // - // Setup - // - this.policyResult = null; - Operation operation = new Operation(); - operation.attempt = ++this.attempts; - operation.clOperation.setActor(this.policy.getActor()); - operation.clOperation.setOperation(this.policy.getRecipe()); - operation.clOperation.setTarget(this.policy.getTarget().toString()); - operation.clOperation.setSubRequestId(Integer.toString(operation.attempt)); - // - // Now determine which actor we need to construct a request for - // - switch (policy.getActor()) { - case "APPC": - /* - * If the recipe is ModifyConfig, a legacy APPC - * request is constructed. Otherwise an LCMRequest - * is constructed. - */ - this.currentOperation = operation; - if ("ModifyConfig".equalsIgnoreCase(policy.getRecipe())) { - this.operationRequest = APPCActorServiceProvider.constructRequest((VirtualControlLoopEvent) onset, - operation.clOperation, this.policy, this.targetEntity); - } - else { - this.operationRequest = AppcLcmActorServiceProvider.constructRequest((VirtualControlLoopEvent) onset, - operation.clOperation, this.policy, this.targetEntity); - } - // - // Save the operation - // - - return operationRequest; - case "SO": - SOActorServiceProvider soActorSP = new SOActorServiceProvider(); - this.operationRequest = soActorSP.constructRequest((VirtualControlLoopEvent)onset, operation.clOperation, this.policy); - - // Save the operation - this.currentOperation = operation; - - if (this.operationRequest == null) { - this.policyResult = PolicyResult.FAILURE; - } - - return operationRequest; - case "VFC": - this.operationRequest = VFCActorServiceProvider.constructRequest((VirtualControlLoopEvent) onset, operation.clOperation, this.policy, this.eventManager.getVnfResponse()); - this.currentOperation = operation; - if (this.operationRequest == null) { - this.policyResult = PolicyResult.FAILURE; - } - return operationRequest; - default: - throw new ControlLoopException("invalid actor " + policy.getActor() + " on policy"); - } - } - - public PolicyResult onResponse(Object response) { - // - // Which response is it? - // - if (response instanceof Response) { - // - // Cast APPC response and handle it - // - return onResponse((Response) response); - } - else if (response instanceof LCMResponseWrapper) { - // - // Cast LCM response and handle it - // - return onResponse((LCMResponseWrapper) response); - } - else if (response instanceof SOResponseWrapper) { - // - // Cast SO response and handle it - // - return onResponse((SOResponseWrapper) response); - } - else if (response instanceof VFCResponse) { - // - // Cast VFC response and handle it - // - return onResponse((VFCResponse) response); - } - else { - return null; - } - } - - /** - * This method handles operation responses from APPC - * @param appcResponse the APPC response - * @return The result of the response handling - */ - private PolicyResult onResponse(Response appcResponse) { - // - // Determine which subrequestID (ie. attempt) - // - Integer operationAttempt = null; - try { - operationAttempt = Integer.parseInt(appcResponse.getCommonHeader().getSubRequestID()); - } catch (NumberFormatException e) { - // - // We cannot tell what happened if this doesn't exist - // - this.completeOperation(operationAttempt, "Policy was unable to parse APP-C SubRequestID (it was null).", PolicyResult.FAILURE_EXCEPTION); - return PolicyResult.FAILURE_EXCEPTION; - } - // - // Sanity check the response message - // - if (appcResponse.getStatus() == null) { - // - // We cannot tell what happened if this doesn't exist - // - this.completeOperation(operationAttempt, "Policy was unable to parse APP-C response status field (it was null).", PolicyResult.FAILURE_EXCEPTION); - return PolicyResult.FAILURE_EXCEPTION; - } - // - // Get the Response Code - // - ResponseCode code = ResponseCode.toResponseCode(appcResponse.getStatus().getCode()); - if (code == null) { - // - // We are unaware of this code - // - this.completeOperation(operationAttempt, "Policy was unable to parse APP-C response status code field.", PolicyResult.FAILURE_EXCEPTION); - return PolicyResult.FAILURE_EXCEPTION; - } - // - // Ok, let's figure out what APP-C's response is - // - switch (code) { - case ACCEPT: - // - // This is good, they got our original message and - // acknowledged it. - // - // Is there any need to track this? - // - return null; - case ERROR: - case REJECT: - // - // We'll consider these two codes as exceptions - // - this.completeOperation(operationAttempt, appcResponse.getStatus().getDescription(), PolicyResult.FAILURE_EXCEPTION); - if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { - return null; - } - return PolicyResult.FAILURE_EXCEPTION; - case SUCCESS: - // - // - // - this.completeOperation(operationAttempt, appcResponse.getStatus().getDescription(), PolicyResult.SUCCESS); - if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { - return null; - } - return PolicyResult.SUCCESS; - case FAILURE: - // - // - // - this.completeOperation(operationAttempt, appcResponse.getStatus().getDescription(), PolicyResult.FAILURE); - if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { - return null; - } - return PolicyResult.FAILURE; - default: - return null; - } - } - - /** - * This method handles operation responses from LCM - * @param dmaapResponse the LCM response - * @return The result of the response handling - */ - private PolicyResult onResponse(LCMResponseWrapper dmaapResponse) { - /* - * Parse out the operation attempt using the subrequestid - */ - Integer operationAttempt = AppcLcmActorServiceProvider.parseOperationAttempt(dmaapResponse.getBody().getCommonHeader().getSubRequestId()); - if (operationAttempt == null) { - this.completeOperation(operationAttempt, "Policy was unable to parse APP-C SubRequestID (it was null).", PolicyResult.FAILURE_EXCEPTION); - } - - /* - * Process the APPCLCM response to see what PolicyResult - * should be returned - */ - AbstractMap.SimpleEntry<PolicyResult, String> result = AppcLcmActorServiceProvider.processResponse(dmaapResponse); - - if (result.getKey() != null) { - this.completeOperation(operationAttempt, result.getValue(), result.getKey()); - if (PolicyResult.FAILURE_TIMEOUT.equals(this.policyResult)) { - return null; - } - return result.getKey(); - } - return null; - } - - /** - * This method handles operation responses from SO - * @param msoResponse the SO response - * @return The result of the response handling - */ - private PolicyResult onResponse(SOResponseWrapper msoResponse) { - switch (msoResponse.getSoResponse().getHttpResponseCode()) { - case 200: - case 202: - // - // Consider it as success - // - this.completeOperation(this.attempts, msoResponse.getSoResponse().getHttpResponseCode() + " Success", PolicyResult.SUCCESS); - if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { - return null; - } - return PolicyResult.SUCCESS; - default: - // - // Consider it as failure - // - this.completeOperation(this.attempts, msoResponse.getSoResponse().getHttpResponseCode() + " Failed", PolicyResult.FAILURE); - if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { - return null; - } - return PolicyResult.FAILURE; - } - } - - /** - * This method handles operation responses from VFC - * @param vfcResponse the VFC response - * @return The result of the response handling - */ - private PolicyResult onResponse(VFCResponse vfcResponse) { - if (vfcResponse.getResponseDescriptor().getStatus().equalsIgnoreCase("finished")) { - // - // Consider it as success - // - this.completeOperation(this.attempts, " Success", PolicyResult.SUCCESS); - if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { - return null; - } - return PolicyResult.SUCCESS; - } else { - // - // Consider it as failure - // - this.completeOperation(this.attempts, " Failed", PolicyResult.FAILURE); - if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { - return null; - } - // increment operation attempts for retries - this.attempts += 1; - return PolicyResult.FAILURE; - } - } - - public Integer getOperationTimeout() { - // - // Sanity check - // - if (this.policy == null) { - logger.debug("getOperationTimeout returning 0"); - return 0; - } - logger.debug("getOperationTimeout returning {}", this.policy.getTimeout()); - return this.policy.getTimeout(); - } - - public String getOperationTimeoutString(int defaultTimeout) { - Integer to = this.getOperationTimeout(); - if (to == null || to == 0) { - return Integer.toString(defaultTimeout) + "s"; - } - return to.toString() + "s"; - } - - public PolicyResult getOperationResult() { - return this.policyResult; - } - - public String getOperationMessage() { - if (this.currentOperation != null && this.currentOperation.clOperation != null) { - return this.currentOperation.clOperation.toMessage(); - } - - if (!this.operationHistory.isEmpty()) { - return this.operationHistory.getLast().clOperation.toMessage(); - } - return null; - } - - public String getOperationMessage(String guardResult) { - if (this.currentOperation != null && this.currentOperation.clOperation != null) { - return this.currentOperation.clOperation.toMessage()+ ", Guard result: " + guardResult; - } - - if (!this.operationHistory.isEmpty()) { - return this.operationHistory.getLast().clOperation.toMessage() + ", Guard result: " + guardResult; - } - return null; - } - - public String getOperationHistory() { - if (this.currentOperation != null && this.currentOperation.clOperation != null) { - return this.currentOperation.clOperation.toHistory(); - } - - if (!this.operationHistory.isEmpty()) { - return this.operationHistory.getLast().clOperation.toHistory(); - } - return null; - } - - public List<ControlLoopOperation> getHistory() { - LinkedList<ControlLoopOperation> history = new LinkedList<>(); - for (Operation op : this.operationHistory) { - history.add(new ControlLoopOperation(op.clOperation)); - - } - return history; - } - - public void setOperationHasTimedOut() { - // - // - // - this.completeOperation(this.attempts, "Operation timed out", PolicyResult.FAILURE_TIMEOUT); - } - - public void setOperationHasGuardDeny() { - // - // - // - this.completeOperation(this.attempts, "Operation denied by Guard", PolicyResult.FAILURE_GUARD); - } - - public void setOperationHasException(String message) { - this.completeOperation(this.attempts, message, PolicyResult.FAILURE_EXCEPTION); - } - - public boolean isOperationComplete() { - // - // Is there currently a result? - // - if (this.policyResult == null) { - // - // either we are in process or we - // haven't started - // - return false; - } - // - // We have some result, check if the operation failed - // - if (this.policyResult.equals(PolicyResult.FAILURE)) { - // - // Check if there were no retries specified - // - if (policy.getRetry() == null || policy.getRetry() == 0) { - // - // The result is the failure - // - return true; - } - // - // Check retries - // - if (this.isRetriesMaxedOut()) { - // - // No more attempts allowed, reset - // that our actual result is failure due to retries - // - this.policyResult = PolicyResult.FAILURE_RETRIES; - return true; - } else { - // - // There are more attempts available to try the - // policy recipe. - // - return false; - } - } - // - // Other results mean we are done - // - return true; - } - - public boolean isOperationRunning() { - return (this.currentOperation != null); - } - - /** - * This method verifies that the operation manager may run an operation. - * @return True if the operation can run, false otherwise - * @throws ControlLoopException if the operation cannot run - */ - private void verifyOperatonCanRun() throws ControlLoopException { - // - // They shouldn't call us if we currently running something - // - if (this.currentOperation != null) { - // - // what do we do if we are already running an operation? - // - throw new ControlLoopException("current operation is not null (an operation is already running)"); - } - // - // Check if we have maxed out on retries - // - if (this.policy.getRetry() == null || this.policy.getRetry() < 1) { - // - // No retries are allowed, so check have we even made - // one attempt to execute the operation? - // - if (this.attempts >= 1) { - // - // We have, let's ensure our PolicyResult is set - // - if (this.policyResult == null) { - this.policyResult = PolicyResult.FAILURE_RETRIES; - } - // - // - // - throw new ControlLoopException("current operation failed and retries are not allowed"); - } - } else { - // - // Have we maxed out on retries? - // - if (this.attempts > this.policy.getRetry()) { - if (this.policyResult == null) { - this.policyResult = PolicyResult.FAILURE_RETRIES; - } - throw new ControlLoopException("current oepration has failed after " + this.attempts + " retries"); - } - } - - return; - } - - private boolean isRetriesMaxedOut() { - if (policy.getRetry() == null || policy.getRetry() == 0) { - // - // There were NO retries specified, so declare - // this as completed. - // - return (this.attempts > 0); - } - return (this.attempts > policy.getRetry()); - } - - private void storeOperationInDataBase(){ - // Only store in DB if enabled - boolean guardEnabled = "false".equalsIgnoreCase(PolicyEngine.manager.getEnvironmentProperty("guard.disabled")); - if( !guardEnabled ){ - return; - } - - - // DB Properties - Properties props = new Properties(); - if(PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_URL) != null && - PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_USER) != null && - PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_PASS) != null){ - props.put(Util.ECLIPSE_LINK_KEY_URL, PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_URL)); - props.put(Util.ECLIPSE_LINK_KEY_USER, PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_USER)); - props.put(Util.ECLIPSE_LINK_KEY_PASS, PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_PASS)); - props.put(PersistenceUnitProperties.CLASSLOADER, ControlLoopOperationManager.class.getClassLoader()); - } - - - String opsHistPU = System.getProperty("OperationsHistoryPU"); - if(opsHistPU == null || !opsHistPU.equals("TestOperationsHistoryPU")){ - opsHistPU = "OperationsHistoryPU"; - } - else{ - props.clear(); - } - EntityManager em; - try{ - em = Persistence.createEntityManagerFactory(opsHistPU, props).createEntityManager(); - }catch(Exception e){ - logger.error("storeOperationInDataBase threw: ", e); - return; - } - - OperationsHistoryDbEntry newEntry = new OperationsHistoryDbEntry(); - - newEntry.setClosedLoopName(this.onset.getClosedLoopControlName()); - newEntry.setRequestId(this.onset.getRequestID().toString()); - newEntry.setActor(this.currentOperation.clOperation.getActor()); - newEntry.setOperation(this.currentOperation.clOperation.getOperation()); - newEntry.setTarget(this.targetEntity); - newEntry.setStarttime(Timestamp.from(this.currentOperation.clOperation.getStart())); - newEntry.setSubrequestId(this.currentOperation.clOperation.getSubRequestId()); - newEntry.setEndtime(new Timestamp(this.currentOperation.clOperation.getEnd().toEpochMilli())); - newEntry.setMessage(this.currentOperation.clOperation.getMessage()); - newEntry.setOutcome(this.currentOperation.clOperation.getOutcome()); - - em.getTransaction().begin(); - em.persist(newEntry); - em.getTransaction().commit(); - - em.close(); - } - - private void completeOperation(Integer attempt, String message, PolicyResult result) { - if (attempt == null) { - logger.debug("attempt cannot be null (i.e. subRequestID)"); - return; - } - if (this.currentOperation != null) { - if (this.currentOperation.attempt == attempt.intValue()) { - this.currentOperation.clOperation.setEnd(Instant.now()); - this.currentOperation.clOperation.setMessage(message); - this.currentOperation.clOperation.setOutcome(result.toString()); - this.currentOperation.policyResult = result; - // - // Save it in history - // - this.operationHistory.add(this.currentOperation); - this.storeOperationInDataBase(); - // - // Set our last result - // - this.policyResult = result; - // - // Clear the current operation field - // - this.currentOperation = null; - return; - } - logger.debug("not current"); - } - for (Operation op : this.operationHistory) { - if (op.attempt == attempt.intValue()) { - op.clOperation.setEnd(Instant.now()); - op.clOperation.setMessage(message); - op.clOperation.setOutcome(result.toString()); - op.policyResult = result; - return; - } - } - logger.debug("Could not find associated operation"); - - } + private static final long serialVersionUID = -3773199283624595410L; + private static final Logger logger = LoggerFactory.getLogger(ControlLoopOperationManager.class); + + private static final String VSERVER_VSERVER_NAME = "vserver.vserver-name"; + private static final String GENERIC_VNF_VNF_NAME = "generic-vnf.vnf-name"; + private static final String GENERIC_VNF_VNF_ID = "generic-vnf.vnf-id"; + + @Override + public String toString() { + return "ControlLoopOperationManager [onset=" + (onset != null ? onset.getRequestID() : "null") + ", policy=" + + (policy != null ? policy.getId() : "null") + ", attempts=" + attempts + ", policyResult=" + + policyResult + ", currentOperation=" + currentOperation + ", operationHistory=" + operationHistory + + "]"; + } + + // + // These properties are not changeable, but accessible + // for Drools Rule statements. + // + public final ControlLoopEvent onset; + public final transient Policy policy; + + // + // Properties used to track the Operation + // + private int attempts = 0; + private transient Operation currentOperation = null; + private LinkedList<Operation> operationHistory = new LinkedList<>(); + private PolicyResult policyResult = null; + private ControlLoopEventManager eventManager = null; + private String targetEntity; + + public ControlLoopEventManager getEventManager() { + return eventManager; + } + + public void setEventManager(ControlLoopEventManager eventManager) { + this.eventManager = eventManager; + } + + public String getTargetEntity() { + return this.targetEntity; + } + + // + // Internal class used for tracking + // + private class Operation { + private ControlLoopOperation clOperation = new ControlLoopOperation(); + private PolicyResult policyResult = null; + private int attempt = 0; + + @Override + public String toString() { + return "Operation [attempt=" + attempt + ", policyResult=" + policyResult + ", operation=" + clOperation + + "]"; + } + } + + private String guardApprovalStatus = "NONE";// "NONE", "PERMIT", "DENY" + private transient Object operationRequest; + + public Object getOperationRequest() { + return operationRequest; + } + + public String getGuardApprovalStatus() { + return guardApprovalStatus; + } + + public void setGuardApprovalStatus(String guardApprovalStatus) { + this.guardApprovalStatus = guardApprovalStatus; + } + + /** + * Get the target for a policy. + * + * @param policy the policy + * @return the target + * @throws ControlLoopException if an error occurs + * @throws AAIException if an error occurs retrieving information from A&AI + */ + public String getTarget(Policy policy) throws ControlLoopException, AAIException { + if (policy.getTarget() == null) { + throw new ControlLoopException("The target is null"); + } + + if (policy.getTarget().getType() == null) { + throw new ControlLoopException("The target type is null"); + } + + switch (policy.getTarget().getType()) { + case PNF: + throw new ControlLoopException("PNF target is not supported"); + case VM: + case VNF: + VirtualControlLoopEvent virtualOnset = (VirtualControlLoopEvent) this.onset; + if (this.onset.getTarget().equalsIgnoreCase(VSERVER_VSERVER_NAME)) { + return virtualOnset.getAAI().get(VSERVER_VSERVER_NAME); + } else if (this.onset.getTarget().equalsIgnoreCase(GENERIC_VNF_VNF_ID)) { + return virtualOnset.getAAI().get(GENERIC_VNF_VNF_ID); + } else if (this.onset.getTarget().equalsIgnoreCase(GENERIC_VNF_VNF_NAME)) { + /* + * If the onset is enriched with the vnf-id, we don't need an A&AI response + */ + if (virtualOnset.getAAI().containsKey(GENERIC_VNF_VNF_ID)) { + return virtualOnset.getAAI().get(GENERIC_VNF_VNF_ID); + } + + /* + * If the vnf-name was retrieved from the onset then the vnf-id must be obtained + * from the event manager's A&AI GET query + */ + String vnfId = this.eventManager.getVnfResponse().getVnfID(); + if (vnfId == null) { + throw new AAIException("No vnf-id found"); + } + return vnfId; + } + throw new ControlLoopException("Target does not match target type"); + default: + throw new ControlLoopException("The target type is not supported"); + } + } + + /** + * Construct an instance. + * + * @param onset the onset event + * @param policy the policy + * @param em the event manager + * @throws ControlLoopException if an error occurs + * @throws AAIException if an error occurs retrieving information from A&AI + */ + public ControlLoopOperationManager(ControlLoopEvent onset, Policy policy, ControlLoopEventManager em) + throws ControlLoopException, AAIException { + this.onset = onset; + this.policy = policy; + this.guardApprovalStatus = "NONE"; + this.eventManager = em; + this.targetEntity = getTarget(policy); + + // + // Let's make a sanity check + // + switch (policy.getActor()) { + case "APPC": + if ("ModifyConfig".equalsIgnoreCase(policy.getRecipe())) { + /* + * The target vnf-id may not be the same as the source vnf-id specified in the + * yaml, the target vnf-id is retrieved by a named query to A&AI. + */ + String targetVnf = AppcLcmActorServiceProvider.vnfNamedQuery(policy.getTarget().getResourceID(), + this.targetEntity); + this.targetEntity = targetVnf; + } + break; + case "SO": + break; + case "VFC": + break; + default: + throw new ControlLoopException("ControlLoopEventManager: policy has an unknown actor."); + } + } + + /** + * Start an operation. + * + * @param onset the onset event + * @return the operation request + * @throws ControlLoopException if an error occurs + */ + public Object startOperation(/* VirtualControlLoopEvent */ControlLoopEvent onset) throws ControlLoopException { + verifyOperatonCanRun(); + + // + // Setup + // + this.policyResult = null; + Operation operation = new Operation(); + operation.attempt = ++this.attempts; + operation.clOperation.setActor(this.policy.getActor()); + operation.clOperation.setOperation(this.policy.getRecipe()); + operation.clOperation.setTarget(this.policy.getTarget().toString()); + operation.clOperation.setSubRequestId(Integer.toString(operation.attempt)); + // + // Now determine which actor we need to construct a request for + // + switch (policy.getActor()) { + case "APPC": + /* + * If the recipe is ModifyConfig, a legacy APPC request is constructed. Otherwise an + * LCMRequest is constructed. + */ + this.currentOperation = operation; + if ("ModifyConfig".equalsIgnoreCase(policy.getRecipe())) { + this.operationRequest = APPCActorServiceProvider.constructRequest((VirtualControlLoopEvent) onset, + operation.clOperation, this.policy, this.targetEntity); + } else { + this.operationRequest = AppcLcmActorServiceProvider.constructRequest( + (VirtualControlLoopEvent) onset, operation.clOperation, this.policy, this.targetEntity); + } + // + // Save the operation + // + + return operationRequest; + case "SO": + SOActorServiceProvider soActorSp = new SOActorServiceProvider(); + this.operationRequest = + soActorSp.constructRequest((VirtualControlLoopEvent) onset, operation.clOperation, this.policy); + + // Save the operation + this.currentOperation = operation; + + if (this.operationRequest == null) { + this.policyResult = PolicyResult.FAILURE; + } + + return operationRequest; + case "VFC": + this.operationRequest = VFCActorServiceProvider.constructRequest((VirtualControlLoopEvent) onset, + operation.clOperation, this.policy, this.eventManager.getVnfResponse()); + this.currentOperation = operation; + if (this.operationRequest == null) { + this.policyResult = PolicyResult.FAILURE; + } + return operationRequest; + default: + throw new ControlLoopException("invalid actor " + policy.getActor() + " on policy"); + } + } + + /** + * Handle a response. + * + * @param response the response + * @return a PolicyResult + */ + public PolicyResult onResponse(Object response) { + // + // Which response is it? + // + if (response instanceof Response) { + // + // Cast APPC response and handle it + // + return onResponse((Response) response); + } else if (response instanceof LCMResponseWrapper) { + // + // Cast LCM response and handle it + // + return onResponse((LCMResponseWrapper) response); + } else if (response instanceof SOResponseWrapper) { + // + // Cast SO response and handle it + // + return onResponse((SOResponseWrapper) response); + } else if (response instanceof VFCResponse) { + // + // Cast VFC response and handle it + // + return onResponse((VFCResponse) response); + } else { + return null; + } + } + + /** + * This method handles operation responses from APPC. + * + * @param appcResponse the APPC response + * @return The result of the response handling + */ + private PolicyResult onResponse(Response appcResponse) { + // + // Determine which subrequestID (ie. attempt) + // + Integer operationAttempt = null; + try { + operationAttempt = Integer.parseInt(appcResponse.getCommonHeader().getSubRequestID()); + } catch (NumberFormatException e) { + // + // We cannot tell what happened if this doesn't exist + // + this.completeOperation(operationAttempt, "Policy was unable to parse APP-C SubRequestID (it was null).", + PolicyResult.FAILURE_EXCEPTION); + return PolicyResult.FAILURE_EXCEPTION; + } + // + // Sanity check the response message + // + if (appcResponse.getStatus() == null) { + // + // We cannot tell what happened if this doesn't exist + // + this.completeOperation(operationAttempt, + "Policy was unable to parse APP-C response status field (it was null).", + PolicyResult.FAILURE_EXCEPTION); + return PolicyResult.FAILURE_EXCEPTION; + } + // + // Get the Response Code + // + ResponseCode code = ResponseCode.toResponseCode(appcResponse.getStatus().getCode()); + if (code == null) { + // + // We are unaware of this code + // + this.completeOperation(operationAttempt, "Policy was unable to parse APP-C response status code field.", + PolicyResult.FAILURE_EXCEPTION); + return PolicyResult.FAILURE_EXCEPTION; + } + // + // Ok, let's figure out what APP-C's response is + // + switch (code) { + case ACCEPT: + // + // This is good, they got our original message and + // acknowledged it. + // + // Is there any need to track this? + // + return null; + case ERROR: + case REJECT: + // + // We'll consider these two codes as exceptions + // + this.completeOperation(operationAttempt, appcResponse.getStatus().getDescription(), + PolicyResult.FAILURE_EXCEPTION); + if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { + return null; + } + return PolicyResult.FAILURE_EXCEPTION; + case SUCCESS: + // + // + // + this.completeOperation(operationAttempt, appcResponse.getStatus().getDescription(), + PolicyResult.SUCCESS); + if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { + return null; + } + return PolicyResult.SUCCESS; + case FAILURE: + // + // + // + this.completeOperation(operationAttempt, appcResponse.getStatus().getDescription(), + PolicyResult.FAILURE); + if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { + return null; + } + return PolicyResult.FAILURE; + default: + return null; + } + } + + /** + * This method handles operation responses from LCM. + * + * @param dmaapResponse the LCM response + * @return The result of the response handling + */ + private PolicyResult onResponse(LCMResponseWrapper dmaapResponse) { + /* + * Parse out the operation attempt using the subrequestid + */ + Integer operationAttempt = AppcLcmActorServiceProvider + .parseOperationAttempt(dmaapResponse.getBody().getCommonHeader().getSubRequestId()); + if (operationAttempt == null) { + this.completeOperation(operationAttempt, "Policy was unable to parse APP-C SubRequestID (it was null).", + PolicyResult.FAILURE_EXCEPTION); + } + + /* + * Process the APPCLCM response to see what PolicyResult should be returned + */ + AbstractMap.SimpleEntry<PolicyResult, String> result = + AppcLcmActorServiceProvider.processResponse(dmaapResponse); + + if (result.getKey() != null) { + this.completeOperation(operationAttempt, result.getValue(), result.getKey()); + if (PolicyResult.FAILURE_TIMEOUT.equals(this.policyResult)) { + return null; + } + return result.getKey(); + } + return null; + } + + /** + * This method handles operation responses from SO. + * + * @param msoResponse the SO response + * @return The result of the response handling + */ + private PolicyResult onResponse(SOResponseWrapper msoResponse) { + switch (msoResponse.getSoResponse().getHttpResponseCode()) { + case 200: + case 202: + // + // Consider it as success + // + this.completeOperation(this.attempts, msoResponse.getSoResponse().getHttpResponseCode() + " Success", + PolicyResult.SUCCESS); + if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { + return null; + } + return PolicyResult.SUCCESS; + default: + // + // Consider it as failure + // + this.completeOperation(this.attempts, msoResponse.getSoResponse().getHttpResponseCode() + " Failed", + PolicyResult.FAILURE); + if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { + return null; + } + return PolicyResult.FAILURE; + } + } + + /** + * This method handles operation responses from VFC. + * + * @param vfcResponse the VFC response + * @return The result of the response handling + */ + private PolicyResult onResponse(VFCResponse vfcResponse) { + if (vfcResponse.getResponseDescriptor().getStatus().equalsIgnoreCase("finished")) { + // + // Consider it as success + // + this.completeOperation(this.attempts, " Success", PolicyResult.SUCCESS); + if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { + return null; + } + return PolicyResult.SUCCESS; + } else { + // + // Consider it as failure + // + this.completeOperation(this.attempts, " Failed", PolicyResult.FAILURE); + if (this.policyResult != null && this.policyResult.equals(PolicyResult.FAILURE_TIMEOUT)) { + return null; + } + // increment operation attempts for retries + this.attempts += 1; + return PolicyResult.FAILURE; + } + } + + /** + * Get the operation timeout. + * + * @return the timeout + */ + public Integer getOperationTimeout() { + // + // Sanity check + // + if (this.policy == null) { + logger.debug("getOperationTimeout returning 0"); + return 0; + } + logger.debug("getOperationTimeout returning {}", this.policy.getTimeout()); + return this.policy.getTimeout(); + } + + /** + * Get the operation timeout as a String. + * + * @param defaultTimeout the default timeout + * @return the timeout as a String + */ + public String getOperationTimeoutString(int defaultTimeout) { + Integer to = this.getOperationTimeout(); + if (to == null || to == 0) { + return Integer.toString(defaultTimeout) + "s"; + } + return to.toString() + "s"; + } + + public PolicyResult getOperationResult() { + return this.policyResult; + } + + /** + * Get the operation as a message. + * + * @return the operation as a message + */ + public String getOperationMessage() { + if (this.currentOperation != null && this.currentOperation.clOperation != null) { + return this.currentOperation.clOperation.toMessage(); + } + + if (!this.operationHistory.isEmpty()) { + return this.operationHistory.getLast().clOperation.toMessage(); + } + return null; + } + + /** + * Get the operation as a message including the guard result. + * + * @param guardResult the guard result + * @return the operation as a message including the guard result + */ + public String getOperationMessage(String guardResult) { + if (this.currentOperation != null && this.currentOperation.clOperation != null) { + return this.currentOperation.clOperation.toMessage() + ", Guard result: " + guardResult; + } + + if (!this.operationHistory.isEmpty()) { + return this.operationHistory.getLast().clOperation.toMessage() + ", Guard result: " + guardResult; + } + return null; + } + + /** + * Get the operation history. + * + * @return the operation history + */ + public String getOperationHistory() { + if (this.currentOperation != null && this.currentOperation.clOperation != null) { + return this.currentOperation.clOperation.toHistory(); + } + + if (!this.operationHistory.isEmpty()) { + return this.operationHistory.getLast().clOperation.toHistory(); + } + return null; + } + + /** + * Get the history. + * + * @return the list of control loop operations + */ + public List<ControlLoopOperation> getHistory() { + LinkedList<ControlLoopOperation> history = new LinkedList<>(); + for (Operation op : this.operationHistory) { + history.add(new ControlLoopOperation(op.clOperation)); + + } + return history; + } + + /** + * Set the operation has timed out. + */ + public void setOperationHasTimedOut() { + // + // + // + this.completeOperation(this.attempts, "Operation timed out", PolicyResult.FAILURE_TIMEOUT); + } + + /** + * Set the operation has been denied by guard. + */ + public void setOperationHasGuardDeny() { + // + // + // + this.completeOperation(this.attempts, "Operation denied by Guard", PolicyResult.FAILURE_GUARD); + } + + public void setOperationHasException(String message) { + this.completeOperation(this.attempts, message, PolicyResult.FAILURE_EXCEPTION); + } + + /** + * Is the operation complete. + * + * @return <code>true</code> if the operation is complete, <code>false</code> otherwise + */ + public boolean isOperationComplete() { + // + // Is there currently a result? + // + if (this.policyResult == null) { + // + // either we are in process or we + // haven't started + // + return false; + } + // + // We have some result, check if the operation failed + // + if (this.policyResult.equals(PolicyResult.FAILURE)) { + // + // Check if there were no retries specified + // + if (policy.getRetry() == null || policy.getRetry() == 0) { + // + // The result is the failure + // + return true; + } + // + // Check retries + // + if (this.isRetriesMaxedOut()) { + // + // No more attempts allowed, reset + // that our actual result is failure due to retries + // + this.policyResult = PolicyResult.FAILURE_RETRIES; + return true; + } else { + // + // There are more attempts available to try the + // policy recipe. + // + return false; + } + } + // + // Other results mean we are done + // + return true; + } + + public boolean isOperationRunning() { + return (this.currentOperation != null); + } + + /** + * This method verifies that the operation manager may run an operation. + * + * @return True if the operation can run, false otherwise + * @throws ControlLoopException if the operation cannot run + */ + private void verifyOperatonCanRun() throws ControlLoopException { + // + // They shouldn't call us if we currently running something + // + if (this.currentOperation != null) { + // + // what do we do if we are already running an operation? + // + throw new ControlLoopException("current operation is not null (an operation is already running)"); + } + // + // Check if we have maxed out on retries + // + if (this.policy.getRetry() == null || this.policy.getRetry() < 1) { + // + // No retries are allowed, so check have we even made + // one attempt to execute the operation? + // + if (this.attempts >= 1) { + // + // We have, let's ensure our PolicyResult is set + // + if (this.policyResult == null) { + this.policyResult = PolicyResult.FAILURE_RETRIES; + } + // + // + // + throw new ControlLoopException("current operation failed and retries are not allowed"); + } + } else { + // + // Have we maxed out on retries? + // + if (this.attempts > this.policy.getRetry()) { + if (this.policyResult == null) { + this.policyResult = PolicyResult.FAILURE_RETRIES; + } + throw new ControlLoopException("current oepration has failed after " + this.attempts + " retries"); + } + } + + return; + } + + private boolean isRetriesMaxedOut() { + if (policy.getRetry() == null || policy.getRetry() == 0) { + // + // There were NO retries specified, so declare + // this as completed. + // + return (this.attempts > 0); + } + return (this.attempts > policy.getRetry()); + } + + private void storeOperationInDataBase() { + // Only store in DB if enabled + boolean guardEnabled = "false".equalsIgnoreCase(PolicyEngine.manager.getEnvironmentProperty("guard.disabled")); + if (!guardEnabled) { + return; + } + + + // DB Properties + Properties props = new Properties(); + if (PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_URL) != null + && PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_USER) != null + && PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_PASS) != null) { + props.put(Util.ECLIPSE_LINK_KEY_URL, PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_URL)); + props.put(Util.ECLIPSE_LINK_KEY_USER, PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_USER)); + props.put(Util.ECLIPSE_LINK_KEY_PASS, PolicyEngine.manager.getEnvironmentProperty(Util.ONAP_KEY_PASS)); + props.put(PersistenceUnitProperties.CLASSLOADER, ControlLoopOperationManager.class.getClassLoader()); + } + + + String opsHistPu = System.getProperty("OperationsHistoryPU"); + if (opsHistPu == null || !opsHistPu.equals("TestOperationsHistoryPU")) { + opsHistPu = "OperationsHistoryPU"; + } else { + props.clear(); + } + EntityManager em; + try { + em = Persistence.createEntityManagerFactory(opsHistPu, props).createEntityManager(); + } catch (Exception e) { + logger.error("storeOperationInDataBase threw: ", e); + return; + } + + OperationsHistoryDbEntry newEntry = new OperationsHistoryDbEntry(); + + newEntry.setClosedLoopName(this.onset.getClosedLoopControlName()); + newEntry.setRequestId(this.onset.getRequestID().toString()); + newEntry.setActor(this.currentOperation.clOperation.getActor()); + newEntry.setOperation(this.currentOperation.clOperation.getOperation()); + newEntry.setTarget(this.targetEntity); + newEntry.setStarttime(Timestamp.from(this.currentOperation.clOperation.getStart())); + newEntry.setSubrequestId(this.currentOperation.clOperation.getSubRequestId()); + newEntry.setEndtime(new Timestamp(this.currentOperation.clOperation.getEnd().toEpochMilli())); + newEntry.setMessage(this.currentOperation.clOperation.getMessage()); + newEntry.setOutcome(this.currentOperation.clOperation.getOutcome()); + + em.getTransaction().begin(); + em.persist(newEntry); + em.getTransaction().commit(); + + em.close(); + } + + private void completeOperation(Integer attempt, String message, PolicyResult result) { + if (attempt == null) { + logger.debug("attempt cannot be null (i.e. subRequestID)"); + return; + } + if (this.currentOperation != null) { + if (this.currentOperation.attempt == attempt.intValue()) { + this.currentOperation.clOperation.setEnd(Instant.now()); + this.currentOperation.clOperation.setMessage(message); + this.currentOperation.clOperation.setOutcome(result.toString()); + this.currentOperation.policyResult = result; + // + // Save it in history + // + this.operationHistory.add(this.currentOperation); + this.storeOperationInDataBase(); + // + // Set our last result + // + this.policyResult = result; + // + // Clear the current operation field + // + this.currentOperation = null; + return; + } + logger.debug("not current"); + } + for (Operation op : this.operationHistory) { + if (op.attempt == attempt.intValue()) { + op.clOperation.setEnd(Instant.now()); + op.clOperation.setMessage(message); + op.clOperation.setOutcome(result.toString()); + op.policyResult = result; + return; + } + } + logger.debug("Could not find associated operation"); + + } } |