diff options
46 files changed, 4370 insertions, 271 deletions
diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml index dd4576ec02..8ca6b0fe02 100644 --- a/cps-application/src/main/resources/application.yml +++ b/cps-application/src/main/resources/application.yml @@ -189,10 +189,10 @@ logging: cps: INFO ncmp: policy-executor: - enabled: true + enabled: false server: - address: "http://localhost" - port: "8785" + address: http://localhost + port: 8785 httpclient: all-services: maximumInMemorySizeInMegabytes: 16 diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml index a9311d0861..112dddf61c 100644 --- a/cps-ncmp-rest/docs/openapi/components.yaml +++ b/cps-ncmp-rest/docs/openapi/components.yaml @@ -505,6 +505,14 @@ components: schema: type: string example: my-cm-handle + cmHandleReferenceInPath: + name: cm-handle + in: path + description: The identifier (cmHandle or alternate) for a network function, network element, subnetwork or any other cm object by managed Network CM Proxy + required: true + schema: + type: string + example: my-cm-handle-reference moduleNameInQuery: name: module-name in: query diff --git a/cps-ncmp-rest/docs/openapi/ncmp.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml index 6972463e3c..9a6c076f03 100755 --- a/cps-ncmp-rest/docs/openapi/ncmp.yml +++ b/cps-ncmp-rest/docs/openapi/ncmp.yml @@ -27,7 +27,7 @@ resourceDataForCmHandle: operationId: getResourceDataForCmHandle parameters: - $ref: 'components.yaml#/components/parameters/datastoreName' - - $ref: 'components.yaml#/components/parameters/cmHandleInPath' + - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - $ref: 'components.yaml#/components/parameters/optionsParamInQuery' - $ref: 'components.yaml#/components/parameters/topicParamInQuery' @@ -60,7 +60,7 @@ resourceDataForCmHandle: operationId: createResourceDataRunningForCmHandle parameters: - $ref: 'components.yaml#/components/parameters/datastoreName' - - $ref: 'components.yaml#/components/parameters/cmHandleInPath' + - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - $ref: 'components.yaml#/components/parameters/contentParamInHeader' - $ref: 'components.yaml#/components/parameters/authorizationParamInHeader' @@ -99,7 +99,7 @@ resourceDataForCmHandle: operationId: updateResourceDataRunningForCmHandle parameters: - $ref: 'components.yaml#/components/parameters/datastoreName' - - $ref: 'components.yaml#/components/parameters/cmHandleInPath' + - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - $ref: 'components.yaml#/components/parameters/contentParamInHeader' - $ref: 'components.yaml#/components/parameters/authorizationParamInHeader' @@ -138,7 +138,7 @@ resourceDataForCmHandle: operationId: patchResourceDataRunningForCmHandle parameters: - $ref: 'components.yaml#/components/parameters/datastoreName' - - $ref: 'components.yaml#/components/parameters/cmHandleInPath' + - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - $ref: 'components.yaml#/components/parameters/contentParamInHeader' - $ref: 'components.yaml#/components/parameters/authorizationParamInHeader' @@ -171,7 +171,7 @@ resourceDataForCmHandle: operationId: deleteResourceDataRunningForCmHandle parameters: - $ref: 'components.yaml#/components/parameters/datastoreName' - - $ref: 'components.yaml#/components/parameters/cmHandleInPath' + - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - $ref: 'components.yaml#/components/parameters/contentParamInHeader' - $ref: 'components.yaml#/components/parameters/authorizationParamInHeader' @@ -377,7 +377,7 @@ getCmHandlePropertiesById: summary: Get CM handle properties operationId: getCmHandlePublicPropertiesByCmHandleId parameters: - - $ref: 'components.yaml#/components/parameters/cmHandleInPath' + - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath' responses: 200: description: OK @@ -400,7 +400,7 @@ getCmHandleStateById: summary: Get CM handle state operationId: getCmHandleStateByCmHandleId parameters: - - $ref: 'components.yaml#/components/parameters/cmHandleInPath' + - $ref: 'components.yaml#/components/parameters/cmHandleReferenceInPath' responses: 200: description: OK diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java index 6a2e9a1541..ca2907b8d6 100755 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java @@ -148,7 +148,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { * Patch resource data. * * @param datastoreName name of the datastore (currently only supports "ncmp-datastore:passthrough-running") - * @param cmHandleReference cm handle or alternate identifier + * @param cmHandleReference cm handle or alternate identifier * @param resourceIdentifier resource identifier * @param requestBody the request body * @param contentType content type of body @@ -177,7 +177,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { * Create resource data for given cm-handle. * * @param datastoreName name of the datastore (currently only supports "ncmp-datastore:passthrough-running") - * @param cmHandleReference cm handle or alternate identifier + * @param cmHandleReference cm handle or alternate identifier * @param resourceIdentifier resource identifier * @param requestBody the request body * @param contentType content type of body @@ -202,7 +202,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { * Update resource data for given cm-handle. * * @param datastoreName name of the datastore (currently only supports "ncmp-datastore:passthrough-running") - * @param cmHandleReference cm handle or alternate identifier + * @param cmHandleReference cm handle or alternate identifier * @param resourceIdentifier resource identifier * @param requestBody the request body * @param contentType content type of the body @@ -228,7 +228,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { * Delete resource data for a given cm-handle. * * @param datastoreName name of the datastore (currently only supports "ncmp-datastore:passthrough-running") - * @param cmHandleReference cm handle or alternate identifier + * @param cmHandleReference cm handle or alternate identifier * @param resourceIdentifier resource identifier * @param contentType content type of the body * @param authorization contents of Authorization header, or null if not present @@ -298,16 +298,16 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { } /** - * Get Cm Handle Properties by Cm Handle Id. + * Get Cm Handle Properties by Cm Handle or alternate Identifier. * - * @param cmHandleId cm-handle identifier + * @param cmHandleReference cm-handle or alternate identifier * @return cm handle properties */ @Override public ResponseEntity<RestOutputCmHandlePublicProperties> getCmHandlePublicPropertiesByCmHandleId( - final String cmHandleId) { + final String cmHandleReference) { final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties(); - cmHandlePublicProperties.add(networkCmProxyInventoryFacade.getCmHandlePublicProperties(cmHandleId)); + cmHandlePublicProperties.add(networkCmProxyInventoryFacade.getCmHandlePublicProperties(cmHandleReference)); final RestOutputCmHandlePublicProperties restOutputCmHandlePublicProperties = new RestOutputCmHandlePublicProperties(); restOutputCmHandlePublicProperties.setPublicCmHandleProperties(cmHandlePublicProperties); diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandler.java index d61d30dc2b..6910003c54 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandler.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandler.java @@ -31,6 +31,7 @@ import org.onap.cps.ncmp.api.exceptions.DmiRequestException; import org.onap.cps.ncmp.api.exceptions.InvalidTopicException; import org.onap.cps.ncmp.api.exceptions.NcmpException; import org.onap.cps.ncmp.api.exceptions.PayloadTooLargeException; +import org.onap.cps.ncmp.api.exceptions.PolicyExecutorException; import org.onap.cps.ncmp.api.exceptions.ServerNcmpException; import org.onap.cps.ncmp.rest.model.DmiErrorMessage; import org.onap.cps.ncmp.rest.model.DmiErrorMessageDmiResponse; @@ -84,8 +85,8 @@ public class NetworkCmProxyRestExceptionHandler { return buildErrorResponse(HttpStatus.BAD_REQUEST, exception); } - @ExceptionHandler({AlreadyDefinedException.class}) - public static ResponseEntity<Object> handleAlreadyDefinedExceptions(final Exception exception) { + @ExceptionHandler({AlreadyDefinedException.class, PolicyExecutorException.class}) + public static ResponseEntity<Object> handleConflictExceptions(final Exception exception) { return buildErrorResponse(HttpStatus.CONFLICT, exception); } @@ -113,8 +114,6 @@ public class NetworkCmProxyRestExceptionHandler { } else { errorMessage.setDetails(CHECK_LOGS_FOR_DETAILS); } - errorMessage.setDetails( - exception instanceof CpsException ? ((CpsException) exception).getDetails() : CHECK_LOGS_FOR_DETAILS); return new ResponseEntity<>(errorMessage, status); } diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy index 92b47c6c8b..80e74caf75 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy @@ -308,13 +308,13 @@ class NetworkCmProxyControllerSpec extends Specification { assert !response.contentAsString.contains("some DMI property") } - def 'Get Cm Handle public properties by Cm Handle id.'() { + def 'Get Cm Handle public properties by Cm Handle Reference.'() { given: 'a cm handle properties endpoint' - def cmHandlePropertiesEndpoint = "$ncmpBasePathV1/ch/some-cm-handle/properties" + def cmHandlePropertiesEndpoint = "$ncmpBasePathV1/ch/some-cm-handle-reference/properties" and: 'some cm handle public properties' def publicProperties = ['public prop': 'some public property'] and: 'the service method is invoked with the cm handle id returning the cm handle public properties' - 1 * mockNetworkCmProxyInventoryFacade.getCmHandlePublicProperties('some-cm-handle') >> publicProperties + 1 * mockNetworkCmProxyInventoryFacade.getCmHandlePublicProperties('some-cm-handle-reference') >> publicProperties when: 'the cm handle properties api is invoked' def response = mvc.perform(get(cmHandlePropertiesEndpoint)).andReturn().response then: 'the correct response is returned' diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy index e6288ffbec..9d36d106c7 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy @@ -29,11 +29,13 @@ import org.onap.cps.ncmp.api.data.exceptions.OperationNotSupportedException import org.onap.cps.ncmp.api.exceptions.DmiClientRequestException import org.onap.cps.ncmp.api.exceptions.DmiRequestException import org.onap.cps.ncmp.api.exceptions.PayloadTooLargeException +import org.onap.cps.ncmp.api.exceptions.PolicyExecutorException import org.onap.cps.ncmp.api.exceptions.ServerNcmpException import org.onap.cps.ncmp.api.inventory.NetworkCmProxyInventoryFacade import org.onap.cps.ncmp.impl.data.NcmpCachedResourceRequestHandler import org.onap.cps.ncmp.impl.data.NcmpPassthroughResourceRequestHandler import org.onap.cps.ncmp.impl.data.NetworkCmProxyFacade +import org.onap.cps.ncmp.impl.data.policyexecutor.PolicyExecutor import org.onap.cps.ncmp.impl.inventory.InventoryPersistence import org.onap.cps.ncmp.rest.util.CmHandleStateMapper import org.onap.cps.ncmp.rest.util.DataOperationRequestMapper @@ -120,25 +122,26 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification { dataNodeBaseEndpointNcmpInventory = "$basePathNcmpInventory/v1" } - def 'Get request with #scenario exception returns correct HTTP Status with #scenario'() { + def 'Get request with #scenario exception returns correct HTTP Status with #scenario exception'() { when: 'an exception is thrown by the service' setupTestException(exception, NCMP) def response = performTestRequest(NCMP) then: 'an HTTP response is returned with correct message and details' assertTestResponse(response, expectedErrorCode, expectedErrorMessage, expectedErrorDetails) where: - scenario | exception || expectedErrorCode | expectedErrorMessage | expectedErrorDetails - 'CPS' | new CpsException(sampleErrorMessage, sampleErrorDetails) || INTERNAL_SERVER_ERROR | sampleErrorMessage | sampleErrorDetails - 'NCMP-server' | new ServerNcmpException(sampleErrorMessage, sampleErrorDetails) || INTERNAL_SERVER_ERROR | sampleErrorMessage | null - 'DMI Request' | new DmiRequestException(sampleErrorMessage, sampleErrorDetails) || BAD_REQUEST | sampleErrorMessage | null - 'Invalid Operation' | new InvalidOperationException('some reason') || BAD_REQUEST | 'some reason' | null - 'Unsupported Operation' | new OperationNotSupportedException('not yet') || BAD_REQUEST | 'not yet' | null - 'DataNode Validation' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || NOT_FOUND | 'DataNode not found' | null - 'other' | new IllegalStateException(sampleErrorMessage) || INTERNAL_SERVER_ERROR | sampleErrorMessage | null - 'Data Node Not Found' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || NOT_FOUND | 'DataNode not found' | 'DataNode not found' - 'Existing entry' | new AlreadyDefinedException('name',null) || CONFLICT | 'Already defined exception' | 'name already exists' - 'Existing entries' | AlreadyDefinedException.forDataNodes(['A', 'B'], 'myAnchorName') || CONFLICT | 'Already defined exception' | '2 data node(s) already exist' - 'Operation too large' | new PayloadTooLargeException(sampleErrorMessage) || PAYLOAD_TOO_LARGE | sampleErrorMessage | 'Check logs' + scenario | exception || expectedErrorCode | expectedErrorMessage | expectedErrorDetails + 'CPS' | new CpsException(sampleErrorMessage, sampleErrorDetails) || INTERNAL_SERVER_ERROR | sampleErrorMessage | sampleErrorDetails + 'NCMP-server' | new ServerNcmpException(sampleErrorMessage, sampleErrorDetails) || INTERNAL_SERVER_ERROR | sampleErrorMessage | null + 'DMI Request' | new DmiRequestException(sampleErrorMessage, sampleErrorDetails) || BAD_REQUEST | sampleErrorMessage | null + 'Invalid Operation' | new InvalidOperationException('some reason') || BAD_REQUEST | 'some reason' | null + 'Unsupported Operation' | new OperationNotSupportedException('not yet') || BAD_REQUEST | 'not yet' | null + 'DataNode Validation' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || NOT_FOUND | 'DataNode not found' | null + 'other' | new IllegalStateException(sampleErrorMessage) || INTERNAL_SERVER_ERROR | sampleErrorMessage | null + 'Data Node Not Found' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || NOT_FOUND | 'DataNode not found' | 'DataNode not found' + 'Existing entry' | new AlreadyDefinedException('name',null) || CONFLICT | 'Already defined exception' | 'name already exists' + 'Existing entries' | AlreadyDefinedException.forDataNodes(['A', 'B'], 'myAnchorName') || CONFLICT | 'Already defined exception' | '2 data node(s) already exist' + 'Operation too large' | new PayloadTooLargeException(sampleErrorMessage) || PAYLOAD_TOO_LARGE | sampleErrorMessage | 'Check logs' + 'Policy Executor' | new PolicyExecutorException(sampleErrorMessage, sampleErrorDetails) || CONFLICT | sampleErrorMessage | sampleErrorDetails } def 'Post request with exception returns correct HTTP Status.'() { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/PolicyExecutorException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/PolicyExecutorException.java new file mode 100644 index 0000000000..333c12271b --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/PolicyExecutorException.java @@ -0,0 +1,42 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * 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.cps.ncmp.api.exceptions; + +import lombok.Getter; + +/** + * Exception to be used when policy execution fails or does not allow to proceed. + */ +@Getter +public class PolicyExecutorException extends NcmpException { + + private static final long serialVersionUID = 6659897770659834798L; + + /** + * Constructor to form exception for policy executor responses. + * + * @param message response message + * @param details response details + */ + public PolicyExecutorException(final String message, final String details) { + super(message, details); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java index 1acd937a31..785eb8f022 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java @@ -26,6 +26,7 @@ package org.onap.cps.ncmp.api.inventory; import static org.onap.cps.ncmp.impl.inventory.CmHandleQueryParametersValidator.validateCmHandleQueryParameters; +import java.util.ArrayList; import java.util.Collection; import java.util.Map; import lombok.RequiredArgsConstructor; @@ -44,6 +45,7 @@ import org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions; import org.onap.cps.ncmp.impl.inventory.models.InventoryQueryConditions; import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelManager; +import org.onap.cps.ncmp.impl.models.RequiredDmiService; import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher; import org.onap.cps.ncmp.impl.utils.YangDataConverter; import org.onap.cps.spi.model.ModuleDefinition; @@ -143,9 +145,13 @@ public class NetworkCmProxyInventoryFacade { final CmHandleQueryServiceParameters cmHandleQueryServiceParameters = jsonObjectMapper.convertToValueType( cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class); validateCmHandleQueryParameters(cmHandleQueryServiceParameters, CmHandleQueryConditions.ALL_CONDITION_NAMES); - final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles = + final Collection<YangModelCmHandle> yangModelCmHandles = parameterizedCmHandleQueryService.queryCmHandles(cmHandleQueryServiceParameters); - ncmpServiceCmHandles.forEach(this::applyCurrentTrustLevel); + final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles = new ArrayList<>(yangModelCmHandles.size()); + for (final YangModelCmHandle yangModelCmHandle : yangModelCmHandles) { + final NcmpServiceCmHandle ncmpServiceCmHandle = toNcmpServiceCmHandleWithTrustLevel(yangModelCmHandle); + ncmpServiceCmHandles.add(ncmpServiceCmHandle); + } return ncmpServiceCmHandles; } @@ -180,19 +186,18 @@ public class NetworkCmProxyInventoryFacade { * @return cm handle details */ public NcmpServiceCmHandle getNcmpServiceCmHandle(final String cmHandleId) { - final NcmpServiceCmHandle ncmpServiceCmHandle = YangDataConverter.toNcmpServiceCmHandle( - inventoryPersistence.getYangModelCmHandle(cmHandleId)); - applyCurrentTrustLevel(ncmpServiceCmHandle); - return ncmpServiceCmHandle; + final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); + return toNcmpServiceCmHandleWithTrustLevel(yangModelCmHandle); } /** - * Get cm handle public properties for a given cm handle id. + * Get cm handle public properties for a given cm handle or alternate id. * - * @param cmHandleId cm handle identifier + * @param cmHandleReference cm handle or alternate identifier * @return cm handle public properties */ - public Map<String, String> getCmHandlePublicProperties(final String cmHandleId) { + public Map<String, String> getCmHandlePublicProperties(final String cmHandleReference) { + final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference); final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); return YangDataConverter.toPropertiesMap(yangModelCmHandle.getPublicProperties()); } @@ -208,10 +213,12 @@ public class NetworkCmProxyInventoryFacade { return inventoryPersistence.getYangModelCmHandle(cmHandleId).getCompositeState(); } - private void applyCurrentTrustLevel(final NcmpServiceCmHandle ncmpServiceCmHandle) { - ncmpServiceCmHandle.setCurrentTrustLevel(trustLevelManager - .getEffectiveTrustLevel(ncmpServiceCmHandle.getCmHandleId())); + private NcmpServiceCmHandle toNcmpServiceCmHandleWithTrustLevel(final YangModelCmHandle yangModelCmHandle) { + final NcmpServiceCmHandle ncmpServiceCmHandle = YangDataConverter.toNcmpServiceCmHandle(yangModelCmHandle); + final String dmiServiceName = yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA); + ncmpServiceCmHandle.setCurrentTrustLevel( + trustLevelManager.getEffectiveTrustLevel(dmiServiceName, ncmpServiceCmHandle.getCmHandleId())); + return ncmpServiceCmHandle; } - } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java index 6b813a6f69..301b8195e4 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java @@ -139,8 +139,7 @@ public class DmiDataOperations { final String requestId, final String authorization) { - final Set<String> cmHandlesIds - = getDistinctCmHandleIds(dataOperationRequest); + final Set<String> cmHandlesIds = getDistinctCmHandleIds(dataOperationRequest); final Collection<YangModelCmHandle> yangModelCmHandles = inventoryPersistence.getYangModelCmHandles(cmHandlesIds); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java index 8e7620ccea..b3aa848394 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java @@ -20,12 +20,29 @@ package org.onap.cps.ncmp.impl.data.policyexecutor; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.data.models.OperationType; +import org.onap.cps.ncmp.api.exceptions.NcmpException; +import org.onap.cps.ncmp.api.exceptions.PolicyExecutorException; +import org.onap.cps.ncmp.api.exceptions.ServerNcmpException; import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder; +import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; @Slf4j @Service @@ -41,7 +58,10 @@ public class PolicyExecutor { @Value("${ncmp.policy-executor.server.port:8080}") private String serverPort; - private static final String PAYLOAD_TYPE_PREFIX = "cm_"; + @Qualifier("policyExecutorWebClient") + private final WebClient policyExecutorWebClient; + + private final ObjectMapper objectMapper; /** * Use the Policy Executor to check permission for a cm write operation. @@ -58,17 +78,108 @@ public class PolicyExecutor { final String authorization, final String resourceIdentifier, final String changeRequestAsJson) { + log.trace("Policy Executor Enabled: {}", enabled); if (enabled) { - final String payloadType = PAYLOAD_TYPE_PREFIX + operationType.getOperationName(); - log.info("Policy Executor Enabled"); - log.info("Address : {}", serverAddress); - log.info("Port : {}", serverPort); - log.info("Authorization : {}", authorization); - log.info("Payload Type : {}", payloadType); - log.info("Target FDN : {}", yangModelCmHandle.getAlternateId()); - log.info("CM Handle Id : {}", yangModelCmHandle.getId()); - log.info("Resource Identifier : {}", resourceIdentifier); - log.info("Change Request (json) : {}", changeRequestAsJson); + final ResponseEntity<JsonNode> responseEntity = + getPolicyExecutorResponse(yangModelCmHandle, operationType, authorization, resourceIdentifier, + changeRequestAsJson); + + if (responseEntity == null) { + log.warn("No valid response from policy, ignored"); + return; + } + + if (responseEntity.getStatusCode().is2xxSuccessful()) { + if (responseEntity.getBody() == null) { + log.warn("No valid response body from policy, ignored"); + return; + } + processResponse(responseEntity.getBody()); + } else { + log.warn("Policy Executor invocation failed with status {}", + responseEntity.getStatusCode().value()); + throw new ServerNcmpException("Policy Executor invocation failed", "HTTP status code: " + + responseEntity.getStatusCode().value()); + } + } + } + + private Map<String, Object> getSingleRequestAsMap(final YangModelCmHandle yangModelCmHandle, + final OperationType operationType, + final String resourceIdentifier, + final String changeRequestAsJson) { + final Map<String, Object> data = new HashMap<>(4); + data.put("cmHandleId", yangModelCmHandle.getId()); + data.put("resourceIdentifier", resourceIdentifier); + data.put("targetIdentifier", yangModelCmHandle.getAlternateId()); + if (!OperationType.DELETE.equals(operationType)) { + try { + final Object changeRequestAsObject = objectMapper.readValue(changeRequestAsJson, Object.class); + data.put("cmChangeRequest", changeRequestAsObject); + } catch (final JsonProcessingException e) { + throw new NcmpException("Cannot convert Change Request data to Object", + "Invalid Json: " + changeRequestAsJson); + } } + + final Map<String, Object> request = new HashMap<>(2); + request.put("schema", getAssociatedPolicyDataSchemaName(operationType)); + request.put("data", data); + return request; + } + + private static String getAssociatedPolicyDataSchemaName(final OperationType operationType) { + return "urn:cps:org.onap.cps.ncmp.policy-executor.ncmp-" + operationType.getOperationName() + "-schema:1.0.0"; + } + + private Object createBodyAsObject(final List<Object> requests) { + final Map<String, Object> bodyAsMap = new HashMap<>(2); + bodyAsMap.put("decisionType", "allow"); + bodyAsMap.put("requests", requests); + return bodyAsMap; } + + private ResponseEntity<JsonNode> getPolicyExecutorResponse(final YangModelCmHandle yangModelCmHandle, + final OperationType operationType, + final String authorization, + final String resourceIdentifier, + final String changeRequestAsJson) { + final String serviceBaseUrl = serverAddress + ":" + serverPort; + + final Map<String, Object> requestAsMap = getSingleRequestAsMap(yangModelCmHandle, + operationType, + resourceIdentifier, + changeRequestAsJson); + + final Object bodyAsObject = createBodyAsObject(Collections.singletonList(requestAsMap)); + + final UrlTemplateParameters urlTemplateParameters = RestServiceUrlTemplateBuilder.newInstance() + .fixedPathSegment("execute") + .createUrlTemplateParameters(serviceBaseUrl, ""); + + return policyExecutorWebClient.post() + .uri(urlTemplateParameters.urlTemplate(), urlTemplateParameters.urlVariables()) + .header(HttpHeaders.AUTHORIZATION, authorization) + .body(BodyInserters.fromValue(bodyAsObject)) + .retrieve() + .toEntity(JsonNode.class) + .block(); + } + + private static void processResponse(final JsonNode responseBody) { + final String decisionId = responseBody.path("decisionId").asText("unknown id"); + log.trace("Policy Executor Decision ID: {} ", decisionId); + final String decision = responseBody.path("decision").asText("unknown"); + if ("allow".equals(decision)) { + log.trace("Policy Executor allows the operation"); + } else { + log.warn("Policy Executor decision: {}", decision); + final String details = responseBody.path("message").asText(); + log.warn("Policy Executor message: {}", details); + final String message = "Policy Executor did not allow request. Decision #" + + decisionId + " : " + decision; + throw new PolicyExecutorException(message, details); + } + } + } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java index e5848c0dfa..03ec30b73b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java @@ -22,7 +22,7 @@ package org.onap.cps.ncmp.impl.inventory; import java.util.Collection; import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters; -import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; public interface ParameterizedCmHandleQueryService { /** @@ -51,22 +51,14 @@ public interface ParameterizedCmHandleQueryService { Collection<String> queryCmHandleIdsForInventory(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); /** - * Query and return cm handle objects that match the given query parameters. + * Query and return yang model cm handle objects that match the given query parameters. * Supported query types: * public properties * modules * cps-path * * @param cmHandleQueryServiceParameters the cm handle query parameters - * @return collection of cm handles + * @return collection of yang model cm handles */ - Collection<NcmpServiceCmHandle> queryCmHandles(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); - - /** - * Get all cm handle objects. - * Note: it is similar to all the queries above but simply no conditions and hence not 'parameterized' - * - * @return collection of cm handles - */ - Collection<NcmpServiceCmHandle> getAllCmHandles(); + Collection<YangModelCmHandle> queryCmHandles(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java index 34eeaccf8f..84229e2895 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java @@ -27,7 +27,6 @@ import static org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions.HA import static org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions.HAS_ALL_PROPERTIES; import static org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions.WITH_CPS_PATH; import static org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions.WITH_TRUST_LEVEL; -import static org.onap.cps.ncmp.impl.utils.YangDataConverter.toNcmpServiceCmHandle; import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY; import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS; @@ -40,10 +39,8 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.onap.cps.cpspath.parser.PathParsingException; import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters; -import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; import org.onap.cps.ncmp.impl.inventory.models.InventoryQueryConditions; import org.onap.cps.ncmp.impl.inventory.models.PropertyType; import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; @@ -54,7 +51,6 @@ import org.onap.cps.spi.model.DataNode; import org.springframework.stereotype.Service; @Service -@Slf4j @RequiredArgsConstructor public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHandleQueryService { @@ -83,22 +79,18 @@ public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHan } @Override - public Collection<NcmpServiceCmHandle> queryCmHandles( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { - + public Collection<YangModelCmHandle> queryCmHandles( + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { if (cmHandleQueryServiceParameters.getCmHandleQueryParameters().isEmpty()) { return getAllCmHandles(); } - final Collection<String> cmHandleIds = queryCmHandleIds(cmHandleQueryServiceParameters); - - return getNcmpServiceCmHandles(cmHandleIds); + return inventoryPersistence.getYangModelCmHandles(cmHandleIds); } - @Override - public Collection<NcmpServiceCmHandle> getAllCmHandles() { + private Collection<YangModelCmHandle> getAllCmHandles() { final DataNode dataNode = inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT).iterator().next(); - return dataNode.getChildDataNodes().stream().map(this::createNcmpServiceCmHandle).collect(Collectors.toSet()); + return dataNode.getChildDataNodes().stream().map(YangDataConverter::toYangModelCmHandle).toList(); } private Collection<String> queryCmHandlesByDmiPlugin( @@ -226,22 +218,6 @@ public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHan return collectCmHandleIdsFromDataNodes(dataNode.getChildDataNodes()); } - private Collection<NcmpServiceCmHandle> getNcmpServiceCmHandles(final Collection<String> cmHandleIds) { - final Collection<YangModelCmHandle> yangModelcmHandles - = inventoryPersistence.getYangModelCmHandles(cmHandleIds); - - final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles = new ArrayList<>(yangModelcmHandles.size()); - - yangModelcmHandles.forEach(yangModelcmHandle -> - ncmpServiceCmHandles.add(YangDataConverter.toNcmpServiceCmHandle(yangModelcmHandle)) - ); - return ncmpServiceCmHandles; - } - - private NcmpServiceCmHandle createNcmpServiceCmHandle(final DataNode dataNode) { - return toNcmpServiceCmHandle(YangDataConverter.toYangModelCmHandle(dataNode)); - } - private Collection<String> executeQueries(final CmHandleQueryServiceParameters cmHandleQueryServiceParameters, final Function<CmHandleQueryServiceParameters, Collection<String>>... queryFunctions) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java index f468127dbc..33310b955e 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java @@ -138,8 +138,8 @@ public class TrustLevelManager { * @param cmHandleId cm handle id * @return TrustLevel effective trust level */ - public TrustLevel getEffectiveTrustLevel(final String cmHandleId) { - final TrustLevel dmiTrustLevel = TrustLevel.COMPLETE; // TODO: CPS-2375 + public TrustLevel getEffectiveTrustLevel(final String dmiServiceName, final String cmHandleId) { + final TrustLevel dmiTrustLevel = trustLevelPerDmiPlugin.get(dmiServiceName); final TrustLevel cmHandleTrustLevel = trustLevelPerCmHandle.get(cmHandleId); return dmiTrustLevel.getEffectiveTrustLevel(cmHandleTrustLevel); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/RestServiceUrlTemplateBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/RestServiceUrlTemplateBuilder.java index fafb09007d..c850ca94a0 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/RestServiceUrlTemplateBuilder.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/RestServiceUrlTemplateBuilder.java @@ -96,9 +96,7 @@ public class RestServiceUrlTemplateBuilder { * @return a UrlTemplateParameters instance containing the complete URL template and URL variables */ public UrlTemplateParameters createUrlTemplateParameters(final String serviceBaseUrl, final String basePath) { - this.uriComponentsBuilder.pathSegment(basePath) - .pathSegment(VERSION_SEGMENT); - + this.uriComponentsBuilder.pathSegment(basePath).pathSegment(VERSION_SEGMENT); final Map<String, String> urlTemplateVariables = new HashMap<>(); pathSegments.forEach((pathSegmentName, variablePathValue) -> { @@ -120,7 +118,7 @@ public class RestServiceUrlTemplateBuilder { } /** - * Constructs a URL for DMI health check based on the given base URL. + * Constructs a URL for a spring actuator health check based on the given base URL. * * @param serviceBaseUrl the base URL of the service, e.g., "http://dmi-service.com". * @return a {@link UrlTemplateParameters} instance containing the complete URL template and empty URL variables, diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/DmiHttpClientConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/DmiHttpClientConfigSpec.groovy index e0ae204c8d..23f5edd890 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/DmiHttpClientConfigSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/DmiHttpClientConfigSpec.groovy @@ -29,7 +29,7 @@ import spock.lang.Specification @SpringBootTest @ContextConfiguration(classes = [DmiHttpClientConfig]) -@EnableConfigurationProperties(DmiHttpClientConfig) +@EnableConfigurationProperties class DmiHttpClientConfigSpec extends Specification { @Autowired diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfigSpec.groovy index 1946a452b1..ca71c345c1 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfigSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfigSpec.groovy @@ -34,7 +34,7 @@ class PolicyExecutorHttpClientConfigSpec extends Specification { @Autowired PolicyExecutorHttpClientConfig policyExecutorHttpClientConfig - def 'Test http client configuration properties of data with custom and default values'() { + def 'Http client configuration properties for policy executor http client.'() { expect: 'properties are populated correctly for all services' with(policyExecutorHttpClientConfig.allServices) { assert maximumInMemorySizeInMegabytes == 31 diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/PolicyExecutorSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/PolicyExecutorSpec.groovy deleted file mode 100644 index 4b09afa2ae..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/PolicyExecutorSpec.groovy +++ /dev/null @@ -1,74 +0,0 @@ -package org.onap.cps.ncmp.impl.data - -import ch.qos.logback.classic.Level -import ch.qos.logback.classic.Logger -import ch.qos.logback.classic.spi.ILoggingEvent -import ch.qos.logback.core.read.ListAppender -import org.onap.cps.ncmp.impl.data.policyexecutor.PolicyExecutor -import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.test.context.ContextConfiguration -import spock.lang.Specification - -import static org.onap.cps.ncmp.api.data.models.OperationType.PATCH - -@SpringBootTest -@ContextConfiguration(classes = [PolicyExecutor]) -class PolicyExecutorSpec extends Specification { - - @Autowired - PolicyExecutor objectUnderTest - - def logAppender = Spy(ListAppender<ILoggingEvent>) - - def setup() { - setupLogger() - } - - def cleanup() { - ((Logger) LoggerFactory.getLogger(PolicyExecutor)).detachAndStopAllAppenders() - } - - def 'Configuration properties.'() { - expect: 'properties used from application.yml' - assert objectUnderTest.enabled - assert objectUnderTest.serverAddress == 'http://localhost' - assert objectUnderTest.serverPort == '8785' - } - - def 'Permission check logging.'() { - when: 'permission is checked for an operation' - def yangModelCmHandle = new YangModelCmHandle(id:'ch-1', alternateId:'fdn1') - objectUnderTest.checkPermission(yangModelCmHandle, PATCH, 'my credentials','my resource','my change') - then: 'correct details are logged ' - assert getLogEntry(0) == 'Policy Executor Enabled' - assert getLogEntry(3).contains('my credentials') - assert getLogEntry(4).contains('cm_patch') - assert getLogEntry(5).contains('fdn1') - assert getLogEntry(6).contains('ch-1') - assert getLogEntry(7).contains('my resource') - assert getLogEntry(8).contains('my change') - } - - def 'Permission check with feature disabled.'() { - given: 'feature is disabled' - objectUnderTest.enabled = false - when: 'permission is checked for an operation' - objectUnderTest.checkPermission(new YangModelCmHandle(), PATCH, 'my credentials','my resource','my change') - then: 'nothing is logged' - assert logAppender.list.isEmpty() - } - - def setupLogger() { - def logger = LoggerFactory.getLogger(PolicyExecutor) - logger.setLevel(Level.DEBUG) - logger.addAppender(logAppender) - logAppender.start() - } - - def getLogEntry(index) { - logAppender.list[index].formattedMessage - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorConfigurationSpec.groovy new file mode 100644 index 0000000000..c859bb0a09 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorConfigurationSpec.groovy @@ -0,0 +1,45 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * 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.cps.ncmp.impl.data.policyexecutor + +import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.ncmp.config.PolicyExecutorHttpClientConfig +import org.onap.cps.ncmp.impl.policyexecutor.PolicyExecutorWebClientConfiguration +import org.onap.cps.ncmp.utils.WebClientBuilderTestConfig +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.ContextConfiguration +import spock.lang.Specification + +@SpringBootTest +@ContextConfiguration(classes = [ObjectMapper, PolicyExecutor, PolicyExecutorWebClientConfiguration, PolicyExecutorHttpClientConfig, WebClientBuilderTestConfig ]) +class PolicyExecutorConfigurationSpec extends Specification { + + @Autowired + PolicyExecutor objectUnderTest + + def 'Policy executor configuration properties.'() { + expect: 'properties used from application.yml' + assert objectUnderTest.enabled + assert objectUnderTest.serverAddress == 'http://localhost' + assert objectUnderTest.serverPort == '8785' + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy new file mode 100644 index 0000000000..63a915ab64 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy @@ -0,0 +1,154 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * 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.cps.ncmp.impl.data.policyexecutor + +import ch.qos.logback.classic.Level +import ch.qos.logback.classic.Logger +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.read.ListAppender +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.ncmp.api.exceptions.NcmpException +import org.onap.cps.ncmp.api.exceptions.PolicyExecutorException +import org.onap.cps.ncmp.api.exceptions.ServerNcmpException +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle +import org.slf4j.LoggerFactory +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.reactive.function.client.WebClient +import reactor.core.publisher.Mono +import spock.lang.Specification + +import static org.onap.cps.ncmp.api.data.models.OperationType.CREATE +import static org.onap.cps.ncmp.api.data.models.OperationType.DELETE +import static org.onap.cps.ncmp.api.data.models.OperationType.PATCH +import static org.onap.cps.ncmp.api.data.models.OperationType.UPDATE + +class PolicyExecutorSpec extends Specification { + + def mockWebClient = Mock(WebClient) + def mockRequestBodyUriSpec = Mock(WebClient.RequestBodyUriSpec) + def mockResponseSpec = Mock(WebClient.ResponseSpec) + def spiedObjectMapper = Spy(ObjectMapper) + + PolicyExecutor objectUnderTest = new PolicyExecutor(mockWebClient, spiedObjectMapper) + + def logAppender = Spy(ListAppender<ILoggingEvent>) + + def someValidJson = '{"Hello":"World"}' + + def setup() { + setupLogger() + objectUnderTest.enabled = true + mockWebClient.post() >> mockRequestBodyUriSpec + mockRequestBodyUriSpec.uri(*_) >> mockRequestBodyUriSpec + mockRequestBodyUriSpec.header(*_) >> mockRequestBodyUriSpec + mockRequestBodyUriSpec.body(*_) >> mockRequestBodyUriSpec + mockRequestBodyUriSpec.retrieve() >> mockResponseSpec + } + + def cleanup() { + ((Logger) LoggerFactory.getLogger(PolicyExecutor)).detachAndStopAllAppenders() + } + + def 'Permission check with allow response.'() { + given: 'allow response' + mockResponse([decision:'allow'], HttpStatus.OK) + when: 'permission is checked for an operation' + objectUnderTest.checkPermission(new YangModelCmHandle(), operationType, 'my credentials','my resource',someValidJson) + then: 'system logs the operation is allowed' + assert getLogEntry(2) == 'Policy Executor allows the operation' + and: 'no exception occurs' + noExceptionThrown() + where: 'all write operations are tested' + operationType << [ CREATE, DELETE, PATCH, UPDATE ] + } + + def 'Permission check with other response (not allowed).'() { + given: 'other response' + mockResponse([decision:'other', decisionId:123, message:'I dont like Mondays' ], HttpStatus.OK) + when: 'permission is checked for an operation' + objectUnderTest.checkPermission(new YangModelCmHandle(), PATCH, 'my credentials','my resource',someValidJson) + then: 'Policy Executor exception is thrown' + def thrownException = thrown(PolicyExecutorException) + assert thrownException.message == 'Policy Executor did not allow request. Decision #123 : other' + assert thrownException.details == 'I dont like Mondays' + } + + def 'Permission check with non 2xx response.'() { + given: 'other response' + mockResponse([], HttpStatus.I_AM_A_TEAPOT) + when: 'permission is checked for an operation' + objectUnderTest.checkPermission(new YangModelCmHandle(), PATCH, 'my credentials','my resource',someValidJson) + then: 'Server Ncmp exception is thrown' + def thrownException = thrown(ServerNcmpException) + assert thrownException.message == 'Policy Executor invocation failed' + assert thrownException.details == 'HTTP status code: 418' + } + + def 'Permission check with invalid response from Policy Executor.'() { + given: 'invalid response from Policy executor' + mockResponseSpec.toEntity(*_) >> invalidResponse + when: 'permission is checked for an operation' + objectUnderTest.checkPermission(new YangModelCmHandle(), CREATE, 'my credentials','my resource',someValidJson) + then: 'system logs the expected message' + assert getLogEntry(1) == expectedMessage + where: 'following invalid responses are received' + invalidResponse || expectedMessage + Mono.empty() || 'No valid response from policy, ignored' + Mono.just(new ResponseEntity<>(null, HttpStatus.OK)) || 'No valid response body from policy, ignored' + } + + def 'Permission check with an invalid change request json.'() { + when: 'permission is checked for an invalid change request' + objectUnderTest.checkPermission(new YangModelCmHandle(), CREATE, 'my credentials', 'my resource', 'invalid json string') + then: 'an ncmp exception thrown' + def ncmpException = thrown(NcmpException) + ncmpException.message == 'Cannot convert Change Request data to Object' + ncmpException.details.contains('invalid json string') + } + + def 'Permission check feature disabled.'() { + given: 'feature is disabled' + objectUnderTest.enabled = false + when: 'permission is checked for an operation' + objectUnderTest.checkPermission(new YangModelCmHandle(), PATCH, 'my credentials','my resource',someValidJson) + then: 'system logs that the feature not enabled' + assert getLogEntry(0) == 'Policy Executor Enabled: false' + } + + def mockResponse(mockResponseAsMap, httpStatus) { + JsonNode jsonNode = spiedObjectMapper.readTree(spiedObjectMapper.writeValueAsString(mockResponseAsMap)) + def mono = Mono.just(new ResponseEntity<>(jsonNode, httpStatus)) + mockResponseSpec.toEntity(*_) >> mono + } + + def setupLogger() { + def logger = LoggerFactory.getLogger(PolicyExecutor) + logger.setLevel(Level.TRACE) + logger.addAppender(logAppender) + logAppender.start() + } + + def getLogEntry(index) { + logAppender.list[index].formattedMessage + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy index 1fed453fa5..9d51fff05a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy @@ -134,17 +134,25 @@ class NetworkCmProxyInventoryFacadeSpec extends Specification { assert result.currentTrustLevel == TrustLevel.COMPLETE } - def 'Get cm handle public properties'() { + def 'Get cm handle public properties using #scenario'() { given: 'a yang modelled cm handle' def dmiProperties = [new YangModelCmHandle.Property('prop', 'some DMI property')] def publicProperties = [new YangModelCmHandle.Property('public prop', 'some public prop')] - def yangModelCmHandle = new YangModelCmHandle(id:'some-cm-handle', dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties) + def cmHandleId = 'some-cm-handle' + def alternateId = 'some-alternate-id' + def yangModelCmHandle = new YangModelCmHandle(id:cmHandleId, alternateId: alternateId, dmiServiceName: 'some service name', dmiProperties: dmiProperties, publicProperties: publicProperties) + and: 'we have corresponding cm handle for the cm handle reference' + 1 * mockAlternateIdMatcher.getCmHandleId(cmHandleRef) >> cmHandleId and: 'the system returns this yang modelled cm handle' - 1 * mockInventoryPersistence.getYangModelCmHandle('some-cm-handle') >> yangModelCmHandle - when: 'getting cm handle public properties for a given cm handle id from ncmp service' - def result = objectUnderTest.getCmHandlePublicProperties('some-cm-handle') + 1 * mockInventoryPersistence.getYangModelCmHandle(cmHandleId) >> yangModelCmHandle + when: 'getting cm handle public properties for a given cm handle reference from ncmp service' + def result = objectUnderTest.getCmHandlePublicProperties(cmHandleRef) then: 'the result returns the correct data' assert result == [ 'public prop' : 'some public prop' ] + where: 'following cm handle reference is used' + scenario | cmHandleRef + 'Cm Handle Reference as cm handle-id' | 'some-cm-handle' + 'Cm Handle Reference as alternate-id' | 'some-alternate-id' } def 'Get cm handle composite state using #scenario'() { @@ -214,7 +222,8 @@ class NetworkCmProxyInventoryFacadeSpec extends Specification { and: 'query cm handle method returns two cm handles' mockParameterizedCmHandleQueryService.queryCmHandles( spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class)) - >> [new NcmpServiceCmHandle(cmHandleId: 'ch-0'), new NcmpServiceCmHandle(cmHandleId: 'ch-1')] + >> [new YangModelCmHandle(id: 'ch-0', dmiProperties: [], publicProperties: []), + new YangModelCmHandle(id: 'ch-1', dmiProperties: [], publicProperties: [])] and: 'a trust level for cm handles' mockTrustLevelManager.getEffectiveTrustLevel(*_) >> TrustLevel.COMPLETE when: 'execute cm handle search is called' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceSpec.groovy index 013bace04d..08644202c6 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation + * Copyright (C) 2022-2024 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,6 @@ package org.onap.cps.ncmp.impl.inventory import org.onap.cps.cpspath.parser.PathParsingException import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters -import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.exceptions.DataInUseException @@ -139,10 +138,10 @@ class ParameterizedCmHandleQueryServiceSpec extends Specification { and: 'the inventory service is called with teh correct if and returns a yang model cm handle' 1 * mockInventoryPersistence.getYangModelCmHandles(['ch1']) >> [new YangModelCmHandle(id: 'abc', dmiProperties: [new YangModelCmHandle.Property('name','value')], publicProperties: [])] - and: 'the expected cm handle(s) are returned as NCMP Service cm handles' - assert result[0] instanceof NcmpServiceCmHandle + and: 'the expected cm handle(s) are returned as Yang Model cm handles' + assert result[0] instanceof YangModelCmHandle assert result.size() == 1 - assert result[0].dmiProperties == [name:'value'] + assert result[0].dmiProperties.size() == 1 } def 'Query cm handle ids when the query is empty.'() { @@ -165,7 +164,7 @@ class ParameterizedCmHandleQueryServiceSpec extends Specification { def result = objectUnderTest.queryCmHandles(cmHandleQueryParameters) then: 'the correct cm handles are returned' assert result.size() == 4 - assert result.cmHandleId.containsAll('PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4') + assert result.id.containsAll('PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4') } def 'Query CMHandleId with #scenario.' () { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy index 95d3db16cb..84d93e0b7f 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy @@ -25,7 +25,6 @@ import org.onap.cps.ncmp.api.inventory.models.TrustLevel import org.onap.cps.ncmp.impl.inventory.InventoryPersistence import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle import org.onap.cps.ncmp.utils.events.CmAvcEventPublisher -import spock.lang.Ignore import spock.lang.Specification class TrustLevelManagerSpec extends Specification { @@ -136,15 +135,13 @@ class TrustLevelManagerSpec extends Specification { 0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_) } - @Ignore - // TODO: CPS-2375 def 'Select effective trust level among CmHandle and dmi plugin'() { given: 'a non trusted dmi' trustLevelPerDmiPlugin.put('my-dmi', TrustLevel.NONE) and: 'a trusted CmHandle' trustLevelPerCmHandle.put('ch-1', TrustLevel.COMPLETE) when: 'effective trust level selected' - def effectiveTrustLevel = objectUnderTest.getEffectiveTrustLevel('ch-1') + def effectiveTrustLevel = objectUnderTest.getEffectiveTrustLevel('my-dmi', 'ch-1') then: 'effective trust level is trusted' assert effectiveTrustLevel == TrustLevel.NONE } diff --git a/cps-ncmp-service/src/test/java/org/onap/cps/ncmp/utils/WebClientBuilderTestConfig.java b/cps-ncmp-service/src/test/java/org/onap/cps/ncmp/utils/WebClientBuilderTestConfig.java new file mode 100644 index 0000000000..2f6b270076 --- /dev/null +++ b/cps-ncmp-service/src/test/java/org/onap/cps/ncmp/utils/WebClientBuilderTestConfig.java @@ -0,0 +1,40 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation. + * ================================================================================ + * 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.cps.ncmp.utils; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.web.reactive.function.client.WebClient; + +@TestConfiguration +public class WebClientBuilderTestConfig { + + /** + * Configures and creates a web client builder bean to make it accessible for the Spring Boot Test Context. + * + * @return a WebClient Builder instance. + */ + @Bean + public WebClient.Builder webClientBuilder() { + return WebClient.builder(); + } + +} diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml index 72d074ab5f..c76831da74 100644 --- a/cps-ncmp-service/src/test/resources/application.yml +++ b/cps-ncmp-service/src/test/resources/application.yml @@ -84,8 +84,8 @@ ncmp: policy-executor: enabled: true server: - address: "http://localhost" - port: "8785" + address: http://localhost + port: 8785 httpclient: all-services: maximumInMemorySizeInMegabytes: 31 diff --git a/docker-compose/config/grafana/jvm-micrometer-dashboard.json b/docker-compose/config/grafana/jvm-micrometer-dashboard.json new file mode 100644 index 0000000000..8f7747c596 --- /dev/null +++ b/docker-compose/config/grafana/jvm-micrometer-dashboard.json @@ -0,0 +1,3613 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "type": "dashboard" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "enable": true, + "expr": "resets(process_uptime_seconds{application=\"$application\", instance=\"$instance\"}[1m]) > 0", + "iconColor": "rgba(255, 96, 96, 1)", + "name": "Restart Detection", + "showIn": 0, + "step": "1m", + "tagKeys": "restart-tag", + "textFormat": "uptime reset", + "titleFormat": "Restart" + } + ] + }, + "description": "Dashboard for Micrometer instrumented applications (Java, Spring Boot, Micronaut)", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 4701, + "graphTooltip": 1, + "id": 1, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 139, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "refId": "A" + } + ], + "title": "Quick Facts", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "decimals": 1, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 0, + "y": 1 + }, + "id": 63, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.4", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "process_uptime_seconds{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 14400 + } + ], + "title": "Uptime", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "dateTimeAsIso" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 6, + "y": 1 + }, + "id": 92, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.4", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "process_start_time_seconds{application=\"$application\", instance=\"$instance\"}*1000", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 14400 + } + ], + "title": "Start time", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "decimals": 2, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgba(50, 172, 45, 0.97)", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 70 + }, + { + "color": "rgba(245, 54, 54, 0.9)", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 12, + "y": 1 + }, + "id": 65, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.4", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})*100/sum(jvm_memory_max_bytes{application=\"$application\",instance=\"$instance\", area=\"heap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 14400 + } + ], + "title": "Heap used", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "decimals": 2, + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + }, + { + "options": { + "from": -1e+32, + "result": { + "text": "N/A" + }, + "to": 0 + }, + "type": "range" + } + ], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgba(50, 172, 45, 0.97)", + "value": null + }, + { + "color": "rgba(237, 129, 40, 0.89)", + "value": 70 + }, + { + "color": "rgba(245, 54, 54, 0.9)", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 18, + "y": 1 + }, + "id": 75, + "maxDataPoints": 100, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.4", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})*100/sum(jvm_memory_max_bytes{application=\"$application\",instance=\"$instance\", area=\"nonheap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 14400 + } + ], + "title": "Non-Heap used", + "type": "stat" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 4 + }, + "id": 140, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "refId": "A" + } + ], + "title": "I/O Overview", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 5 + }, + "id": 111, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\"}[1m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "HTTP", + "refId": "A" + } + ], + "title": "Rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "HTTP" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#890f02", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "HTTP - 5xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#bf1b00", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 112, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\", status=~\"5..\"}[1m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "HTTP - 5xx", + "refId": "A" + } + ], + "title": "Errors", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 113, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(rate(http_server_requests_seconds_sum{application=\"$application\", instance=\"$instance\", status!~\"5..\"}[1m]))/sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\", status!~\"5..\"}[1m]))", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "HTTP - AVG", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "max(http_server_requests_seconds_max{application=\"$application\", instance=\"$instance\", status!~\"5..\"})", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "HTTP - MAX", + "refId": "B" + } + ], + "title": "Duration", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 5 + }, + "id": 119, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "tomcat_threads_busy_threads{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "TOMCAT - BSY", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "tomcat_threads_current_threads{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "TOMCAT - CUR", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "tomcat_threads_config_max_threads{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "TOMCAT - MAX", + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jetty_threads_busy{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "JETTY - BSY", + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jetty_threads_current{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "JETTY - CUR", + "refId": "E" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jetty_threads_config_max{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "JETTY - MAX", + "refId": "F" + } + ], + "title": "Utilisation", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 12 + }, + "id": 141, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "refId": "A" + } + ], + "title": "JVM Memory", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 13 + }, + "id": 24, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "committed", + "refId": "B", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "C", + "step": 2400 + } + ], + "title": "JVM Heap", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 13 + }, + "id": 25, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "committed", + "refId": "B", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "C", + "step": 2400 + } + ], + "title": "JVM Non-Heap", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 13 + }, + "id": 26, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "committed", + "refId": "B", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "C", + "step": 2400 + } + ], + "title": "JVM Total", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 13 + }, + "id": 86, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "process_memory_vss_bytes{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "vss", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "process_memory_rss_bytes{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "rss", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "process_memory_swap_bytes{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "swap", + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "process_memory_rss_bytes{application=\"$application\", instance=\"$instance\"} + process_memory_swap_bytes{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "total", + "refId": "D" + } + ], + "title": "JVM Process Memory", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 20 + }, + "id": 142, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "refId": "A" + } + ], + "title": "JVM Misc", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 1, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 21 + }, + "id": 106, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "system_cpu_usage{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "system", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "process_cpu_usage{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "process", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "avg_over_time(process_cpu_usage{application=\"$application\", instance=\"$instance\"}[15m])", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "process-15m", + "refId": "C" + } + ], + "title": "CPU Usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 1, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 21 + }, + "id": 93, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "system_load_average_1m{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "system-1m", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "system_cpu_count{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cpus", + "refId": "B" + } + ], + "title": "Load", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 21 + }, + "id": 32, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_threads_live_threads{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "live", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_threads_daemon_threads{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "daemon", + "metric": "", + "refId": "B", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_threads_peak_threads{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "peak", + "refId": "C", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "process_threads{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "process", + "refId": "D", + "step": 2400 + } + ], + "title": "Threads", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "blocked" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#bf1b00", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "new" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#fce2de", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "runnable" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7eb26d", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "terminated" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#511749", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "timed-waiting" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#c15c17", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "waiting" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#eab839", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 21 + }, + "id": 124, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_threads_states_threads{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{state}}", + "refId": "A" + } + ], + "title": "Thread States", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "The percent of time spent on Garbage Collection over all CPUs assigned to the JVM process.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 1, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 28 + }, + "id": 138, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "sum(rate(jvm_gc_pause_seconds_sum{application=\"$application\", instance=\"$instance\"}[1m])) by (application, instance) / on(application, instance) system_cpu_count", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "CPU time spent on GC", + "refId": "A" + } + ], + "title": "GC Pressure", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "opm" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "debug" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#1F78C1", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#BF1B00", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "info" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#508642", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "trace" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#6ED0E0", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "warn" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EAB839", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 6, + "y": 28 + }, + "id": 91, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "increase(logback_events_total{application=\"$application\", instance=\"$instance\"}[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{level}}", + "metric": "", + "refId": "A", + "step": 1200 + } + ], + "title": "Log Events", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 10, + "type": "log" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 28 + }, + "id": 61, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "process_files_open_files{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "open", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "process_files_max_files{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "B", + "step": 2400 + } + ], + "title": "File Descriptors", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 35 + }, + "id": 143, + "panels": [], + "repeat": "persistence_counts", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "refId": "A" + } + ], + "title": "JVM Memory Pools (Heap)", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 36 + }, + "id": 3, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "repeat": "jvm_memory_pool_heap", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 1800 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "commited", + "metric": "", + "refId": "B", + "step": 1800 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "C", + "step": 1800 + } + ], + "title": "$jvm_memory_pool_heap", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 43 + }, + "id": 144, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "refId": "A" + } + ], + "title": "JVM Memory Pools (Non-Heap)", + "type": "row" + }, + { + "aliasColors": {}, + "autoMigrateFrom": "graph", + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "rightLogBase": 1 + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 44 + }, + "id": 78, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "maxPerRow": 3, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": "jvm_memory_pool_nonheap", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 1800 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "commited", + "metric": "", + "refId": "B", + "step": 1800 + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "C", + "step": 1800 + } + ], + "thresholds": [], + "title": "$jvm_memory_pool_nonheap", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "timeseries", + "x-axis": true, + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": [ + "mbytes", + "short" + ], + "yaxes": [ + { + "format": "bytes", + "logBase": 1, + "min": 0, + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ] + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 58 + }, + "id": 145, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "refId": "A" + } + ], + "title": "Garbage Collection", + "type": "row" + }, + { + "aliasColors": {}, + "autoMigrateFrom": "graph", + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 59 + }, + "id": 98, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "rate(jvm_gc_pause_seconds_count{application=\"$application\", instance=\"$instance\"}[1m])", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "{{action}} ({{cause}})", + "refId": "A" + } + ], + "thresholds": [], + "title": "Collections", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "timeseries", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "logBase": 1, + "min": "0", + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "show": true + } + ] + }, + { + "aliasColors": {}, + "autoMigrateFrom": "graph", + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 59 + }, + "id": 101, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "rate(jvm_gc_pause_seconds_sum{application=\"$application\", instance=\"$instance\"}[1m])/rate(jvm_gc_pause_seconds_count{application=\"$application\", instance=\"$instance\"}[1m])", + "format": "time_series", + "hide": false, + "instant": false, + "intervalFactor": 1, + "legendFormat": "avg {{action}} ({{cause}})", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_gc_pause_seconds_max{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "instant": false, + "intervalFactor": 1, + "legendFormat": "max {{action}} ({{cause}})", + "refId": "B" + } + ], + "thresholds": [], + "title": "Pause Durations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "timeseries", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "logBase": 1, + "min": "0", + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "show": true + } + ] + }, + { + "aliasColors": {}, + "autoMigrateFrom": "graph", + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 59 + }, + "id": 99, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "rate(jvm_gc_memory_allocated_bytes_total{application=\"$application\", instance=\"$instance\"}[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "allocated", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "rate(jvm_gc_memory_promoted_bytes_total{application=\"$application\", instance=\"$instance\"}[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "promoted", + "refId": "B" + } + ], + "thresholds": [], + "title": "Allocated/Promoted", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "timeseries", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "logBase": 1, + "min": "0", + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ] + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 66 + }, + "id": 146, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "refId": "A" + } + ], + "title": "Classloading", + "type": "row" + }, + { + "aliasColors": {}, + "autoMigrateFrom": "graph", + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "rightLogBase": 1 + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 67 + }, + "id": 37, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_classes_loaded_classes{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "loaded", + "metric": "", + "refId": "A", + "step": 1200 + } + ], + "thresholds": [], + "title": "Classes loaded", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "timeseries", + "x-axis": true, + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": [ + "short", + "short" + ], + "yaxes": [ + { + "format": "short", + "logBase": 1, + "min": 0, + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ] + }, + { + "aliasColors": {}, + "autoMigrateFrom": "graph", + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "rightLogBase": 1 + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 67 + }, + "id": 38, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "delta(jvm_classes_loaded_classes{application=\"$application\",instance=\"$instance\"}[1m])", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "delta-1m", + "metric": "", + "refId": "A", + "step": 1200 + } + ], + "thresholds": [], + "title": "Class delta", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "timeseries", + "x-axis": true, + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": [ + "ops", + "short" + ], + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ] + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 74 + }, + "id": 147, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "refId": "A" + } + ], + "title": "Buffer Pools", + "type": "row" + }, + { + "aliasColors": {}, + "autoMigrateFrom": "graph", + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 75 + }, + "id": 131, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "maxPerRow": 3, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": "jvm_buffer_pool", + "seriesOverrides": [ + { + "alias": "count", + "yaxis": 2 + }, + { + "alias": "buffers", + "yaxis": 2 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_buffer_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_buffer_pool\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "used", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_buffer_total_capacity_bytes{application=\"$application\", instance=\"$instance\", id=~\"$jvm_buffer_pool\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "capacity", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "expr": "jvm_buffer_count_buffers{application=\"$application\", instance=\"$instance\", id=~\"$jvm_buffer_pool\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "buffers", + "refId": "C" + } + ], + "thresholds": [], + "title": "$jvm_buffer_pool", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "timeseries", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "logBase": 1, + "min": "0", + "show": true + }, + { + "decimals": 0, + "format": "short", + "label": "", + "logBase": 1, + "min": "0", + "show": true + } + ] + } + ], + "refresh": "auto", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": { + "isNone": true, + "selected": false, + "text": "None", + "value": "" + }, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "definition": "", + "hide": 0, + "includeAll": false, + "label": "Application", + "multi": false, + "name": "application", + "options": [], + "query": "label_values(application)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allFormat": "glob", + "current": { + "selected": false, + "text": "docker-compose-cps-and-ncmp-1:8080", + "value": "docker-compose-cps-and-ncmp-1:8080" + }, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "definition": "", + "hide": 0, + "includeAll": false, + "label": "Instance", + "multi": false, + "multiFormat": "glob", + "name": "instance", + "options": [], + "query": "label_values(jvm_memory_used_bytes{application=\"$application\"}, instance)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allFormat": "glob", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "definition": "", + "hide": 2, + "includeAll": true, + "label": "JVM Memory Pools Heap", + "multi": false, + "multiFormat": "glob", + "name": "jvm_memory_pool_heap", + "options": [], + "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"},id)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allFormat": "glob", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "definition": "", + "hide": 2, + "includeAll": true, + "label": "JVM Memory Pools Non-Heap", + "multi": false, + "multiFormat": "glob", + "name": "jvm_memory_pool_nonheap", + "options": [], + "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"},id)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 2, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allFormat": "glob", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "definition": "", + "hide": 2, + "includeAll": true, + "label": "JVM Buffer Pools", + "multi": false, + "multiFormat": "glob", + "name": "jvm_buffer_pool", + "options": [], + "query": "label_values(jvm_buffer_memory_used_bytes{application=\"$application\", instance=\"$instance\"},id)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "now": true, + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "JVM (Micrometer)", + "uid": "bdvp1kgecrda8f", + "version": 1, + "weekStart": "" +} diff --git a/docker-compose/config/grafana/provisioning/dashboards/dashboard.yml b/docker-compose/config/grafana/provisioning/dashboards/dashboard.yml new file mode 100644 index 0000000000..b6dba86680 --- /dev/null +++ b/docker-compose/config/grafana/provisioning/dashboards/dashboard.yml @@ -0,0 +1,9 @@ +apiVersion: 1 + +providers: + - name: default + orgId: 1 + type: file + options: + path: /var/lib/grafana/dashboards + foldersFromFilesStructure: true diff --git a/docker-compose/config/grafana/provisioning/datasources/datasource.yml b/docker-compose/config/grafana/provisioning/datasources/datasource.yml new file mode 100644 index 0000000000..86fd3465e1 --- /dev/null +++ b/docker-compose/config/grafana/provisioning/datasources/datasource.yml @@ -0,0 +1,8 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true diff --git a/docker-compose/nginx.conf b/docker-compose/config/nginx/nginx.conf index 17fad1b876..17fad1b876 100644 --- a/docker-compose/nginx.conf +++ b/docker-compose/config/nginx/nginx.conf diff --git a/docker-compose/proxy_params b/docker-compose/config/nginx/proxy_params index ab092a98f3..ab092a98f3 100644 --- a/docker-compose/proxy_params +++ b/docker-compose/config/nginx/proxy_params diff --git a/docker-compose/postgres-init.sql b/docker-compose/config/postgres-init.sql index 0c96de5b55..0c96de5b55 100644 --- a/docker-compose/postgres-init.sql +++ b/docker-compose/config/postgres-init.sql diff --git a/docker-compose/prometheus.yml b/docker-compose/config/prometheus.yml index 9640709089..89af4a6800 100644 --- a/docker-compose/prometheus.yml +++ b/docker-compose/config/prometheus.yml @@ -3,8 +3,10 @@ global: evaluation_interval: 5s scrape_configs: - - job_name: 'cps-and-ncm' + - job_name: 'cps-and-ncmp' metrics_path: '/actuator/prometheus' scrape_interval: 5s static_configs: - - targets: ['cps-and-ncmp:8080'] + - targets: + - 'docker-compose-cps-and-ncmp-1:8080' + - 'docker-compose-cps-and-ncmp-2:8080' diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index 5af325a50b..6b372c189c 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -33,7 +33,7 @@ services: POSTGRES_USER: ${DB_USERNAME:-cps} POSTGRES_PASSWORD: ${DB_PASSWORD:-cps} volumes: - - ./postgres-init.sql:/docker-entrypoint-initdb.d/postgres-init.sql + - ./config/postgres-init.sql:/docker-entrypoint-initdb.d/postgres-init.sql deploy: resources: reservations: @@ -79,8 +79,8 @@ services: depends_on: - cps-and-ncmp volumes: - - ./nginx.conf:/etc/nginx/nginx.conf - - ./proxy_params:/etc/nginx/proxy_params + - ./config/nginx/nginx.conf:/etc/nginx/nginx.conf + - ./config/nginx/proxy_params:/etc/nginx/proxy_params ### if kafka is not required comment out zookeeper and kafka ### zookeeper: @@ -158,26 +158,28 @@ services: - policy-executor-stub prometheus: - container_name: prometheus-container + container_name: prometheus image: prom/prometheus:latest ports: - 9090:9090 restart: always volumes: - - ./prometheus.yml:/etc/prometheus/prometheus.yml + - ./config/prometheus.yml:/etc/prometheus/prometheus.yml profiles: - monitoring grafana: image: grafana/grafana-oss:latest user: "" - container_name: grafana-container + container_name: grafana depends_on: prometheus: condition: service_started ports: - 3000:3000 volumes: + - ./config/grafana/provisioning/:/etc/grafana/provisioning/ + - ./config/grafana/jvm-micrometer-dashboard.json:/var/lib/grafana/dashboards/jvm-micrometer-dashboard.json - grafana:/var/lib/grafana environment: - GF_SECURITY_ADMIN_PASSWORD=admin diff --git a/docs/schemas/policy-executor/ncmp-create-schema-1.0.0.json b/docs/schemas/policy-executor/ncmp-create-schema-1.0.0.json index 2ec9daf949..4d98bc8632 100644 --- a/docs/schemas/policy-executor/ncmp-create-schema-1.0.0.json +++ b/docs/schemas/policy-executor/ncmp-create-schema-1.0.0.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "urn:cps:org.onap.cps.ncmp.policy-executor:ncmp-create-schema:1.0.0", + "$id": "urn:cps:org.onap.cps.ncmp.policy-executor.ncmp-create-schema:1.0.0", "$ref": "#/definitions/NcmpCreate", "definitions": { "NcmpCreate": { diff --git a/docs/schemas/policy-executor/ncmp-delete-schema-1.0.0.json b/docs/schemas/policy-executor/ncmp-delete-schema-1.0.0.json index 5df0325e39..1246d9d815 100644 --- a/docs/schemas/policy-executor/ncmp-delete-schema-1.0.0.json +++ b/docs/schemas/policy-executor/ncmp-delete-schema-1.0.0.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "urn:cps:org.onap.cps.ncmp.policy-executor:ncmp-delete-schema:1.0.0", + "$id": "urn:cps:org.onap.cps.ncmp.policy-executor.ncmp-delete-schema:1.0.0", "$ref": "#/definitions/NcmpDelete", "definitions": { "NcmpDelete": { diff --git a/docs/schemas/policy-executor/ncmp-patch-schema-1.0.0.json b/docs/schemas/policy-executor/ncmp-patch-schema-1.0.0.json index e26c244c94..4917aea146 100644 --- a/docs/schemas/policy-executor/ncmp-patch-schema-1.0.0.json +++ b/docs/schemas/policy-executor/ncmp-patch-schema-1.0.0.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "urn:cps:org.onap.cps.ncmp.policy-executor:ncmp-patch-schema:1.0.0", + "$id": "urn:cps:org.onap.cps.ncmp.policy-executor.ncmp-patch-schema:1.0.0", "$ref": "#/definitions/NcmpPatch", "definitions": { "NcmpPatch": { diff --git a/docs/schemas/policy-executor/ncmp-update-schema-1.0.0.json b/docs/schemas/policy-executor/ncmp-update-schema-1.0.0.json index 0a497e38c5..831526cd52 100644 --- a/docs/schemas/policy-executor/ncmp-update-schema-1.0.0.json +++ b/docs/schemas/policy-executor/ncmp-update-schema-1.0.0.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "urn:cps:org.onap.cps.ncmp.policy-executor:ncmp-update-schema:1.0.0", + "$id": "urn:cps:org.onap.cps.ncmp.policy-executor.ncmp-update-schema:1.0.0", "$ref": "#/definitions/NcmpUpdate", "definitions": { "NcmpUpdate": { diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy index 5e46e95a0c..bd53c4ea13 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy @@ -46,6 +46,7 @@ import org.onap.cps.spi.utils.SessionManager import org.onap.cps.utils.ContentType import org.onap.cps.utils.JsonObjectMapper import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.EnableAutoConfiguration import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc @@ -125,12 +126,19 @@ abstract class CpsIntegrationSpecBase extends Specification { @Autowired AlternateIdMatcher alternateIdMatcher + + @Value('${ncmp.policy-executor.server.port:8080}') + private String policyServerPort; + MockWebServer mockDmiServer1 = new MockWebServer() MockWebServer mockDmiServer2 = new MockWebServer() + MockWebServer mockPolicyServer = new MockWebServer() DmiDispatcher dmiDispatcher1 = new DmiDispatcher() DmiDispatcher dmiDispatcher2 = new DmiDispatcher() + PolicyDispatcher policyDispatcher = new PolicyDispatcher(); + def DMI1_URL = null def DMI2_URL = null @@ -155,13 +163,18 @@ abstract class CpsIntegrationSpecBase extends Specification { mockDmiServer2.setDispatcher(dmiDispatcher2) mockDmiServer2.start() + mockPolicyServer.setDispatcher(policyDispatcher) + mockPolicyServer.start(Integer.valueOf(policyServerPort)) + DMI1_URL = String.format("http://%s:%s", mockDmiServer1.getHostName(), mockDmiServer1.getPort()) DMI2_URL = String.format("http://%s:%s", mockDmiServer2.getHostName(), mockDmiServer2.getPort()) + } def cleanup() { mockDmiServer1.shutdown() mockDmiServer2.shutdown() + mockPolicyServer.shutdown() } def static readResourceDataFile(filename) { diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy index fcc23db782..35a7b6a7c2 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy @@ -20,18 +20,18 @@ package org.onap.cps.integration.base -import static org.onap.cps.integration.base.CpsIntegrationSpecBase.readResourceDataFile - import groovy.json.JsonSlurper -import java.util.regex.Matcher import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.RecordedRequest -import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteRequest import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus import org.springframework.http.MediaType +import java.util.regex.Matcher + +import static org.onap.cps.integration.base.CpsIntegrationSpecBase.readResourceDataFile + /** * This class simulates responses from the DMI server in NCMP integration tests. * @@ -117,32 +117,32 @@ class DmiDispatcher extends Dispatcher { return mockResponseWithBody(HttpStatus.OK, response) } - private getModuleReferencesResponse(cmHandleId) { + def getModuleReferencesResponse(cmHandleId) { def moduleReferences = '{"schemas":[' + getModuleNamesForCmHandle(cmHandleId).collect { MODULE_REFERENCES_RESPONSE_TEMPLATE.replaceAll("<MODULE_NAME>", it) }.join(',') + ']}' return mockResponseWithBody(HttpStatus.OK, moduleReferences) } - private getModuleResourcesResponse(cmHandleId) { + def getModuleResourcesResponse(cmHandleId) { def moduleResources = '[' + getModuleNamesForCmHandle(cmHandleId).collect { MODULE_RESOURCES_RESPONSE_TEMPLATE.replaceAll("<MODULE_NAME>", it) }.join(',') + ']' return mockResponseWithBody(HttpStatus.OK, moduleResources) } - private getModuleNamesForCmHandle(cmHandleId) { + def getModuleNamesForCmHandle(cmHandleId) { if (!moduleNamesPerCmHandleId.containsKey(cmHandleId)) { throw new IllegalArgumentException('Mock DMI has no modules configured for ' + cmHandleId) } return moduleNamesPerCmHandleId.get(cmHandleId) } - private static mockResponse(status) { + def static mockResponse(status) { return new MockResponse().setResponseCode(status.value()) } - private static mockResponseWithBody(status, responseBody) { + def static mockResponseWithBody(status, responseBody) { return new MockResponse() .setResponseCode(status.value()) .addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy new file mode 100644 index 0000000000..27e7563e61 --- /dev/null +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy @@ -0,0 +1,74 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * 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.cps.integration.base + + +import okhttp3.mockwebserver.Dispatcher +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.RecordedRequest +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper + +/** + * This class simulates responses from the Policy Execution server in NCMP integration tests. + */ +class PolicyDispatcher extends Dispatcher { + + def objectMapper = new ObjectMapper() + def expectedAuthorizationToken = 'ABC' + def allowAll = true; // Prevents legacy test being affected + + @Override + MockResponse dispatch(RecordedRequest recordedRequest) { + + if (!allowAll && !recordedRequest.getHeader('Authorization').contains(expectedAuthorizationToken)) { + return new MockResponse().setResponseCode(401) + } + + if (recordedRequest.path != '/v1/execute') { + return new MockResponse().setResponseCode(400) + } + + def body = objectMapper.readValue(recordedRequest.getBody().readUtf8(), Map.class) + def targetIdentifier = body.get('requests').get(0).get('data').get('targetIdentifier') + def responseAsMap = [:] + responseAsMap.put('decisionId',1) + if (allowAll || targetIdentifier == 'fdn1') { + responseAsMap.put('decision','allow') + responseAsMap.put('message','') + } else { + responseAsMap.put('decision','deny') + responseAsMap.put('message','I only like fdn1') + } + def responseAsString = objectMapper.writeValueAsString(responseAsMap) + + return mockResponseWithBody(HttpStatus.OK, responseAsString) + } + + static mockResponseWithBody(status, responseBody) { + return new MockResponse() + .setResponseCode(status.value()) + .addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) + .setBody(responseBody) + } +} diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy new file mode 100644 index 0000000000..99f245ae8c --- /dev/null +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy @@ -0,0 +1,63 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * 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.cps.integration.functional.ncmp + +import org.onap.cps.integration.base.CpsIntegrationSpecBase +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType + +import static org.springframework.http.HttpMethod.POST +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request + +class PolicyExecutorIntegrationSpec extends CpsIntegrationSpecBase { + + def setup() { + // Enable mocked policy executor logic + policyDispatcher.allowAll = false; + //minimum setup for 2 cm handles with alternate ids + dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': [], 'ch-2': []] + registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'fdn1') + registerCmHandle(DMI1_URL, 'ch-2', NO_MODULE_SET_TAG, 'fdn2') + } + + def cleanup() { + deregisterCmHandle(DMI1_URL, 'ch-1') + deregisterCmHandle(DMI1_URL, 'ch-2') + } + + def 'Policy Executor create request with #scenario.'() { + when: 'a pass-through write request is sent to NCMP' + def response = mvc.perform(request(POST, "/ncmp/v1/ch/$cmHandle/data/ds/ncmp-datastore:passthrough-running") + .queryParam('resourceIdentifier', 'my-resource-id') + .contentType(MediaType.APPLICATION_JSON) + .content('{ "some-json": "data" }') + .header(HttpHeaders.AUTHORIZATION, authorization)) + .andReturn().response + then: 'the expected status code is returned' + response.getStatus() == execpectedStatusCode + where: 'following parameters are used' + scenario | cmHandle | authorization || execpectedStatusCode + 'accepted cm handle' | 'ch-1' | 'mock expects "ABC"' || 201 + 'un-accepted cm handle' | 'ch-2' | 'mock expects "ABC"' || 409 + 'invalid authorization' | 'ch-1' | 'something else' || 500 + } + +} diff --git a/integration-test/src/test/resources/application.yml b/integration-test/src/test/resources/application.yml index fefae345e6..760dad01af 100644 --- a/integration-test/src/test/resources/application.yml +++ b/integration-test/src/test/resources/application.yml @@ -213,6 +213,20 @@ ncmp: init: mode: ALWAYS + policy-executor: + enabled: true + server: + address: http://localhost + port: 8790 + httpclient: + all-services: + maximumInMemorySizeInMegabytes: 1 + maximumConnectionsTotal: 10 + pendingAcquireMaxCount: 10 + connectionTimeoutInSeconds: 30 + readTimeoutInSeconds: 30 + writeTimeoutInSeconds: 30 + hazelcast: cluster-name: cps-and-ncmp-test-caches mode: diff --git a/k6-tests/ncmp/common/search-base.js b/k6-tests/ncmp/common/search-base.js index bc964856af..a7d0c925c4 100644 --- a/k6-tests/ncmp/common/search-base.js +++ b/k6-tests/ncmp/common/search-base.js @@ -26,7 +26,7 @@ const SEARCH_PARAMETERS_PER_SCENARIO = { 'cmHandleQueryParameters': [ { 'conditionName': 'hasAllModules', - 'conditionParameters': [{'moduleName': 'ietf-yang-types-1'}] + 'conditionParameters': [{'moduleName': 'ietf-yang-types'}] } ] }, diff --git a/k6-tests/ncmp/common/utils.js b/k6-tests/ncmp/common/utils.js index 88da750e2b..98c9eb86f1 100644 --- a/k6-tests/ncmp/common/utils.js +++ b/k6-tests/ncmp/common/utils.js @@ -54,22 +54,15 @@ export function getRandomCmHandleId() { export function makeCustomSummaryReport(data, options) { const summaryCsvLines = [ '#,Test Name,Unit,Limit,Actual', + makeSummaryCsvLine('0', 'HTTP request failures for all tests', 'rate of failed requests', 'http_req_failed', data, options), makeSummaryCsvLine('1', 'Registration of CM-handles', 'CM-handles/second', 'cmhandles_created_per_second', data, options), makeSummaryCsvLine('2', 'De-registration of CM-handles', 'CM-handles/second', 'cmhandles_deleted_per_second', data, options), - makeSummaryCsvLine('3', 'CM-handle ID search with Module filter', 'milliseconds', 'http_req_duration{scenario:id_search_module}', data, options), - makeSummaryCsvLine('4', 'CM-handle search with Module filter', 'milliseconds', 'http_req_duration{scenario:cm_search_module}', data, options), + makeSummaryCsvLine('3', 'CM-handle ID search with Module filter', 'milliseconds', 'id_search_duration', data, options), + makeSummaryCsvLine('4', 'CM-handle search with Module filter', 'milliseconds', 'cm_search_duration', data, options), makeSummaryCsvLine('5a', 'NCMP overhead for Synchronous single CM-handle pass-through read', 'milliseconds', 'ncmp_overhead_passthrough_read', data, options), makeSummaryCsvLine('5b', 'NCMP overhead for Synchronous single CM-handle pass-through read with alternate id', 'milliseconds', 'ncmp_overhead_passthrough_read_alt_id', data, options), makeSummaryCsvLine('6', 'NCMP overhead for Synchronous single CM-handle pass-through write', 'milliseconds', 'ncmp_overhead_passthrough_write', data, options), makeSummaryCsvLine('7', 'Data operations batch read', 'events/second', 'data_operations_batch_read_cmhandles_per_second', data, options), - makeSummaryCsvLine('1x', 'Failures of Registration of CM-handles', 'number of failed requests', 'http_req_failed{group:::setup}', data, options), - makeSummaryCsvLine('2x', 'Failures of De-registration of CM-handles', 'number of failed requests', 'http_req_failed{group:::teardown}', data, options), - makeSummaryCsvLine('3x', 'Failures of CM-handle ID search with Module filter', 'number of failed requests', 'http_req_failed{scenario:id_search_module}', data, options), - makeSummaryCsvLine('4x', 'Failures of CM-handle search with Module filter', 'number of failed requests', 'http_req_failed{scenario:cm_search_module}', data, options), - makeSummaryCsvLine('5x', 'Failures of Synchronous single CM-handle pass-through read', 'number of failed requests', 'http_req_failed{scenario:passthrough_read}', data, options), - makeSummaryCsvLine('6x', 'Failures of Synchronous single CM-handle pass-through write', 'number of failed requests', 'http_req_failed{scenario:passthrough_write}', data, options), - makeSummaryCsvLine('7ax', 'Failures of Data operations batch read', 'number of failed requests', 'http_req_failed{scenario:data_operation_send_async_http_request}', data, options), - makeSummaryCsvLine('7bx', 'Failures of Data operations batch read consume kafka responses', 'number of failed requests', 'kafka_reader_error_count{scenario:data_operation_consume_kafka_responses}', data, options), ]; return summaryCsvLines.join('\n') + '\n'; } diff --git a/k6-tests/ncmp/ncmp-kpi.js b/k6-tests/ncmp/ncmp-kpi.js index 013849b866..3fc138c4e5 100644 --- a/k6-tests/ncmp/ncmp-kpi.js +++ b/k6-tests/ncmp/ncmp-kpi.js @@ -19,7 +19,7 @@ */ import { check } from 'k6'; -import { Gauge, Trend } from 'k6/metrics'; +import { Trend } from 'k6/metrics'; import { Reader } from 'k6/x/kafka'; import { TOTAL_CM_HANDLES, READ_DATA_FOR_CM_HANDLE_DELAY_MS, WRITE_DATA_FOR_CM_HANDLE_DELAY_MS, @@ -34,12 +34,14 @@ import { import { executeCmHandleSearch, executeCmHandleIdSearch } from './common/search-base.js'; import { passthroughRead, passthroughReadWithAltId, passthroughWrite, batchRead } from './common/passthrough-crud.js'; -let cmHandlesCreatedPerSecondGauge = new Gauge('cmhandles_created_per_second'); -let cmHandlesDeletedPerSecondGauge = new Gauge('cmhandles_deleted_per_second'); +let cmHandlesCreatedPerSecondTrend = new Trend('cmhandles_created_per_second', false); +let cmHandlesDeletedPerSecondTrend = new Trend('cmhandles_deleted_per_second', false); let passthroughReadNcmpOverheadTrend = new Trend('ncmp_overhead_passthrough_read', true); let passthroughReadNcmpOverheadTrendWithAlternateId = new Trend('ncmp_overhead_passthrough_read_alt_id', true); let passthroughWriteNcmpOverheadTrend = new Trend('ncmp_overhead_passthrough_write', true); -let dataOperationsBatchReadCmHandlePerSecondTrend = new Trend('data_operations_batch_read_cmhandles_per_second'); +let idSearchDurationTrend = new Trend('id_search_duration', true); +let cmSearchDurationTrend = new Trend('cm_search_duration', true); +let dataOperationsBatchReadCmHandlePerSecondTrend = new Trend('data_operations_batch_read_cmhandles_per_second', false); const reader = new Reader({ brokers: KAFKA_BOOTSTRAP_SERVERS, @@ -49,7 +51,7 @@ const reader = new Reader({ const DURATION = '15m'; export const options = { - setupTimeout: '6m', + setupTimeout: '8m', teardownTimeout: '6m', scenarios: { passthrough_read: { @@ -100,21 +102,14 @@ export const options = { } }, thresholds: { - 'cmhandles_created_per_second': ['value >= 22'], - 'cmhandles_deleted_per_second': ['value >= 22'], + 'http_req_failed': ['rate == 0'], + 'cmhandles_created_per_second': ['avg >= 22'], + 'cmhandles_deleted_per_second': ['avg >= 22'], 'ncmp_overhead_passthrough_read': ['avg <= 100'], 'ncmp_overhead_passthrough_read_alt_id': ['avg <= 100'], 'ncmp_overhead_passthrough_write': ['avg <= 100'], - 'http_req_duration{scenario:id_search_module}': ['avg <= 625'], - 'http_req_duration{scenario:cm_search_module}': ['avg <= 13000'], - 'http_req_failed{scenario:id_search_module}': ['rate == 0'], - 'http_req_failed{scenario:cm_search_module}': ['rate == 0'], - 'http_req_failed{scenario:passthrough_read}': ['rate == 0'], - 'http_req_failed{scenario:passthrough_write}': ['rate == 0'], - 'http_req_failed{group:::setup}':['rate == 0'], - 'http_req_failed{group:::teardown}':['rate == 0'], - 'http_req_failed{scenario:data_operation_send_async_http_request}': ['rate == 0'], - 'kafka_reader_error_count{scenario:data_operation_consume_kafka_responses}': ['count == 0'], + 'id_search_duration': ['avg <= 625'], + 'cm_search_duration': ['avg <= 13000'], 'data_operations_batch_read_cmhandles_per_second': ['avg >= 150'], }, }; @@ -134,7 +129,7 @@ export function setup() { const endTimeInMillis = Date.now(); const totalRegistrationTimeInSeconds = (endTimeInMillis - startTimeInMillis) / 1000.0; - cmHandlesCreatedPerSecondGauge.add(TOTAL_CM_HANDLES / totalRegistrationTimeInSeconds); + cmHandlesCreatedPerSecondTrend.add(TOTAL_CM_HANDLES / totalRegistrationTimeInSeconds); } export function teardown() { @@ -150,45 +145,52 @@ export function teardown() { const endTimeInMillis = Date.now(); const totalDeregistrationTimeInSeconds = (endTimeInMillis - startTimeInMillis) / 1000.0; - cmHandlesDeletedPerSecondGauge.add(TOTAL_CM_HANDLES / totalDeregistrationTimeInSeconds); + cmHandlesDeletedPerSecondTrend.add(TOTAL_CM_HANDLES / totalDeregistrationTimeInSeconds); } export function passthrough_read() { const response = passthroughRead(); - check(response, { 'passthrough read status equals 200': (r) => r.status === 200 }); - const overhead = response.timings.duration - READ_DATA_FOR_CM_HANDLE_DELAY_MS; - passthroughReadNcmpOverheadTrend.add(overhead); + if (check(response, { 'passthrough read status equals 200': (r) => r.status === 200 })) { + const overhead = response.timings.duration - READ_DATA_FOR_CM_HANDLE_DELAY_MS; + passthroughReadNcmpOverheadTrend.add(overhead); + } } export function passthrough_read_alt_id() { const response = passthroughReadWithAltId(); - check(response, { 'passthrough read with alternate Id status equals 200': (r) => r.status === 200 }); - const overhead = response.timings.duration - READ_DATA_FOR_CM_HANDLE_DELAY_MS; - passthroughReadNcmpOverheadTrendWithAlternateId.add(overhead); + if (check(response, { 'passthrough read with alternate Id status equals 200': (r) => r.status === 200 })) { + const overhead = response.timings.duration - READ_DATA_FOR_CM_HANDLE_DELAY_MS; + passthroughReadNcmpOverheadTrendWithAlternateId.add(overhead); + } } export function passthrough_write() { const response = passthroughWrite(); - check(response, { 'passthrough write status equals 201': (r) => r.status === 201 }); - const overhead = response.timings.duration - WRITE_DATA_FOR_CM_HANDLE_DELAY_MS; - passthroughWriteNcmpOverheadTrend.add(overhead); + if (check(response, { 'passthrough write status equals 201': (r) => r.status === 201 })) { + const overhead = response.timings.duration - WRITE_DATA_FOR_CM_HANDLE_DELAY_MS; + passthroughWriteNcmpOverheadTrend.add(overhead); + } } export function id_search_module() { const response = executeCmHandleIdSearch('module'); - check(response, { 'module ID search status equals 200': (r) => r.status === 200 }); - check(JSON.parse(response.body), { 'module ID search returned expected CM-handles': (arr) => arr.length === TOTAL_CM_HANDLES }); + if (check(response, { 'CM handle ID search status equals 200': (r) => r.status === 200 }) + && check(response, { 'CM handle ID search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { + idSearchDurationTrend.add(response.timings.duration); + } } export function cm_search_module() { const response = executeCmHandleSearch('module'); - check(response, { 'module search status equals 200': (r) => r.status === 200 }); - check(JSON.parse(response.body), { 'module search returned expected CM-handles': (arr) => arr.length === TOTAL_CM_HANDLES }); + if (check(response, { 'CM handle search status equals 200': (r) => r.status === 200 }) + && check(response, { 'CM handle search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { + cmSearchDurationTrend.add(response.timings.duration); + } } export function data_operation_send_async_http_request() { - const nextBatchOfCmHandleIds = makeBatchOfCmHandleIds(DATA_OPERATION_READ_BATCH_SIZE,1); - const response = batchRead(nextBatchOfCmHandleIds) + const nextBatchOfCmHandleIds = makeBatchOfCmHandleIds(DATA_OPERATION_READ_BATCH_SIZE, 0); + const response = batchRead(nextBatchOfCmHandleIds); check(response, { 'data operation batch read status equals 200': (r) => r.status === 200 }); } diff --git a/spotbugs/src/main/resources/spotbugs-exclude.xml b/spotbugs/src/main/resources/spotbugs-exclude.xml index 78f61d290e..23e62cdbc7 100644 --- a/spotbugs/src/main/resources/spotbugs-exclude.xml +++ b/spotbugs/src/main/resources/spotbugs-exclude.xml @@ -36,6 +36,7 @@ <Bug pattern="NP_NULL_PARAM_DEREF" /> <Bug pattern="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE" /> <Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" /> + <Bug pattern="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE" /> <!-- https://stackoverflow.com/a/34674776. Doesn't detect Lombok All Args Constructor variables being used with map get key and value, which can lead to spotbugs being detected on used fields --> |