sort deploy and undeploy policy lists 19/116719/1
authorjhh <jorge.hernandez-herrero@att.com>
Mon, 11 Jan 2021 13:39:17 +0000 (07:39 -0600)
committerjhh <jorge.hernandez-herrero@att.com>
Mon, 11 Jan 2021 13:39:17 +0000 (07:39 -0600)
An order is imposed in the deployment and undeployment actions when
the list of active policies is retrieved from PAP.   This is to
ensure that the operations are applied in a sane way, for example
to try to prevent to undeploy policies before deleting a controller,
etc ..

The deployment order is 1) native controller, 2) native rule, and 3)
non-native policies.

The undeployment order is 1) non-native, 2) native rule, and 3)
native controller policies.

Issue-ID: POLICY-2762
Signed-off-by: jhh <jorge.hernandez-herrero@att.com>
Change-Id: Icb15dcec87fd5d9917ee152ab15ca7277e13590a

feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFsm.java
feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleFsmTest.java [new file with mode: 0644]

index ef47e1d..5478136 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * ONAP
  * ================================================================================
- * Copyright (C) 2019-2020 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2019-2021 AT&T Intellectual Property. All rights reserved.
  * Modifications Copyright (C) 2021 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +22,7 @@
 package org.onap.policy.drools.lifecycle;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -347,13 +348,61 @@ public class LifecycleFsm implements Startable {
     protected List<ToscaPolicy> getDeployablePoliciesAction(@NonNull List<ToscaPolicy> policies) {
         List<ToscaPolicy> deployPolicies = new ArrayList<>(policies);
         deployPolicies.removeAll(policiesMap.values());
-        return deployPolicies;
+
+        // Ensure that the sequence of policy deployments is sane to minimize potential errors,
+        // First policies to deploy are the controller related ones, those that affect the lifecycle of
+        // controllers, starting with the ones that affect the existence of the controller (native controller),
+        // second the ones that "brain" the controller with application logic (native artifacts).
+        // Lastly the application specific ones such as operational policies.
+
+        // group policies by policy types
+        Map<String, List<ToscaPolicy>> policyTypeGroups = groupPoliciesByPolicyType(deployPolicies);
+
+        // place native controller policies at the start of the list
+        List<ToscaPolicy> orderedDeployableList =
+            new ArrayList<>(policyTypeGroups.getOrDefault(POLICY_TYPE_DROOLS_NATIVE_CONTROLLER.getName(),
+                Collections.EMPTY_LIST));
+
+        // add to the working list the native controller policies
+        orderedDeployableList.addAll(
+            policyTypeGroups.getOrDefault(POLICY_TYPE_DROOLS_NATIVE_RULES.getName(), Collections.EMPTY_LIST));
+
+        // place non-native policies to place at the end of the list
+        orderedDeployableList.addAll(getNonNativePolicies(policyTypeGroups));
+
+        return orderedDeployableList;
     }
 
     protected List<ToscaPolicy> getUndeployablePoliciesAction(@NonNull List<ToscaPolicy> policies) {
         List<ToscaPolicy> undeployPolicies = new ArrayList<>(policiesMap.values());
         undeployPolicies.removeAll(policies);
-        return undeployPolicies;
+        if (undeployPolicies.isEmpty()) {
+            return undeployPolicies;
+        }
+
+        // Ensure that the sequence of policy undeployments is sane to minimize potential errors,
+        // as it is assumed not smart ordering from the policies sent by the PAP.
+        // First policies to undeploy are those that are only of relevance within a drools container,
+        // such as the operational policies.   The next set of policies to undeploy are those that
+        // affect the overall PDP-D application support, firstly the ones that supports the
+        // application software wiring (native rules policies), and second those that relate
+        // to the PDP-D controllers lifecycle.
+
+        // group policies by policy types
+        Map<String, List<ToscaPolicy>> policyTypeGroups = groupPoliciesByPolicyType(undeployPolicies);
+
+        // place controller only (non-native policies) at the start of the list of the undeployment list
+        List<ToscaPolicy> orderedUndeployableList = getNonNativePolicies(policyTypeGroups);
+
+        // add to the working list the native rules policies if any
+        orderedUndeployableList.addAll(
+            policyTypeGroups.getOrDefault(POLICY_TYPE_DROOLS_NATIVE_RULES.getName(), Collections.EMPTY_LIST));
+
+        // finally add to the working list native controller policies if any
+        orderedUndeployableList.addAll(
+            policyTypeGroups.getOrDefault(POLICY_TYPE_DROOLS_NATIVE_CONTROLLER.getName(), Collections.EMPTY_LIST));
+
+        return orderedUndeployableList;
     }
 
     protected void deployedPolicyAction(@NonNull ToscaPolicy policy) {
@@ -378,6 +427,17 @@ public class LifecycleFsm implements Startable {
         return policyTypesMap.get(policyType);
     }
 
+    protected Map<String, List<ToscaPolicy>> groupPoliciesByPolicyType(List<ToscaPolicy> deployPolicies) {
+        return deployPolicies.stream().collect(Collectors.groupingBy(policy -> policy.getTypeIdentifier().getName()));
+    }
+
+    protected List<ToscaPolicy> getNonNativePolicies(@NonNull Map<String, List<ToscaPolicy>> policyTypeGroups) {
+        return policyTypeGroups.entrySet().stream()
+            .filter(entry -> !entry.getKey().equals(POLICY_TYPE_DROOLS_NATIVE_RULES.getName())
+                && !entry.getKey().equals(POLICY_TYPE_DROOLS_NATIVE_CONTROLLER.getName()))
+            .flatMap(entry -> entry.getValue().stream()).collect(Collectors.toList());
+    }
+
     /**
      * Do I support the mandatory policy types?.
      */
diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleFsmTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleFsmTest.java
new file mode 100644 (file)
index 0000000..b5c4f1b
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * =============LICENSE_END========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.common.utils.time.PseudoScheduledExecutorService;
+import org.onap.policy.common.utils.time.TestTimeMulti;
+import org.onap.policy.drools.persistence.SystemPersistenceConstants;
+import org.onap.policy.drools.utils.logging.LoggerUtil;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
+
+/**
+ * Lifecycle FSM Test.
+ */
+public class LifecycleFsmTest {
+
+    private static final String EXAMPLE_NATIVE_CONTROLLER_POLICY_NAME = "example.controller";
+    private static final String EXAMPLE_NATIVE_CONTROLLER_POLICY_JSON =
+            "src/test/resources/tosca-policy-native-controller-example.json";
+
+    private static final String EXAMPLE_NATIVE_ARTIFACT_POLICY_NAME = "example.artifact";
+    private static final String EXAMPLE_NATIVE_ARTIFACT_POLICY_JSON =
+            "src/test/resources/tosca-policy-native-artifact-example.json";
+
+    private static final String EXAMPLE_OTHER_UNVAL_POLICY_NAME = "other-unvalidated";
+    private static final String EXAMPLE_OTHER_UNVAL_POLICY_JSON =
+            "src/test/resources/tosca-policy-other-unvalidated.json";
+
+    private static final String EXAMPLE_OTHER_VAL_POLICY_NAME = "other-validated";
+    private static final String EXAMPLE_OTHER_VAL_POLICY_JSON =
+            "src/test/resources/tosca-policy-other-validated.json";
+
+    private static final String VCPE_OP_POLICY_NAME = "operational.restart";
+    private static final String VCPE_OPERATIONAL_DROOLS_POLICY_JSON =
+            "policies/vCPE.policy.operational.input.tosca.json";
+
+    private static final String VFW_OP_POLICY_NAME = "operational.modifyconfig";
+    private static final String VFW_OPERATIONAL_DROOLS_POLICY_JSON =
+            "policies/vFirewall.policy.operational.input.tosca.json";
+
+    private static final String USECASES_NATIVE_CONTROLLER_POLICY_NAME = "usecases";
+    private static final String USECASES_NATIVE_CONTROLLER_JSON =
+            "policies/usecases.native.controller.policy.input.tosca.json";
+
+    private static final String USECASES_NATIVE_ARTIFACT_POLICY_NAME = "usecases.artifacts";
+    private static final String USECASES_NATIVE_ARTIFACT_JSON =
+            "policies/usecases.native.artifact.policy.input.tosca.json";
+
+    private static final StandardCoder coder = new StandardCoder();
+
+    protected LifecycleFsm fsm;
+    private ToscaPolicy opPolicy;
+    private ToscaPolicy op2Policy;
+    private ToscaPolicy valPolicy;
+    private ToscaPolicy unvalPolicy;
+    private ToscaPolicy controllerPolicy;
+    private ToscaPolicy controller2Policy;
+    private ToscaPolicy artifactPolicy;
+    private ToscaPolicy artifact2Policy;
+
+    /**
+     * Test initialization.
+     */
+    @Before
+    public void init() throws CoderException, IOException {
+        LoggerUtil.setLevel(LoggerUtil.ROOT_LOGGER, "INFO");
+        LoggerUtil.setLevel("org.onap.policy.common.endpoints", "WARN");
+        LoggerUtil.setLevel("org.onap.policy.drools", "INFO");
+        SystemPersistenceConstants.getManager().setConfigurationDir("target/test-classes");
+
+        fsm = new LifecycleFsm() {
+            @Override
+            protected ScheduledExecutorService makeExecutor() {
+                return new PseudoScheduledExecutorService(new TestTimeMulti());
+            }
+        };
+
+        opPolicy = getExamplesPolicy(VFW_OPERATIONAL_DROOLS_POLICY_JSON, VFW_OP_POLICY_NAME);
+        op2Policy = getExamplesPolicy(VCPE_OPERATIONAL_DROOLS_POLICY_JSON, VCPE_OP_POLICY_NAME);
+        controllerPolicy =
+            getPolicyFromFile(EXAMPLE_NATIVE_CONTROLLER_POLICY_JSON, EXAMPLE_NATIVE_CONTROLLER_POLICY_NAME);
+        controller2Policy = getExamplesPolicy(USECASES_NATIVE_CONTROLLER_JSON, USECASES_NATIVE_CONTROLLER_POLICY_NAME);
+        artifactPolicy =
+            getPolicyFromFile(EXAMPLE_NATIVE_ARTIFACT_POLICY_JSON, EXAMPLE_NATIVE_ARTIFACT_POLICY_NAME);
+        artifact2Policy = getExamplesPolicy(USECASES_NATIVE_ARTIFACT_JSON, USECASES_NATIVE_ARTIFACT_POLICY_NAME);
+        valPolicy =
+            getPolicyFromFile(EXAMPLE_OTHER_VAL_POLICY_JSON, EXAMPLE_OTHER_VAL_POLICY_NAME);
+        unvalPolicy =
+            getPolicyFromFile(EXAMPLE_OTHER_UNVAL_POLICY_JSON, EXAMPLE_OTHER_UNVAL_POLICY_NAME);
+    }
+
+    @Test
+    public void testGetDeployableActions() {
+        List<ToscaPolicy> expectedDeployOrder =
+            List.of(controllerPolicy, controller2Policy, artifact2Policy, artifactPolicy,
+                op2Policy, opPolicy, unvalPolicy, valPolicy);
+
+        assertEquals(expectedDeployOrder, fsm.getDeployablePoliciesAction(expectedDeployOrder));
+        assertEquals(expectedDeployOrder,
+            fsm.getDeployablePoliciesAction(
+                List.of(op2Policy, artifact2Policy, valPolicy, opPolicy, unvalPolicy, artifactPolicy,
+                    controllerPolicy, controller2Policy)));
+        assertEquals(expectedDeployOrder,
+                fsm.getDeployablePoliciesAction(
+                    List.of(artifact2Policy, op2Policy, artifactPolicy, controllerPolicy, opPolicy,
+                        controller2Policy, valPolicy, unvalPolicy)));
+    }
+
+    @Test
+    public void testGetUndeployableActions() {
+        fsm.deployedPolicyAction(controllerPolicy);
+        fsm.deployedPolicyAction(controller2Policy);
+        fsm.deployedPolicyAction(artifactPolicy);
+        fsm.deployedPolicyAction(artifact2Policy);
+        fsm.deployedPolicyAction(opPolicy);
+        fsm.deployedPolicyAction(valPolicy);
+        fsm.deployedPolicyAction(unvalPolicy);
+        fsm.deployedPolicyAction(op2Policy);
+
+        List<ToscaPolicy> expectedUndeployOrder =
+                List.of(opPolicy, op2Policy, unvalPolicy, valPolicy, artifactPolicy,
+                        artifact2Policy, controller2Policy, controllerPolicy);
+
+        assertEquals(expectedUndeployOrder, fsm.getUndeployablePoliciesAction(Collections.EMPTY_LIST));
+        assertEquals(expectedUndeployOrder, fsm.getUndeployablePoliciesAction(Collections.EMPTY_LIST));
+        assertEquals(expectedUndeployOrder, fsm.getUndeployablePoliciesAction(Collections.EMPTY_LIST));
+    }
+
+    protected ToscaPolicy getPolicyFromFile(String filePath, String policyName) throws CoderException, IOException {
+        String policyJson = Files.readString(Paths.get(filePath));
+        ToscaServiceTemplate serviceTemplate = coder.decode(policyJson, ToscaServiceTemplate.class);
+        return serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
+    }
+
+    protected ToscaPolicy getExamplesPolicy(String resourcePath, String policyName) throws CoderException {
+        String policyJson = ResourceUtils.getResourceAsString(resourcePath);
+        ToscaServiceTemplate serviceTemplate = new StandardCoder().decode(policyJson, ToscaServiceTemplate.class);
+        return serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
+    }
+
+}
\ No newline at end of file