diff options
11 files changed, 840 insertions, 24 deletions
diff --git a/main/src/main/java/org/onap/policy/pap/main/notification/PolicyCommonTracker.java b/main/src/main/java/org/onap/policy/pap/main/notification/PolicyCommonTracker.java index 0404e1fe..67d9b988 100644 --- a/main/src/main/java/org/onap/policy/pap/main/notification/PolicyCommonTracker.java +++ b/main/src/main/java/org/onap/policy/pap/main/notification/PolicyCommonTracker.java @@ -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; @@ -56,6 +58,39 @@ public abstract class PolicyCommonTracker { } /** + * 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. * * @param data data to be added 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(); @@ -187,13 +222,23 @@ 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. * * @param policyId policy ID * @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); diff --git a/main/src/main/java/org/onap/policy/pap/main/notification/PolicyNotifier.java b/main/src/main/java/org/onap/policy/pap/main/notification/PolicyNotifier.java index 97a862be..c24cafca 100644 --- a/main/src/main/java/org/onap/policy/pap/main/notification/PolicyNotifier.java +++ b/main/src/main/java/org/onap/policy/pap/main/notification/PolicyNotifier.java @@ -21,17 +21,36 @@ 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 index 00000000..cbacd5bf --- /dev/null +++ b/main/src/main/java/org/onap/policy/pap/main/rest/PolicyStatusControllerV1.java @@ -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(); + } +} diff --git a/main/src/main/java/org/onap/policy/pap/main/startstop/PapActivator.java b/main/src/main/java/org/onap/policy/pap/main/startstop/PapActivator.java index 8d2fd3ea..1fc9784e 100644 --- a/main/src/main/java/org/onap/policy/pap/main/startstop/PapActivator.java +++ b/main/src/main/java/org/onap/policy/pap/main/startstop/PapActivator.java @@ -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(); }, diff --git a/main/src/test/java/org/onap/policy/pap/main/notification/PolicyCommonTrackerTest.java b/main/src/test/java/org/onap/policy/pap/main/notification/PolicyCommonTrackerTest.java index 3318684c..e8c03d1d 100644 --- a/main/src/test/java/org/onap/policy/pap/main/notification/PolicyCommonTrackerTest.java +++ b/main/src/test/java/org/onap/policy/pap/main/notification/PolicyCommonTrackerTest.java @@ -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; @@ -52,6 +55,54 @@ public class PolicyCommonTrackerTest extends PolicyCommonSupport { } @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)); assertEquals(1, map.size()); @@ -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() { diff --git a/main/src/test/java/org/onap/policy/pap/main/notification/PolicyNotifierTest.java b/main/src/test/java/org/onap/policy/pap/main/notification/PolicyNotifierTest.java index 1c65dd10..8c84337a 100644 --- a/main/src/test/java/org/onap/policy/pap/main/notification/PolicyNotifierTest.java +++ b/main/src/test/java/org/onap/policy/pap/main/notification/PolicyNotifierTest.java @@ -21,15 +21,22 @@ 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; @@ -49,6 +65,12 @@ public class PolicyNotifierTest extends PolicyCommonSupport { private Publisher<PolicyNotification> publisher; @Mock + private PolicyModelsProviderFactoryWrapper daoFactory; + + @Mock + private PolicyModelsProvider dao; + + @Mock private PolicyDeployTracker deploy; @Mock @@ -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 diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/CommonPapRestServer.java b/main/src/test/java/org/onap/policy/pap/main/rest/CommonPapRestServer.java index 91245084..8660d005 100644 --- a/main/src/test/java/org/onap/policy/pap/main/rest/CommonPapRestServer.java +++ b/main/src/test/java/org/onap/policy/pap/main/rest/CommonPapRestServer.java @@ -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 index 00000000..1f7c6d0f --- /dev/null +++ b/main/src/test/java/org/onap/policy/pap/main/rest/TestPolicyStatusControllerV1.java @@ -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()); + } +} diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/e2e/End2EndBase.java b/main/src/test/java/org/onap/policy/pap/main/rest/e2e/End2EndBase.java index 7e217423..10c791ea 100644 --- a/main/src/test/java/org/onap/policy/pap/main/rest/e2e/End2EndBase.java +++ b/main/src/test/java/org/onap/policy/pap/main/rest/e2e/End2EndBase.java @@ -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 index 00000000..afabb892 --- /dev/null +++ b/main/src/test/java/org/onap/policy/pap/main/rest/e2e/PolicyStatusTest.java @@ -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 index 00000000..973e2c0e --- /dev/null +++ b/main/src/test/resources/e2e/policyStatus.json @@ -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" + } + ] + } + ] + } + ] +} |