Add REST api to query policy status from PAP 75/97875/4
authorJim Hahn <jrh3@att.com>
Fri, 1 Nov 2019 19:13:10 +0000 (15:13 -0400)
committerJim Hahn <jrh3@att.com>
Mon, 4 Nov 2019 21:17:24 +0000 (16:17 -0500)
As part of this, added code to pre-load the deployed policy tracker
by reading the policies and groups from the DB.

Change-Id: Ifc6c787d114a3a7add4ea54acc1cc969d6c3ca1c
Issue-ID: POLICY-2024
Signed-off-by: Jim Hahn <jrh3@att.com>
main/src/main/java/org/onap/policy/pap/main/notification/PolicyCommonTracker.java
main/src/main/java/org/onap/policy/pap/main/notification/PolicyNotifier.java
main/src/main/java/org/onap/policy/pap/main/rest/PolicyStatusControllerV1.java [new file with mode: 0644]
main/src/main/java/org/onap/policy/pap/main/startstop/PapActivator.java
main/src/test/java/org/onap/policy/pap/main/notification/PolicyCommonTrackerTest.java
main/src/test/java/org/onap/policy/pap/main/notification/PolicyNotifierTest.java
main/src/test/java/org/onap/policy/pap/main/rest/CommonPapRestServer.java
main/src/test/java/org/onap/policy/pap/main/rest/TestPolicyStatusControllerV1.java [new file with mode: 0644]
main/src/test/java/org/onap/policy/pap/main/rest/e2e/End2EndBase.java
main/src/test/java/org/onap/policy/pap/main/rest/e2e/PolicyStatusTest.java [new file with mode: 0644]
main/src/test/resources/e2e/policyStatus.json [new file with mode: 0644]

index 0404e1f..67d9b98 100644 (file)
@@ -27,8 +27,10 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Optional;
 import java.util.Set;
 import java.util.function.BiPredicate;
+import java.util.stream.Collectors;
 import org.onap.policy.models.pap.concepts.PolicyStatus;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;
 
@@ -55,6 +57,39 @@ public abstract class PolicyCommonTracker {
         super();
     }
 
+    /**
+     * Gets the status of all policies being tracked.
+     *
+     * @return the status of all policies
+     */
+    public List<PolicyStatus> getStatus() {
+        return policy2data.entrySet().stream().map(this::makeStatus).collect(Collectors.toList());
+    }
+
+    /**
+     * Gets the status of all versions of a policy.
+     *
+     * @param policyId ID of the policy of interest, without the version
+     * @return the status of all versions of the policy having the given ID
+     */
+    public List<PolicyStatus> getStatus(String policyId) {
+        // version is not specified - have to scan the whole list
+        return policy2data.entrySet().stream().filter(ent -> ent.getKey().getName().equals(policyId))
+                        .map(this::makeStatus).collect(Collectors.toList());
+    }
+
+    /**
+     * Gets the status of a particular policy.
+     *
+     * @param ident identifier of the policy of interest
+     * @return the status of the given policy, or empty if the policy is not found
+     */
+    public Optional<PolicyStatus> getStatus(ToscaPolicyIdentifier ident) {
+        ToscaPolicyIdentifier ident2 = new ToscaPolicyIdentifier(ident.getName(), ident.getVersion());
+        PolicyTrackerData data = policy2data.get(ident2);
+        return Optional.ofNullable(data == null ? null : makeStatus(ident2, data));
+    }
+
     /**
      * Adds data to the tracker.
      *
@@ -156,7 +191,7 @@ public abstract class PolicyCommonTracker {
             }
 
             // this policy is complete - notify
-            statusList.add(makeStatus(policyId, data));
+            statusList.add(makeStatus(ent));
 
             if (shouldRemove(data)) {
                 iter.remove();
@@ -186,6 +221,16 @@ public abstract class PolicyCommonTracker {
      */
     protected abstract boolean shouldRemove(PolicyTrackerData data);
 
+    /**
+     * Makes a status notification for the given policy entry.
+     *
+     * @param entry policy entry
+     * @return a new status notification
+     */
+    private PolicyStatus makeStatus(Map.Entry<ToscaPolicyIdentifier, PolicyTrackerData> entry) {
+        return makeStatus(entry.getKey(), entry.getValue());
+    }
+
     /**
      * Makes a status notification for the given policy.
      *
@@ -193,7 +238,7 @@ public abstract class PolicyCommonTracker {
      * @param data data to be used to set the status fields
      * @return a new status notification
      */
-    private synchronized PolicyStatus makeStatus(ToscaPolicyIdentifier policyId, PolicyTrackerData data) {
+    private PolicyStatus makeStatus(ToscaPolicyIdentifier policyId, PolicyTrackerData data) {
 
         PolicyStatus status = new PolicyStatus(data.getPolicyType(), policyId);
         data.putValuesInto(status);
index 97a862b..c24cafc 100644 (file)
 package org.onap.policy.pap.main.notification;
 
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
+import org.onap.policy.models.base.PfModelException;
 import org.onap.policy.models.pap.concepts.PolicyNotification;
+import org.onap.policy.models.pap.concepts.PolicyStatus;
+import org.onap.policy.models.pdp.concepts.Pdp;
+import org.onap.policy.models.pdp.concepts.PdpGroup;
+import org.onap.policy.models.pdp.concepts.PdpSubGroup;
+import org.onap.policy.models.provider.PolicyModelsProvider;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyFilter;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
+import org.onap.policy.pap.main.PolicyModelsProviderFactoryWrapper;
 import org.onap.policy.pap.main.comm.Publisher;
 import org.onap.policy.pap.main.comm.QueueToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Notifier for completion of policy updates.
  */
 public class PolicyNotifier {
+    private static final Logger logger = LoggerFactory.getLogger(PolicyNotifier.class);
+
     /**
      * Notification publisher.
      */
@@ -49,12 +68,110 @@ public class PolicyNotifier {
 
 
     /**
-     * Constructs the object.
+     * Constructs the object. Loads all deployed policies into the internal cache.
      *
      * @param publisher notification publisher
+     * @param daoFactory factory used to load policy deployment data from the DB
+     * @throws PfModelException if a DB error occurs
      */
-    public PolicyNotifier(Publisher<PolicyNotification> publisher) {
+    public PolicyNotifier(Publisher<PolicyNotification> publisher, PolicyModelsProviderFactoryWrapper daoFactory)
+                    throws PfModelException {
+
         this.publisher = publisher;
+
+        try (PolicyModelsProvider dao = daoFactory.create()) {
+            Map<ToscaPolicyIdentifier, ToscaPolicyTypeIdentifier> id2type = loadPolicyTypes(dao);
+            loadPolicies(dao, id2type);
+        }
+    }
+
+    /**
+     * Loads policy types from the DB.
+     *
+     * @param dao provider used to retrieve policies from the DB
+     * @return a mapping from policy id to policy type
+     * @throws PfModelException if a DB error occurs
+     */
+    private Map<ToscaPolicyIdentifier, ToscaPolicyTypeIdentifier> loadPolicyTypes(PolicyModelsProvider dao)
+                    throws PfModelException {
+
+        Map<ToscaPolicyIdentifier, ToscaPolicyTypeIdentifier> id2type = new HashMap<>();
+
+        for (ToscaPolicy policy : dao.getFilteredPolicyList(ToscaPolicyFilter.builder().build())) {
+            id2type.put(policy.getIdentifier(), policy.getTypeIdentifier());
+        }
+
+        return id2type;
+    }
+
+    /**
+     * Loads deployed policies.
+     *
+     * @param id2type mapping from policy id to policy type
+     * @param dao provider used to retrieve policies from the DB
+     * @throws PfModelException if a DB error occurs
+     */
+    private void loadPolicies(PolicyModelsProvider dao, Map<ToscaPolicyIdentifier, ToscaPolicyTypeIdentifier> id2type)
+                    throws PfModelException {
+        for (PdpGroup group : dao.getPdpGroups(null)) {
+            for (PdpSubGroup subgrp : group.getPdpSubgroups()) {
+                loadPolicies(id2type, group, subgrp);
+            }
+        }
+    }
+
+    /**
+     * Loads a subgroup's deployed policies.
+     *
+     * @param id2type maps a policy id to its type
+     * @param group group containing the subgroup
+     * @param subgrp subgroup whose policies are to be loaded
+     */
+    private void loadPolicies(Map<ToscaPolicyIdentifier, ToscaPolicyTypeIdentifier> id2type, PdpGroup group,
+                    PdpSubGroup subgrp) {
+
+        for (ToscaPolicyIdentifier policyId : subgrp.getPolicies()) {
+
+            ToscaPolicyTypeIdentifier type = id2type.get(policyId);
+            if (type == null) {
+                logger.error("group {}:{} refers to non-existent policy {}:{}", group.getName(), subgrp.getPdpType(),
+                                policyId.getName(), policyId.getVersion());
+                continue;
+            }
+
+            PolicyPdpNotificationData data = new PolicyPdpNotificationData(policyId, type);
+            data.addAll(subgrp.getPdpInstances().stream().map(Pdp::getInstanceId).collect(Collectors.toList()));
+            deployTracker.addData(data);
+        }
+    }
+
+    /**
+     * Gets the status of all deployed policies.
+     *
+     * @return the status of all deployed policies
+     */
+    public synchronized List<PolicyStatus> getStatus() {
+        return deployTracker.getStatus();
+    }
+
+    /**
+     * Gets the status of a particular deployed policy.
+     *
+     * @param policyId ID of the policy of interest, without the version
+     * @return the status of all deployed policies matching the given identifier
+     */
+    public synchronized List<PolicyStatus> getStatus(String policyId) {
+        return deployTracker.getStatus(policyId);
+    }
+
+    /**
+     * Gets the status of a particular deployed policy.
+     *
+     * @param ident identifier of the policy of interest
+     * @return the status of the given policy, or empty if the policy is not found
+     */
+    public synchronized Optional<PolicyStatus> getStatus(ToscaPolicyIdentifier ident) {
+        return deployTracker.getStatus(ident);
     }
 
     /**
diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/PolicyStatusControllerV1.java b/main/src/main/java/org/onap/policy/pap/main/rest/PolicyStatusControllerV1.java
new file mode 100644 (file)
index 0000000..cbacd5b
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP PAP
+ * ================================================================================
+ * Copyright (C) 2019 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.pap.main.rest;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+import io.swagger.annotations.Extension;
+import io.swagger.annotations.ExtensionProperty;
+import io.swagger.annotations.ResponseHeader;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Response;
+import org.onap.policy.common.utils.services.Registry;
+import org.onap.policy.models.pap.concepts.PolicyStatus;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;
+import org.onap.policy.pap.main.PapConstants;
+import org.onap.policy.pap.main.notification.PolicyNotifier;
+
+/**
+ * Class to provide REST end points for PAP component to retrieve the status of deployed
+ * policies.
+ */
+public class PolicyStatusControllerV1 extends PapRestControllerV1 {
+    private final PolicyNotifier notifier;
+
+    public PolicyStatusControllerV1() {
+        this.notifier = Registry.get(PapConstants.REG_POLICY_NOTIFIER, PolicyNotifier.class);
+    }
+
+    /**
+     * Queries status of all deployed policies.
+     *
+     * @param requestId request ID used in ONAP logging
+     * @return a response
+     */
+    // @formatter:off
+    @GET
+    @Path("policies/deployed")
+    @ApiOperation(value = "Queries status of all deployed policies",
+        notes = "Queries status of all deployed policies, returning success and failure counts of the PDPs",
+        responseContainer = "List", response = PolicyStatus.class,
+        tags = {"Policy Administration (PAP) API"},
+        authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+        responseHeaders = {
+            @ResponseHeader(name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
+                            response = String.class),
+            @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
+                            response = String.class),
+            @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
+                            response = String.class),
+            @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
+                            response = UUID.class)},
+        extensions = {@Extension(name = EXTENSION_NAME,
+            properties = {@ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+                @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)})})
+    @ApiResponses(value = {@ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+                    @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+                    @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)})
+    // @formatter:on
+
+    public Response queryAllDeployedPolicies(
+                    @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) final UUID requestId) {
+
+        return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId)
+                        .entity(notifier.getStatus()).build();
+    }
+
+
+    /**
+     * Queries status of specific deployed policies.
+     *
+     * @param requestId request ID used in ONAP logging
+     * @return a response
+     */
+    // @formatter:off
+    @GET
+    @Path("policies/deployed/{name}")
+    @ApiOperation(value = "Queries status of specific deployed policies",
+        notes = "Queries status of specific deployed policies, returning success and failure counts of the PDPs",
+        responseContainer = "List", response = PolicyStatus.class,
+        tags = {"Policy Administration (PAP) API"},
+        authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+        responseHeaders = {
+            @ResponseHeader(name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
+                            response = String.class),
+            @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
+                            response = String.class),
+            @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
+                            response = String.class),
+            @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
+                            response = UUID.class)},
+        extensions = {@Extension(name = EXTENSION_NAME,
+            properties = {@ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+                @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)})})
+    @ApiResponses(value = {@ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+                    @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+                    @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)})
+    // @formatter:on
+
+    public Response queryDeployedPolicies(
+                    @ApiParam(value = "Policy Id", required = true) @PathParam("name") String name,
+                    @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) final UUID requestId) {
+
+        List<PolicyStatus> result = notifier.getStatus(name);
+        if (result.isEmpty()) {
+            return makeNotFoundResponse(requestId);
+
+        } else {
+            return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId)
+                            .entity(result).build();
+        }
+    }
+
+
+    /**
+     * Queries status of a specific deployed policy.
+     *
+     * @param requestId request ID used in ONAP logging
+     * @return a response
+     */
+    // @formatter:off
+    @GET
+    @Path("policies/deployed/{name}/{version}")
+    @ApiOperation(value = "Queries status of a specific deployed policy",
+        notes = "Queries status of a specific deployed policy, returning success and failure counts of the PDPs",
+        response = PolicyStatus.class,
+        tags = {"Policy Administration (PAP) API"},
+        authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+        responseHeaders = {
+            @ResponseHeader(name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
+                            response = String.class),
+            @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
+                            response = String.class),
+            @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
+                            response = String.class),
+            @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
+                            response = UUID.class)},
+        extensions = {@Extension(name = EXTENSION_NAME,
+            properties = {@ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+                @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)})})
+    @ApiResponses(value = {@ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+                    @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+                    @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)})
+    // @formatter:on
+
+    public Response queryDeployedPolicy(@ApiParam(value = "Policy Id", required = true) @PathParam("name") String name,
+                    @ApiParam(value = "Policy Version", required = true) @PathParam("version") String version,
+                    @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) final UUID requestId) {
+
+        ToscaPolicyIdentifier ident = new ToscaPolicyIdentifier(name, version);
+        Optional<PolicyStatus> result = notifier.getStatus(ident);
+        if (result.isPresent()) {
+            return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId)
+                            .entity(result.get()).build();
+
+        } else {
+            return makeNotFoundResponse(requestId);
+        }
+    }
+
+
+    /**
+     * Makes a "not found" response.
+     *
+     * @param requestId request ID
+     * @return a "not found" response
+     */
+    private Response makeNotFoundResponse(final UUID requestId) {
+        return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.NOT_FOUND)), requestId)
+                        .build();
+    }
+}
index 8d2fd3e..1fc9784 100644 (file)
@@ -53,6 +53,7 @@ import org.onap.policy.pap.main.rest.PapStatisticsManager;
 import org.onap.policy.pap.main.rest.PdpGroupHealthCheckControllerV1;
 import org.onap.policy.pap.main.rest.PdpGroupQueryControllerV1;
 import org.onap.policy.pap.main.rest.PdpGroupStateChangeControllerV1;
+import org.onap.policy.pap.main.rest.PolicyStatusControllerV1;
 import org.onap.policy.pap.main.rest.StatisticsRestControllerV1;
 import org.onap.policy.pap.main.rest.depundep.PdpGroupDeleteControllerV1;
 import org.onap.policy.pap.main.rest.depundep.PdpGroupDeployControllerV1;
@@ -170,7 +171,7 @@ public class PapActivator extends ServiceManagerContainer {
             () -> {
                 notifyPub.set(new Publisher<>(PapConstants.TOPIC_POLICY_NOTIFICATION));
                 startThread(notifyPub.get());
-                notifier.set(new PolicyNotifier(notifyPub.get()));
+                notifier.set(new PolicyNotifier(notifyPub.get(), daoFactory.get()));
             },
             () -> notifyPub.get().stop());
 
@@ -238,7 +239,8 @@ public class PapActivator extends ServiceManagerContainer {
                                 PdpGroupDeleteControllerV1.class,
                                 PdpGroupStateChangeControllerV1.class,
                                 PdpGroupQueryControllerV1.class,
-                                PdpGroupHealthCheckControllerV1.class);
+                                PdpGroupHealthCheckControllerV1.class,
+                                PolicyStatusControllerV1.class);
                 restServer.set(server);
                 restServer.get().start();
             },
index 3318684..e8c03d1 100644 (file)
@@ -30,6 +30,9 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
 import org.junit.Before;
 import org.junit.Test;
 import org.onap.policy.models.pap.concepts.PolicyStatus;
@@ -51,6 +54,54 @@ public class PolicyCommonTrackerTest extends PolicyCommonSupport {
         tracker = new MyTracker();
     }
 
+    @Test
+    public void testGetStatus() {
+        tracker.addData(makeData(policy1, PDP1, PDP2));
+        tracker.addData(makeData(policy2, PDP2));
+
+        List<PolicyStatus> statusList = tracker.getStatus();
+        assertEquals(2, statusList.size());
+
+        Set<String> names = statusList.stream().map(PolicyStatus::getPolicyId).collect(Collectors.toSet());
+        assertTrue(names.contains(policy1.getName()));
+        assertTrue(names.contains(policy2.getName()));
+    }
+
+    @Test
+    public void testGetStatusString() {
+        tracker.addData(makeData(policy1, PDP1, PDP2));
+        tracker.addData(makeData(policy2, PDP2));
+
+        policy3 = new ToscaPolicyIdentifier(policy1.getName(), policy1.getVersion() + "0");
+        tracker.addData(makeData(policy3, PDP3));
+
+        List<PolicyStatus> statusList = tracker.getStatus(policy1.getName());
+        assertEquals(2, statusList.size());
+
+        Set<ToscaPolicyIdentifier> idents =
+                        statusList.stream().map(PolicyStatus::getPolicy).collect(Collectors.toSet());
+        assertTrue(idents.contains(policy1));
+        assertTrue(idents.contains(policy3));
+    }
+
+    @Test
+    public void testGetStatusToscaPolicyIdentifier() {
+        tracker.addData(makeData(policy1, PDP1, PDP2));
+        tracker.addData(makeData(policy2, PDP2));
+
+        policy3 = new ToscaPolicyIdentifier(policy1.getName(), policy1.getVersion() + "0");
+        tracker.addData(makeData(policy3, PDP3));
+
+        Optional<PolicyStatus> status = tracker.getStatus(policy1);
+        assertTrue(status.isPresent());
+
+        assertEquals(policy1, status.get().getPolicy());
+
+        // check not-found case
+        status = tracker.getStatus(policy4);
+        assertFalse(status.isPresent());
+    }
+
     @Test
     public void testAddData() {
         tracker.addData(makeData(policy1, PDP1, PDP2));
@@ -113,7 +164,8 @@ public class PolicyCommonTrackerTest extends PolicyCommonSupport {
     }
 
     /**
-     * Tests removeData() when the subclass indicates that the policy should NOT be removed.
+     * Tests removeData() when the subclass indicates that the policy should NOT be
+     * removed.
      */
     @Test
     public void testRemoveDataDoNotRemovePolicy() {
index 1c65dd1..8c84337 100644 (file)
 package org.onap.policy.pap.main.notification;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
 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.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Optional;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -37,9 +44,18 @@ import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.stubbing.Answer;
+import org.onap.policy.models.base.PfModelException;
 import org.onap.policy.models.pap.concepts.PolicyNotification;
 import org.onap.policy.models.pap.concepts.PolicyStatus;
+import org.onap.policy.models.pdp.concepts.Pdp;
+import org.onap.policy.models.pdp.concepts.PdpGroup;
+import org.onap.policy.models.pdp.concepts.PdpSubGroup;
+import org.onap.policy.models.provider.PolicyModelsProvider;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
+import org.onap.policy.pap.main.PolicyModelsProviderFactoryWrapper;
+import org.onap.policy.pap.main.PolicyPapRuntimeException;
 import org.onap.policy.pap.main.comm.Publisher;
 import org.onap.policy.pap.main.comm.QueueToken;
 
@@ -48,6 +64,12 @@ public class PolicyNotifierTest extends PolicyCommonSupport {
     @Mock
     private Publisher<PolicyNotification> publisher;
 
+    @Mock
+    private PolicyModelsProviderFactoryWrapper daoFactory;
+
+    @Mock
+    private PolicyModelsProvider dao;
+
     @Mock
     private PolicyDeployTracker deploy;
 
@@ -80,7 +102,123 @@ public class PolicyNotifierTest extends PolicyCommonSupport {
 
         super.setUp();
 
+        try {
+            when(daoFactory.create()).thenReturn(dao);
+            when(dao.getPdpGroups(null)).thenReturn(Collections.emptyList());
+
+            notifier = new MyNotifier(publisher);
+
+        } catch (PfModelException e) {
+            throw new PolicyPapRuntimeException(e);
+        }
+    }
+
+    @Test
+    public void testLoadPoliciesPolicyModelsProviderFactoryWrapper() throws PfModelException {
+        final PdpGroup group1 = makeGroup("my group #1", makeSubGroup("sub #1 A", 2, policy1, policy4),
+                        makeSubGroup("sub #1 B", 1, policy2));
+
+        // one policy is a duplicate
+        final PdpGroup group2 = makeGroup("my group #2", makeSubGroup("sub #2 A", 1, policy1, policy3));
+
+        when(dao.getPdpGroups(null)).thenReturn(Arrays.asList(group1, group2));
+
+        ToscaPolicyTypeIdentifier type2 = new ToscaPolicyTypeIdentifier("my other type", "8.8.8");
+
+        // note: no mapping for policy4
+        when(dao.getFilteredPolicyList(any())).thenReturn(Arrays.asList(makePolicy(policy1, type),
+                        makePolicy(policy2, type2), makePolicy(policy3, type)));
+
+        // load it
         notifier = new MyNotifier(publisher);
+
+        ArgumentCaptor<PolicyPdpNotificationData> captor = ArgumentCaptor.forClass(PolicyPdpNotificationData.class);
+
+        // should have added policy1, policy2, policy1 (duplicate), policy3, but not
+        // policy4
+        verify(deploy, times(4)).addData(captor.capture());
+
+        Iterator<PolicyPdpNotificationData> iter = captor.getAllValues().iterator();
+        PolicyPdpNotificationData data = iter.next();
+        assertEquals(policy1, data.getPolicyId());
+        assertEquals(type, data.getPolicyType());
+        assertEquals("[sub #1 A 0, sub #1 A 1]", data.getPdps().toString());
+
+        data = iter.next();
+        assertEquals(policy2, data.getPolicyId());
+        assertEquals(type2, data.getPolicyType());
+        assertEquals("[sub #1 B 0]", data.getPdps().toString());
+
+        data = iter.next();
+        assertEquals(policy1, data.getPolicyId());
+        assertEquals(type, data.getPolicyType());
+        assertEquals("[sub #2 A 0]", data.getPdps().toString());
+
+        data = iter.next();
+        assertEquals(policy3, data.getPolicyId());
+        assertEquals(type, data.getPolicyType());
+        assertEquals("[sub #2 A 0]", data.getPdps().toString());
+    }
+
+    private ToscaPolicy makePolicy(ToscaPolicyIdentifier policyId, ToscaPolicyTypeIdentifier type) {
+        ToscaPolicy policy = new ToscaPolicy();
+
+        policy.setName(policyId.getName());
+        policy.setVersion(policyId.getVersion());
+        policy.setType(type.getName());
+        policy.setTypeVersion(type.getVersion());
+
+        return policy;
+    }
+
+    private PdpGroup makeGroup(String name, PdpSubGroup... subgrps) {
+        final PdpGroup group = new PdpGroup();
+        group.setName(name);
+
+        group.setPdpSubgroups(Arrays.asList(subgrps));
+
+        return group;
+    }
+
+    private PdpSubGroup makeSubGroup(String name, int numPdps, ToscaPolicyIdentifier... policies) {
+        final PdpSubGroup subgrp = new PdpSubGroup();
+        subgrp.setPdpType(name);
+        subgrp.setPdpInstances(new ArrayList<>(numPdps));
+
+        for (int x = 0; x < numPdps; ++x) {
+            Pdp pdp = new Pdp();
+            pdp.setInstanceId(name + " " + x);
+
+            subgrp.getPdpInstances().add(pdp);
+        }
+
+        subgrp.setPolicies(Arrays.asList(policies));
+
+        return subgrp;
+    }
+
+    @Test
+    public void testGetStatus() {
+        List<PolicyStatus> statusList = Arrays.asList(status1);
+        when(deploy.getStatus()).thenReturn(statusList);
+
+        assertSame(statusList, notifier.getStatus());
+    }
+
+    @Test
+    public void testGetStatusString() {
+        List<PolicyStatus> statusList = Arrays.asList(status1);
+        when(deploy.getStatus("a policy")).thenReturn(statusList);
+
+        assertSame(statusList, notifier.getStatus("a policy"));
+    }
+
+    @Test
+    public void testGetStatusToscaPolicyIdentifier() {
+        Optional<PolicyStatus> status = Optional.of(status1);
+        when(deploy.getStatus(policy1)).thenReturn(status);
+
+        assertSame(status, notifier.getStatus(policy1));
     }
 
     @Test
@@ -161,9 +299,9 @@ public class PolicyNotifierTest extends PolicyCommonSupport {
     }
 
     @Test
-    public void testMakeDeploymentTracker_testMakeUndeploymentTracker() {
+    public void testMakeDeploymentTracker_testMakeUndeploymentTracker() throws PfModelException {
         // make real object, which will invoke the real makeXxx() methods
-        new PolicyNotifier(publisher).removePdp(PDP1);
+        new PolicyNotifier(publisher, daoFactory).removePdp(PDP1);
 
         verify(publisher, never()).enqueue(any());
     }
@@ -197,8 +335,8 @@ public class PolicyNotifierTest extends PolicyCommonSupport {
 
     private class MyNotifier extends PolicyNotifier {
 
-        public MyNotifier(Publisher<PolicyNotification> publisher) {
-            super(publisher);
+        public MyNotifier(Publisher<PolicyNotification> publisher) throws PfModelException {
+            super(publisher, daoFactory);
         }
 
         @Override
index 9124508..8660d00 100644 (file)
@@ -64,6 +64,8 @@ import org.slf4j.LoggerFactory;
  */
 public class CommonPapRestServer {
 
+    protected static final String CONFIG_FILE = "src/test/resources/parameters/TestConfigParams.json";
+
     private static final Logger LOGGER = LoggerFactory.getLogger(CommonPapRestServer.class);
 
     private static String KEYSTORE = System.getProperty("user.dir") + "/src/test/resources/ssl/policy-keystore";
@@ -88,6 +90,17 @@ public class CommonPapRestServer {
      */
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
+        setUpBeforeClass(true);
+    }
+
+    /**
+     * Allocates a port for the server, writes a config file, and then starts Main, if
+     * specified.
+     *
+     * @param shouldStart {@code true} if Main should be started, {@code false} otherwise
+     * @throws Exception if an error occurs
+     */
+    public static void setUpBeforeClass(boolean shouldStart) throws Exception {
         port = NetworkUtil.allocPort();
 
         httpsPrefix = "https://localhost:" + port + "/";
@@ -99,7 +112,9 @@ public class CommonPapRestServer {
 
         CommonTestData.newDb();
 
-        startMain();
+        if (shouldStart) {
+            startMain();
+        }
     }
 
     /**
@@ -159,7 +174,7 @@ public class CommonPapRestServer {
     private static void makeConfigFile() throws Exception {
         String json = new CommonTestData().getPapParameterGroupAsString(port);
 
-        File file = new File("src/test/resources/parameters/TestConfigParams.json");
+        File file = new File(CONFIG_FILE);
         file.deleteOnExit();
 
         try (FileOutputStream output = new FileOutputStream(file)) {
@@ -172,7 +187,7 @@ public class CommonPapRestServer {
      *
      * @throws Exception if an error occurs
      */
-    private static void startMain() throws Exception {
+    protected static void startMain() throws Exception {
         Registry.newRegistry();
 
         // make sure port is available
@@ -185,7 +200,7 @@ public class CommonPapRestServer {
         systemProps.put("javax.net.ssl.keyStorePassword", "Pol1cy_0nap");
         System.setProperties(systemProps);
 
-        final String[] papConfigParameters = { "-c", "src/test/resources/parameters/TestConfigParams.json" };
+        final String[] papConfigParameters = { "-c", CONFIG_FILE };
 
         main = new Main(papConfigParameters);
 
diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/TestPolicyStatusControllerV1.java b/main/src/test/java/org/onap/policy/pap/main/rest/TestPolicyStatusControllerV1.java
new file mode 100644 (file)
index 0000000..1f7c6d0
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ *  Modifications Copyright (C) 2019 AT&T Intellectual Property.
+ * ================================================================================
+ * 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.pap.main.rest;
+
+import static org.junit.Assert.assertEquals;
+
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.core.Response;
+import org.junit.Test;
+
+/**
+ * Note: this tests failure cases; success cases are tested by tests in the "e2e" package.
+ */
+public class TestPolicyStatusControllerV1 extends CommonPapRestServer {
+
+    private static final String POLICY_STATUS_ENDPOINT = "policies/deployed";
+
+    @Test
+    public void testSwagger() throws Exception {
+        super.testSwagger(POLICY_STATUS_ENDPOINT);
+
+        super.testSwagger(POLICY_STATUS_ENDPOINT + "/{name}");
+        super.testSwagger(POLICY_STATUS_ENDPOINT + "/{name}/{version}");
+    }
+
+    @Test
+    public void queryAllDeployedPolicies() throws Exception {
+        String uri = POLICY_STATUS_ENDPOINT;
+
+        // verify it fails when no authorization info is included
+        checkUnauthRequest(uri, req -> req.get());
+    }
+
+    @Test
+    public void testQueryDeployedPolicies() throws Exception {
+        String uri = POLICY_STATUS_ENDPOINT + "/my-name";
+
+        Invocation.Builder invocationBuilder = sendRequest(uri);
+        Response rawresp = invocationBuilder.get();
+        assertEquals(Response.Status.NOT_FOUND.getStatusCode(), rawresp.getStatus());
+
+        // verify it fails when no authorization info is included
+        checkUnauthRequest(uri, req -> req.get());
+    }
+
+    @Test
+    public void queryDeployedPolicy() throws Exception {
+        String uri = POLICY_STATUS_ENDPOINT + "/my-name/1.2.3";
+
+        Invocation.Builder invocationBuilder = sendRequest(uri);
+        Response rawresp = invocationBuilder.get();
+        assertEquals(Response.Status.NOT_FOUND.getStatusCode(), rawresp.getStatus());
+
+        // verify it fails when no authorization info is included
+        checkUnauthRequest(uri, req -> req.get());
+    }
+}
index 7e21742..10c791e 100644 (file)
@@ -32,14 +32,13 @@ import org.onap.policy.common.utils.coder.Coder;
 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.services.Registry;
 import org.onap.policy.models.base.PfModelException;
 import org.onap.policy.models.pdp.concepts.PdpGroups;
 import org.onap.policy.models.provider.PolicyModelsProvider;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
-import org.onap.policy.pap.main.PapConstants;
 import org.onap.policy.pap.main.PolicyModelsProviderFactoryWrapper;
 import org.onap.policy.pap.main.PolicyPapRuntimeException;
+import org.onap.policy.pap.main.parameters.PapParameterGroup;
 import org.onap.policy.pap.main.rest.CommonPapRestServer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -75,15 +74,21 @@ public class End2EndBase extends CommonPapRestServer {
      */
     @BeforeClass
     public static void setUpBeforeClass() throws Exception {
-        CommonPapRestServer.setUpBeforeClass();
+        setUpBeforeClass(true);
+    }
 
-        daoFactory = Registry.get(PapConstants.REG_PAP_DAO_FACTORY, PolicyModelsProviderFactoryWrapper.class);
+    /**
+     * Starts Main, if specified, and connects to the DB.
+     *
+     * @param shouldStart {@code true} if Main should be started, {@code false} otherwise
+     * @throws Exception if an error occurs
+     */
+    public static void setUpBeforeClass(boolean shouldStart) throws Exception {
+        CommonPapRestServer.setUpBeforeClass(shouldStart);
 
-        try {
-            dbConn = daoFactory.create();
-        } catch (PfModelException e) {
-            throw new PolicyPapRuntimeException("cannot connect to DB", e);
-        }
+        PapParameterGroup params = new StandardCoder().decode(new File(CONFIG_FILE), PapParameterGroup.class);
+        daoFactory = new PolicyModelsProviderFactoryWrapper(params.getDatabaseProviderParameters());
+        dbConn = daoFactory.create();
     }
 
     /**
diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/e2e/PolicyStatusTest.java b/main/src/test/java/org/onap/policy/pap/main/rest/e2e/PolicyStatusTest.java
new file mode 100644 (file)
index 0000000..afabb89
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP PAP
+ * ================================================================================
+ * Copyright (C) 2019 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.pap.main.rest.e2e;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.policy.models.pap.concepts.PolicyStatus;
+
+public class PolicyStatusTest extends End2EndBase {
+    private static final String POLICY_STATUS_ENDPOINT = "policies/deployed";
+
+    /**
+     * Starts Main and adds policies to the DB.
+     *
+     * @throws Exception if an error occurs
+     */
+    @BeforeClass
+    public static void setUpBeforeClass() throws Exception {
+        // don't start Main until AFTER we add the policies to the DB
+        End2EndBase.setUpBeforeClass(false);
+
+        addToscaPolicyTypes("monitoring.policy-type.yaml");
+        addToscaPolicies("monitoring.policy.yaml");
+        addGroups("policyStatus.json");
+
+        startMain();
+    }
+
+    @Test
+    public void testQueryAllDeployedPolicies() throws Exception {
+        String uri = POLICY_STATUS_ENDPOINT;
+
+        Invocation.Builder invocationBuilder = sendRequest(uri);
+        Response rawresp = invocationBuilder.get();
+        assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus());
+
+        List<PolicyStatus> resp = rawresp.readEntity(new GenericType<List<PolicyStatus>>() {});
+        assertEquals(1, resp.size());
+
+        PolicyStatus status = resp.get(0);
+        assertEquals("onap.restart.tca", status.getPolicyId());
+        assertEquals("1.0.0", status.getPolicyVersion());
+        assertEquals("onap.policies.monitoring.cdap.tca.hi.lo.app", status.getPolicyTypeId());
+        assertEquals("1.0.0", status.getPolicyTypeVersion());
+        assertEquals(0, status.getFailureCount());
+        assertEquals(1, status.getIncompleteCount());
+        assertEquals(0, status.getSuccessCount());
+    }
+
+    @Test
+    public void testQueryDeployedPolicies() throws Exception {
+        String uri = POLICY_STATUS_ENDPOINT + "/onap.restart.tca";
+
+        Invocation.Builder invocationBuilder = sendRequest(uri);
+        Response rawresp = invocationBuilder.get();
+        assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus());
+
+        List<PolicyStatus> resp = rawresp.readEntity(new GenericType<List<PolicyStatus>>() {});
+        assertEquals(1, resp.size());
+
+        PolicyStatus status = resp.get(0);
+        assertEquals("onap.restart.tca", status.getPolicyId());
+        assertEquals("1.0.0", status.getPolicyVersion());
+        assertEquals("onap.policies.monitoring.cdap.tca.hi.lo.app", status.getPolicyTypeId());
+        assertEquals("1.0.0", status.getPolicyTypeVersion());
+        assertEquals(0, status.getFailureCount());
+        assertEquals(1, status.getIncompleteCount());
+        assertEquals(0, status.getSuccessCount());
+    }
+
+    @Test
+    public void testQueryDeployedPolicy() throws Exception {
+        String uri = POLICY_STATUS_ENDPOINT + "/onap.restart.tca/1.0.0";
+
+        Invocation.Builder invocationBuilder = sendRequest(uri);
+        Response rawresp = invocationBuilder.get();
+        assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus());
+
+        PolicyStatus status = rawresp.readEntity(PolicyStatus.class);
+        assertEquals("onap.restart.tca", status.getPolicyId());
+        assertEquals("1.0.0", status.getPolicyVersion());
+        assertEquals("onap.policies.monitoring.cdap.tca.hi.lo.app", status.getPolicyTypeId());
+        assertEquals("1.0.0", status.getPolicyTypeVersion());
+        assertEquals(0, status.getFailureCount());
+        assertEquals(1, status.getIncompleteCount());
+        assertEquals(0, status.getSuccessCount());
+    }
+}
diff --git a/main/src/test/resources/e2e/policyStatus.json b/main/src/test/resources/e2e/policyStatus.json
new file mode 100644 (file)
index 0000000..973e2c0
--- /dev/null
@@ -0,0 +1,56 @@
+{
+    "groups": [
+        {
+            "name": "policyStatus",
+            "pdpGroupState": "ACTIVE",
+            "pdpSubgroups": [
+                {
+                    "pdpType": "pdpTypeA",
+                    "desiredInstanceCount": 2,
+                    "pdpInstances": [
+                        {
+                            "instanceId": "pdpA_1",
+                            "pdpState": "ACTIVE",
+                            "healthy": "HEALTHY"
+                        },
+                        {
+                            "instanceId": "pdpA_2",
+                            "pdpState": "ACTIVE",
+                            "healthy": "HEALTHY"
+                        }
+                    ],
+                    "supportedPolicyTypes": [
+                        {
+                            "name": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+                            "version": "1.0.0"
+                        }
+                    ],
+                    "policies": []
+                },
+                {
+                    "pdpType": "pdpTypeB",
+                    "desiredInstanceCount": 1,
+                    "pdpInstances": [
+                        {
+                            "instanceId": "pdpB_1",
+                            "pdpState": "ACTIVE",
+                            "healthy": "HEALTHY"
+                        }
+                    ],
+                    "supportedPolicyTypes": [
+                        {
+                            "name": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+                            "version": "1.0.0"
+                        }
+                    ],
+                    "policies": [
+                        {
+                            "name": "onap.restart.tca",
+                            "version": "1.0.0"
+                        }
+                    ]
+                }
+            ]
+        }
+    ]
+}