From 4408df5175c3e759f93afceb597ea30af752abb4 Mon Sep 17 00:00:00 2001 From: Bartosz Gardziejewski Date: Thu, 16 Apr 2020 07:46:53 +0200 Subject: Move netconf integration tests to separate catalog Issue-ID: INT-1517 Signed-off-by: Bartosz Gardziejewski Change-Id: I814439a6b0ab7fbe08253c7bd9639bde3320ece2 --- .../it/java/integration/NetconfFunctionsIT.java | 212 +++++++++++++++++++++ .../java/integration/NetconfSimulatorClient.java | 150 +++++++++++++++ 2 files changed, 362 insertions(+) create mode 100644 netconfsimulator/src/it/java/integration/NetconfFunctionsIT.java create mode 100644 netconfsimulator/src/it/java/integration/NetconfSimulatorClient.java (limited to 'netconfsimulator/src/it/java') diff --git a/netconfsimulator/src/it/java/integration/NetconfFunctionsIT.java b/netconfsimulator/src/it/java/integration/NetconfFunctionsIT.java new file mode 100644 index 0000000..5d1a25a --- /dev/null +++ b/netconfsimulator/src/it/java/integration/NetconfFunctionsIT.java @@ -0,0 +1,212 @@ +/*- + * ============LICENSE_START======================================================= + * Simulator + * ================================================================================ + * Copyright (C) 2019 Nokia. 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 integration; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.palantir.docker.compose.connection.DockerMachine; +import com.palantir.docker.compose.connection.waiting.HealthChecks; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.bitbucket.radistao.test.annotation.BeforeAllMethods; +import org.bitbucket.radistao.test.runner.BeforeAfterSpringTestRunner; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; +import com.palantir.docker.compose.DockerComposeRule; +import org.onap.netconfsimulator.kafka.model.KafkaMessage; +import org.springframework.http.HttpStatus; + +import java.io.IOException; + +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static junit.framework.TestCase.fail; +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(BeforeAfterSpringTestRunner.class) +public class NetconfFunctionsIT { + + private static NetconfSimulatorClient client; + private static ObjectMapper objectMapper; + + private static final DockerMachine dockerMachine = DockerMachine + .localMachine() + .build(); + + private static DockerComposeRule docker = DockerComposeRule.builder() + .file("docker-compose.yml") + .machine(dockerMachine) + .removeConflictingContainersOnStartup(true) + .waitingForService("sftp-server", HealthChecks.toHaveAllPortsOpen()) + .waitingForService("ftpes-server", HealthChecks.toHaveAllPortsOpen()) + .waitingForService("zookeeper", HealthChecks.toHaveAllPortsOpen()) + .waitingForService("netopeer", HealthChecks.toHaveAllPortsOpen()) + .waitingForService("kafka1", HealthChecks.toHaveAllPortsOpen()) + .waitingForService("netconf-simulator", HealthChecks.toHaveAllPortsOpen()) + .build(); + + @ClassRule + public static TestRule exposePortMappings = docker; + + @BeforeClass + public static void setUpClass() { + objectMapper = new ObjectMapper(); + client = new NetconfSimulatorClient(String.format("http://%s:%d", docker.containers().ip(), 9000)); + } + + @BeforeAllMethods + public void setupBeforeAll() throws InterruptedException { + if (client.isServiceAvailable(Instant.now(), Duration.ofSeconds(45))) { + Thread.sleep(60000); + return; + } + fail("Application failed to start within established timeout: 45 seconds. Exiting."); + } + + @Before + public void setUp() { + client.reinitializeClient(); + } + + @After + public void tearDown() throws Exception { + client.releaseClient(); + } + + @Test + public void testShouldLoadModelEditConfigurationAndDeleteModule() throws IOException { + // do load + try (CloseableHttpResponse response = client + .loadModel("newyangmodel", "newYangModel.yang", "initialConfig.xml")) { + assertResponseStatusCode(response, HttpStatus.OK); + String original = client.getResponseContentAsString(response); + assertThat(original).isEqualTo("\"Successfully started\"\n"); + } + // do edit-config + try (CloseableHttpResponse updateResponse = client.updateConfig()) { + String afterUpdateConfigContent = client.getResponseContentAsString(updateResponse); + assertResponseStatusCode(updateResponse, HttpStatus.ACCEPTED); + assertThat(afterUpdateConfigContent).isEqualTo("New configuration has been activated"); + } + // do delete + try (CloseableHttpResponse deleteResponse = client.deleteModel("newyangmodel")) { + assertResponseStatusCode(deleteResponse, HttpStatus.OK); + String original = client.getResponseContentAsString(deleteResponse); + assertThat(original).isEqualTo("\"Successfully deleted\"\n"); + } + } + + @Test + public void testShouldGetCurrentConfigurationAndEditItSuccessfully() throws IOException { + try (CloseableHttpResponse updateResponse = client.updateConfig(); + CloseableHttpResponse newCurrentConfigResponse = client.getCurrentConfig()) { + String afterUpdateConfigContent = client.getResponseContentAsString(updateResponse); + + assertResponseStatusCode(updateResponse, HttpStatus.ACCEPTED); + assertResponseStatusCode(newCurrentConfigResponse, HttpStatus.OK); + + assertThat(afterUpdateConfigContent).isEqualTo("New configuration has been activated"); + } + } + + @Test + public void testShouldPersistConfigChangesAndGetAllWhenRequested() throws IOException { + client.updateConfig(); + + try (CloseableHttpResponse newAllConfigChangesResponse = client.getAllConfigChanges()) { + String newAllConfigChangesString = client.getResponseContentAsString(newAllConfigChangesResponse); + assertResponseStatusCode(newAllConfigChangesResponse, HttpStatus.OK); + + List kafkaMessages = objectMapper + .readValue(newAllConfigChangesString, new TypeReference>() { + }); + + assertThat(kafkaMessages.size()).isGreaterThanOrEqualTo(1); + Set configChangeContent = kafkaMessages.stream().map(KafkaMessage::getConfiguration) + .collect(Collectors.toSet()); + assertThat(configChangeContent) + .anyMatch(el -> el.contains("\"new\": {\"path\": \"/pnf-simulator:config/itemValue1\", \"value\": \"100\"}")); + assertThat(configChangeContent) + .anyMatch(el -> el.contains("\"new\": {\"path\": \"/pnf-simulator:config/itemValue2\", \"value\": \"200\"}")); + } + } + + @Test + public void testShouldGetLastMessage() throws IOException { + client.updateConfig(); + + try (CloseableHttpResponse lastConfigChangesResponse = client.getLastConfigChanges(2)) { + String newAllConfigChangesString = client.getResponseContentAsString(lastConfigChangesResponse); + List kafkaMessages = objectMapper + .readValue(newAllConfigChangesString, new TypeReference>() { + }); + + assertThat(kafkaMessages).hasSize(2); + assertThat(kafkaMessages.get(0).getConfiguration()) + .contains("\"new\": {\"path\": \"/pnf-simulator:config/itemValue1\", \"value\": \"100\"}"); + assertThat(kafkaMessages.get(1).getConfiguration()) + .contains("\"new\": {\"path\": \"/pnf-simulator:config/itemValue2\", \"value\": \"200\"}"); + } + } + + @Test + public void testShouldLoadNewYangModelAndReconfigure() throws IOException { + try (CloseableHttpResponse response = client + .loadModel("newyangmodel", "newYangModel.yang", "initialConfig.xml")) { + assertResponseStatusCode(response, HttpStatus.OK); + + String original = client.getResponseContentAsString(response); + + assertThat(original).isEqualTo("\"Successfully started\"\n"); + } + } + + // ToDo: fix this integration test + // https://jira.onap.org/browse/INT-1535 + public void shouldGetLoadedModelByName() throws IOException { + testShouldLoadNewYangModelAndReconfigure(); + + try (CloseableHttpResponse response = client.getConfigByModelAndContainerNames("newyangmodel", "config2")) { + assertResponseStatusCode(response, HttpStatus.OK); + String config = client.getResponseContentAsString(response); + + assertThat(config).isEqualTo( + "\n" + + " 100\n" + + "\n"); + } + + } + + private void assertResponseStatusCode(HttpResponse response, HttpStatus expectedStatus) { + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(expectedStatus.value()); + } + +} diff --git a/netconfsimulator/src/it/java/integration/NetconfSimulatorClient.java b/netconfsimulator/src/it/java/integration/NetconfSimulatorClient.java new file mode 100644 index 0000000..61f2ef1 --- /dev/null +++ b/netconfsimulator/src/it/java/integration/NetconfSimulatorClient.java @@ -0,0 +1,150 @@ +/*- + * ============LICENSE_START======================================================= + * Simulator + * ================================================================================ + * Copyright (C) 2019 Nokia. 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 integration; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.junit.platform.commons.logging.Logger; +import org.junit.platform.commons.logging.LoggerFactory; +import org.springframework.util.ResourceUtils; + +import java.io.IOException; +import java.time.Duration; +import java.time.Instant; + +class NetconfSimulatorClient { + + private CloseableHttpClient netconfClient; + private String simulatorBaseUrl; + private static final Logger LOG = LoggerFactory.getLogger(NetconfSimulatorClient.class); + + NetconfSimulatorClient(String simulatorBaseUrl) { + this.netconfClient = HttpClients.createDefault(); + this.simulatorBaseUrl = simulatorBaseUrl; + } + + CloseableHttpResponse loadModel(String moduleName, String yangModelFileName, String initialiConfigFileName) throws IOException { + String updateConfigUrl = String.format("%s/netconf/model/%s", simulatorBaseUrl, moduleName); + HttpPost httpPost = new HttpPost(updateConfigUrl); + HttpEntity updatedConfig = MultipartEntityBuilder + .create() + .addBinaryBody("yangModel", ResourceUtils.getFile(String.format("classpath:%s", yangModelFileName))) + .addBinaryBody("initialConfig", ResourceUtils.getFile(String.format("classpath:%s",initialiConfigFileName))) + .addTextBody("moduleName", moduleName) + .build(); + httpPost.setEntity(updatedConfig); + return netconfClient.execute(httpPost); + } + + CloseableHttpResponse deleteModel(String moduleName) throws IOException { + String deleteModuleUrl = String.format("%s/netconf/model/%s", simulatorBaseUrl, moduleName); + HttpDelete httpDelete = new HttpDelete(deleteModuleUrl); + return netconfClient.execute(httpDelete); + } + + boolean isServiceAvailable(Instant startTime, Duration maxWaitingDuration) throws InterruptedException { + boolean isServiceReady = false; + while (Duration.between(startTime, Instant.now()).compareTo(maxWaitingDuration) < 1){ + if(checkIfSimResponds()){ + return true; + } + else { + LOG.info(() -> "Simulator not ready yet, retrying in 5s..."); + Thread.sleep(5000); + } + } + return isServiceReady; + } + + private boolean checkIfSimResponds() throws InterruptedException { + try(CloseableHttpResponse pingResponse = getCurrentConfig()){ + String responseString = getResponseContentAsString(pingResponse); + if(pingResponse.getStatusLine().getStatusCode() == 200 && !responseString.trim().isEmpty()){ + return true; + } + } + catch(IOException ex){ + LOG.error(ex, () -> "EXCEPTION"); + Thread.sleep(5000); + } + return false; + } + + CloseableHttpResponse getCurrentConfig() throws IOException { + String netconfAddress = String.format("%s/netconf/get", simulatorBaseUrl); + HttpGet get = new HttpGet(netconfAddress); + return netconfClient.execute(get); + } + + CloseableHttpResponse getConfigByModelAndContainerNames(String model, String container) throws IOException { + String netconfAddress = String + .format("%s/netconf/get/%s/%s", simulatorBaseUrl, model, container); + HttpGet get = new HttpGet(netconfAddress); + return netconfClient.execute(get); + } + + CloseableHttpResponse updateConfig() throws IOException { + String updateConfigUrl = String.format("%s/netconf/edit-config", simulatorBaseUrl); + HttpPost httpPost = new HttpPost(updateConfigUrl); + HttpEntity updatedConfig = MultipartEntityBuilder + .create() + .addBinaryBody("editConfigXml", ResourceUtils.getFile("classpath:updatedConfig.xml")) + .build(); + httpPost.setEntity(updatedConfig); + return netconfClient.execute(httpPost); + } + + CloseableHttpResponse getAllConfigChanges() throws IOException { + String netconfStoreCmHistoryAddress = String.format("%s/store/cm-history", simulatorBaseUrl); + HttpGet configurationChangesResponse = new HttpGet(netconfStoreCmHistoryAddress); + return netconfClient.execute(configurationChangesResponse); + } + + CloseableHttpResponse getLastConfigChanges(int howManyLastChanges) throws IOException { + String netconfStoreCmHistoryAddress = String.format("%s/store/less?offset=%d", simulatorBaseUrl, howManyLastChanges); + HttpGet configurationChangesResponse = new HttpGet(netconfStoreCmHistoryAddress); + return netconfClient.execute(configurationChangesResponse); + } + + void releaseClient() throws IOException { + netconfClient.close(); + } + + void reinitializeClient(){ + netconfClient = HttpClients.createDefault(); + } + + String getResponseContentAsString(HttpResponse response) throws IOException { + HttpEntity entity = response.getEntity(); + String entityStringRepr = EntityUtils.toString(entity); + EntityUtils.consume(entity); + return entityStringRepr; + } + +} -- cgit 1.2.3-korg