diff options
Diffstat (limited to 'prh-app-server/src')
6 files changed, 388 insertions, 5 deletions
diff --git a/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/exceptions/AaiFailureException.java b/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/exceptions/AaiFailureException.java new file mode 100644 index 00000000..6741bb2d --- /dev/null +++ b/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/exceptions/AaiFailureException.java @@ -0,0 +1,28 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2019 NOKIA Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.dcaegen2.services.prh.exceptions; + +public class AaiFailureException extends PrhTaskException { + + public AaiFailureException(String message) { + super(message); + } +} diff --git a/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/tasks/BbsActionsTask.java b/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/tasks/BbsActionsTask.java new file mode 100644 index 00000000..755d1282 --- /dev/null +++ b/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/tasks/BbsActionsTask.java @@ -0,0 +1,145 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2019 NOKIA Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.dcaegen2.services.prh.tasks; + +import static org.onap.dcaegen2.services.sdk.rest.services.adapters.http.HttpMethod.PUT; + +import com.google.gson.JsonObject; +import io.netty.buffer.ByteBuf; +import io.vavr.collection.HashMap; +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; +import org.onap.dcaegen2.services.prh.configuration.Config; +import org.onap.dcaegen2.services.prh.exceptions.AaiFailureException; +import org.onap.dcaegen2.services.prh.model.ConsumerDmaapModel; +import org.onap.dcaegen2.services.prh.model.bbs.ImmutableLogicalLink; +import org.onap.dcaegen2.services.prh.model.bbs.ImmutableRelationship; +import org.onap.dcaegen2.services.prh.model.bbs.ImmutableRelationshipWrapper; +import org.onap.dcaegen2.services.prh.model.bbs.RelationshipWrapper; +import org.onap.dcaegen2.services.prh.model.utils.GsonSerializer; +import org.onap.dcaegen2.services.prh.model.utils.HttpUtils; +import org.onap.dcaegen2.services.sdk.rest.services.aai.client.config.AaiClientConfiguration; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.HttpResponse; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.ImmutableHttpRequest; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.RequestBody; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.RxHttpClient; +import org.onap.dcaegen2.services.sdk.rest.services.uri.URI.URIBuilder; +import org.reactivestreams.Publisher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +@Component +public class BbsActionsTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(BbsActionsTask.class); + private static final String ATTACHMENT_POINT = "attachmentPoint"; + private static final String LOGICAL_LINK_URI = "/network/logical-links/logical-link/"; + private static final String PNF_URI = "/network/pnfs/pnf/"; + + private final AaiClientConfiguration aaiConfig; + private final RxHttpClient httpClient; + + @Autowired + BbsActionsTask(Config config) { + this(config, RxHttpClient.create()); + } + + BbsActionsTask(Config config, RxHttpClient httpClient) { + this.aaiConfig = config.getAaiClientConfiguration(); + this.httpClient = httpClient; + } + + public Mono<ConsumerDmaapModel> execute(ConsumerDmaapModel consumerDmaapModel) { + JsonObject additionalFields = consumerDmaapModel.getAdditionalFields(); + if (additionalFields == null || !additionalFields.has(ATTACHMENT_POINT)) { + return Mono.just(consumerDmaapModel); + } + String linkName = additionalFields.get(ATTACHMENT_POINT).getAsString(); + if (linkName.isEmpty()) { + LOGGER.warn("Attachment point is empty! Ignore related actions."); + return Mono.just(consumerDmaapModel); + } + String pnfName = consumerDmaapModel.getCorrelationId(); + return createLogicalLinkInAai(linkName, pnfName).flatMap(handleResponse(consumerDmaapModel)); + } + + private Function<HttpResponse, Mono<ConsumerDmaapModel>> handleResponse(ConsumerDmaapModel consumerDmaapModel) { + return response -> HttpUtils.isSuccessfulResponseCode(response.statusCode()) + ? Mono.just(consumerDmaapModel) + : Mono.error(new AaiFailureException( + "Incorrect response when performing BBS-related actions: " + response.statusCode())); + } + + private Mono<HttpResponse> createLogicalLinkInAai(String linkName, String pnfName) { + ImmutableHttpRequest request = buildLogicalLinkRequest(linkName, pnfName); + + return httpClient.call(request); + } + + private ImmutableHttpRequest buildLogicalLinkRequest(String linkName, String pnfName) { + String uri = buildLogicalLinkUri(linkName); + ImmutableLogicalLink logicalLink = buildModel(linkName, pnfName); + Publisher<ByteBuf> jsonPayload = RequestBody.fromString(GsonSerializer.createJsonBody(logicalLink)); + + return ImmutableHttpRequest + .builder() + .method(PUT) + .url(uri) + .body(jsonPayload) + .customHeaders(HashMap.ofAll(aaiConfig.aaiHeaders())) + .build(); + } + + private ImmutableLogicalLink buildModel(String linkName, String pnfName) { + List<RelationshipWrapper> relationships = buildRelationLink(pnfName); + + return ImmutableLogicalLink + .builder() + .linkName(linkName) + .linkType(ATTACHMENT_POINT) + .relationshipList(relationships) + .build(); + } + + private List<RelationshipWrapper> buildRelationLink(String pnfName) { + return Arrays.asList(ImmutableRelationshipWrapper + .builder() + .relationship(ImmutableRelationship + .builder() + .relatedLink(PNF_URI + pnfName) + .build()) + .build()); + } + + private String buildLogicalLinkUri(String linkName) { + return new URIBuilder() + .scheme(aaiConfig.aaiProtocol()) + .host(aaiConfig.aaiHost()) + .port(aaiConfig.aaiPort()) + .path(aaiConfig.aaiBasePath() + LOGICAL_LINK_URI + linkName) + .build() + .toString(); + } +}
\ No newline at end of file diff --git a/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/tasks/ScheduledTasks.java b/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/tasks/ScheduledTasks.java index 6ae8a3c7..16a6f8c5 100644 --- a/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/tasks/ScheduledTasks.java +++ b/prh-app-server/src/main/java/org/onap/dcaegen2/services/prh/tasks/ScheduledTasks.java @@ -54,6 +54,7 @@ public class ScheduledTasks { private final DmaapConsumerTask dmaapConsumerTask; private final DmaapPublisherTask dmaapProducerTask; private final AaiProducerTask aaiProducerTask; + private final BbsActionsTask bbsActionsTask; private Map<String, String> mdcContextMap; /** @@ -64,11 +65,16 @@ public class ScheduledTasks { * @param aaiPublisherTask - second task */ @Autowired - public ScheduledTasks(DmaapConsumerTask dmaapConsumerTask, DmaapPublisherTask dmaapPublisherTask, - AaiProducerTask aaiPublisherTask, Map<String, String> mdcContextMap) { + public ScheduledTasks( + DmaapConsumerTask dmaapConsumerTask, + DmaapPublisherTask dmaapPublisherTask, + AaiProducerTask aaiPublisherTask, + BbsActionsTask bbsActionsTask, + Map<String, String> mdcContextMap) { this.dmaapConsumerTask = dmaapConsumerTask; this.dmaapProducerTask = dmaapPublisherTask; this.aaiProducerTask = aaiPublisherTask; + this.bbsActionsTask = bbsActionsTask; this.mdcContextMap = mdcContextMap; } @@ -88,6 +94,9 @@ public class ScheduledTasks { .doOnError(exception -> logger.warn("AAIProducerTask exception has been registered: ", exception)) .onErrorResume(resumePrhPredicate(), exception -> Mono.empty()) + .flatMap(this::processAdditionalFields) + .doOnError(exception -> + logger.warn("BBSActionsTask exception has been registered: ", exception)) .flatMap(this::publishToDmaapConfiguration) .doOnError(exception -> logger.warn("DMaaPProducerTask exception has been registered: ", exception)) @@ -102,7 +111,6 @@ public class ScheduledTasks { } } - private void onComplete() { logger.info("PRH tasks have been completed"); } @@ -121,7 +129,6 @@ public class ScheduledTasks { } } - private Flux<ConsumerDmaapModel> consumeFromDMaaPMessage() { return Flux.defer(() -> { MdcVariables.setMdcContextMap(mdcContextMap); @@ -148,6 +155,10 @@ public class ScheduledTasks { } } + private Mono<ConsumerDmaapModel> processAdditionalFields(ConsumerDmaapModel consumerDmaapModel) { + return bbsActionsTask.execute(consumerDmaapModel); + } + private Mono<HttpClientResponse> publishToDmaapConfiguration(ConsumerDmaapModel monoAaiModel) { try { return dmaapProducerTask.execute(monoAaiModel); diff --git a/prh-app-server/src/test/java/org/onap/dcaegen2/services/prh/tasks/BbsActionsTaskTest.java b/prh-app-server/src/test/java/org/onap/dcaegen2/services/prh/tasks/BbsActionsTaskTest.java new file mode 100644 index 00000000..197d9a8c --- /dev/null +++ b/prh-app-server/src/test/java/org/onap/dcaegen2/services/prh/tasks/BbsActionsTaskTest.java @@ -0,0 +1,181 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2019 NOKIA Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.dcaegen2.services.prh.tasks; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.http.HttpResponseStatus; +import java.io.InputStreamReader; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.onap.dcaegen2.services.prh.TestAppConfiguration; +import org.onap.dcaegen2.services.prh.configuration.AppConfig; +import org.onap.dcaegen2.services.prh.exceptions.AaiFailureException; +import org.onap.dcaegen2.services.prh.model.ConsumerDmaapModel; +import org.onap.dcaegen2.services.prh.model.ImmutableConsumerDmaapModel; +import org.onap.dcaegen2.services.sdk.rest.services.aai.client.config.AaiClientConfiguration; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.HttpRequest; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.HttpResponse; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.ImmutableHttpResponse; +import org.onap.dcaegen2.services.sdk.rest.services.adapters.http.RxHttpClient; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; + +class BbsActionsTaskTest { + + private static final String CORRECT_LOGICAL_LINK_JSON = "bbs_action/correct_logical_link.json"; + public static final String AAI_URL = "https://aai.onap.svc.cluster.local:8443/aai/v12/network/logical-links/logical-link/some-link"; + + private AaiClientConfiguration aaiClientConfiguration = TestAppConfiguration.createDefaultAaiClientConfiguration(); + private AppConfig appConfig = mock(AppConfig.class); + private RxHttpClient httpClient = mock(RxHttpClient.class); + + @Test + void whenPassedObjectDoesntHaveAdditionalFields_ReturnPayloadTransparently() { + // given + given(appConfig.getAaiClientConfiguration()).willReturn(aaiClientConfiguration); + ConsumerDmaapModel consumerDmaapModel = buildConsumerDmaapModel(null); + + // when + ConsumerDmaapModel result = new BbsActionsTask(appConfig, httpClient).execute(consumerDmaapModel).block(); + + // then + verifyZeroInteractions(httpClient); + assertThat(result).isEqualTo(consumerDmaapModel); + } + + @Test + void whenPassedObjectHasEmptyLogicalLink_ReturnPayloadTransparently() { + // given + given(appConfig.getAaiClientConfiguration()).willReturn(aaiClientConfiguration); + + JsonObject additionalFields = new JsonObject(); + additionalFields.addProperty("attachmentPoint", ""); + ConsumerDmaapModel consumerDmaapModel = buildConsumerDmaapModel(additionalFields); + + // when + ConsumerDmaapModel result = new BbsActionsTask(appConfig, httpClient).execute(consumerDmaapModel).block(); + + // then + verifyZeroInteractions(httpClient); + assertThat(result).isEqualTo(consumerDmaapModel); + } + + @Test + void whenPassedObjectHasLogicalLink_createLogicalLink_and_associateWithPnf_and_ReturnPayloadTransparently() { + // given + given(appConfig.getAaiClientConfiguration()).willReturn(aaiClientConfiguration); + + JsonObject additionalFields = new JsonObject(); + additionalFields.addProperty("attachmentPoint", "some-link"); + ConsumerDmaapModel consumerDmaapModel = buildConsumerDmaapModel(additionalFields); + + given(httpClient.call(any())).willReturn(Mono.just(buildAaiResponse(HttpResponseStatus.OK))); + + // when + Mono<ConsumerDmaapModel> response = new BbsActionsTask(appConfig, httpClient).execute(consumerDmaapModel); + + // then + ArgumentCaptor<HttpRequest> captor = ArgumentCaptor.forClass(HttpRequest.class); + verify(httpClient).call(captor.capture()); + verifyNoMoreInteractions(httpClient); + + HttpRequest request = captor.getValue(); + assertThat(request.url()).isEqualTo( + "https://aai.onap.svc.cluster.local:8443/aai/v12/network/logical-links/logical-link/some-link"); + assertJsonEquals(request.body(), CORRECT_LOGICAL_LINK_JSON); + assertThat(request.headers().toJavaMap()).containsOnlyKeys("X-InvocationID", "X-RequestID"); + assertEquals(consumerDmaapModel, response.block()); + } + + @Test + void whenPassedObjectHasLogicalLink_butAaiQueryFails_returnError() { + // given + given(appConfig.getAaiClientConfiguration()).willReturn(aaiClientConfiguration); + + JsonObject additionalFields = new JsonObject(); + additionalFields.addProperty("attachmentPoint", "some-link"); + ConsumerDmaapModel consumerDmaapModel = buildConsumerDmaapModel(additionalFields); + + given(httpClient.call(any())).willReturn(Mono.just(buildAaiResponse(HttpResponseStatus.INTERNAL_SERVER_ERROR))); + + // when + Mono<ConsumerDmaapModel> response = new BbsActionsTask(appConfig, httpClient).execute(consumerDmaapModel); + + // then + ArgumentCaptor<HttpRequest> captor = ArgumentCaptor.forClass(HttpRequest.class); + verify(httpClient).call(captor.capture()); + verifyNoMoreInteractions(httpClient); + + HttpRequest request = captor.getValue(); + assertThat(request.url()).isEqualTo(AAI_URL); + assertJsonEquals(request.body(), CORRECT_LOGICAL_LINK_JSON); + assertThat(request.headers().toJavaMap()).containsOnlyKeys("X-InvocationID", "X-RequestID"); + + assertThatThrownBy(response::block).hasCauseInstanceOf(AaiFailureException.class); + } + + private ConsumerDmaapModel buildConsumerDmaapModel(JsonObject additionalFields) { + return ImmutableConsumerDmaapModel.builder() + .ipv4("10.16.123.234") + .ipv6("0:0:0:0:0:FFFF:0A10:7BEA") + .correlationId("NOKQTFCOC540002E") + .serialNumber("QTFCOC540002E") + .equipVendor("nokia") + .equipModel("3310") + .equipType("type") + .nfRole("role") + .swVersion("v4.5.0.1") + .additionalFields(additionalFields) + .build(); + } + + private HttpResponse buildAaiResponse(HttpResponseStatus status) { + return ImmutableHttpResponse + .builder() + .statusCode(status.code()) + .url("") + .rawBody("".getBytes()) + .build(); + } + + private void assertJsonEquals(Publisher<ByteBuf> requestBody, String path) { + JsonParser parser = new JsonParser(); + JsonElement result = parser.parse(Mono.from(requestBody).block().toString(UTF_8)); + JsonElement expected = parser + .parse(new InputStreamReader(getClass().getClassLoader().getResourceAsStream(path))); + + assertThat(result).isEqualTo(expected); + } +}
\ No newline at end of file diff --git a/prh-app-server/src/test/java/org/onap/dcaegen2/services/prh/tasks/ScheduleControllerSpy.java b/prh-app-server/src/test/java/org/onap/dcaegen2/services/prh/tasks/ScheduleControllerSpy.java index 2f7ff61c..b12b3d39 100644 --- a/prh-app-server/src/test/java/org/onap/dcaegen2/services/prh/tasks/ScheduleControllerSpy.java +++ b/prh-app-server/src/test/java/org/onap/dcaegen2/services/prh/tasks/ScheduleControllerSpy.java @@ -45,12 +45,19 @@ public class ScheduleControllerSpy { private AaiProducerTask aaiPublisherTaskImplSpy; @Autowired + private BbsActionsTask bbsActionsTaskImplSpy; + + @Autowired private Map<String, String> mdcContextMap; @Bean @Primary public ScheduledTasks registerSimpleScheduledTask() { - return spy(new ScheduledTasks(dmaapConsumerTaskImplSpy, dmaapPublisherTaskImplSpy, aaiPublisherTaskImplSpy, + return spy(new ScheduledTasks( + dmaapConsumerTaskImplSpy, + dmaapPublisherTaskImplSpy, + aaiPublisherTaskImplSpy, + bbsActionsTaskImplSpy, mdcContextMap)); } } diff --git a/prh-app-server/src/test/resources/bbs_action/correct_logical_link.json b/prh-app-server/src/test/resources/bbs_action/correct_logical_link.json new file mode 100644 index 00000000..e49a45ba --- /dev/null +++ b/prh-app-server/src/test/resources/bbs_action/correct_logical_link.json @@ -0,0 +1,11 @@ +{ + "link-name": "some-link", + "link-type": "attachmentPoint", + "relationship-list": [ + { + "relationship": { + "related-link": "/network/pnfs/pnf/NOKQTFCOC540002E" + } + } + ] +}
\ No newline at end of file |