Add Step classes for usecases controller 53/111653/5
authorJim Hahn <jrh3@att.com>
Thu, 30 Jul 2020 22:05:02 +0000 (18:05 -0400)
committerJim Hahn <jrh3@att.com>
Mon, 24 Aug 2020 18:31:13 +0000 (14:31 -0400)
Add usecases-specific classes for managing preprocessor steps.

Issue-ID: POLICY-2748
Change-Id: I62a2bf8fbeb656a5017531a3f3de6a6dbad5a004
Signed-off-by: Jim Hahn <jrh3@att.com>
14 files changed:
controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/AaiCqStep2.java [new file with mode: 0644]
controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/AaiGetPnfStep2.java [new file with mode: 0644]
controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/AaiGetTenantStep2.java [new file with mode: 0644]
controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/GetTargetEntityStep2.java [new file with mode: 0644]
controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/GuardStep2.java [new file with mode: 0644]
controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/LockStep2.java [new file with mode: 0644]
controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/Step2.java [new file with mode: 0644]
controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/AaiCqStep2Test.java [new file with mode: 0644]
controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/AaiGetPnfStep2Test.java [new file with mode: 0644]
controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/AaiGetTenantStep2Test.java [new file with mode: 0644]
controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/GetTargetEntityStep2Test.java [new file with mode: 0644]
controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/GuardStep2Test.java [new file with mode: 0644]
controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/LockStep2Test.java [new file with mode: 0644]
controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/Step2Test.java [new file with mode: 0644]

diff --git a/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/AaiCqStep2.java b/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/AaiCqStep2.java
new file mode 100644 (file)
index 0000000..9ae1d7f
--- /dev/null
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import org.onap.policy.aai.AaiCqResponse;
+import org.onap.policy.controlloop.actor.aai.AaiActor;
+import org.onap.policy.controlloop.actor.aai.AaiCustomQueryOperation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+
+/**
+ * Wrapper for {@link AaiCustomQueryOperation}. The {@link #success(OperationOutcome)}
+ * method stores the resultant "tenant" object in the step's context.
+ */
+public class AaiCqStep2 extends Step2 {
+
+    /**
+     * Constructs the object using information from another step.
+     *
+     * @param otherStep step whose information should be used
+     */
+    public AaiCqStep2(Step2 otherStep) {
+        super(otherStep, AaiActor.NAME, AaiCustomQueryOperation.NAME);
+    }
+
+    /**
+     * Skips the operation if we already have the data.
+     */
+    @Override
+    public boolean start(long remainingMs) {
+        if (stepContext.contains(AaiCqResponse.CONTEXT_KEY)) {
+            // already have the data
+            return false;
+        }
+
+        return super.start(remainingMs);
+    }
+
+    /**
+     * Saves the response for later use.
+     */
+    @Override
+    public void success(OperationOutcome outcome) {
+        AaiCqResponse resp = outcome.getResponse();
+        stepContext.setProperty(AaiCqResponse.CONTEXT_KEY, resp);
+
+        super.success(outcome);
+    }
+}
diff --git a/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/AaiGetPnfStep2.java b/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/AaiGetPnfStep2.java
new file mode 100644 (file)
index 0000000..131daef
--- /dev/null
@@ -0,0 +1,70 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actor.aai.AaiActor;
+import org.onap.policy.controlloop.actor.aai.AaiGetPnfOperation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+
+/**
+ * Wrapper for {@link AaiGetPnfOperation}. The {@link #success(OperationOutcome)} method
+ * stores the resultant PNF object in the step's context.
+ * <p/>
+ * Note: this assumes that the target entity is one of the properties returned by
+ * {@link AaiGetPnfOperation#getPropertyNames()}.
+ */
+public class AaiGetPnfStep2 extends Step2 {
+
+
+    /**
+     * Constructs the object using information from another step.
+     *
+     * @param otherStep step whose information should be used
+     */
+    public AaiGetPnfStep2(Step2 otherStep) {
+        super(otherStep, AaiActor.NAME, AaiGetPnfOperation.NAME);
+    }
+
+    /**
+     * Skips the operation if we already have the data.
+     */
+    @Override
+    public boolean start(long remainingMs) {
+        if (stepContext.contains(AaiGetPnfOperation.getKey(getTargetEntity()))) {
+            // already have the data
+            return false;
+        }
+
+        return super.start(remainingMs);
+    }
+
+    /**
+     * Saves the response for later use.
+     */
+    @Override
+    public void success(OperationOutcome outcome) {
+        StandardCoderObject resp = outcome.getResponse();
+        stepContext.setProperty(AaiGetPnfOperation.getKey(getTargetEntity()), resp);
+
+        super.success(outcome);
+    }
+}
diff --git a/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/AaiGetTenantStep2.java b/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/AaiGetTenantStep2.java
new file mode 100644 (file)
index 0000000..d1851dd
--- /dev/null
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import java.util.Collections;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actor.aai.AaiActor;
+import org.onap.policy.controlloop.actor.aai.AaiGetTenantOperation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
+
+/**
+ * Wrapper for {@link AaiGetTenantOperation}. The vserver name is extracted from the
+ * enrichment data and passed as the target entity for the operation. The
+ * {@link #success(OperationOutcome)} method stores the resultant "tenant" object in the
+ * step's context.
+ */
+public class AaiGetTenantStep2 extends Step2 {
+
+    private final String vserver;
+
+
+    /**
+     * Constructs the object using information from another step.
+     *
+     * @param otherStep step whose information should be used
+     */
+    public AaiGetTenantStep2(Step2 otherStep) {
+        super(otherStep, AaiActor.NAME, AaiGetTenantOperation.NAME);
+
+        vserver = event.getAai().get(VSERVER_VSERVER_NAME);
+        if (StringUtils.isBlank(vserver)) {
+            throw new IllegalArgumentException("missing " + VSERVER_VSERVER_NAME + " in enrichment data");
+        }
+    }
+
+    /**
+     * The vserver is passed as the target entity, thus we don't need to include the
+     * target entity in the property list.
+     */
+    @Override
+    public List<String> getPropertyNames() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public void setProperties() {
+        getOperation().setProperty(OperationProperties.AAI_TARGET_ENTITY, vserver);
+    }
+
+    /**
+     * Skips the operation if we already have the data.
+     */
+    @Override
+    public boolean start(long remainingMs) {
+        if (stepContext.contains(AaiGetTenantOperation.getKey(vserver))) {
+            // already have the data
+            return false;
+        }
+
+        return super.start(remainingMs);
+    }
+
+    /**
+     * Saves the response for later use.
+     */
+    @Override
+    public void success(OperationOutcome outcome) {
+        StandardCoderObject resp = outcome.getResponse();
+        stepContext.setProperty(AaiGetTenantOperation.getKey(vserver), resp);
+
+        super.success(outcome);
+    }
+}
diff --git a/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/GetTargetEntityStep2.java b/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/GetTargetEntityStep2.java
new file mode 100644 (file)
index 0000000..06201b1
--- /dev/null
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.drools.apps.controller.usecases.GetTargetEntityOperation2;
+import org.onap.policy.drools.apps.controller.usecases.UsecasesConstants;
+
+/**
+ * Wrapper for {@link GetTargetEntityOperation2}. The {@link #start(long)} method always
+ * returns {@code false}, as the Operation should never actually be started.
+ */
+public class GetTargetEntityStep2 extends Step2 {
+
+
+    /**
+     * Constructs the object using information from another step.
+     *
+     * @param otherStep step whose information should be used
+     */
+    public GetTargetEntityStep2(Step2 otherStep) {
+        super(otherStep, UsecasesConstants.GET_TARGET_ENTITY_ACTOR, UsecasesConstants.GET_TARGET_ENTITY_OPERATION);
+    }
+
+    @Override
+    protected Operation buildOperation() {
+        return new GetTargetEntityOperation2(stepContext, event, params);
+    }
+
+    /**
+     * The operation does not actually have to be executed, as it populates the target
+     * entity as soon as {@link Operation#setProperty(String, Object)} is invoked.
+     */
+    @Override
+    public boolean start(long remainingMs) {
+        if (!isInitialized()) {
+            throw new IllegalStateException("step has not been initialized");
+        }
+
+        return false;
+    }
+}
diff --git a/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/GuardStep2.java b/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/GuardStep2.java
new file mode 100644 (file)
index 0000000..b7247ce
--- /dev/null
@@ -0,0 +1,115 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.onap.policy.controlloop.actor.guard.DecisionOperation;
+import org.onap.policy.controlloop.actor.guard.GuardActor;
+import org.onap.policy.controlloop.actor.so.VfModuleCreate;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
+
+/**
+ * Wrapper for a Guard operation. Note: this makes a clone of the operation parameters,
+ * replacing the payload. It overrides the operation's property names with that are
+ * relevant for guards. In addition, it overrides the relevant loadXxx() methods to load
+ * the data into the payload instead of into the operation's properties. It also
+ * increments or decrements the VF Count, depending whether the operation is a "VF Module
+ * Create" or not.
+ */
+public class GuardStep2 extends Step2 {
+    public static final String PAYLOAD_KEY_TARGET_ENTITY = "target";
+    public static final String PAYLOAD_KEY_VF_COUNT = "vfCount";
+
+    private final Operation policyOper;
+
+
+    /**
+     * Constructs the object using information from another step.
+     *
+     * @param otherStep step whose information should be used
+     */
+    public GuardStep2(Step2 otherStep, String closedLoopControlName) {
+        super(otherStep, GuardActor.NAME, DecisionOperation.NAME);
+
+        if (!otherStep.isInitialized()) {
+            throw new IllegalStateException("policy operation must be initialized before the guard operation");
+        }
+
+        this.policyOper = otherStep.getOperation();
+
+        Map<String, Object> payload = new LinkedHashMap<>();
+        payload.put("actor", otherStep.getActorName());
+        payload.put("operation", otherStep.getOperationName());
+        payload.put("requestId", params.getRequestId());
+        payload.put("clname", closedLoopControlName);
+
+        params = params.toBuilder().payload(payload).build();
+    }
+
+    @Override
+    public boolean acceptsEvent() {
+        return true;
+    }
+
+    /**
+     * Builds the list of properties on the policy's actual operation.
+     */
+    @Override
+    public List<String> getPropertyNames() {
+        List<String> names = new ArrayList<>(1);
+
+        // include VF Count if the policy's operation needs it
+        if (policyOper.getPropertyNames().contains(OperationProperties.DATA_VF_COUNT)) {
+            names.add(OperationProperties.DATA_VF_COUNT);
+        }
+
+        return names;
+    }
+
+    /**
+     * Load the target entity into the payload instead of the operation's properties.
+     */
+    @Override
+    protected void loadTargetEntity(String propName) {
+        params.getPayload().put(PAYLOAD_KEY_TARGET_ENTITY, getTargetEntity());
+    }
+
+    /**
+     * Load the VF Count into the payload instead of the operation's properties.
+     * Increments the count for "VF Module Create". Decrements it otherwise.
+     */
+    @Override
+    protected void loadVfCount(String propName) {
+        // run guard with the proposed VF count
+        int count = getVfCount();
+        if (VfModuleCreate.NAME.equals(policyOper.getName())) {
+            ++count;
+        } else {
+            --count;
+        }
+
+        params.getPayload().put(PAYLOAD_KEY_VF_COUNT, count);
+    }
+}
diff --git a/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/LockStep2.java b/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/LockStep2.java
new file mode 100644 (file)
index 0000000..507a081
--- /dev/null
@@ -0,0 +1,71 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.eventmanager.ActorConstants;
+import org.onap.policy.drools.apps.controller.usecases.LockOperation2;
+
+/**
+ * Wrapper for the LOCK pseudo operation. Arranges for the lock's future to generate an
+ * outcome.
+ */
+public class LockStep2 extends Step2 {
+
+    /**
+     * Constructs the object using information from another step.
+     *
+     * @param otherStep step whose information should be used
+     */
+    public LockStep2(Step2 otherStep) {
+        super(otherStep, ActorConstants.LOCK_ACTOR, ActorConstants.LOCK_OPERATION);
+    }
+
+    @Override
+    public boolean acceptsEvent() {
+        return true;
+    }
+
+    @Override
+    public boolean start(long remainingMs) {
+        super.start(remainingMs);
+
+        if (future != null) {
+            future.whenComplete((outcome, thrown) -> {
+                outcome.setFinalOutcome(true);
+                params.getCompleteCallback().accept(outcome);
+            });
+        }
+
+        /*
+         * ELSE: The operation must have thrown an exception. Nothing more to do as
+         * super.start() will have already propagated the exception to the completion
+         * handler.
+         */
+
+        return true;
+    }
+
+    @Override
+    protected Operation buildOperation() {
+        return new LockOperation2(stepContext, params);
+    }
+}
diff --git a/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/Step2.java b/controlloop/common/controller-usecases/src/main/java/org/onap/policy/drools/apps/controller/usecases/step/Step2.java
new file mode 100644 (file)
index 0000000..9275f79
--- /dev/null
@@ -0,0 +1,433 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.aai.domain.yang.CloudRegion;
+import org.onap.aai.domain.yang.GenericVnf;
+import org.onap.aai.domain.yang.ModelVer;
+import org.onap.aai.domain.yang.ServiceInstance;
+import org.onap.aai.domain.yang.Tenant;
+import org.onap.policy.aai.AaiCqResponse;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actor.aai.AaiGetPnfOperation;
+import org.onap.policy.controlloop.actor.aai.AaiGetTenantOperation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.eventmanager.Step;
+import org.onap.policy.controlloop.eventmanager.StepContext;
+import org.onap.policy.drools.apps.controller.usecases.UsecasesConstants;
+
+/**
+ * Steps specific to the usecases controller. The {@link #setProperties()} method is used
+ * to load the various properties into the operation, extracting enrichment data where
+ * appropriate, and extracting other data from the step's context. For each property,
+ * there is a getXxx() method for extracting the value and a loadXxx() method for loading
+ * the extracted value into the operation. In addition, the
+ * {@link #success(OperationOutcome)} method is responsible for extracting responses from
+ * an operation outcome and recording the data in the step's context for use by subsequent
+ * steps.
+ */
+public class Step2 extends Step {
+    public static final String TARGET_INFO_MSG = "Target information";
+    public static final String ENRICHMENT_PREFIX = "enrichment/";
+    public static final String VSERVER_VSERVER_NAME = "vserver.vserver-name";
+    public static final String RESOURCE_LINK = "resource-link";
+    public static final String RESULT_DATA = "result-data";
+
+    private static final Map<String, BiConsumer<Step2, String>> PROPERTY_LOADER;
+    private static final Map<String, Consumer<Step2>> PROPERTY_SAVER;
+
+    static {
+        /*
+         * Populate map for PROPERTY_LOADER.
+         */
+        Map<String, BiConsumer<Step2, String>> map = new HashMap<>();
+
+        map.put(OperationProperties.AAI_DEFAULT_CLOUD_REGION, Step2::loadCloudRegion);
+        map.put(OperationProperties.AAI_DEFAULT_TENANT, Step2::loadTenant);
+        map.put(OperationProperties.AAI_PNF, Step2::loadPnf);
+        map.put(OperationProperties.AAI_RESOURCE_VNF, Step2::loadResourceVnf);
+        map.put(OperationProperties.AAI_SERVICE, Step2::loadService);
+        map.put(OperationProperties.AAI_SERVICE_MODEL, Step2::loadServiceModel);
+        map.put(OperationProperties.AAI_TARGET_ENTITY, Step2::loadTargetEntity);
+        map.put(OperationProperties.AAI_VNF, Step2::loadVnf);
+        map.put(OperationProperties.AAI_VNF_MODEL, Step2::loadVnfModel);
+        map.put(OperationProperties.AAI_VSERVER_LINK, Step2::loadVserverLink);
+        map.put(OperationProperties.DATA_VF_COUNT, Step2::loadVfCount);
+        map.put(OperationProperties.EVENT_ADDITIONAL_PARAMS, Step2::loadAdditionalEventParams);
+        map.put(OperationProperties.EVENT_PAYLOAD, Step2::loadEventPayload);
+        map.put(OperationProperties.OPT_CDS_GRPC_AAI_PROPERTIES, Step2::loadOptCdsGrpcAaiProperties);
+
+        map.put(UsecasesConstants.AAI_DEFAULT_GENERIC_VNF, Step2::loadDefaultGenericVnf);
+
+        PROPERTY_LOADER = Collections.unmodifiableMap(map);
+
+
+        /*
+         * Populate map for PROPERTY_SAVER.
+         */
+        Map<String, Consumer<Step2>> map2 = new HashMap<>();
+
+        map2.put(OperationProperties.DATA_VF_COUNT, Step2::storeVfCount);
+
+        PROPERTY_SAVER = Collections.unmodifiableMap(map2);
+    }
+
+
+    protected final StepContext stepContext;
+    protected final VirtualControlLoopEvent event;
+
+    /**
+     * {@code True} if the associated preprocessing steps have been loaded, {@code false}
+     * otherwise.
+     */
+    @Getter
+    @Setter
+    private boolean preprocessed;
+
+    /**
+     * Actions to take to store the Operation's properties back into the context.
+     */
+    private List<Consumer<Step2>> postProcessors = new LinkedList<>();
+
+
+    /**
+     * Constructs the object. This is used when constructing the step for the policy's
+     * actual operation.
+     *
+     * @param stepContext the step's context
+     * @param params operation parameters
+     * @param event the event being processed
+     */
+    public Step2(StepContext stepContext, ControlLoopOperationParams params, VirtualControlLoopEvent event) {
+        super(params, new AtomicReference<>());
+        this.stepContext = stepContext;
+        this.event = event;
+    }
+
+    /**
+     * Constructs the object using information from another step. This is used when
+     * constructing a preprocessing step.
+     *
+     * @param otherStep step whose information should be used
+     * @param actor actor name
+     * @param operation operation name
+     */
+    public Step2(Step2 otherStep, String actor, String operation) {
+        super(otherStep, actor, operation);
+        this.stepContext = otherStep.stepContext;
+        this.event = otherStep.event;
+    }
+
+    /**
+     * Determines if starting this step indicates acceptance of the event. The default
+     * method simply invokes {@link #isPolicyStep()}.
+     *
+     * @return {@code true} if this step accepts the event, {@code false} if it is still
+     *         indeterminate
+     */
+    public boolean acceptsEvent() {
+        return isPolicyStep();
+    }
+
+    /**
+     * Indicates that the step succeeded with the given outcome. Invoked by the rules. The
+     * default method invokes the post processors.
+     *
+     * @param outcome operation's outcome
+     */
+    public void success(OperationOutcome outcome) {
+        for (Consumer<Step2> proc : postProcessors) {
+            proc.accept(this);
+        }
+    }
+
+    /**
+     * Gets the names of the properties required by the operation. The default method just
+     * delegates to the operation to identify the properties.
+     *
+     * @return the names of the properties required by the operation
+     */
+    public List<String> getPropertyNames() {
+        return getOperation().getPropertyNames();
+    }
+
+    /**
+     * Sets the operation's properties. This is invoked <i>after</i> any preprocessor
+     * steps have been performed. It also adds items to {@link #postProcessors}.
+     */
+    public void setProperties() {
+        postProcessors.clear();
+
+        for (String propName : getPropertyNames()) {
+            // identify the saver, if any
+            Consumer<Step2> saver = PROPERTY_SAVER.get(propName);
+            if (saver != null) {
+                postProcessors.add(saver);
+            }
+
+
+            // load data
+            if (propName.startsWith(ENRICHMENT_PREFIX)) {
+                loadEnrichment(propName);
+                continue;
+            }
+
+            BiConsumer<Step2, String> loader = PROPERTY_LOADER.get(propName);
+            if (loader == null) {
+                throw new IllegalArgumentException("unknown property " + propName + " needed by " + getActorName() + "."
+                                + getOperationName());
+            }
+
+            loader.accept(this, propName);
+        }
+    }
+
+    protected void loadCloudRegion(String propName) {
+        getOperation().setProperty(propName, getCloudRegion());
+    }
+
+    protected void loadTenant(String propName) {
+        getOperation().setProperty(propName, getTenant());
+    }
+
+    protected void loadPnf(String propName) {
+        getOperation().setProperty(propName, getPnf());
+    }
+
+    protected void loadResourceVnf(String propName) {
+        getOperation().setProperty(propName, getResourceVnf());
+    }
+
+    protected void loadService(String propName) {
+        getOperation().setProperty(propName, getService());
+    }
+
+    protected void loadServiceModel(String propName) {
+        getOperation().setProperty(propName, getServiceModel());
+    }
+
+    protected void loadTargetEntity(String propName) {
+        getOperation().setProperty(propName, getTargetEntity());
+    }
+
+    protected void loadVnf(String propName) {
+        getOperation().setProperty(propName, getVnf());
+    }
+
+    protected void loadVnfModel(String propName) {
+        getOperation().setProperty(propName, getVnfModel());
+    }
+
+    protected void loadVserverLink(String propName) {
+        getOperation().setProperty(propName, getVserverLink());
+    }
+
+    protected void loadVfCount(String propName) {
+        getOperation().setProperty(propName, getVfCount());
+    }
+
+    protected void loadEnrichment(String propName) {
+        getOperation().setProperty(propName, getEnrichment(propName));
+    }
+
+    protected void loadAdditionalEventParams(String propName) {
+        getOperation().setProperty(propName, getAdditionalEventParams());
+    }
+
+    protected void loadEventPayload(String propName) {
+        getOperation().setProperty(propName, getEventPayload());
+    }
+
+    protected void loadOptCdsGrpcAaiProperties(String propName) {
+        // do nothing
+    }
+
+    protected void loadDefaultGenericVnf(String propName) {
+        getOperation().setProperty(propName, getDefaultGenericVnf());
+    }
+
+    protected CloudRegion getCloudRegion() {
+        AaiCqResponse aaicq = getCustomQueryData();
+        return aaicq.getDefaultCloudRegion();
+    }
+
+    protected Tenant getTenant() {
+        AaiCqResponse aaicq = getCustomQueryData();
+        return aaicq.getDefaultTenant();
+    }
+
+    protected StandardCoderObject getPnf() {
+        return stepContext.getProperty(AaiGetPnfOperation.getKey(getTargetEntity()));
+    }
+
+    protected GenericVnf getResourceVnf() {
+        verifyNotNull(TARGET_INFO_MSG, params.getTarget());
+
+        String resourceId = params.getTarget().getResourceID();
+
+        verifyNotNull("Target resource ID", resourceId);
+
+        AaiCqResponse aaicq = getCustomQueryData();
+        return aaicq.getGenericVnfByModelInvariantId(resourceId);
+    }
+
+    protected ServiceInstance getService() {
+        AaiCqResponse aaicq = getCustomQueryData();
+        return aaicq.getServiceInstance();
+    }
+
+    protected ModelVer getServiceModel() {
+        AaiCqResponse aaicq = getCustomQueryData();
+        ServiceInstance service = aaicq.getServiceInstance();
+        return aaicq.getModelVerByVersionId(service.getModelVersionId());
+    }
+
+    /**
+     * The default method assumes there is only one target entity and that it's stored
+     * within the step's context.
+     */
+    protected String getTargetEntity() {
+        return stepContext.getProperty(OperationProperties.AAI_TARGET_ENTITY);
+    }
+
+    protected GenericVnf getVnf() {
+        verifyNotNull(TARGET_INFO_MSG, params.getTarget());
+
+        String modelInvariantId = params.getTarget().getModelInvariantId();
+
+        verifyNotNull("modelInvariantId", modelInvariantId);
+
+        AaiCqResponse aaicq = getCustomQueryData();
+        return aaicq.getGenericVnfByVfModuleModelInvariantId(modelInvariantId);
+    }
+
+    protected ModelVer getVnfModel() {
+        GenericVnf vnf = getVnf();
+        AaiCqResponse aaicq = getCustomQueryData();
+        return aaicq.getModelVerByVersionId(vnf.getModelVersionId());
+    }
+
+    protected String getVserverLink() {
+        String vserver = event.getAai().get(VSERVER_VSERVER_NAME);
+        if (StringUtils.isBlank(vserver)) {
+            throw new IllegalArgumentException("missing " + VSERVER_VSERVER_NAME + " in enrichment data");
+        }
+
+        StandardCoderObject tenant = stepContext.getProperty(AaiGetTenantOperation.getKey(vserver));
+        verifyNotNull("tenant data", tenant);
+
+        String resourceLink = tenant.getString(RESULT_DATA, 0, RESOURCE_LINK);
+        verifyNotNull("tenant data resource-link", resourceLink);
+
+        return stripPrefix(resourceLink, 3);
+    }
+
+    protected Integer getVfCount() {
+        if (stepContext.contains(OperationProperties.DATA_VF_COUNT)) {
+            return stepContext.getProperty(OperationProperties.DATA_VF_COUNT);
+        }
+
+        verifyNotNull(TARGET_INFO_MSG, params.getTarget());
+
+        String modelCustomizationId = params.getTarget().getModelCustomizationId();
+        String modelInvariantId = params.getTarget().getModelInvariantId();
+        String modelVersionId = params.getTarget().getModelVersionId();
+
+        verifyNotNull("target modelCustomizationId", modelCustomizationId);
+        verifyNotNull("target modelInvariantId", modelInvariantId);
+        verifyNotNull("target modelVersionId", modelVersionId);
+
+        AaiCqResponse aaicq = getCustomQueryData();
+        return aaicq.getVfModuleCount(modelCustomizationId, modelInvariantId, modelVersionId);
+    }
+
+    protected String getEnrichment(String propName) {
+        String enrichmentKey = propName.substring(ENRICHMENT_PREFIX.length());
+        String value = event.getAai().get(enrichmentKey);
+        verifyNotNull(propName, value);
+
+        return value;
+    }
+
+    protected Map<String, String> getAdditionalEventParams() {
+        return event.getAdditionalEventParams();
+    }
+
+    protected String getEventPayload() {
+        return event.getPayload();
+    }
+
+    protected GenericVnf getDefaultGenericVnf() {
+        AaiCqResponse aaicq = getCustomQueryData();
+        return aaicq.getDefaultGenericVnf();
+    }
+
+    protected AaiCqResponse getCustomQueryData() {
+        AaiCqResponse aaicq = stepContext.getProperty(AaiCqResponse.CONTEXT_KEY);
+        verifyNotNull("custom query data", aaicq);
+
+        return aaicq;
+    }
+
+    protected void storeVfCount() {
+        if (!getOperation().containsProperty(OperationProperties.DATA_VF_COUNT)) {
+            return;
+        }
+
+        int vfcount = getOperation().getProperty(OperationProperties.DATA_VF_COUNT);
+        stepContext.setProperty(OperationProperties.DATA_VF_COUNT, vfcount);
+    }
+
+    protected void verifyNotNull(String propName, Object value) {
+        if (value == null) {
+            throw new IllegalArgumentException(
+                            "missing " + propName + " for " + getActorName() + "." + getOperationName());
+        }
+    }
+
+    protected static String stripPrefix(String resourceLink, int ncomponents) {
+        int previdx = -1;
+        for (int nslashes = 0; nslashes < ncomponents; ++nslashes) {
+            int idx = resourceLink.indexOf('/', previdx + 1);
+            if (idx < 0) {
+                break;
+            }
+
+            previdx = idx;
+        }
+
+        return resourceLink.substring(Math.max(0, previdx));
+    }
+}
diff --git a/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/AaiCqStep2Test.java b/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/AaiCqStep2Test.java
new file mode 100644 (file)
index 0000000..9869c21
--- /dev/null
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.aai.AaiCqResponse;
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actor.aai.AaiActor;
+import org.onap.policy.controlloop.actor.aai.AaiCustomQueryOperation;
+import org.onap.policy.controlloop.actorserviceprovider.ActorService;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.Operator;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
+import org.onap.policy.controlloop.eventmanager.StepContext;
+
+public class AaiCqStep2Test {
+    private static final String MY_TARGET = "my-target";
+    private static final UUID REQ_ID = UUID.randomUUID();
+
+    @Mock
+    private Operator policyOperator;
+    @Mock
+    private Operation policyOperation;
+    @Mock
+    private Actor policyActor;
+    @Mock
+    private ActorService actors;
+    @Mock
+    private ControlLoopOperationParams params;
+    @Mock
+    private StepContext stepContext;
+    @Mock
+    private VirtualControlLoopEvent event;
+
+    private CompletableFuture<OperationOutcome> future;
+    private Step2 master;
+    private AaiCqStep2 step;
+
+    /**
+     * Sets up.
+     */
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        future = new CompletableFuture<>();
+
+        when(params.toBuilder()).thenReturn(ControlLoopOperationParams.builder().actorService(actors)
+                        .targetEntity(MY_TARGET).requestId(REQ_ID));
+
+        // configure policy operation
+        when(actors.getActor(AaiActor.NAME)).thenReturn(policyActor);
+        when(policyActor.getOperator(AaiCustomQueryOperation.NAME)).thenReturn(policyOperator);
+        when(policyOperator.buildOperation(any())).thenReturn(policyOperation);
+        when(policyOperation.start()).thenReturn(future);
+
+        master = new Step2(stepContext, params, event);
+        step = new AaiCqStep2(master);
+    }
+
+    @Test
+    public void testConstructor() {
+        assertEquals(AaiActor.NAME, step.getActorName());
+        assertEquals(AaiCustomQueryOperation.NAME, step.getOperationName());
+        assertSame(stepContext, step.stepContext);
+        assertSame(event, step.event);
+    }
+
+    @Test
+    public void testStart() {
+        step.init();
+        assertTrue(step.start(100));
+        verify(policyOperation).start();
+    }
+
+    /**
+     * Tests start() when the data has already been retrieved.
+     */
+    @Test
+    public void testStartAlreadyHaveData() {
+        when(stepContext.contains(AaiCqResponse.CONTEXT_KEY)).thenReturn(true);
+
+        step.init();
+        assertFalse(step.start(200));
+        verify(policyOperation, never()).start();
+    }
+
+    @Test
+    public void testSuccess() {
+        AaiCqResponse data = new AaiCqResponse("{}");
+        OperationOutcome outcome = new OperationOutcome();
+        outcome.setResponse(data);
+
+        step.success(outcome);
+        verify(stepContext).setProperty(AaiCqResponse.CONTEXT_KEY, data);
+    }
+}
diff --git a/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/AaiGetPnfStep2Test.java b/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/AaiGetPnfStep2Test.java
new file mode 100644 (file)
index 0000000..7580c2c
--- /dev/null
@@ -0,0 +1,133 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actor.aai.AaiActor;
+import org.onap.policy.controlloop.actor.aai.AaiGetPnfOperation;
+import org.onap.policy.controlloop.actorserviceprovider.ActorService;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
+import org.onap.policy.controlloop.actorserviceprovider.Operator;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
+import org.onap.policy.controlloop.eventmanager.StepContext;
+
+public class AaiGetPnfStep2Test {
+    private static final String MY_TARGET = "my-target";
+    private static final UUID REQ_ID = UUID.randomUUID();
+
+    @Mock
+    private Operator policyOperator;
+    @Mock
+    private Operation policyOperation;
+    @Mock
+    private Actor policyActor;
+    @Mock
+    private ActorService actors;
+    @Mock
+    private ControlLoopOperationParams params;
+    @Mock
+    private StepContext stepContext;
+    @Mock
+    private VirtualControlLoopEvent event;
+
+    private CompletableFuture<OperationOutcome> future;
+    private Step2 master;
+    private AaiGetPnfStep2 step;
+
+    /**
+     * Sets up.
+     */
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        future = new CompletableFuture<>();
+
+        when(params.toBuilder()).thenReturn(ControlLoopOperationParams.builder().actorService(actors)
+                        .requestId(REQ_ID));
+
+        // configure policy operation
+        when(actors.getActor(AaiActor.NAME)).thenReturn(policyActor);
+        when(policyActor.getOperator(AaiGetPnfOperation.NAME)).thenReturn(policyOperator);
+        when(policyOperator.buildOperation(any())).thenReturn(policyOperation);
+        when(policyOperation.start()).thenReturn(future);
+        when(stepContext.getProperty(OperationProperties.AAI_TARGET_ENTITY)).thenReturn(MY_TARGET);
+
+        master = new Step2(stepContext, params, event);
+        step = new AaiGetPnfStep2(master);
+    }
+
+    @Test
+    public void testConstructor() {
+        assertEquals(AaiActor.NAME, step.getActorName());
+        assertEquals(AaiGetPnfOperation.NAME, step.getOperationName());
+        assertSame(stepContext, step.stepContext);
+        assertSame(event, step.event);
+    }
+
+    @Test
+    public void testStart() {
+        step.init();
+        assertTrue(step.start(100));
+        verify(policyOperation).start();
+    }
+
+    /**
+     * Tests start() when the data has already been retrieved.
+     */
+    @Test
+    public void testStartAlreadyHaveData() {
+        when(stepContext.contains(AaiGetPnfOperation.getKey(MY_TARGET))).thenReturn(true);
+
+        step.init();
+        assertFalse(step.start(200));
+        verify(policyOperation, never()).start();
+    }
+
+    @Test
+    public void testSuccess() {
+        StandardCoderObject data = new StandardCoderObject();
+        OperationOutcome outcome = new OperationOutcome();
+        outcome.setResponse(data);
+
+        step.success(outcome);
+        verify(stepContext).setProperty(AaiGetPnfOperation.getKey(MY_TARGET), data);
+    }
+}
diff --git a/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/AaiGetTenantStep2Test.java b/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/AaiGetTenantStep2Test.java
new file mode 100644 (file)
index 0000000..65c27b4
--- /dev/null
@@ -0,0 +1,161 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actor.aai.AaiActor;
+import org.onap.policy.controlloop.actor.aai.AaiGetTenantOperation;
+import org.onap.policy.controlloop.actorserviceprovider.ActorService;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
+import org.onap.policy.controlloop.actorserviceprovider.Operator;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
+import org.onap.policy.controlloop.eventmanager.StepContext;
+
+public class AaiGetTenantStep2Test {
+    private static final String MY_VSERVER = "my-vserver";
+    private static final UUID REQ_ID = UUID.randomUUID();
+
+    @Mock
+    private Operator policyOperator;
+    @Mock
+    private Operation policyOperation;
+    @Mock
+    private Actor policyActor;
+    @Mock
+    private ActorService actors;
+    @Mock
+    private ControlLoopOperationParams params;
+    @Mock
+    private StepContext stepContext;
+    @Mock
+    private VirtualControlLoopEvent event;
+
+    private CompletableFuture<OperationOutcome> future;
+    private Step2 master;
+    private AaiGetTenantStep2 step;
+
+    /**
+     * Sets up.
+     */
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        future = new CompletableFuture<>();
+
+        when(params.toBuilder()).thenReturn(ControlLoopOperationParams.builder().actorService(actors)
+                        .targetEntity("my-target").requestId(REQ_ID));
+
+        // configure policy operation
+        when(actors.getActor(AaiActor.NAME)).thenReturn(policyActor);
+        when(policyActor.getOperator(AaiGetTenantOperation.NAME)).thenReturn(policyOperator);
+        when(policyOperator.buildOperation(any())).thenReturn(policyOperation);
+        when(policyOperation.start()).thenReturn(future);
+
+        when(event.getAai()).thenReturn(Map.of(Step2.VSERVER_VSERVER_NAME, MY_VSERVER));
+
+        master = new Step2(stepContext, params, event);
+        step = new AaiGetTenantStep2(master);
+    }
+
+    @Test
+    public void testConstructor() {
+        assertEquals(AaiActor.NAME, step.getActorName());
+        assertEquals(AaiGetTenantOperation.NAME, step.getOperationName());
+        assertSame(stepContext, step.stepContext);
+        assertSame(event, step.event);
+
+        // empty vserver name
+        when(event.getAai()).thenReturn(Map.of(Step2.VSERVER_VSERVER_NAME, ""));
+        assertThatIllegalArgumentException().isThrownBy(() -> new AaiGetTenantStep2(master))
+                        .withMessage("missing " + Step2.VSERVER_VSERVER_NAME + " in enrichment data");
+
+        // missing vserver name
+        when(event.getAai()).thenReturn(Map.of());
+        assertThatIllegalArgumentException().isThrownBy(() -> new AaiGetTenantStep2(master))
+                        .withMessage("missing " + Step2.VSERVER_VSERVER_NAME + " in enrichment data");
+    }
+
+    @Test
+    public void testGetPropertyNames() {
+        assertThat(step.getPropertyNames()).isEmpty();
+    }
+
+    @Test
+    public void testSetProperties() {
+        step.init();
+
+        step.setProperties();
+
+        verify(policyOperation).setProperty(OperationProperties.AAI_TARGET_ENTITY, MY_VSERVER);
+    }
+
+    @Test
+    public void testStart() {
+        step.init();
+        assertTrue(step.start(100));
+        verify(policyOperation).start();
+    }
+
+    /**
+     * Tests start() when the data has already been retrieved.
+     */
+    @Test
+    public void testStartAlreadyHaveData() {
+        when(stepContext.contains(AaiGetTenantOperation.getKey(MY_VSERVER))).thenReturn(true);
+
+        step.init();
+        assertFalse(step.start(200));
+        verify(policyOperation, never()).start();
+    }
+
+    @Test
+    public void testSuccess() {
+        StandardCoderObject data = new StandardCoderObject();
+        OperationOutcome outcome = new OperationOutcome();
+        outcome.setResponse(data);
+
+        step.success(outcome);
+        verify(stepContext).setProperty(AaiGetTenantOperation.getKey(MY_VSERVER), data);
+    }
+}
diff --git a/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/GetTargetEntityStep2Test.java b/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/GetTargetEntityStep2Test.java
new file mode 100644 (file)
index 0000000..965d04d
--- /dev/null
@@ -0,0 +1,87 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.eventmanager.StepContext;
+import org.onap.policy.drools.apps.controller.usecases.GetTargetEntityOperation2;
+import org.onap.policy.drools.apps.controller.usecases.UsecasesConstants;
+
+public class GetTargetEntityStep2Test {
+    @Mock
+    private ControlLoopOperationParams params;
+    @Mock
+    private StepContext stepContext;
+    @Mock
+    private VirtualControlLoopEvent event;
+
+    private Step2 master;
+    private GetTargetEntityStep2 step;
+
+    /**
+     * Sets up.
+     */
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(params.toBuilder()).thenReturn(ControlLoopOperationParams.builder());
+
+        master = new Step2(stepContext, params, event);
+        step = new GetTargetEntityStep2(master);
+    }
+
+    @Test
+    public void testConstructor() {
+        assertEquals(UsecasesConstants.GET_TARGET_ENTITY_ACTOR, step.getActorName());
+        assertEquals(UsecasesConstants.GET_TARGET_ENTITY_OPERATION, step.getOperationName());
+        assertSame(stepContext, step.stepContext);
+        assertSame(event, step.event);
+    }
+
+    @Test
+    public void testBuildOperation() {
+        Operation oper = step.buildOperation();
+        assertTrue(oper instanceof GetTargetEntityOperation2);
+    }
+
+    @Test
+    public void testStart() {
+        assertThatIllegalStateException().isThrownBy(() -> step.start(200))
+                        .withMessage("step has not been initialized");
+
+        step.init();
+        assertFalse(step.start(100));
+    }
+}
diff --git a/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/GuardStep2Test.java b/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/GuardStep2Test.java
new file mode 100644 (file)
index 0000000..43319ef
--- /dev/null
@@ -0,0 +1,166 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actor.guard.DecisionOperation;
+import org.onap.policy.controlloop.actor.guard.GuardActor;
+import org.onap.policy.controlloop.actor.so.VfModuleCreate;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
+import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.eventmanager.StepContext;
+
+public class GuardStep2Test {
+    private static final String CL_NAME = "my-closed-loop";
+    private static final String MASTER_ACTOR = "master-actor";
+    private static final String MASTER_OPERATION = "master-operation";
+    private static final String MY_TARGET = "my-target";
+    private static final UUID REQ_ID = UUID.randomUUID();
+    private static final int VF_COUNT = 10;
+
+    @Mock
+    private ControlLoopEventContext context;
+    @Mock
+    private StepContext stepContext;
+    @Mock
+    private VirtualControlLoopEvent event;
+    @Mock
+    private Operation policyOper;
+
+    private ControlLoopOperationParams params;
+    private Step2 master;
+    private GuardStep2 step;
+
+    /**
+     * Sets up.
+     */
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(event.getRequestId()).thenReturn(REQ_ID);
+
+        when(context.getEvent()).thenReturn(event);
+
+        when(stepContext.getProperty(OperationProperties.AAI_TARGET_ENTITY)).thenReturn(MY_TARGET);
+        when(stepContext.contains(OperationProperties.DATA_VF_COUNT)).thenReturn(true);
+        when(stepContext.getProperty(OperationProperties.DATA_VF_COUNT)).thenReturn(VF_COUNT);
+
+
+        params = ControlLoopOperationParams.builder().actor(MASTER_ACTOR).operation(MASTER_OPERATION)
+                        .targetEntity(MY_TARGET).context(context).build();
+
+        master = new Step2(stepContext, params, event) {
+            @Override
+            protected Operation buildOperation() {
+                return policyOper;
+            }
+        };
+
+        // force it to build the operation
+        master.init();
+
+        step = new GuardStep2(master, CL_NAME);
+    }
+
+    @Test
+    public void testConstructor() {
+        assertEquals(GuardActor.NAME, step.getActorName());
+        assertEquals(DecisionOperation.NAME, step.getOperationName());
+        assertSame(stepContext, step.stepContext);
+        assertSame(event, step.event);
+
+        // test when master is uninitialized
+        master = new Step2(stepContext, params, event);
+        assertThatIllegalStateException().isThrownBy(() -> new GuardStep2(master, CL_NAME));
+
+        ControlLoopOperationParams params2 = step.getParams();
+
+        // @formatter:off
+        assertThat(params2.getPayload()).isEqualTo(Map.of(
+                        "actor", MASTER_ACTOR,
+                        "operation", MASTER_OPERATION,
+                        "requestId", REQ_ID,
+                        "clname", CL_NAME));
+        // @formatter:on
+    }
+
+    @Test
+    public void testAcceptsEvent() {
+        // it should always accept events
+        assertTrue(step.acceptsEvent());
+    }
+
+    @Test
+    public void testGetPropertyNames() {
+        // unmatching property names
+        when(policyOper.getPropertyNames()).thenReturn(List.of("propA", "propB"));
+        assertThat(step.getPropertyNames()).isEmpty();
+
+        // matching property names
+        when(policyOper.getPropertyNames()).thenReturn(List.of("propA", OperationProperties.DATA_VF_COUNT, "propB"));
+        assertThat(step.getPropertyNames()).isEqualTo(List.of(OperationProperties.DATA_VF_COUNT));
+    }
+
+    @Test
+    public void testLoadTargetEntity() {
+        step.loadTargetEntity(OperationProperties.AAI_TARGET_ENTITY);
+        assertThat(step.getParams().getPayload().get(GuardStep2.PAYLOAD_KEY_TARGET_ENTITY)).isEqualTo(MY_TARGET);
+    }
+
+    /**
+     * Tests loadVfCount() when the policy operation is NOT "VF Module Create".
+     */
+    @Test
+    public void testLoadVfCountNotVfModuleCreate() {
+        // should decrement the count
+        step.loadVfCount("");
+        assertThat(step.getParams().getPayload().get(GuardStep2.PAYLOAD_KEY_VF_COUNT)).isEqualTo(VF_COUNT - 1);
+    }
+
+    /**
+     * Tests loadVfCount() when the policy operation is "VF Module Create".
+     */
+    @Test
+    public void testLoadVfCountVfModuleCreate() {
+        when(policyOper.getName()).thenReturn(VfModuleCreate.NAME);
+
+        // should increment the count
+        step.loadVfCount("");
+        assertThat(step.getParams().getPayload().get(GuardStep2.PAYLOAD_KEY_VF_COUNT)).isEqualTo(VF_COUNT + 1);
+    }
+}
diff --git a/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/LockStep2Test.java b/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/LockStep2Test.java
new file mode 100644 (file)
index 0000000..2afd395
--- /dev/null
@@ -0,0 +1,134 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.eventmanager.ActorConstants;
+import org.onap.policy.controlloop.eventmanager.StepContext;
+
+public class LockStep2Test {
+    private static final String MY_TARGET = "my-target";
+
+    @Mock
+    private StepContext stepContext;
+    @Mock
+    private Operation policyOper;
+    @Mock
+    private VirtualControlLoopEvent event;
+    @Mock
+    private Consumer<OperationOutcome> callback;
+
+    private ControlLoopOperationParams params;
+    private CompletableFuture<OperationOutcome> future;
+    private Step2 master;
+    private LockStep2 step;
+
+    /**
+     * Sets up.
+     */
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        future = new CompletableFuture<>();
+
+        when(stepContext.requestLock(MY_TARGET)).thenReturn(future);
+        when(stepContext.getProperty(OperationProperties.AAI_TARGET_ENTITY)).thenReturn(MY_TARGET);
+
+        params = ControlLoopOperationParams.builder().completeCallback(callback).build();
+
+        master = new Step2(stepContext, params, event) {
+            @Override
+            protected Operation buildOperation() {
+                return policyOper;
+            }
+        };
+
+        // force it to build the operation
+        master.init();
+
+        step = new LockStep2(master);
+    }
+
+    @Test
+    public void testConstructor() {
+        assertEquals(ActorConstants.LOCK_ACTOR, step.getActorName());
+        assertEquals(ActorConstants.LOCK_OPERATION, step.getOperationName());
+        assertSame(stepContext, step.stepContext);
+        assertSame(event, step.event);
+    }
+
+    @Test
+    public void testAcceptsEvent() {
+        // it should always accept events
+        assertTrue(step.acceptsEvent());
+    }
+
+    @Test
+    public void testStart() {
+        // start the operation
+        step.init();
+        step.setProperties();
+        assertTrue(step.start(100));
+
+        // complete the operation's future
+        OperationOutcome outcome = params.makeOutcome();
+        outcome.setTarget(MY_TARGET);
+
+        future.complete(outcome);
+
+        // ensure it invoked the callback
+        verify(callback).accept(outcome);
+    }
+
+    /**
+     * Tests start when the operation throws an exception.
+     */
+    @Test
+    public void testStartNoFuture() {
+        // start the operation
+        step.init();
+
+        // force an exception by NOT invoking setProperties()
+
+        assertTrue(step.start(100));
+
+        // should have already invoked the callback
+        verify(callback).accept(any());
+    }
+}
diff --git a/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/Step2Test.java b/controlloop/common/controller-usecases/src/test/java/org/onap/policy/drools/apps/controller/usecases/step/Step2Test.java
new file mode 100644 (file)
index 0000000..75fe486
--- /dev/null
@@ -0,0 +1,514 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * 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.drools.apps.controller.usecases.step;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.LinkedBlockingQueue;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.aai.domain.yang.CloudRegion;
+import org.onap.aai.domain.yang.GenericVnf;
+import org.onap.aai.domain.yang.ModelVer;
+import org.onap.aai.domain.yang.ServiceInstance;
+import org.onap.aai.domain.yang.Tenant;
+import org.onap.policy.aai.AaiCqResponse;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actor.aai.AaiGetPnfOperation;
+import org.onap.policy.controlloop.actor.aai.AaiGetTenantOperation;
+import org.onap.policy.controlloop.actorserviceprovider.ActorService;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
+import org.onap.policy.controlloop.actorserviceprovider.Operator;
+import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
+import org.onap.policy.controlloop.eventmanager.ControlLoopOperationManager2;
+import org.onap.policy.controlloop.eventmanager.StepContext;
+import org.onap.policy.controlloop.policy.Policy;
+import org.onap.policy.controlloop.policy.Target;
+import org.onap.policy.controlloop.policy.TargetType;
+import org.onap.policy.drools.apps.controller.usecases.UsecasesConstants;
+
+public class Step2Test {
+    private static final UUID REQ_ID = UUID.randomUUID();
+    private static final String POLICY_ID = "my-policy";
+    private static final String POLICY_ACTOR = "my-actor";
+    private static final String POLICY_OPERATION = "my-operation";
+    private static final String MY_TARGET = "my-target";
+    private static final String PAYLOAD_KEY = "payload-key";
+    private static final String PAYLOAD_VALUE = "payload-value";
+    private static final Integer POLICY_RETRY = 3;
+    private static final Integer POLICY_TIMEOUT = 20;
+    private static final String NO_SLASH = "noslash";
+    private static final String ONE_SLASH = "/one";
+
+    @Mock
+    private Operator policyOperator;
+    @Mock
+    private Operation policyOperation;
+    @Mock
+    private Actor policyActor;
+    @Mock
+    private ActorService actors;
+    @Mock
+    private StepContext stepContext;
+    @Mock
+    private AaiCqResponse aaicq;
+
+    private CompletableFuture<OperationOutcome> future;
+    private Target target;
+    private Map<String, String> payload;
+    private Policy policy;
+    private VirtualControlLoopEvent event;
+    private ControlLoopEventContext context;
+    private BlockingQueue<OperationOutcome> starts;
+    private BlockingQueue<OperationOutcome> completions;
+    private ControlLoopOperationParams params;
+    private Step2 step;
+
+    /**
+     * Sets up.
+     */
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        future = new CompletableFuture<>();
+
+        // configure policy operation
+        when(actors.getActor(POLICY_ACTOR)).thenReturn(policyActor);
+        when(policyActor.getOperator(POLICY_OPERATION)).thenReturn(policyOperator);
+        when(policyOperator.buildOperation(any())).thenReturn(policyOperation);
+        when(policyOperation.start()).thenReturn(future);
+
+        when(policyOperation.getPropertyNames()).thenReturn(List.of());
+
+        when(stepContext.getProperty(AaiCqResponse.CONTEXT_KEY)).thenReturn(aaicq);
+
+        target = new Target();
+        target.setType(TargetType.VM);
+
+        payload = Map.of(PAYLOAD_KEY, PAYLOAD_VALUE);
+
+        policy = new Policy();
+        policy.setId(POLICY_ID);
+        policy.setActor(POLICY_ACTOR);
+        policy.setRecipe(POLICY_OPERATION);
+        policy.setTarget(target);
+        policy.setPayload(payload);
+        policy.setRetry(POLICY_RETRY);
+        policy.setTimeout(POLICY_TIMEOUT);
+
+        event = new VirtualControlLoopEvent();
+        event.setRequestId(REQ_ID);
+        event.setTarget(ControlLoopOperationManager2.VSERVER_VSERVER_NAME);
+        event.setAai(new TreeMap<>(Map.of(ControlLoopOperationManager2.VSERVER_VSERVER_NAME, MY_TARGET)));
+
+        context = new ControlLoopEventContext(event);
+
+        starts = new LinkedBlockingQueue<>();
+        completions = new LinkedBlockingQueue<>();
+
+        params = ControlLoopOperationParams.builder().actor(POLICY_ACTOR).actorService(actors)
+                        .completeCallback(completions::add).context(context).executor(ForkJoinPool.commonPool())
+                        .operation(POLICY_OPERATION).payload(new TreeMap<>(payload)).startCallback(starts::add)
+                        .target(target).targetEntity(MY_TARGET).build();
+
+        step = new Step2(stepContext, params, event);
+        step.init();
+    }
+
+    @Test
+    public void testConstructor() {
+        assertSame(stepContext, step.stepContext);
+        assertSame(event, step.event);
+        assertSame(actors, step.getParams().getActorService());
+    }
+
+    @Test
+    public void testConstructorStep2() {
+        step = new Step2(step, "actorB", "operationB");
+        assertSame(stepContext, step.stepContext);
+        assertSame(event, step.event);
+
+        assertEquals("actorB", step.getActorName());
+        assertEquals("operationB", step.getOperationName());
+        assertSame(actors, step.getParams().getActorService());
+    }
+
+    @Test
+    public void testAcceptsEvent() {
+        // it's a policy step, thus it accepts events
+        assertTrue(step.acceptsEvent());
+
+        step = new Step2(step, "actorB", "operationB");
+
+        // it's not a policy step, thus it does not accept events
+        assertFalse(step.acceptsEvent());
+    }
+
+    @Test
+    public void testSuccess() {
+        assertThatCode(() -> step.success(null)).doesNotThrowAnyException();
+    }
+
+    @Test
+    public void testGetPropertyNames() {
+        // empty property list
+        assertThat(step.getPropertyNames()).isEmpty();
+
+        // try with non-empty list
+        when(policyOperation.getPropertyNames()).thenReturn(List.of("propA", "propB"));
+        assertThat(step.getPropertyNames()).isEqualTo(List.of("propA", "propB"));
+    }
+
+    @Test
+    public void testSetProperties() {
+        CloudRegion cloudRegion = new CloudRegion();
+        when(aaicq.getDefaultCloudRegion()).thenReturn(cloudRegion);
+
+        Tenant tenant = new Tenant();
+        when(aaicq.getDefaultTenant()).thenReturn(tenant);
+
+        when(policyOperation.getPropertyNames()).thenReturn(
+                        List.of(OperationProperties.AAI_DEFAULT_CLOUD_REGION, OperationProperties.AAI_DEFAULT_TENANT));
+
+        step.setProperties();
+
+        // should have been exactly two properties set
+        verify(policyOperation, times(2)).setProperty(any(), any());
+        verify(policyOperation).setProperty(OperationProperties.AAI_DEFAULT_CLOUD_REGION, cloudRegion);
+        verify(policyOperation).setProperty(OperationProperties.AAI_DEFAULT_TENANT, tenant);
+    }
+
+    /**
+     * Tests setProperties() when the property is unknown.
+     */
+    @Test
+    public void testSetPropertiesUnknown() {
+        when(policyOperation.getPropertyNames()).thenReturn(List.of("unknown-property"));
+
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
+                        .withMessage("unknown property unknown-property needed by my-actor.my-operation");
+    }
+
+    @Test
+    public void testLoadCloudRegion_testGetCloudRegion() {
+        CloudRegion data = new CloudRegion();
+        when(aaicq.getDefaultCloudRegion()).thenReturn(data);
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_DEFAULT_CLOUD_REGION));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.AAI_DEFAULT_CLOUD_REGION, data);
+    }
+
+    @Test
+    public void testLoadTenant_testGetTenant() {
+        Tenant data = new Tenant();
+        when(aaicq.getDefaultTenant()).thenReturn(data);
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_DEFAULT_TENANT));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.AAI_DEFAULT_TENANT, data);
+    }
+
+    @Test
+    public void testLoadPnf_testGetPnf() {
+        StandardCoderObject data = new StandardCoderObject();
+        when(stepContext.getProperty(OperationProperties.AAI_TARGET_ENTITY)).thenReturn(MY_TARGET);
+        when(stepContext.getProperty(AaiGetPnfOperation.getKey(MY_TARGET))).thenReturn(data);
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_PNF));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.AAI_PNF, data);
+    }
+
+    @Test
+    public void testLoadResourceVnf_testGetResourceVnf() {
+        target.setResourceID("my-resource");
+        GenericVnf data = new GenericVnf();
+        when(aaicq.getGenericVnfByModelInvariantId("my-resource")).thenReturn(data);
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_RESOURCE_VNF));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.AAI_RESOURCE_VNF, data);
+
+        // missing resource ID
+        target.setResourceID(null);
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
+                        .withMessageContaining("missing Target resource ID");
+
+        // missing target
+        params = params.toBuilder().target(null).build();
+        step = new Step2(stepContext, params, event);
+        step.init();
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
+                        .withMessageContaining(Step2.TARGET_INFO_MSG);
+    }
+
+    @Test
+    public void testLoadService_testGetService() {
+        ServiceInstance data = new ServiceInstance();
+        when(aaicq.getServiceInstance()).thenReturn(data);
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_SERVICE));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.AAI_SERVICE, data);
+    }
+
+    @Test
+    public void testLoadServiceModel_testGetServiceModel() {
+        ServiceInstance service = new ServiceInstance();
+        service.setModelVersionId("my-service-version");
+        when(aaicq.getServiceInstance()).thenReturn(service);
+
+        ModelVer data = new ModelVer();
+        when(aaicq.getModelVerByVersionId("my-service-version")).thenReturn(data);
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_SERVICE_MODEL));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.AAI_SERVICE_MODEL, data);
+    }
+
+    @Test
+    public void testLoadVnf_testGetVnf() {
+        target.setModelInvariantId("my-model-invariant");
+        GenericVnf data = new GenericVnf();
+        when(aaicq.getGenericVnfByVfModuleModelInvariantId("my-model-invariant")).thenReturn(data);
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_VNF));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.AAI_VNF, data);
+
+        // missing model invariant ID
+        target.setModelInvariantId(null);
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
+                        .withMessageContaining("missing modelInvariantId");
+
+        // missing target
+        params = params.toBuilder().target(null).build();
+        step = new Step2(stepContext, params, event);
+        step.init();
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
+                        .withMessageContaining(Step2.TARGET_INFO_MSG);
+    }
+
+    @Test
+    public void testLoadVnfModel_testGetVnfModel() {
+        target.setModelInvariantId("my-model-invariant");
+        GenericVnf vnf = new GenericVnf();
+        when(aaicq.getGenericVnfByVfModuleModelInvariantId("my-model-invariant")).thenReturn(vnf);
+
+        vnf.setModelVersionId("my-vnf-model-version-id");
+        ModelVer data = new ModelVer();
+        when(aaicq.getModelVerByVersionId("my-vnf-model-version-id")).thenReturn(data);
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_VNF_MODEL));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.AAI_VNF_MODEL, data);
+    }
+
+    @Test
+    public void testLoadVserverLink_testGetVserverLink() {
+        event.setAai(Map.of(Step2.VSERVER_VSERVER_NAME, "vserverB"));
+
+        StandardCoderObject tenant = mock(StandardCoderObject.class);
+        when(stepContext.getProperty(AaiGetTenantOperation.getKey("vserverB"))).thenReturn(tenant);
+
+        when(tenant.getString("result-data", 0, "resource-link")).thenReturn("/aai/v7/some/link/bbb");
+
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.AAI_VSERVER_LINK));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.AAI_VSERVER_LINK, "/some/link/bbb");
+
+        // missing resource link
+        when(tenant.getString("result-data", 0, "resource-link")).thenReturn(null);
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
+                        .withMessageContaining("missing tenant data resource-link");
+
+        // missing tenant data
+        when(stepContext.getProperty(AaiGetTenantOperation.getKey("vserverB"))).thenReturn(null);
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
+                        .withMessageContaining("missing tenant data for");
+
+        // empty vserver name
+        event.setAai(Map.of(Step2.VSERVER_VSERVER_NAME, ""));
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
+                        .withMessageContaining("missing vserver.vserver-name");
+
+        // missing vserver name
+        event.setAai(Map.of());
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
+                        .withMessageContaining("missing vserver.vserver-name");
+    }
+
+    @Test
+    public void testLoadVfCount_testGetVfCount() {
+        target.setModelCustomizationId("vf-count-customization");
+        target.setModelInvariantId("vf-count-invariant");
+        target.setModelVersionId("vf-count-version");
+        when(aaicq.getVfModuleCount("vf-count-customization", "vf-count-invariant", "vf-count-version")).thenReturn(11);
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.DATA_VF_COUNT));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.DATA_VF_COUNT, 11);
+
+        // missing model version id
+        target.setModelVersionId(null);
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
+                        .withMessageContaining("missing target modelVersionId");
+
+        // missing model invariant id
+        target.setModelInvariantId(null);
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
+                        .withMessageContaining("missing target modelInvariantId");
+
+        // missing model customization id
+        target.setModelCustomizationId(null);
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
+                        .withMessageContaining("missing target modelCustomizationId");
+
+        // missing target
+        params = params.toBuilder().target(null).build();
+        step = new Step2(stepContext, params, event);
+        step.init();
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties())
+                        .withMessageContaining(Step2.TARGET_INFO_MSG);
+
+        // get it from the step context
+        when(stepContext.contains(OperationProperties.DATA_VF_COUNT)).thenReturn(true);
+        when(stepContext.getProperty(OperationProperties.DATA_VF_COUNT)).thenReturn(22);
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.DATA_VF_COUNT, 22);
+    }
+
+    @Test
+    public void testLoadEnrichment_testGetEnrichment() {
+        event.setAai(Map.of("bandwidth", "bandwidth-value"));
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.ENRICHMENT_BANDWIDTH));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.ENRICHMENT_BANDWIDTH, "bandwidth-value");
+
+        // missing enrichment data
+        event.setAai(Map.of());
+        assertThatIllegalArgumentException().isThrownBy(() -> step.setProperties());
+    }
+
+    @Test
+    public void testLoadAdditionalEventParams_testGetAdditionalEventParams() {
+        Map<String, String> data = Map.of("addA", "add-valueA", "addB", "add-valueB");
+        event.setAdditionalEventParams(data);
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.EVENT_ADDITIONAL_PARAMS));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.EVENT_ADDITIONAL_PARAMS, data);
+    }
+
+    @Test
+    public void testLoadEventPayload_testGetEventPayload() {
+        event.setPayload("some-event-payload");
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.EVENT_PAYLOAD));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(OperationProperties.EVENT_PAYLOAD, "some-event-payload");
+    }
+
+    @Test
+    public void testLoadOptCdsGrpcAaiProperties() {
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(OperationProperties.OPT_CDS_GRPC_AAI_PROPERTIES));
+
+        step.setProperties();
+        verify(policyOperation, never()).setProperty(any(), anyString());
+    }
+
+    @Test
+    public void testLoadDefaultGenericVnf_testGetDefaultGenericVnf() {
+        GenericVnf data = new GenericVnf();
+        when(aaicq.getDefaultGenericVnf()).thenReturn(data);
+        when(policyOperation.getPropertyNames()).thenReturn(List.of(UsecasesConstants.AAI_DEFAULT_GENERIC_VNF));
+
+        step.setProperties();
+        verify(policyOperation).setProperty(UsecasesConstants.AAI_DEFAULT_GENERIC_VNF, data);
+    }
+
+    @Test
+    public void testGetCustomQueryData() {
+        assertSame(aaicq, step.getCustomQueryData());
+
+        when(stepContext.getProperty(AaiCqResponse.CONTEXT_KEY)).thenReturn(null);
+
+        assertThatIllegalArgumentException().isThrownBy(() -> step.getCustomQueryData())
+                        .withMessage("missing custom query data for my-actor.my-operation");
+    }
+
+    @Test
+    public void testVerifyNotNull() {
+        assertThatCode(() -> step.verifyNotNull("verifyA", "verify-value-A")).doesNotThrowAnyException();
+
+        assertThatIllegalArgumentException().isThrownBy(() -> step.verifyNotNull("verifyB", null))
+                        .withMessage("missing verifyB for my-actor.my-operation");
+    }
+
+    @Test
+    public void testStripPrefix() {
+        assertEquals(NO_SLASH, Step2.stripPrefix(NO_SLASH, 0));
+        assertEquals(NO_SLASH, Step2.stripPrefix(NO_SLASH, 1));
+        assertEquals(NO_SLASH, Step2.stripPrefix(NO_SLASH, 2));
+
+        assertEquals(ONE_SLASH, Step2.stripPrefix(ONE_SLASH, 1));
+        assertEquals(ONE_SLASH, Step2.stripPrefix(ONE_SLASH, 2));
+
+        assertEquals("/slashes", Step2.stripPrefix("/two/slashes", 2));
+        assertEquals("/slashes", Step2.stripPrefix("/two/slashes", 3));
+
+        assertEquals("/and/more", Step2.stripPrefix("/three/slashes/and/more", 3));
+
+        assertEquals("/and/more", Step2.stripPrefix("prefix/three/slashes/and/more", 3));
+    }
+
+}