diff options
Diffstat (limited to 'controlloop/m2/appclcm')
9 files changed, 2205 insertions, 0 deletions
diff --git a/controlloop/m2/appclcm/pom.xml b/controlloop/m2/appclcm/pom.xml new file mode 100644 index 000000000..78200454a --- /dev/null +++ b/controlloop/m2/appclcm/pom.xml @@ -0,0 +1,86 @@ +<?xml version="1.0"?> +<!-- + ============LICENSE_START======================================================= + ONAP Policy Engine - Drools PDP + ================================================================================ + Copyright (C) 2020 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. + 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. + See the License for the specific language governing permissions and + limitations under the License. + ============LICENSE_END========================================================= + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.policy.drools-applications.controlloop.m2</groupId> + <artifactId>m2</artifactId> + <version>1.6.0-SNAPSHOT</version> + </parent> + + <artifactId>appclcm</artifactId> + <name>Experimental Control Loop Model - appclcm</name> + + <dependencies> + <dependency> + <groupId>org.onap.policy.drools-applications.controlloop.m2</groupId> + <artifactId>adapters</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onap.policy.drools-applications.controlloop.m2</groupId> + <artifactId>base</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onap.policy.drools-applications.controlloop.m2</groupId> + <artifactId>lock</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId> + <artifactId>appclcm</artifactId> + <version>${policy.models.version}</version> + </dependency> + + <dependency> + <groupId>org.onap.policy.drools-pdp</groupId> + <artifactId>policy-management</artifactId> + <version>${version.policy.drools-pdp}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-mockito</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + +</project> diff --git a/controlloop/m2/appclcm/src/main/java/org/onap/policy/m2/appclcm/AppcLcmActor.java b/controlloop/m2/appclcm/src/main/java/org/onap/policy/m2/appclcm/AppcLcmActor.java new file mode 100644 index 000000000..f89d3b873 --- /dev/null +++ b/controlloop/m2/appclcm/src/main/java/org/onap/policy/m2/appclcm/AppcLcmActor.java @@ -0,0 +1,76 @@ +/*- + * ============LICENSE_START======================================================= + * m2/appclcm + * ================================================================================ + * Copyright (C) 2020 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. + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.m2.appclcm; + +import java.io.Serializable; + +import org.onap.policy.controlloop.ControlLoopEvent; +import org.onap.policy.controlloop.policy.Policy; + +import org.onap.policy.m2.adapters.VirtualOnsetAdapter; +import org.onap.policy.m2.base.Actor; +import org.onap.policy.m2.base.Operation; +import org.onap.policy.m2.base.Transaction; + +/** + * A single instance of this class is created, and resides within the + * 'nameToActor' table within class 'Transaction', under the key 'APPC'. + */ +public class AppcLcmActor implements Actor, Serializable { + /* *******************/ + /* 'Actor' interface */ + /* *******************/ + + private static final long serialVersionUID = -593438898257647144L; + + + static { + // ensures that 'VirtualOnsetAdapter' has an entry in the + // 'OnsetAdapter' table + VirtualOnsetAdapter.register(); + } + + /** + * Return the name associated with this 'Actor'. + * + * {@inheritDoc} + */ + @Override + public String getName() { + return "APPCLCM"; + } + + /** + * Create an 'Operation' for this 'Actor'. + * + * {@inheritDoc} + */ + @Override + public Operation createOperation( + Transaction transaction, Policy policy, ControlLoopEvent onset, + int attempt) { + + if ("healthcheck".equalsIgnoreCase(policy.getRecipe())) { + return new AppcLcmHealthCheckOperation(transaction, policy, onset, attempt); + } + return new AppcLcmOperation(transaction, policy, onset, attempt); + } +} diff --git a/controlloop/m2/appclcm/src/main/java/org/onap/policy/m2/appclcm/AppcLcmHealthCheckOperation.java b/controlloop/m2/appclcm/src/main/java/org/onap/policy/m2/appclcm/AppcLcmHealthCheckOperation.java new file mode 100644 index 000000000..42e06f98f --- /dev/null +++ b/controlloop/m2/appclcm/src/main/java/org/onap/policy/m2/appclcm/AppcLcmHealthCheckOperation.java @@ -0,0 +1,248 @@ +/*- + * ============LICENSE_START======================================================= + * m2/appclcm + * ================================================================================ + * Copyright (C) 2020 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. + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.m2.appclcm; + +import java.util.HashMap; +import java.util.Map; + +import org.onap.policy.appclcm.AppcLcmDmaapWrapper; +import org.onap.policy.appclcm.AppcLcmOutput; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.controlloop.ControlLoopEvent; +import org.onap.policy.controlloop.ControlLoopException; +import org.onap.policy.controlloop.VirtualControlLoopEvent; +import org.onap.policy.controlloop.policy.Policy; +import org.onap.policy.controlloop.policy.PolicyResult; +import org.onap.policy.guard.PolicyGuardResponse; +import org.onap.policy.m2.appclcm.model.AppcLcmResponseCode; +import org.onap.policy.m2.base.Transaction; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AppcLcmHealthCheckOperation extends AppcLcmOperation { + public static final String DCAE_IPV4_ADDR = + "vserver.l-interface.l3-interface-ipv4-address-list.l3-inteface-ipv4-address"; + + private static Logger logger = LoggerFactory.getLogger(AppcLcmHealthCheckOperation.class); + + private static final long serialVersionUID = 4969322301462776173L; + + public AppcLcmHealthCheckOperation(Transaction transaction, Policy policy, + ControlLoopEvent onset, int attempt) { + super(transaction, policy, onset, attempt); + } + + /** + * This method will attempt to deserialize the json payload from appc and + * then parse the response to determine if the vnf is healthy or unhealthy. + * The "state" field in the payload will contain "healthy" or "unhealthy" + * based on the condition of the vnf. + * + * @param jsonPayload + * the appc lcm response json payload + * @return the string that contains the state of the vnf + */ + private String getVnfHealthState(String jsonPayload) { + HashMap<String, Object> healthCheckPayloadMap; + try { + healthCheckPayloadMap = coder.decode(jsonPayload, HashMap.class); + } catch (CoderException e) { + return null; + } + + String stateOfHealth = null; + if (healthCheckPayloadMap.containsKey("state")) { + stateOfHealth = healthCheckPayloadMap.get("state").toString(); + } else { + return null; + } + return stateOfHealth; + } + + /** + * An incoming message is being delivered to the operation. + * + * {@inheritDoc} + */ + @Override + public void incomingMessage(Object object) { + if (!(object instanceof AppcLcmDmaapWrapper)) { + if (object instanceof PolicyGuardResponse) { + incomingGuardMessage((PolicyGuardResponse) object); + return; + } + // ignore this message (not sure why we even got it) + return; + } + + // If we reach this point, we have a 'AppcLcmDmaapWrapper' instance. + // The rest of this method is mostly copied from + // 'ControlLoopOperationManager.onResponse'. + + AppcLcmOutput response = ((AppcLcmDmaapWrapper)object).getBody().getOutput(); + + // + // Determine which subrequestID (ie. attempt) + // + int operationAttempt; + try { + operationAttempt = Integer + .parseInt(response.getCommonHeader().getSubRequestId()); + } catch (NumberFormatException e) { + // + // We cannot tell what happened if this doesn't exist + // + this.completeOperation( + this.getAttempt(), + "Policy was unable to parse APP-C SubRequestID (it was null).", + PolicyResult.FAILURE_EXCEPTION); + return; + } + // + // Sanity check the response message + // + if (response.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; + } + // + // Get the Response Code + // + AppcLcmResponseCode responseValue = AppcLcmResponseCode + .toResponseValue(response.getStatus().getCode()); + if (responseValue == 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; + } + // + // Ok, let's figure out what APP-C's response is + // + switch (responseValue) { + case ACCEPTED: + // + // This is good, they got our original message and + // acknowledged it. + // + // Is there any need to track this? + // + return; + case ERROR: + case REJECT: + // + // We'll consider these two codes as exceptions + // + this.completeOperation(operationAttempt, + response.getStatus().getMessage(), + PolicyResult.FAILURE_EXCEPTION); + return; + case FAILURE: + // + // APPC could not do a healthcheck + // + this.completeOperation(operationAttempt, + response.getStatus().getMessage(), + PolicyResult.FAILURE); + return; + case SUCCESS: + // + // This means APPC was able to execute the health check. + // The payload has to be parsed to see if the VNF is + // healthy or unhealthy + // + + // + // sanity check the payload + // + if (response.getPayload() == null || response.getPayload().isEmpty()) { + // + // We are cannot parse the payload + // + this.completeOperation( + operationAttempt, + "Policy was unable to parse APP-C response payload because it was null.", + PolicyResult.FAILURE_EXCEPTION); + return; + } + + // + // parse the payload to see if the VNF is healthy/unhealthy + // + String vnfHealthState = getVnfHealthState(response.getPayload()); + if ("healthy".equalsIgnoreCase(vnfHealthState)) { + this.completeOperation(operationAttempt, "VNF is healthy", + PolicyResult.SUCCESS); + } else if ("unhealthy".equalsIgnoreCase(vnfHealthState)) { + this.completeOperation(operationAttempt, "VNF is unhealthy", + PolicyResult.FAILURE); + } else { + this.completeOperation( + operationAttempt, + "Error: Could not determine the state of the VNF." + + " The state field in the APPC response payload was unrecognized or null.", + PolicyResult.FAILURE_EXCEPTION); + } + return; + default: + return; + } + } + + /** + * This method will construct a payload for a health check. + * The payload must be an escaped json string so gson is used + * to convert the payload hashmap into json + * + * @return an escaped json string representation of the payload + * @throws ControlLoopException if it occurs + */ + @Override + protected String setPayload(Map<String, String> aai, String recipe) throws ControlLoopException { + Map<String, String> payload = new HashMap<>(); + + // Extract oam ip address from the onset + String ipAddr = aai.get(DCAE_IPV4_ADDR); + if (ipAddr != null) { + payload.put("host-ip-address", ipAddr); + } else { + logger.error("Error - IPv4 Address not found in the onset"); + setErrorStatus("Error - IPv4 Address not found in the onset"); + } + + try { + return coder.encode(payload); + } catch (CoderException e) { + return null; + } + } +} diff --git a/controlloop/m2/appclcm/src/main/java/org/onap/policy/m2/appclcm/AppcLcmOperation.java b/controlloop/m2/appclcm/src/main/java/org/onap/policy/m2/appclcm/AppcLcmOperation.java new file mode 100644 index 000000000..6a2518f46 --- /dev/null +++ b/controlloop/m2/appclcm/src/main/java/org/onap/policy/m2/appclcm/AppcLcmOperation.java @@ -0,0 +1,703 @@ +/*- + * ============LICENSE_START======================================================= + * m2/appclcm + * ================================================================================ + * Copyright (C) 2020 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. + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.m2.appclcm; + +import com.google.common.collect.ImmutableList; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; + +import org.onap.policy.appclcm.AppcLcmBody; +import org.onap.policy.appclcm.AppcLcmCommonHeader; +import org.onap.policy.appclcm.AppcLcmDmaapWrapper; +import org.onap.policy.appclcm.AppcLcmInput; +import org.onap.policy.appclcm.AppcLcmOutput; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.controlloop.ControlLoopEvent; +import org.onap.policy.controlloop.ControlLoopException; +import org.onap.policy.controlloop.ControlLoopOperation; +import org.onap.policy.controlloop.VirtualControlLoopEvent; +import org.onap.policy.controlloop.policy.Policy; +import org.onap.policy.controlloop.policy.PolicyResult; +import org.onap.policy.controlloop.policy.TargetType; +import org.onap.policy.drools.m2.lock.LockAdjunct; +import org.onap.policy.guard.PolicyGuardResponse; +import org.onap.policy.m2.appclcm.model.AppcLcmResponseCode; +import org.onap.policy.m2.base.GuardAdjunct; +import org.onap.policy.m2.base.Operation; +import org.onap.policy.m2.base.Transaction; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is used for all APPC LCM operations. The only difference between + * operation types (Restart, Rebuild, Migrate, Evacuate, or HealthCheck) as + * far as DroolsPDP is concerned, is the operation name (policy.recipe). + * It is up to APPC to interpret these operations. + */ +public class AppcLcmOperation implements Operation, LockAdjunct.Requestor, Serializable { + public static final String DCAE_CLOSEDLOOP_DISABLED_FIELD = "vserver.is-closed-loop-disabled"; + public static final String DCAE_VSERVER_SELF_LINK_FIELD = "vserver.selflink"; + public static final String DCAE_IDENTITY_FIELD = "cloud-region.identity-url"; + public static final String DCAE_VNF_NAME_FIELD = "generic-vnf.vnf-name"; + public static final String DCAE_VNF_ID_FIELD = "generic-vnf.vnf-id"; + public static final String DCAE_VSERVER_ID_FIELD = "vserver.vserver-id"; + public static final String DCAE_TENANT_ID_FIELD = "tenant.tenant-id"; + + public static final String APPC_LCM_VM_ID_FIELD = "vm-id"; + public static final String APPC_LCM_IDENTITY_URL_FIELD = "identity-url"; + public static final String APPC_LCM_TENANT_ID_FIELD = "tenant-id"; + + private static Logger logger = LoggerFactory.getLogger(AppcLcmOperation.class); + + private static final long serialVersionUID = 5062964240000304989L; + + // state when waiting for a lock + public static final String LCM_WAIT_FOR_LOCK = "LCM.WAIT_FOR_LOCK"; + + // state when waiting for a response from 'guard' + public static final String LCM_GUARD_PENDING = "LCM.GUARD_PENDING"; + + // state when ready to send out the LCM message + public static final String LCM_BEGIN = "LCM.BEGIN"; + + // state when waiting for a response from APPC + public static final String LCM_PENDING = "LCM.PENDING"; + + // state when processing can't continue due to errors + public static final String LCM_ERROR = "LCM.ERROR"; + + // state when the operation has completed (success, failure, or timeout) + public static final String LCM_COMPLETE = "LCM.COMPLETE"; + + // the APPC LCM recipes supported by Policy + private static final ImmutableList<String> recipes = ImmutableList.of( + "Restart", "Rebuild", "Migrate", "Evacuate", + "HealthCheck", "Reboot", "Start", "Stop"); + + // used for JSON <-> String conversion + protected static StandardCoder coder = new StandardCoder(); + + // current state -- one of the 6 values, above + @Getter(onMethod = @__({@Override})) + private String state; + + // transaction associated with this operation + private Transaction transaction; + + // policy associated with this operation + @Getter(onMethod = @__({@Override})) + private Policy policy; + + // initial onset message + private VirtualControlLoopEvent onset; + + // attempt associated with this operation + @Getter(onMethod = @__({@Override})) + private int attempt; + + // message, if any, associated with the result of this operation + @Getter(onMethod = @__({@Override})) + private String message = null; + + // operation result -- set to a non-null value when the operation completes + @Getter(onMethod = @__({@Override})) + private PolicyResult result = null; + + // the APPC LCM 'target' derived from the onset + private String target; + + // reference to a Transaction adjunct supporting guard operations + private GuardAdjunct guardAdjunct; + + // counter for how many partial failures were received from appc + private int partialFailureCount = 0; + + // counter for how many partial success were received from appc + private int partialSuccessCount = 0; + + /** + * Constructor -- initialize an LCM operation instance, + * try to acquire a lock, and start the guard query if we are ready. + * + * @param transaction the transaction the operation is running under + * @param policy the policy associated with this operation + * @param onset the initial onset event that triggered the transaction + * @param attempt this value starts at 1, and is incremented for each retry + */ + public AppcLcmOperation(Transaction transaction, Policy policy, ControlLoopEvent onset, + int attempt) { + // state prior to aquiring the lock + // (will be changed when the lock is acquired) + this.state = LCM_WAIT_FOR_LOCK; + this.transaction = transaction; + this.policy = policy; + this.attempt = attempt; + + if (!(onset instanceof VirtualControlLoopEvent)) { + // we need the correct 'onset' event type + state = LCM_COMPLETE; + result = PolicyResult.FAILURE; + message = "Onset event has the wrong type"; + return; + } + + this.onset = (VirtualControlLoopEvent)onset; + + // fetch or create the guard adjunct -- note that 'guard' operations are + // only performed if a 'GuardContext' is present, and the adjunct was + // created by the Drools rules prior to creating this operation + this.guardAdjunct = transaction.getAdjunct(GuardAdjunct.class); + + // attempt to get a lock for the VM -- if we get it immediately, + // we can go to the 'LCM_GUARD_PENDING' or 'LCM_BEGIN' state + + target = this.onset.getAai().get(onset.getTarget()).toString(); + String key = onset.getTargetType() + ":" + target; + if (transaction.getAdjunct(LockAdjunct.class).getLock(this, key, + transaction.getRequestId().toString(), false)) { + // lock was acquired immediately -- move on to the 'guard query' + // phase + guardQuery(); + } + } + + /** + * A method returning true if the A&AI subtag exists + * and the control loop exists and is not disabled and + * the target field exists as a key in the A&AI subtag. + * + * @param transaction the transaction corresponding to an event + * @param event the onset containing the A&AI subtag + * @return true if the A&AI subtag is valid, false otherwise + */ + public static boolean isAaiValid(Transaction transaction, VirtualControlLoopEvent event) { + if (event.getAai() == null) { + transaction.setNotificationMessage("No A&AI Subtag"); + return false; + } else if (!event.getAai().containsKey(DCAE_CLOSEDLOOP_DISABLED_FIELD)) { + transaction.setNotificationMessage(DCAE_CLOSEDLOOP_DISABLED_FIELD + + " information missing"); + return false; + } else if (isClosedLoopDisabled(event.getAai())) { + transaction.setNotificationMessage(DCAE_CLOSEDLOOP_DISABLED_FIELD + + " is set to true"); + return false; + } else if (!event.getAai().containsKey(event.getTarget())) { + transaction.setNotificationMessage("target field invalid - must have corresponding AAI value"); + return false; + } + return true; + } + + private static boolean isClosedLoopDisabled(Map<String, String> map) { + if (!map.containsKey(DCAE_CLOSEDLOOP_DISABLED_FIELD)) { + return false; + } + String disabled = map.get(DCAE_CLOSEDLOOP_DISABLED_FIELD); + return ("true".equalsIgnoreCase(disabled) || "y".equalsIgnoreCase(disabled)); + } + + /** + * trigger an asynchronous guard query -- if guard is not enabled, + * we go directly to the 'LCM_BEGIN' state. + */ + private void guardQuery() { + if (guardAdjunct.asyncQuery(policy, target, onset.getRequestId().toString())) { + // 'GuardContext' is available -- + // wait for an incoming 'PolicyGuardResponse' message + this.state = LCM_GUARD_PENDING; + } else { + // no 'GuardContext' is available -- go directly to the 'begin' state + this.state = LCM_BEGIN; + transaction.modify(); + } + } + + /*=====================================*/ + /* 'LockAdjunct.Requestor' interface */ + /*=====================================*/ + + /** + * This method is called by 'LockAdjunct' if we initially had to wait for + * the lock, but it has now became available. + */ + public void lockAvailable() { + if (this.state == LCM_WAIT_FOR_LOCK) { + // we have the lock -- invoke 'quardQuery()', + // go to the appropriate state, and mark the transaction as modified + guardQuery(); + + // the 'lockAvailable' method was presumably triggered by the + // release + // of the lock by an unrelated transaction -- 'transaction.modify' + // is + // called to let Drools know that our transaction has gone through a + // state change + transaction.modify(); + } + } + + /** + * This method is called by 'LockAdjunct' if the lock was unable to be + * obtained. + */ + public void lockUnavailable() { + if (this.state == LCM_WAIT_FOR_LOCK) { + try { + setErrorStatus("Already processing event with this target"); + } catch (ControlLoopException e) { + logger.debug("Lock could not be obtained for this operation"); + } + } + } + + /*=======================*/ + /* 'Operation' interface */ + /*=======================*/ + + /** + * This method maps the recipe to the correct rpc-name syntax. + */ + private String toRpcName(String recipe) { + String rpcName = recipe.toLowerCase(); + if ("healthcheck".equals(rpcName)) { + rpcName = "health-check"; + } + return rpcName; + } + + /** + * This method forwards the construction of the recipe's + * payload to the proper handler. + * + * @return a json representation of the payload + * @throws ControlLoopException if it occurs + */ + protected String setPayload(Map<String, String> aai, String recipe) throws ControlLoopException { + Map<String, String> payload = null; + + switch (recipe) { + case "restart": + case "rebuild": + case "migrate": + case "evacuate": + case "start": + case "stop": + if (this.policy.getTarget().getType() == TargetType.VM) { + payload = setCommonPayload(aai); + } + break; + case "reboot": + payload = setRebootPayload(); + break; + default: + payload = null; + break; + } + + if (payload == null) { + return null; + } + + try { + return coder.encode(payload); + } catch (CoderException e) { + return null; + } + } + + /** + * This method will construct a payload for a restart, rebuild, + * migrate, or evacuate. The payload must be an escaped json + * string so gson is used to convert the payload hashmap into + * json + * + * @return a hashmap representation of the payload + * @throws ControlLoopException if it occurs + */ + private Map<String, String> setCommonPayload(Map<String, String> aai) throws ControlLoopException { + Map<String, String> payload = new HashMap<>(); + + for (Map.Entry<String, String> entry : aai.entrySet()) { + switch (entry.getKey()) { + case DCAE_VSERVER_SELF_LINK_FIELD: + if (entry.getValue() != null) { + payload.put(APPC_LCM_VM_ID_FIELD, entry.getValue()); + } else { + setErrorStatus("dcae onset is missing " + DCAE_VSERVER_SELF_LINK_FIELD); + } + break; + case DCAE_IDENTITY_FIELD: + if (entry.getValue() != null) { + payload.put(APPC_LCM_IDENTITY_URL_FIELD, entry.getValue()); + } else { + setErrorStatus("dcae onset is missing " + DCAE_IDENTITY_FIELD); + } + break; + case DCAE_TENANT_ID_FIELD: + if (entry.getValue() != null) { + payload.put(APPC_LCM_TENANT_ID_FIELD, entry.getValue()); + } else { + setErrorStatus("dcae onset is missing " + DCAE_TENANT_ID_FIELD); + } + break; + default: + break; + } + } + + return payload; + } + + /** + * This method will construct a payload for a reboot. + * The payload must be an escaped json string so gson is used + * to convert the payload hashmap into json. The reboot payload + * requires a type of "HARD" or "SOFT" reboot from the policy + * defined through CLAMP. + * + * @return an escaped json string representation of the payload + */ + private Map<String, String> setRebootPayload() throws ControlLoopException { + Map<String, String> payload = new HashMap<>(); + + if (this.policy.getTarget().getType() == TargetType.VM) { + payload = setCommonPayload(onset.getAai()); + // The tenant-id is not used for the reboot request so we can remove + // it after being added by the common payload + payload.remove(APPC_LCM_TENANT_ID_FIELD); + } + + // Extract "HARD" or "SOFT" from YAML policy + String type = this.policy.getPayload().get("type").toUpperCase(); + payload.put("type", type); + + return payload; + } + + /** + * Return the request message associated with this operation. + * + * {@inheritDoc} + * + * @throws ControlLoopException if it occurs + */ + @Override + public Object getRequest() throws ControlLoopException { + AppcLcmCommonHeader commonHeader = new AppcLcmCommonHeader(); + commonHeader.setRequestId(onset.getRequestId()); + commonHeader.setOriginatorId("POLICY"); + commonHeader.setSubRequestId(String.valueOf(attempt)); + + // Policy will send a default ttl of 10 minutes (600 seconds) + Map<String, String> flags = new HashMap<>(); + flags.put("ttl", "600"); + commonHeader.setFlags(flags); + + String action = null; + for (String recipe: recipes) { + if (recipe.equalsIgnoreCase(policy.getRecipe())) { + action = recipe; + break; + } + } + + if (action == null) { + setErrorStatus("Error - invalid recipe"); + } + + Map<String, String> actionIdentifiers = new HashMap<>(); + + // The vnf-id is needed for both VNF and VM level operations + if (onset.getAai().containsKey(DCAE_VNF_NAME_FIELD)) { + actionIdentifiers.put("vnf-id", onset.getAai().get(DCAE_VNF_ID_FIELD)); + } else { + logger.error("Error - no AAI DCAE VNF NAME key in the onset"); + setErrorStatus("Error - no VNF NAME key in the onset"); + } + + if (this.policy.getTarget().getType() == TargetType.VM) { + if (onset.getAai().containsKey(DCAE_VSERVER_ID_FIELD)) { + actionIdentifiers.put("vserver-id", onset.getAai().get(DCAE_VSERVER_ID_FIELD)); + } else { + logger.error("Error - no DCAE VSERVER ID key in the onset AAI\n"); + setErrorStatus("Error - no VSERVER ID key in the onset"); + } + } + + String payload = setPayload(onset.getAai(), action.toLowerCase()); + + // construct an APPC LCM 'Request' message + AppcLcmInput request = new AppcLcmInput(); + + request.setCommonHeader(commonHeader); + request.setAction(action); + request.setActionIdentifiers(actionIdentifiers); + request.setPayload(payload); + + // Pass the LCM request to the LCM wrapper + AppcLcmDmaapWrapper dmaapWrapper = new AppcLcmDmaapWrapper(); + dmaapWrapper.setVersion("2.0"); + AppcLcmBody appcBody = new AppcLcmBody(); + appcBody.setInput(request); + dmaapWrapper.setBody(appcBody); + dmaapWrapper.setCorrelationId(onset.getRequestId() + "-" + attempt); + dmaapWrapper.setRpcName(toRpcName(action)); + dmaapWrapper.setType("request"); + + // go to the LCM_PENDING state, under the assumption that the + // calling Drools code will send out the message we are returning + this.state = LCM_PENDING; + transaction.modify(); + return dmaapWrapper; + } + + /** + * This method is called by 'incomingMessage' when the message is a + * 'PolicyGuardResponse' message (leaving 'incomingMessage' to focus on + * 'Response' messages). + * + * @param response the received guard response message + */ + void incomingGuardMessage(PolicyGuardResponse response) { + // this message is only meaningful if we are waiting for a + // 'guard' response -- ignore it, if this isn't the case + if (this.state == LCM_GUARD_PENDING) { + if ("Deny".equals(response.getResult())) { + // this is a guard failure + logger.error("LCM operation denied by 'Guard'"); + this.message = "Denied by Guard"; + this.result = PolicyResult.FAILURE_GUARD; + this.state = LCM_COMPLETE; + } else { + // everything else is treated as 'Permit' + this.state = LCM_BEGIN; + transaction.modify(); + } + } + } + + /** + * An incoming message is being delivered to the operation. + * + * {@inheritDoc} + */ + @Override + public void incomingMessage(Object object) { + if (! (object instanceof AppcLcmDmaapWrapper)) { + if (object instanceof PolicyGuardResponse) { + incomingGuardMessage((PolicyGuardResponse)object); + return; + } else if (object instanceof ControlLoopEvent) { + incomingAbatedEvent((ControlLoopEvent) object); + return; + } + // ignore this message (not sure why we even got it) + return; + } + + // If we reach this point, we have a 'AppcLcmDmaapWrapper' instance. + // The rest of this method is mostly copied from + // 'ControlLoopOperationManager.onResponse'. + + AppcLcmOutput response = ((AppcLcmDmaapWrapper)object).getBody().getOutput(); + + // + // Determine which subrequestID (ie. attempt) + // + int operationAttempt; + try { + operationAttempt = Integer.parseInt(response.getCommonHeader() + .getSubRequestId()); + } catch (NumberFormatException e) { + // + // We cannot tell what happened if this doesn't exist + // If the attempt cannot be parsed then we assume it is + // the current attempt + // + this.completeOperation(this.attempt, "Policy was unable to parse APP-C SubRequestID (it was null).", + PolicyResult.FAILURE_EXCEPTION); + return; + } + // + // Sanity check the response message + // + if (response.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; + } + // + // Get the Response Code + // + AppcLcmResponseCode responseValue = AppcLcmResponseCode.toResponseValue(response.getStatus().getCode()); + if (responseValue == 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; + } + // + // Ok, let's figure out what APP-C's response is + // + switch (responseValue) { + case ACCEPTED: + // + // This is good, they got our original message and + // acknowledged it. + // + // Is there any need to track this? + // + return; + case PARTIAL_SUCCESS: + // + // Keep count of partial successes to determine + // if retries should be done at the vnf level + // + this.partialSuccessCount++; + return; + case PARTIAL_FAILURE: + // + // Keep count of partial failures to determine + // if no retries should be done + // + this.partialFailureCount++; + return; + case ERROR: + case REJECT: + // + // We'll consider these two codes as exceptions + // + this.completeOperation(operationAttempt, response.getStatus() + .getMessage(), PolicyResult.FAILURE_EXCEPTION); + return; + case SUCCESS: + // + // + // + this.completeOperation(operationAttempt, response.getStatus() + .getMessage(), PolicyResult.SUCCESS); + return; + case FAILURE: + // For the VNF level operations, retries will be attempted only + // if ALL individual VMs failed the operation + if (this.partialSuccessCount == 0) { + // Since there are no partial successes, that means all VMs failed + // if all vms fail, we can retry the VNF level action + this.completeOperation(operationAttempt, response.getStatus() + .getMessage(), PolicyResult.FAILURE); + } else if (this.partialFailureCount > 0) { + // Since only a subset of VMs had partial failures, + // the result should go to final failure and not + // retry or move on to the next policy in the chain. + this.completeOperation(operationAttempt, response.getStatus() + .getMessage(), PolicyResult.FAILURE_EXCEPTION); + } + return; + default: + break; + } + } + + /** + * This method is called by 'incomingMessage' only when an 'ABATED' event is received before an APPC + * request is sent. + * + * @param event the control loop event that was received + */ + private void incomingAbatedEvent(ControlLoopEvent event) { + // check if ClosedLoopEventStatus is 'abated' + if (event.isEventStatusValid() && "ABATED".equalsIgnoreCase(event.getClosedLoopEventStatus().toString())) { + this.result = PolicyResult.SUCCESS; + this.message = "Abatement received before APPC request was sent"; + this.state = LCM_COMPLETE; + } + } + + /** + * This method is called by 'incomingMessage' in order to complete the + * operation. + * + * @param attempt the operation attempt indicated in the response message + * @param message the value to store in the 'message' field' + * @param result the value to store in the 'result' field + */ + void completeOperation(int attempt, String message, PolicyResult result) { + logger.debug("LCM: completeOperation(" + + "this.attempt=" + this.attempt + + ", attempt=" + attempt + + ", result=" + result + + ", message=" + message); + if (this.attempt == attempt) { + // we need to verify that the attempt matches in order to reduce the + // chances that we are reacting to a prior 'Response' message that + // was received after we timed out (unfortunately, we can't guarantee + // this, because we have no reliable way to verify the 'recipe') + + this.message = message; + this.result = result; + state = LCM_COMPLETE; + } + } + + /** + * The operation has timed out. + * + * {@inheritDoc} + */ + @Override + public void timeout() { + result = PolicyResult.FAILURE_TIMEOUT; + state = LCM_COMPLETE; + } + + void setErrorStatus(String message) throws ControlLoopException { + result = PolicyResult.FAILURE_EXCEPTION; + state = LCM_ERROR; + this.message = message; + transaction.modify(); + throw new ControlLoopException(message); + } + + /** + * This is called right after it's history entry has been completed. + * + * {@inheritDoc} + */ + @Override + public void histEntryCompleted(ControlLoopOperation histEntry) { + // give 'guard' a chance to create a DB entry (this only happens if + // we really have a 'GuardContext', and all of the needed parameters + // were provided in the '*-controller.properties' file) + guardAdjunct.asyncCreateDbEntry(histEntry, target); + } +} diff --git a/controlloop/m2/appclcm/src/main/java/org/onap/policy/m2/appclcm/model/AppcLcmResponseCode.java b/controlloop/m2/appclcm/src/main/java/org/onap/policy/m2/appclcm/model/AppcLcmResponseCode.java new file mode 100644 index 000000000..ca812a4db --- /dev/null +++ b/controlloop/m2/appclcm/src/main/java/org/onap/policy/m2/appclcm/model/AppcLcmResponseCode.java @@ -0,0 +1,58 @@ +/*- + * ============LICENSE_START======================================================= + * m2/appclcm + * ================================================================================ + * Copyright (C) 2020 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. + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.m2.appclcm.model; + +import java.io.Serializable; + +public enum AppcLcmResponseCode implements Serializable { + ACCEPTED, ERROR, REJECT, SUCCESS, FAILURE, PARTIAL_SUCCESS, PARTIAL_FAILURE; + + private static final long serialVersionUID = 1L; + + /** + * Translates the code to a string value that represents the meaning of the + * code. + * + * @param code + * the numeric value that is returned by APPC based on success, + * failure, etc. of the action requested + * @return the enum value equivalent of the APPC response code + */ + public static AppcLcmResponseCode toResponseValue(int code) { + if (code == 100) { + return ACCEPTED; + } else if (code == 200) { + return ERROR; + } else if (code >= 300 && code <= 316) { + return REJECT; + } else if (code == 400) { + return SUCCESS; + } else if (code == 450 || (code >= 401 && code <= 406)) { + return FAILURE; + } else if (code == 500) { + return PARTIAL_SUCCESS; + } else if (code >= 501 && code <= 599) { + return PARTIAL_FAILURE; + } + return null; + } + +} diff --git a/controlloop/m2/appclcm/src/main/resources/META-INF/services/org.onap.policy.m2.base.Actor b/controlloop/m2/appclcm/src/main/resources/META-INF/services/org.onap.policy.m2.base.Actor new file mode 100644 index 000000000..2e6065608 --- /dev/null +++ b/controlloop/m2/appclcm/src/main/resources/META-INF/services/org.onap.policy.m2.base.Actor @@ -0,0 +1 @@ +org.onap.policy.m2.appclcm.AppcLcmActor diff --git a/controlloop/m2/appclcm/src/test/java/appclcm/AppcLcmHealthCheckOperationTest.java b/controlloop/m2/appclcm/src/test/java/appclcm/AppcLcmHealthCheckOperationTest.java new file mode 100644 index 000000000..0748b6ee0 --- /dev/null +++ b/controlloop/m2/appclcm/src/test/java/appclcm/AppcLcmHealthCheckOperationTest.java @@ -0,0 +1,281 @@ +/*- + * ============LICENSE_START======================================================= + * m2/appclcm + * ================================================================================ + * Copyright (C) 2020 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. + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package appclcm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; + +import java.util.Properties; +import java.util.UUID; + +import org.drools.core.WorkingMemory; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.onap.policy.appclcm.AppcLcmDmaapWrapper; +import org.onap.policy.appclcm.AppcLcmInput; +import org.onap.policy.appclcm.util.Serialization; +import org.onap.policy.controlloop.ControlLoopEventStatus; +import org.onap.policy.controlloop.ControlLoopException; +import org.onap.policy.controlloop.ControlLoopTargetType; +import org.onap.policy.controlloop.VirtualControlLoopEvent; +import org.onap.policy.controlloop.policy.Policy; +import org.onap.policy.controlloop.policy.PolicyResult; +import org.onap.policy.controlloop.policy.Target; +import org.onap.policy.controlloop.policy.TargetType; +import org.onap.policy.drools.m2.lock.LockAdjunct; +import org.onap.policy.drools.system.PolicyEngineConstants; +import org.onap.policy.m2.appclcm.AppcLcmHealthCheckOperation; +import org.onap.policy.m2.base.Transaction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AppcLcmHealthCheckOperationTest { + private static Logger logger = LoggerFactory.getLogger(AppcLcmHealthCheckOperationTest.class); + + public static Policy policy; + public static VirtualControlLoopEvent event; + public static Transaction transaction; + public static AppcLcmHealthCheckOperation operation; + + /** + * Class-level setup. + */ + @BeforeClass + public static void setup() { + PolicyEngineConstants.getManager().configure(new Properties()); + PolicyEngineConstants.getManager().start(); + + policy = new Policy(); + policy.setActor("APPCLCM"); + policy.setTarget(new Target(TargetType.VM)); + + event = new VirtualControlLoopEvent(); + event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET); + event.setRequestId(UUID.randomUUID()); + event.setTarget("vserver.vserver-name"); + event.setTargetType(ControlLoopTargetType.VM); + event.getAai().put("vserver.is-closed-loop-disabled", "false"); + event.getAai().put("complex.state", "NJ"); + event.getAai().put("vserver.l-interface.interface-name", "89ee9ee6-1e96-4063-b690-aa5ca9f73b32"); + event.getAai().put("vserver.l-interface.l3-interface-ipv4-address-list.l3-inteface-ipv4-address", + "135.144.3.49"); + event.getAai().put("vserver.l-interface.l3-interface-ipv6-address-list.l3-inteface-ipv6-address", null); + event.getAai().put("vserver.in-maint", "N"); + event.getAai().put("complex.city", "AAIDefault"); + event.getAai().put("vserver.vserver-id", "aa7a24f9-8791-491f-b31a-c8ba5ad9e2aa"); + event.getAai().put("vserver.l-interface.network-name", "vUSP_DPA3_OAM_3750"); + event.getAai().put("vserver.vserver-name", "ctsf0002vm013"); + event.getAai().put("generic-vnf.vnf-name", "ctsf0002v"); + event.getAai().put("generic-vnf.vnf-id", "0f551f1b-e4e5-4ce2-84da-eda916e06e1c"); + event.getAai().put("generic-vnf.service-id", "e433710f-9217-458d-a79d-1c7aff376d89"); + event.getAai().put("vserver.selflink", "https://compute-aic.dpa3.cci.att.com:8774/v2/d0719b845a804b368f8ac0bba39e188b/servers/aa7a24f9-8791-491f-b31a-c8ba5ad9e2aa"); + event.getAai().put("generic-vnf.vnf-type", "vUSP - vCTS"); + event.getAai().put("tenant.tenant-id", "d0719b845a804b368f8ac0bba39e188b"); + event.getAai().put("cloud-region.identity-url", "https://compute-aic.dpa3.cci.att.com:8774/"); + event.getAai().put("vserver.prov-status", "PROV"); + event.getAai().put("complex.physical-location-id", "LSLEILAA"); + + WorkingMemory wm = mock(WorkingMemory.class); + transaction = new Transaction(wm, "clvusptest", event.getRequestId(), null); + + } + + @AfterClass + public static void cleanup() { + transaction.cleanup(); + PolicyEngineConstants.getManager().stop(); + } + + @Test + public void getVnfHealthCheckRequestTest() throws ControlLoopException { + + policy.setRecipe("HEALTHCHECK"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmHealthCheckOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("health-check", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("HealthCheck", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertNotNull(appcRequest.getPayload()); + assertTrue(appcRequest.getPayload().contains("host-ip-address")); + + logger.info("health-check request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void incomingHealthCheckMessageHealthyStateTest() { + policy.setRecipe("HEALTHCHECK"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmHealthCheckOperation(transaction, policy, event, 1); + + String lcmRespJson = "{\"body\":{\"output\":{\"common-header\":{\"timestamp\":\"2017-08-25T21:06:23.037Z\"," + + "\"api-ver\":\"5.00\",\"originator-id\":\"POLICY\"," + + "\"request-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200\",\"sub-request-id\":\"1\",\"flags\":{}}," + + "\"status\":{\"code\":400,\"message\":\"HealthCheckSuccessful\"}," + + "\"payload\":\"{\\\"identifier\\\":\\\"scoperepresented\\\",\\\"state\\\":\\\"healthy\\\"," + + "\\\"time\\\":\\\"01-01-1000:0000\\\"}\"}},\"version\":\"2.0\",\"rpc-name\":\"health-check\"," + + "\"correlation-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200-1\",\"type\":\"response\"}"; + AppcLcmDmaapWrapper healthCheckResp = Serialization.gson.fromJson(lcmRespJson, AppcLcmDmaapWrapper.class); + + operation.incomingMessage(healthCheckResp); + assertEquals(operation.getResult(), PolicyResult.SUCCESS); + } + + @Test + public void incomingHealthCheckMessageUnhealthyStateTest() { + policy.setRecipe("HEALTHCHECK"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmHealthCheckOperation(transaction, policy, event, 1); + + String lcmRespJson = "{\"body\":{\"output\":{\"common-header\":{\"timestamp\":\"2017-08-25T21:06:23.037Z\"," + + "\"api-ver\":\"5.00\",\"originator-id\":\"POLICY\"," + + "\"request-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200\",\"sub-request-id\":\"1\",\"flags\":{}}," + + "\"status\":{\"code\":400,\"message\":\"VNF is unhealthy\"}," + + "\"payload\":\"{\\\"identifier\\\":\\\"scoperepresented\\\",\\\"state\\\":\\\"unhealthy\\\"," + + "\\\"info\\\":\\\"Systemthresholdexceededdetails\\\",\\\"fault\\\":{\\\"cpuOverall\\\":0.80," + + "\\\"cpuThreshold\\\":0.45},\\\"time\\\":\\\"01-01-1000:0000\\\"}\"}},\"version\":\"2.0\"," + + "\"rpc-name\":\"health-check\",\"correlation-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200-1\"," + + "\"type\":\"response\"}"; + AppcLcmDmaapWrapper healthCheckResp = Serialization.gson.fromJson(lcmRespJson, AppcLcmDmaapWrapper.class); + + operation.incomingMessage(healthCheckResp); + assertEquals(operation.getResult(), PolicyResult.FAILURE); + } + + @Test + public void incomingHealthCheckMessageUnknownStateTest() { + policy.setRecipe("HEALTHCHECK"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmHealthCheckOperation(transaction, policy, event, 1); + + String lcmRespJson = "{\"body\":{\"output\":{\"common-header\":{\"timestamp\":\"2017-08-25T21:06:23.037Z\"," + + "\"api-ver\":\"5.00\",\"originator-id\":\"POLICY\"," + + "\"request-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200\",\"sub-request-id\":\"1\",\"flags\":{}}," + + "\"status\":{\"code\":400,\"message\":\"VNF is unhealthy\"}," + + "\"payload\":\"{\\\"identifier\\\":\\\"scoperepresented\\\",\\\"state\\\":\\\"unknown\\\"," + + "\\\"info\\\":\\\"Systemthresholdexceededdetails\\\",\\\"fault\\\":{\\\"cpuOverall\\\":0.80," + + "\\\"cpuThreshold\\\":0.45},\\\"time\\\":\\\"01-01-1000:0000\\\"}\"}},\"version\":\"2.0\"," + + "\"rpc-name\":\"health-check\",\"correlation-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200-1\"," + + "\"type\":\"response\"}"; + AppcLcmDmaapWrapper healthCheckResp = Serialization.gson.fromJson(lcmRespJson, AppcLcmDmaapWrapper.class); + + operation.incomingMessage(healthCheckResp); + assertEquals(operation.getResult(), PolicyResult.FAILURE_EXCEPTION); + } + + @Test + public void incomingHealthCheckMessageNoStateTest() { + policy.setRecipe("HEALTHCHECK"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmHealthCheckOperation(transaction, policy, event, 1); + + String lcmRespJson = "{\"body\":{\"output\":{\"common-header\":{\"timestamp\":\"2017-08-25T21:06:23.037Z\"," + + "\"api-ver\":\"5.00\",\"originator-id\":\"POLICY\"," + + "\"request-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200\",\"sub-request-id\":\"1\",\"flags\":{}}," + + "\"status\":{\"code\":400,\"message\":\"VNF is unhealthy\"}," + + "\"payload\":\"{\\\"identifier\\\":\\\"scoperepresented\\\"," + + "\\\"info\\\":\\\"Systemthresholdexceededdetails\\\",\\\"fault\\\":{\\\"cpuOverall\\\":0.80," + + "\\\"cpuThreshold\\\":0.45},\\\"time\\\":\\\"01-01-1000:0000\\\"}\"}},\"version\":\"2.0\"," + + "\"rpc-name\":\"health-check\",\"correlation-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200-1\"," + + "\"type\":\"response\"}"; + AppcLcmDmaapWrapper healthCheckResp = Serialization.gson.fromJson(lcmRespJson, AppcLcmDmaapWrapper.class); + + operation.incomingMessage(healthCheckResp); + assertEquals(operation.getResult(), PolicyResult.FAILURE_EXCEPTION); + } + + @Test + public void incomingHealthCheckMessageUnsuccessfulTest() { + policy.setRecipe("HEALTHCHECK"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmHealthCheckOperation(transaction, policy, event, 1); + + String lcmRespJson = "{\"body\":{\"output\":{\"common-header\":{\"timestamp\":\"2017-08-25T21:06:23.037Z\"," + + "\"api-ver\":\"5.00\",\"originator-id\":\"POLICY\"," + + "\"request-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200\",\"sub-request-id\":\"1\",\"flags\":{}}," + + "\"status\":{\"code\":401,\"message\":\"Could not complete HealthCheck\"}," + + "\"payload\":\"{\\\"identifier\\\":\\\"scoperepresented\\\"," + + "\\\"info\\\":\\\"Systemthresholdexceededdetails\\\",\\\"fault\\\":{\\\"cpuOverall\\\":0.80," + + "\\\"cpuThreshold\\\":0.45},\\\"time\\\":\\\"01-01-1000:0000\\\"}\"}},\"version\":\"2.0\"," + + "\"rpc-name\":\"health-check\",\"correlation-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200-1\"," + + "\"type\":\"response\"}"; + AppcLcmDmaapWrapper healthCheckResp = Serialization.gson.fromJson(lcmRespJson, AppcLcmDmaapWrapper.class); + + operation.incomingMessage(healthCheckResp); + assertEquals(operation.getResult(), PolicyResult.FAILURE); + } + + @Test + public void incomingHealthCheckMessageNoPayloadTest() { + policy.setRecipe("HEALTHCHECK"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmHealthCheckOperation(transaction, policy, event, 1); + + String lcmRespJson = "{\"body\":{\"output\":{\"common-header\":{\"timestamp\":\"2017-08-25T21:06:23.037Z\"," + + "\"api-ver\":\"5.00\",\"originator-id\":\"POLICY\"," + + "\"request-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200\",\"sub-request-id\":\"1\",\"flags\":{}}," + + "\"status\":{\"code\":400,\"message\":\"VNF is unhealthy\"}}},\"version\":\"2.0\"," + + "\"rpc-name\":\"health-check\",\"correlation-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200-1\"," + + "\"type\":\"response\"}"; + AppcLcmDmaapWrapper healthCheckResp = Serialization.gson.fromJson(lcmRespJson, AppcLcmDmaapWrapper.class); + + operation.incomingMessage(healthCheckResp); + assertEquals(operation.getResult(), PolicyResult.FAILURE_EXCEPTION); + } + + @Test + public void incomingHealthCheckMessageEmptyPayloadTest() { + policy.setRecipe("HEALTHCHECK"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmHealthCheckOperation(transaction, policy, event, 1); + + String lcmRespJson = "{\"body\":{\"output\":{\"common-header\":{\"timestamp\":\"2017-08-25T21:06:23.037Z\"," + + "\"api-ver\":\"5.00\",\"originator-id\":\"POLICY\"," + + "\"request-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200\",\"sub-request-id\":\"1\",\"flags\":{}}," + + "\"status\":{\"code\":400,\"message\":\"VNF is unhealthy\"},\"payload\":\"\"}},\"version\":\"2.0\"," + + "\"rpc-name\":\"health-check\",\"correlation-id\":\"664be3d2-6c12-4f4b-a3e7-c349acced200-1\"," + + "\"type\":\"response\"}"; + AppcLcmDmaapWrapper healthCheckResp = Serialization.gson.fromJson(lcmRespJson, AppcLcmDmaapWrapper.class); + + operation.incomingMessage(healthCheckResp); + assertEquals(operation.getResult(), PolicyResult.FAILURE_EXCEPTION); + } +} diff --git a/controlloop/m2/appclcm/src/test/java/appclcm/AppcLcmOperationTest.java b/controlloop/m2/appclcm/src/test/java/appclcm/AppcLcmOperationTest.java new file mode 100644 index 000000000..0ddfb5b2c --- /dev/null +++ b/controlloop/m2/appclcm/src/test/java/appclcm/AppcLcmOperationTest.java @@ -0,0 +1,708 @@ +/*- + * ============LICENSE_START======================================================= + * m2/appclcm + * ================================================================================ + * Copyright (C) 2020 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. + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package appclcm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; + +import java.util.HashMap; +import java.util.Properties; +import java.util.UUID; + +import org.drools.core.WorkingMemory; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.onap.policy.appclcm.AppcLcmDmaapWrapper; +import org.onap.policy.appclcm.AppcLcmInput; +import org.onap.policy.appclcm.util.Serialization; +import org.onap.policy.controlloop.ControlLoopEventStatus; +import org.onap.policy.controlloop.ControlLoopException; +import org.onap.policy.controlloop.ControlLoopTargetType; +import org.onap.policy.controlloop.VirtualControlLoopEvent; +import org.onap.policy.controlloop.policy.Policy; +import org.onap.policy.controlloop.policy.PolicyResult; +import org.onap.policy.controlloop.policy.Target; +import org.onap.policy.controlloop.policy.TargetType; +import org.onap.policy.drools.m2.lock.LockAdjunct; +import org.onap.policy.drools.system.PolicyEngineConstants; +import org.onap.policy.m2.appclcm.AppcLcmOperation; +import org.onap.policy.m2.base.Transaction; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AppcLcmOperationTest { + private static Logger logger = LoggerFactory.getLogger(AppcLcmOperationTest.class); + + public static Policy policy; + public static VirtualControlLoopEvent event; + public static Transaction transaction; + public static AppcLcmOperation operation; + + /** + * Class-level setup. + */ + @BeforeClass + public static void start() { + PolicyEngineConstants.getManager().configure(new Properties()); + PolicyEngineConstants.getManager().start(); + + policy = new Policy(); + policy.setActor("APPCLCM"); + policy.setTarget(new Target(TargetType.VM)); + + event = new VirtualControlLoopEvent(); + event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET); + event.setRequestId(UUID.randomUUID()); + event.setTarget("vserver.vserver-name"); + event.setTargetType(ControlLoopTargetType.VM); + event.getAai().put("vserver.is-closed-loop-disabled", "false"); + event.getAai().put("complex.state", "NJ"); + event.getAai().put("vserver.l-interface.interface-name", "89ee9ee6-1e96-4063-b690-aa5ca9f73b32"); + event.getAai().put("vserver.l-interface.l3-interface-ipv4-address-list.l3-inteface-ipv4-address", + "135.144.3.49"); + event.getAai().put("vserver.l-interface.l3-interface-ipv6-address-list.l3-inteface-ipv6-address", null); + event.getAai().put("vserver.in-maint", "N"); + event.getAai().put("complex.city", "AAIDefault"); + event.getAai().put("vserver.vserver-id", "aa7a24f9-8791-491f-b31a-c8ba5ad9e2aa"); + event.getAai().put("vserver.l-interface.network-name", "vUSP_DPA3_OAM_3750"); + event.getAai().put("vserver.vserver-name", "ctsf0002vm013"); + event.getAai().put("generic-vnf.vnf-name", "ctsf0002v"); + event.getAai().put("generic-vnf.vnf-id", "0f551f1b-e4e5-4ce2-84da-eda916e06e1c"); + event.getAai().put("generic-vnf.service-id", "e433710f-9217-458d-a79d-1c7aff376d89"); + event.getAai().put("vserver.selflink", "https://compute-aic.dpa3.cci.att.com:8774/v2/d0719b845a804b368f8ac0bba39e188b/servers/aa7a24f9-8791-491f-b31a-c8ba5ad9e2aa"); + event.getAai().put("generic-vnf.vnf-type", "vUSP - vCTS"); + event.getAai().put("tenant.tenant-id", "d0719b845a804b368f8ac0bba39e188b"); + event.getAai().put("cloud-region.identity-url", "https://compute-aic.dpa3.cci.att.com:8774/"); + event.getAai().put("vserver.prov-status", "PROV"); + event.getAai().put("complex.physical-location-id", "LSLEILAA"); + + WorkingMemory wm = mock(WorkingMemory.class); + transaction = new Transaction(wm, "clvusptest", event.getRequestId(), null); + } + + @AfterClass + public static void cleanup() { + transaction.cleanup(); + PolicyEngineConstants.getManager().stop(); + } + + @Test + public void getVmRestartRequestTest() throws ControlLoopException { + + policy.setRecipe("RESTART"); + policy.getTarget().setType(TargetType.VM); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("restart", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Restart", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(event.getAai().get("vserver.vserver-id"), appcRequest.getActionIdentifiers().get("vserver-id")); + assertNotNull(appcRequest.getPayload()); + + logger.info("vm restart request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void getVnfRestartRequestTest() throws ControlLoopException { + + policy.setRecipe("RESTART"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("restart", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Restart", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(appcRequest.getActionIdentifiers().get("vserver-id"), null); + assertNull(appcRequest.getPayload()); + + logger.info("vnf restart request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void getVmRebuildRequestTest() throws ControlLoopException { + + policy.setRecipe("REBUILD"); + policy.getTarget().setType(TargetType.VM); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("rebuild", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Rebuild", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(event.getAai().get("vserver.vserver-id"), appcRequest.getActionIdentifiers().get("vserver-id")); + assertNotNull(appcRequest.getPayload()); + + logger.info("vm rebuild request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void getVnfRebuildRequestTest() throws ControlLoopException { + + policy.setRecipe("REBUILD"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("rebuild", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Rebuild", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(appcRequest.getActionIdentifiers().get("vserver-id"), null); + assertNull(appcRequest.getPayload()); + + logger.info("vnf rebuild request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void getVmMigrateRequestTest() throws ControlLoopException { + + policy.setRecipe("MIGRATE"); + policy.getTarget().setType(TargetType.VM); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("migrate", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Migrate", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(event.getAai().get("vserver.vserver-id"), appcRequest.getActionIdentifiers().get("vserver-id")); + assertNotNull(appcRequest.getPayload()); + + logger.info("vm migrate request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void getVnfMigrateRequestTest() throws ControlLoopException { + + policy.setRecipe("MIGRATE"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("migrate", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Migrate", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(appcRequest.getActionIdentifiers().get("vserver-id"), null); + assertNull(appcRequest.getPayload()); + + logger.info("vnf migrate request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void getVmEvacuateRequestTest() throws ControlLoopException { + + policy.setRecipe("EVACUATE"); + policy.getTarget().setType(TargetType.VM); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("evacuate", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Evacuate", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(event.getAai().get("vserver.vserver-id"), appcRequest.getActionIdentifiers().get("vserver-id")); + assertNotNull(appcRequest.getPayload()); + + logger.info("vm evacuate request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void getVnfEvacuateRequestTest() throws ControlLoopException { + + policy.setRecipe("EVACUATE"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("evacuate", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Evacuate", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(appcRequest.getActionIdentifiers().get("vserver-id"), null); + assertNull(appcRequest.getPayload()); + + logger.info("vnf evacuate request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void getVmRebootRequestTest() throws ControlLoopException { + + policy.setRecipe("REBOOT"); + policy.getTarget().setType(TargetType.VM); + policy.setPayload(new HashMap<String, String>()); + policy.getPayload().put("type", "HARD"); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("reboot", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Reboot", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(event.getAai().get("vserver.vserver-id"), appcRequest.getActionIdentifiers().get("vserver-id")); + assertNotNull(appcRequest.getPayload()); + + logger.info("vm reboot request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void getVnfRebootRequestTest() throws ControlLoopException { + + policy.setRecipe("REBOOT"); + policy.getTarget().setType(TargetType.VNF); + policy.setPayload(new HashMap<String, String>()); + policy.getPayload().put("type", "HARD"); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("reboot", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Reboot", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(appcRequest.getActionIdentifiers().get("vserver-id"), null); + assertNotNull(appcRequest.getPayload()); + + logger.info("vnf reboot request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void getVnfStartRequestTest() throws ControlLoopException { + + policy.setRecipe("START"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("start", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Start", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(appcRequest.getActionIdentifiers().get("vserver-id"), null); + assertNull(appcRequest.getPayload()); + + logger.info("vnf start request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void getVmStartRequestTest() throws ControlLoopException { + + policy.setRecipe("START"); + policy.getTarget().setType(TargetType.VM); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("start", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Start", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(event.getAai().get("vserver.vserver-id"), appcRequest.getActionIdentifiers().get("vserver-id")); + assertNotNull(appcRequest.getPayload()); + + logger.info("vm start request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void getVnfStopRequestTest() throws ControlLoopException { + + policy.setRecipe("STOP"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + assertEquals("stop", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Stop", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(appcRequest.getActionIdentifiers().get("vserver-id"), null); + assertNull(appcRequest.getPayload()); + + logger.info("vnf stop request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + @Test + public void getVmStopRequestTest() throws ControlLoopException { + + policy.setRecipe("STOP"); + policy.getTarget().setType(TargetType.VM); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + Object request = operation.getRequest(); + assertTrue(request instanceof AppcLcmDmaapWrapper); + + AppcLcmDmaapWrapper dmaapRequest = (AppcLcmDmaapWrapper) request; + assertEquals("request", dmaapRequest.getType()); + + assertEquals("stop", dmaapRequest.getRpcName()); + assertNotNull(dmaapRequest.getBody()); + + AppcLcmInput appcRequest = dmaapRequest.getBody().getInput(); + assertNotNull(appcRequest.getCommonHeader()); + assertEquals("2.00", appcRequest.getCommonHeader().getApiVer()); + assertEquals("POLICY", appcRequest.getCommonHeader().getOriginatorId()); + assertNotNull(appcRequest.getAction()); + assertEquals("Stop", appcRequest.getAction()); + assertNotNull(appcRequest.getActionIdentifiers()); + assertEquals(event.getAai().get("generic-vnf.vnf-id"), appcRequest.getActionIdentifiers().get("vnf-id")); + assertEquals(event.getAai().get("vserver.vserver-id"), appcRequest.getActionIdentifiers().get("vserver-id")); + assertNotNull(appcRequest.getPayload()); + + logger.info("vm stop request: {}", Serialization.gson.toJson(request, AppcLcmDmaapWrapper.class)); + + } + + /* ===================================================================== */ + + /* + * these tests are for ensuring the incoming response messages process + * properly and translate to the expected policy result + */ + + @Test + public void incomingVmSuccessMessageTest() { + policy.setRecipe("RESTART"); + policy.getTarget().setType(TargetType.VM); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + String lcmRespJson = "{\"version\": \"2.0\",\"rpc-name\": \"Restart\",\"correlation-id\": \"baf5ba32-6b8c-430c-a91b-02d2c0ba3064-1\",\"type\": \"response\",\"body\": {\"output\": {\"status\": {\"code\": 400,\"message\": \"Restart Successful\"},\"common-header\": {\"timestamp\": \"2017-07-18T16:52:06.186Z\",\"api-ver\": \"2.01\",\"request-id\": \"baf5ba32-6b8c-430c-a91b-02d2c0ba3064\",\"sub-request-id\": \"1\",\"flags\": {\"ttl\": 600}},\"payload\": \"{\\\"vm-id\\\":\\\"http://135.25.246.131:8774/v2/81fc2bc61f974de1b5a49e8c2ec090bb/servers/75dce20c-97f9-454d-abcc-aa904a33df5a\\\",\\\"tenant-id\\\":\\\"test2\\\"}\"}}}"; + AppcLcmDmaapWrapper restartResponse = Serialization.gson.fromJson(lcmRespJson, AppcLcmDmaapWrapper.class); + + operation.incomingMessage(restartResponse); + assertEquals(operation.getResult(), PolicyResult.SUCCESS); + } + + @Test + public void incomingVnfSuccessMessageTest() { + policy.setRecipe("RESTART"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + String lcmRespJson = "{\"version\": \"2.0\",\"rpc-name\": \"Restart\",\"correlation-id\": \"baf5ba32-6b8c-430c-a91b-02d2c0ba3064-1\",\"type\": \"response\",\"body\": {\"output\": {\"status\": {\"code\": 500,\"message\": \"Restart Successful\"},\"common-header\": {\"timestamp\": \"2017-07-18T16:52:06.186Z\",\"api-ver\": \"2.01\",\"request-id\": \"baf5ba32-6b8c-430c-a91b-02d2c0ba3064\",\"sub-request-id\": \"1\",\"flags\": {\"ttl\": 600}},\"payload\": \"{\\\"vm-id\\\":\\\"http://135.25.246.131:8774/v2/81fc2bc61f974de1b5a49e8c2ec090bb/servers/75dce20c-97f9-454d-abcc-aa904a33df5a\\\",\\\"tenant-id\\\":\\\"test2\\\"}\"}}}"; + AppcLcmDmaapWrapper restartResponse = Serialization.gson.fromJson(lcmRespJson, AppcLcmDmaapWrapper.class); + + /* Send in several partial success messages */ + for (int i = 0; i < 5; i++) { + operation.incomingMessage(restartResponse); + assertEquals(operation.getResult(), null); + } + + /* Send in an operation success */ + restartResponse.getBody().getOutput().getStatus().setCode(400); + operation.incomingMessage(restartResponse); + assertEquals(operation.getResult(), PolicyResult.SUCCESS); + } + + @Test + public void incomingVmFailureMessageTest() { + policy.setRecipe("RESTART"); + policy.getTarget().setType(TargetType.VM); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + String lcmRespJson = "{\"version\": \"2.0\",\"rpc-name\": \"Restart\",\"correlation-id\": \"baf5ba32-6b8c-430c-a91b-02d2c0ba3064-1\",\"type\": \"response\",\"body\": {\"output\": {\"status\": {\"code\": 401,\"message\": \"Restart Successful\"},\"common-header\": {\"timestamp\": \"2017-07-18T16:52:06.186Z\",\"api-ver\": \"2.01\",\"request-id\": \"baf5ba32-6b8c-430c-a91b-02d2c0ba3064\",\"sub-request-id\": \"1\",\"flags\": {\"ttl\": 600}},\"payload\": \"{\\\"vm-id\\\":\\\"http://135.25.246.131:8774/v2/81fc2bc61f974de1b5a49e8c2ec090bb/servers/75dce20c-97f9-454d-abcc-aa904a33df5a\\\",\\\"tenant-id\\\":\\\"test2\\\"}\"}}}"; + AppcLcmDmaapWrapper restartResponse = Serialization.gson.fromJson(lcmRespJson, AppcLcmDmaapWrapper.class); + + operation.incomingMessage(restartResponse); + assertEquals(operation.getResult(), PolicyResult.FAILURE); + } + + @Test + public void incomingAllVnfFailureMessageTest() { + policy.setRecipe("RESTART"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + String lcmRespJson = "{\"version\": \"2.0\",\"rpc-name\": \"Restart\",\"correlation-id\": \"baf5ba32-6b8c-430c-a91b-02d2c0ba3064-1\",\"type\": \"response\",\"body\": {\"output\": {\"status\": {\"code\": 501,\"message\": \"Restart Successful\"},\"common-header\": {\"timestamp\": \"2017-07-18T16:52:06.186Z\",\"api-ver\": \"2.01\",\"request-id\": \"baf5ba32-6b8c-430c-a91b-02d2c0ba3064\",\"sub-request-id\": \"1\",\"flags\": {\"ttl\": 600}},\"payload\": \"{\\\"vm-id\\\":\\\"http://135.25.246.131:8774/v2/81fc2bc61f974de1b5a49e8c2ec090bb/servers/75dce20c-97f9-454d-abcc-aa904a33df5a\\\",\\\"tenant-id\\\":\\\"test2\\\"}\"}}}"; + AppcLcmDmaapWrapper restartResponse = Serialization.gson.fromJson(lcmRespJson, AppcLcmDmaapWrapper.class); + + /* Send in ALL failure messages */ + for (int i = 0; i < 5; i++) { + operation.incomingMessage(restartResponse); + assertEquals(operation.getResult(), null); + } + + /* Send in an operation failure */ + restartResponse.getBody().getOutput().getStatus().setCode(401); + operation.incomingMessage(restartResponse); + + /* Because every VM failed in the VNF, it should be failure result */ + assertEquals(operation.getResult(), PolicyResult.FAILURE); + } + + @Test + public void incomingPartialVnfFailureMessageTest() { + policy.setRecipe("RESTART"); + policy.getTarget().setType(TargetType.VNF); + operation = new AppcLcmOperation(transaction, policy, event, 1); + + String lcmRespJson = "{\"version\": \"2.0\",\"rpc-name\": \"Restart\",\"correlation-id\": \"baf5ba32-6b8c-430c-a91b-02d2c0ba3064-1\",\"type\": \"response\",\"body\": {\"output\": {\"status\": {\"code\": 500,\"message\": \"Restart Successful\"},\"common-header\": {\"timestamp\": \"2017-07-18T16:52:06.186Z\",\"api-ver\": \"2.01\",\"request-id\": \"baf5ba32-6b8c-430c-a91b-02d2c0ba3064\",\"sub-request-id\": \"1\",\"flags\": {\"ttl\": 600}},\"payload\": \"{\\\"vm-id\\\":\\\"http://135.25.246.131:8774/v2/81fc2bc61f974de1b5a49e8c2ec090bb/servers/75dce20c-97f9-454d-abcc-aa904a33df5a\\\",\\\"tenant-id\\\":\\\"test2\\\"}\"}}}"; + AppcLcmDmaapWrapper restartResponse = Serialization.gson.fromJson(lcmRespJson, AppcLcmDmaapWrapper.class); + + /* Send in several partial success messages */ + for (int i = 0; i < 5; i++) { + operation.incomingMessage(restartResponse); + assertEquals(operation.getResult(), null); + } + + /* Change status to partial failure */ + restartResponse.getBody().getOutput().getStatus().setCode(501); + + /* Send in several partial failures messages */ + for (int i = 0; i < 5; i++) { + operation.incomingMessage(restartResponse); + assertEquals(operation.getResult(), null); + } + + /* Send in an operation failure */ + restartResponse.getBody().getOutput().getStatus().setCode(401); + operation.incomingMessage(restartResponse); + + /* + * Only a subset of VMs failed in the VNF so the + * result will be failure_exception + */ + assertEquals(operation.getResult(), PolicyResult.FAILURE_EXCEPTION); + } + + /* ===================================================================== */ + + /* + * these tests are for validating the A&AI subtag and target in an onset + */ + + @Test + public void validAaiSubtagTest() { + transaction.setNotificationMessage(null); + VirtualControlLoopEvent validEvent = new VirtualControlLoopEvent(); + validEvent.setTarget("vserver.vserver-name"); + validEvent.getAai().put(AppcLcmOperation.DCAE_CLOSEDLOOP_DISABLED_FIELD, "false"); + validEvent.getAai().put(validEvent.getTarget(), "VM001"); + assertTrue(AppcLcmOperation.isAaiValid(transaction, validEvent)); + assertNull(transaction.getNotificationMessage()); + } + + @Test + public void noAaiSubtagTest() { + transaction.setNotificationMessage(null); + VirtualControlLoopEvent noAaiTag = new VirtualControlLoopEvent(); + noAaiTag.setAai(null); + assertFalse(AppcLcmOperation.isAaiValid(transaction, noAaiTag)); + assertEquals(transaction.getNotificationMessage(), "No A&AI Subtag"); + } + + @Test + public void noClosedLoopDisabledInAaiTest() { + transaction.setNotificationMessage(null); + VirtualControlLoopEvent invalidEvent = new VirtualControlLoopEvent(); + assertFalse(AppcLcmOperation.isAaiValid(transaction, invalidEvent)); + assertEquals(AppcLcmOperation.DCAE_CLOSEDLOOP_DISABLED_FIELD + + " information missing", transaction.getNotificationMessage()); + } + + @Test + public void closedLoopDisabledInAaiTest() { + transaction.setNotificationMessage(null); + VirtualControlLoopEvent invalidEvent = new VirtualControlLoopEvent(); + invalidEvent.getAai().put(AppcLcmOperation.DCAE_CLOSEDLOOP_DISABLED_FIELD, "true"); + assertFalse(AppcLcmOperation.isAaiValid(transaction, invalidEvent)); + assertEquals(AppcLcmOperation.DCAE_CLOSEDLOOP_DISABLED_FIELD + + " is set to true", transaction.getNotificationMessage()); + } + + @Test + public void targetMismatchInAaiTest() { + transaction.setNotificationMessage(null); + VirtualControlLoopEvent validEvent = new VirtualControlLoopEvent(); + validEvent.setTarget("vserver.vserver-name"); + validEvent.getAai().put(AppcLcmOperation.DCAE_CLOSEDLOOP_DISABLED_FIELD, "false"); + assertFalse(AppcLcmOperation.isAaiValid(transaction, validEvent)); + assertEquals("target field invalid - must have corresponding AAI value", + transaction.getNotificationMessage()); + } +} diff --git a/controlloop/m2/appclcm/src/test/java/model/AppcLcmResponseCodeTest.java b/controlloop/m2/appclcm/src/test/java/model/AppcLcmResponseCodeTest.java new file mode 100644 index 000000000..fc3349253 --- /dev/null +++ b/controlloop/m2/appclcm/src/test/java/model/AppcLcmResponseCodeTest.java @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * appclcm + * ================================================================================ + * Copyright (C) 2020 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. + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Test; + +import org.onap.policy.m2.appclcm.model.AppcLcmResponseCode; + + +public class AppcLcmResponseCodeTest { + + @Test + public void test() { + assertEquals(AppcLcmResponseCode.ACCEPTED, AppcLcmResponseCode.toResponseValue(100)); + assertEquals(AppcLcmResponseCode.ERROR, AppcLcmResponseCode.toResponseValue(200)); + assertEquals(AppcLcmResponseCode.REJECT, AppcLcmResponseCode.toResponseValue(300)); + assertEquals(AppcLcmResponseCode.SUCCESS, AppcLcmResponseCode.toResponseValue(400)); + assertEquals(AppcLcmResponseCode.FAILURE, AppcLcmResponseCode.toResponseValue(450)); + assertEquals(AppcLcmResponseCode.PARTIAL_SUCCESS, AppcLcmResponseCode.toResponseValue(500)); + assertEquals(AppcLcmResponseCode.PARTIAL_FAILURE, AppcLcmResponseCode.toResponseValue(501)); + assertNull(AppcLcmResponseCode.toResponseValue(600)); + } +} |