diff options
author | 2020-04-08 09:31:13 +0200 | |
---|---|---|
committer | 2020-04-08 09:43:31 +0000 | |
commit | 3c494af52c476a86ae1389991b464914517774b8 (patch) | |
tree | e6d9b4f261eac5f7b3fd0f42e740840a106842e6 /netconfsimulator/src | |
parent | 75496bfc5b2f7e03e49ab4929d1f20962b39c992 (diff) |
Move PNF simulator from /test/mocks to new project
This code is a copy of pnfsimulator located in integration repository
(/test/mocks/pnfsimulator) with added profile "docker" in pom.xml
located in pnfsimulator and netconfsimulator subprojects
Issue-ID: INT-1517
Signed-off-by: Bartosz Gardziejewski <bartosz.gardziejewski@nokia.com>
Change-Id: I725fa0530c41b13cb12705979dee8b8b354dc1a1
Diffstat (limited to 'netconfsimulator/src')
44 files changed, 2911 insertions, 0 deletions
diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/Configuration.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/Configuration.java new file mode 100644 index 0000000..92e5b23 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/Configuration.java @@ -0,0 +1,34 @@ +/*- + * ============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 org.onap.netconfsimulator; + +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.springframework.context.annotation.Bean; + +@org.springframework.context.annotation.Configuration +public class Configuration { + + @Bean + public HttpClient httpClient() { + return HttpClientBuilder.create().build(); + } +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/Main.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/Main.java new file mode 100644 index 0000000..e2a0ed0 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/Main.java @@ -0,0 +1,31 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 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 org.onap.netconfsimulator; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Main { + + public static void main(String[] args) { + SpringApplication.run(Main.class, args); + } +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/SwaggerConfig.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/SwaggerConfig.java new file mode 100644 index 0000000..2e9df99 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/SwaggerConfig.java @@ -0,0 +1,43 @@ +/*- + * ============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 org.onap.netconfsimulator; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@EnableSwagger2 +@Configuration +class SwaggerConfig { + + @Bean + Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors.basePackage("org.onap.netconfsimulator")) + .paths(PathSelectors.any()) + .build(); + } +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/Config.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/Config.java new file mode 100644 index 0000000..9ae5641 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/Config.java @@ -0,0 +1,70 @@ +/*- + * ============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 org.onap.netconfsimulator.kafka; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.onap.netconfsimulator.kafka.listener.KafkaListenerHandler; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; + +@Configuration +@EnableKafka +class Config { + + @Value("${spring.kafka.bootstrap-servers}") + private String bootstrapServer; + + @Value("${spring.kafka.consumer.auto-offset-reset}") + private String offsetReset; + + @Bean + ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory(ConsumerFactory<String, String> consumerFactory) { + ConcurrentKafkaListenerContainerFactory<String, String> containerFactory = new ConcurrentKafkaListenerContainerFactory<>(); + containerFactory.setConsumerFactory(consumerFactory); + return containerFactory; + } + + @Bean + ConsumerFactory<String, String> consumerFactory() { + Map<String, Object> props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServer); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, offsetReset); + return new DefaultKafkaConsumerFactory<>(props); + } + + + @Bean + KafkaListenerHandler kafkaListenerHandler(ConsumerFactory<String, String> consumerFactory) { + return new KafkaListenerHandler(consumerFactory); + } + +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/MessageDTO.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/MessageDTO.java new file mode 100644 index 0000000..4311cd6 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/MessageDTO.java @@ -0,0 +1,31 @@ +/*- + * ============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 org.onap.netconfsimulator.kafka; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +class MessageDTO { + private long timestamp; + private String configuration; +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreController.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreController.java new file mode 100644 index 0000000..33bbdf7 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreController.java @@ -0,0 +1,59 @@ +/*- + * ============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 org.onap.netconfsimulator.kafka; + +import java.util.List; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Slf4j +@RequestMapping("/store") +public class StoreController { + + private StoreService service; + + @Autowired + public StoreController(StoreService service) { + this.service = service; + } + + @GetMapping("/ping") + String ping() { + return "pong"; + } + + @GetMapping("cm-history") + List<MessageDTO> getAllConfigurationChanges() { + return service.getAllMessages(); + } + + @GetMapping("/less") + List<MessageDTO> less(@RequestParam(value = "offset", required = false, defaultValue = "${spring.kafka.default-offset}") long offset) { + return service.getLastMessages(offset); + } + +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreService.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreService.java new file mode 100644 index 0000000..5fddff5 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreService.java @@ -0,0 +1,91 @@ +/*- + * ============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 org.onap.netconfsimulator.kafka; + +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.consumer.Consumer; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.common.TopicPartition; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Slf4j +@Service +public class StoreService { + + private static final String CONFIG_TOPIC = "config"; + private static final long CONSUMING_DURATION_IN_MS = 1000; + + private ConsumerFactory<String, String> consumerFactory; + static final List<String> TOPICS_TO_SUBSCRIBE = Collections.singletonList(CONFIG_TOPIC); + + @Autowired + StoreService(ConsumerFactory<String, String> consumerFactory) { + this.consumerFactory = consumerFactory; + } + + List<MessageDTO> getAllMessages() { + List<MessageDTO> messages = new ArrayList<>(); + String clientID = Long.toString(Instant.now().getEpochSecond()); + try (Consumer<String, String> consumer = consumerFactory.createConsumer(clientID, clientID)) { + consumer.subscribe(TOPICS_TO_SUBSCRIBE); + ConsumerRecords<String, String> consumerRecords = consumer.poll(CONSUMING_DURATION_IN_MS); + consumerRecords.forEach( + consumerRecord -> + messages.add(new MessageDTO(consumerRecord.timestamp(), consumerRecord.value()))); + log.debug(String.format("consumed %d messages", consumerRecords.count())); + } + return messages; + } + + List<MessageDTO> getLastMessages(long offset) { + List<MessageDTO> messages = new ArrayList<>(); + try (Consumer<String, String> consumer = createConsumer(offset)) { + ConsumerRecords<String, String> consumerRecords = consumer.poll(CONSUMING_DURATION_IN_MS); + consumerRecords.forEach(consumerRecord -> + messages.add(new MessageDTO(consumerRecord.timestamp(), consumerRecord.value()))); + } + return messages; + } + + private Consumer<String, String> createConsumer(long offsetFromLastIndex) { + String clientID = Long.toString(Instant.now().getEpochSecond()); + Consumer<String, String> consumer = consumerFactory.createConsumer(clientID, clientID); + consumer.subscribe(TOPICS_TO_SUBSCRIBE); + seekConsumerTo(consumer, offsetFromLastIndex); + return consumer; + } + + private void seekConsumerTo(Consumer<String, String> consumer, long offsetFromLastIndex) { + consumer.seekToEnd(consumer.assignment()); + consumer.poll(CONSUMING_DURATION_IN_MS); + TopicPartition topicPartition = consumer.assignment().iterator().next(); + long topicCurrentSize = consumer.position(topicPartition); + long indexToSeek = offsetFromLastIndex > topicCurrentSize ? 0 : topicCurrentSize - offsetFromLastIndex; + consumer.seek(topicPartition, indexToSeek); + } +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerEntry.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerEntry.java new file mode 100644 index 0000000..e3c04c9 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerEntry.java @@ -0,0 +1,36 @@ +/*- + * ============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 org.onap.netconfsimulator.kafka.listener; + +import lombok.Getter; +import org.springframework.kafka.listener.AbstractMessageListenerContainer; + +@Getter +public class KafkaListenerEntry { + + private String clientId; + private AbstractMessageListenerContainer listenerContainer; + + public KafkaListenerEntry(String clientId, AbstractMessageListenerContainer listenerContainer) { + this.clientId = clientId; + this.listenerContainer = listenerContainer; + } +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandler.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandler.java new file mode 100644 index 0000000..604315d --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandler.java @@ -0,0 +1,67 @@ +/*- + * ============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 org.onap.netconfsimulator.kafka.listener; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.core.ConsumerFactory; + +import org.springframework.kafka.listener.ContainerProperties; +import org.springframework.kafka.listener.KafkaMessageListenerContainer; +import org.springframework.kafka.listener.MessageListener; + + +import org.springframework.kafka.support.TopicPartitionInitialOffset; + +import java.time.Instant; + +public class KafkaListenerHandler { + + private static final int PARTITION = 0; + private static final long NUMBER_OF_HISTORICAL_MESSAGES_TO_SHOW = -10L; + private static final boolean RELATIVE_TO_CURRENT = false; + private ConsumerFactory<String, String> consumerFactory; + + + @Autowired + public KafkaListenerHandler(ConsumerFactory<String, String> consumerFactory) { + this.consumerFactory = consumerFactory; + } + + + public KafkaListenerEntry createKafkaListener(MessageListener messageListener, String topicName) { + String clientId = Long.toString(Instant.now().getEpochSecond()); + ContainerProperties containerProperties = new ContainerProperties(topicName); + containerProperties.setGroupId(clientId); + KafkaMessageListenerContainer<String, String> listenerContainer = createListenerContainer(containerProperties, + topicName); + + listenerContainer.setupMessageListener(messageListener); + return new KafkaListenerEntry(clientId, listenerContainer); + } + + + KafkaMessageListenerContainer<String, String> createListenerContainer(ContainerProperties containerProperties, + String topicName) { + TopicPartitionInitialOffset config = new TopicPartitionInitialOffset(topicName, PARTITION, + NUMBER_OF_HISTORICAL_MESSAGES_TO_SHOW, RELATIVE_TO_CURRENT); + return new KafkaMessageListenerContainer<>(consumerFactory, containerProperties, config); + } +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/model/KafkaMessage.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/model/KafkaMessage.java new file mode 100644 index 0000000..90f283a --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/model/KafkaMessage.java @@ -0,0 +1,37 @@ +/*- + * ============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 org.onap.netconfsimulator.kafka.model; + +import lombok.Getter; + +@Getter +public class KafkaMessage { + private long timestamp; + private String configuration; + + public KafkaMessage(long timestamp, String configuration) { + this.timestamp = timestamp; + this.configuration = configuration; + } + + KafkaMessage() { + } +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/NetconfController.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/NetconfController.java new file mode 100644 index 0000000..cdb4a8f --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/NetconfController.java @@ -0,0 +1,111 @@ +/* + * ============LICENSE_START======================================================= + * NETCONF-CONTROLLER + * ================================================================================ + * Copyright (C) 2018 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 org.onap.netconfsimulator.netconfcore; + +import com.tailf.jnc.JNCException; + +import java.io.IOException; + +import lombok.extern.slf4j.Slf4j; +import org.onap.netconfsimulator.netconfcore.configuration.NetconfConfigurationService; +import org.onap.netconfsimulator.netconfcore.model.LoadModelResponse; +import org.onap.netconfsimulator.netconfcore.model.NetconfModelLoaderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@Slf4j +@RestController +@RequestMapping("netconf") +class NetconfController { + + private final NetconfConfigurationService netconfService; + private final NetconfModelLoaderService netconfModelLoaderService; + + @Autowired + NetconfController(NetconfConfigurationService netconfService, + NetconfModelLoaderService netconfModelLoaderService) { + this.netconfService = netconfService; + this.netconfModelLoaderService = netconfModelLoaderService; + } + + @GetMapping(value = "get", produces = "application/xml") + ResponseEntity<String> getNetconfConfiguration() throws IOException, JNCException { + return ResponseEntity.ok(netconfService.getCurrentConfiguration()); + } + + @GetMapping(value = "get/{model}/{container}", produces = "application/xml") + ResponseEntity<String> getNetconfConfiguration(@PathVariable String model, + @PathVariable String container) + throws IOException { + ResponseEntity<String> entity; + try { + entity = ResponseEntity.ok(netconfService.getCurrentConfiguration(model, container)); + } catch (JNCException exception) { + log.error("Get configuration for model {} and container {} failed.", model, container, + exception); + entity = ResponseEntity.badRequest().body(exception.toString()); + } + return entity; + } + + @PostMapping(value = "edit-config", produces = "application/xml") + @ResponseStatus(HttpStatus.ACCEPTED) + ResponseEntity<String> editConfig(@RequestPart("editConfigXml") MultipartFile editConfig) + throws IOException, JNCException { + log.info("Loading updated configuration"); + if (editConfig == null || editConfig.isEmpty()) { + throw new IllegalArgumentException("No XML file with proper name: editConfigXml found."); + } + return ResponseEntity + .status(HttpStatus.ACCEPTED) + .body(netconfService.editCurrentConfiguration(editConfig)); + } + + @PostMapping("model/{moduleName}") + ResponseEntity<String> loadNewYangModel(@RequestBody MultipartFile yangModel, + @RequestBody MultipartFile initialConfig, @PathVariable String moduleName) + throws IOException { + LoadModelResponse response = netconfModelLoaderService.loadYangModel(yangModel, initialConfig, moduleName); + return ResponseEntity + .status(response.getStatusCode()) + .body(response.getMessage()); + } + + @DeleteMapping("model/{modelName}") + ResponseEntity<String> deleteYangModel(@PathVariable String modelName) + throws IOException { + LoadModelResponse response = netconfModelLoaderService.deleteYangModel(modelName); + return ResponseEntity + .status(response.getStatusCode()) + .body(response.getMessage()); + } +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfBeanConfiguration.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfBeanConfiguration.java new file mode 100644 index 0000000..d90c60d --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfBeanConfiguration.java @@ -0,0 +1,60 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 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 org.onap.netconfsimulator.netconfcore.configuration; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class NetconfBeanConfiguration { + + private static final Logger LOGGER = LoggerFactory.getLogger(NetconfBeanConfiguration.class); + + @Value("${netconf.port}") + private Integer netconfPort; + + @Value("${netconf.address}") + private String netconfAddress; + + @Value("${netconf.user}") + private String netconfUser; + + @Value("${netconf.password}") + private String netconfPassword; + + @Bean + NetconfConfigurationReader configurationReader() { + NetconfConnectionParams params = new NetconfConnectionParams(netconfAddress, netconfPort, netconfUser, netconfPassword); + LOGGER.info("Configuration params are : {}", params); + return new NetconfConfigurationReader(params, new NetconfSessionHelper()); + } + + @Bean + NetconfConfigurationEditor configurationEditor() { + NetconfConnectionParams params = + new NetconfConnectionParams(netconfAddress, netconfPort, netconfUser, netconfPassword); + return new NetconfConfigurationEditor(params, new NetconfSessionHelper()); + } + +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditor.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditor.java new file mode 100644 index 0000000..992c88d --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditor.java @@ -0,0 +1,50 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 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 org.onap.netconfsimulator.netconfcore.configuration; + +import com.tailf.jnc.Element; +import com.tailf.jnc.JNCException; +import com.tailf.jnc.NetconfSession; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; + +@Slf4j +public class NetconfConfigurationEditor { + + private NetconfConnectionParams params; + private NetconfSessionHelper netconfSessionHelper; + + public NetconfConfigurationEditor(NetconfConnectionParams params, NetconfSessionHelper netconfSessionHelper) { + this.params = params; + this.netconfSessionHelper = netconfSessionHelper; + } + + void editConfig(Element configurationXmlElement) throws JNCException, IOException { + log.debug("New configuration passed to simulator: {}", configurationXmlElement.toXMLString()); + NetconfSession session = netconfSessionHelper.createNetconfSession(params); + session.editConfig(configurationXmlElement); + session.closeSession(); + + log.info("Successfully updated configuration"); + } + +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReader.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReader.java new file mode 100644 index 0000000..10fe40e --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReader.java @@ -0,0 +1,57 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 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 org.onap.netconfsimulator.netconfcore.configuration; + +import com.tailf.jnc.JNCException; +import com.tailf.jnc.NetconfSession; +import com.tailf.jnc.NodeSet; +import java.io.IOException; +import java.util.Objects; + +class NetconfConfigurationReader { + + private NetconfConnectionParams params; + private NetconfSessionHelper netconfSessionHelper; + + NetconfConfigurationReader(NetconfConnectionParams params, NetconfSessionHelper netconfSessionHelper) { + this.params = params; + this.netconfSessionHelper = netconfSessionHelper; + } + + String getRunningConfig() throws IOException, JNCException { + NetconfSession session = netconfSessionHelper.createNetconfSession(params); + String config = session.getConfig().toXMLString(); + session.closeSession(); + return config; + } + + String getRunningConfig(String modelPath) throws IOException, JNCException { + NetconfSession session = netconfSessionHelper.createNetconfSession(params); + NodeSet config = session.getConfig(modelPath); + if (Objects.isNull(config) || Objects.isNull(config.first())) { + throw new JNCException(JNCException.ELEMENT_MISSING, modelPath); + } + session.closeSession(); + return config.first().toXMLString(); + } + + +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationService.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationService.java new file mode 100644 index 0000000..248aec4 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationService.java @@ -0,0 +1,76 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 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 org.onap.netconfsimulator.netconfcore.configuration; + +import com.tailf.jnc.Element; +import com.tailf.jnc.JNCException; +import com.tailf.jnc.XMLParser; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import org.xml.sax.InputSource; + +@Service +public class NetconfConfigurationService { + + private static final Logger LOGGER = LoggerFactory.getLogger(NetconfConfigurationService.class); + private static final String CONFIGURATION_HAS_BEEN_ACTIVATED = "New configuration has been activated"; + + private final NetconfConfigurationReader netconfConfigurationReader; + private NetconfConfigurationEditor configurationEditor; + private XMLParser parser; + + @Autowired + public NetconfConfigurationService(NetconfConfigurationReader netconfConfigurationReader, + NetconfConfigurationEditor netconfConfigurationEditor) throws JNCException { + this.netconfConfigurationReader = netconfConfigurationReader; + this.configurationEditor = netconfConfigurationEditor; + this.parser = new XMLParser(); + } + + public String getCurrentConfiguration() throws IOException, JNCException { + return netconfConfigurationReader.getRunningConfig(); + } + + public String getCurrentConfiguration(String model, String container) throws IOException, JNCException { + String path = String.format("/%s:%s", model, container); + return netconfConfigurationReader.getRunningConfig(path); + } + + public String editCurrentConfiguration(MultipartFile newConfiguration) throws IOException, JNCException { + Element configurationElement = convertMultipartToXmlElement(newConfiguration); + configurationEditor.editConfig(configurationElement); + + LOGGER.debug("Loading new configuration: \n{}", configurationElement.toXMLString()); + return CONFIGURATION_HAS_BEEN_ACTIVATED; + } + + private Element convertMultipartToXmlElement(MultipartFile editConfig) throws IOException, JNCException { + InputSource inputSourceUpdateConfig = new InputSource(new ByteArrayInputStream(editConfig.getBytes())); + return parser.parse(inputSourceUpdateConfig); + } +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationTO.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationTO.java new file mode 100644 index 0000000..e43ff69 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationTO.java @@ -0,0 +1,32 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 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 org.onap.netconfsimulator.netconfcore.configuration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class NetconfConfigurationTO { + + private String configuration; + +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConnectionParams.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConnectionParams.java new file mode 100644 index 0000000..ace0ee0 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConnectionParams.java @@ -0,0 +1,37 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 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 org.onap.netconfsimulator.netconfcore.configuration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +@AllArgsConstructor +@ToString +@Getter +class NetconfConnectionParams { + + private final String address; + private final int port; + private final String user; + private final String password; + +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfSessionHelper.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfSessionHelper.java new file mode 100644 index 0000000..69fda7d --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfSessionHelper.java @@ -0,0 +1,37 @@ +/*- + * ============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 org.onap.netconfsimulator.netconfcore.configuration; + +import com.tailf.jnc.JNCException; +import com.tailf.jnc.NetconfSession; +import com.tailf.jnc.SSHConnection; +import com.tailf.jnc.SSHSession; +import java.io.IOException; + +class NetconfSessionHelper { + + NetconfSession createNetconfSession(NetconfConnectionParams params) throws IOException, JNCException { + SSHConnection sshConnection = new SSHConnection(params.getAddress(), params.getPort()); + sshConnection.authenticateWithPassword(params.getUser(), params.getPassword()); + return new NetconfSession(new SSHSession(sshConnection)); + } + +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/LoadModelResponse.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/LoadModelResponse.java new file mode 100644 index 0000000..a6e292f --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/LoadModelResponse.java @@ -0,0 +1,40 @@ +/*- + * ============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 org.onap.netconfsimulator.netconfcore.model; + +public class LoadModelResponse { + + private Integer statusCode; + private String message; + + public LoadModelResponse(Integer statusCode, String message) { + this.statusCode = statusCode; + this.message = message; + } + + public Integer getStatusCode() { + return this.statusCode; + } + + public String getMessage() { + return this.message; + } +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderService.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderService.java new file mode 100644 index 0000000..7e07395 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderService.java @@ -0,0 +1,104 @@ +/*- + * ============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 org.onap.netconfsimulator.netconfcore.model; + +import java.io.IOException; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +@Service +public class NetconfModelLoaderService { + + private static final Logger LOGGER = LoggerFactory.getLogger(NetconfModelLoaderService.class); + + @Value("${netconf.address}") + private String netconfIp; + + @Value("${netconf.model-loader.port}") + private String modelLoaderPort; + + private final HttpClient httpClient; + + @Autowired + public NetconfModelLoaderService(HttpClient httpClient) { + this.httpClient = httpClient; + } + + public LoadModelResponse deleteYangModel(String yangModelName) throws IOException { + String uri = getDeleteAddress(yangModelName); + HttpDelete httpDelete = new HttpDelete(uri); + HttpResponse httpResponse = httpClient.execute(httpDelete); + return parseResponse(httpResponse); + } + + public LoadModelResponse loadYangModel(MultipartFile yangModel, MultipartFile initialConfig, String moduleName) + throws IOException { + HttpPost httpPost = new HttpPost(getBackendAddress()); + HttpEntity httpEntity = MultipartEntityBuilder.create() + .addBinaryBody("yangModel", yangModel.getInputStream(), ContentType.MULTIPART_FORM_DATA, + yangModel.getOriginalFilename()) + .addBinaryBody("initialConfig", initialConfig.getInputStream(), ContentType.MULTIPART_FORM_DATA, + initialConfig.getOriginalFilename()) + .addTextBody("moduleName", moduleName) + .build(); + httpPost.setEntity(httpEntity); + HttpResponse response = httpClient.execute(httpPost); + return parseResponse(response); + } + + String getBackendAddress() { + return String.format("http://%s:%s/model", netconfIp, modelLoaderPort); + } + + String getDeleteAddress(String yangModelName) { + return String.format("%s?yangModelName=%s", getBackendAddress(), yangModelName); + } + + + private LoadModelResponse parseResponse(HttpResponse response) throws IOException { + int statusCode = response.getStatusLine().getStatusCode(); + String responseBody = EntityUtils.toString(response.getEntity()); + + logResponse(statusCode, responseBody); + return new LoadModelResponse(statusCode, responseBody); + } + + private void logResponse(int statusCode, String responseBody) { + if (statusCode >= HttpStatus.BAD_REQUEST.value()) { + LOGGER.error(responseBody); + } else { + LOGGER.info(responseBody); + } + } +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/EndpointConfig.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/EndpointConfig.java new file mode 100644 index 0000000..4eaa850 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/EndpointConfig.java @@ -0,0 +1,46 @@ +/*- + * ============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 org.onap.netconfsimulator.websocket; + +import java.util.Collections; +import org.onap.netconfsimulator.websocket.message.NetconfMessageEncoder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; +import org.springframework.web.socket.server.standard.ServerEndpointRegistration; + +@Configuration +class EndpointConfig { + + @Bean + ServerEndpointRegistration endpointRegistration() { + ServerEndpointRegistration serverEndpointRegistration = new ServerEndpointRegistration("/netconf", + NetconfEndpoint.class); + serverEndpointRegistration.setEncoders(Collections.singletonList(NetconfMessageEncoder.class)); + return serverEndpointRegistration; + } + + @Bean + ServerEndpointExporter endpointExporter() { + return new ServerEndpointExporter(); + } +} + diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/NetconfEndpoint.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/NetconfEndpoint.java new file mode 100644 index 0000000..5870ee1 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/NetconfEndpoint.java @@ -0,0 +1,95 @@ +/*- + * ============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 org.onap.netconfsimulator.websocket; + + +import java.util.Optional; +import javax.websocket.CloseReason; +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfig; +import javax.websocket.RemoteEndpoint; +import javax.websocket.Session; + +import org.onap.netconfsimulator.kafka.listener.KafkaListenerEntry; +import org.onap.netconfsimulator.kafka.listener.KafkaListenerHandler; +import org.onap.netconfsimulator.websocket.message.NetconfMessageListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.listener.AbstractMessageListenerContainer; +import org.springframework.kafka.listener.MessageListener; +import org.springframework.stereotype.Component; + +//instance of this class is created every each websocket request +@Component +class NetconfEndpoint extends Endpoint { + + private static final Logger LOGGER = LoggerFactory.getLogger(NetconfEndpoint.class); + private static final String TOPIC_NAME = "config"; + + private KafkaListenerHandler kafkaListenerHandler; + + public Optional<KafkaListenerEntry> getEntry() { + return entry; + } + + public void setEntry(Optional<KafkaListenerEntry> entry) { + this.entry = entry; + } + + private Optional<KafkaListenerEntry> entry = Optional.empty(); + + + @Autowired + NetconfEndpoint(KafkaListenerHandler listenerHandler) { + this.kafkaListenerHandler = listenerHandler; + } + + @Override + public void onOpen(Session session, EndpointConfig endpointConfig) { + RemoteEndpoint.Basic basicRemote = session.getBasicRemote(); + + addKafkaListener(basicRemote); + entry.ifPresent(x -> LOGGER.info("Session with client: {} established", x.getClientId())); + } + + @Override + public void onError(Session session, Throwable throwable) { + LOGGER.error("Unexpected error occurred", throwable); + } + + @Override + public void onClose(Session session, CloseReason closeReason) { + entry.ifPresent(x -> x.getListenerContainer().stop()); + entry.ifPresent(x -> LOGGER.info("Closing connection for client: {}", x.getClientId())); + } + + + private void addKafkaListener(RemoteEndpoint.Basic remoteEndpoint) { + MessageListener messageListener = new NetconfMessageListener(remoteEndpoint); + + KafkaListenerEntry kafkaListener = kafkaListenerHandler.createKafkaListener(messageListener, TOPIC_NAME); + + AbstractMessageListenerContainer listenerContainer = kafkaListener.getListenerContainer(); + listenerContainer.start(); + entry = Optional.of(kafkaListener); + } +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageEncoder.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageEncoder.java new file mode 100644 index 0000000..349b7e2 --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageEncoder.java @@ -0,0 +1,34 @@ +/*- + * ============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 org.onap.netconfsimulator.websocket.message; + +import org.onap.netconfsimulator.kafka.model.KafkaMessage; +import org.springframework.web.socket.adapter.standard.ConvertingEncoderDecoderSupport; + +public class NetconfMessageEncoder extends ConvertingEncoderDecoderSupport.TextEncoder<KafkaMessage> { + + private static final String MESSAGE_FORMAT = "%s: %s"; + + @Override + public String encode(KafkaMessage netconfMessage) { + return String.format(MESSAGE_FORMAT, netconfMessage.getTimestamp(), netconfMessage.getConfiguration()); + } +} diff --git a/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListener.java b/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListener.java new file mode 100644 index 0000000..61610de --- /dev/null +++ b/netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListener.java @@ -0,0 +1,51 @@ +/*- + * ============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 org.onap.netconfsimulator.websocket.message; + +import java.io.IOException; +import javax.websocket.EncodeException; +import javax.websocket.RemoteEndpoint; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.onap.netconfsimulator.kafka.model.KafkaMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.kafka.listener.MessageListener; + +public class NetconfMessageListener implements MessageListener<String, String> { + + private static final Logger LOGGER = LoggerFactory.getLogger(NetconfMessageListener.class); + private RemoteEndpoint.Basic remoteEndpoint; + + public NetconfMessageListener(RemoteEndpoint.Basic remoteEndpoint) { + this.remoteEndpoint = remoteEndpoint; + } + + @Override + public void onMessage(ConsumerRecord<String, String> message) { + LOGGER.debug("Attempting to send message to {}", remoteEndpoint); + try { + remoteEndpoint + .sendObject(new KafkaMessage(message.timestamp(), message.value())); + } catch (IOException | EncodeException exception) { + LOGGER.error("Error during sending message to remote", exception); + } + } +} diff --git a/netconfsimulator/src/main/resources/application.properties b/netconfsimulator/src/main/resources/application.properties new file mode 100644 index 0000000..3947cf3 --- /dev/null +++ b/netconfsimulator/src/main/resources/application.properties @@ -0,0 +1,8 @@ +netconf.port=830 +netconf.address=netopeer +netconf.user=netconf +netconf.password=netconf +netconf.model-loader.port=5002 +spring.kafka.bootstrap-servers=kafka1:9092 +spring.kafka.default-offset=100 +spring.kafka.consumer.auto-offset-reset=earliest diff --git a/netconfsimulator/src/test/java/integration/NetconfFunctionsIT.java b/netconfsimulator/src/test/java/integration/NetconfFunctionsIT.java new file mode 100644 index 0000000..95ef586 --- /dev/null +++ b/netconfsimulator/src/test/java/integration/NetconfFunctionsIT.java @@ -0,0 +1,211 @@ +/*- + * ============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<KafkaMessage> kafkaMessages = objectMapper + .readValue(newAllConfigChangesString, new TypeReference<List<KafkaMessage>>() { + }); + + assertThat(kafkaMessages.size()).isGreaterThanOrEqualTo(1); + Set<String> configChangeContent = kafkaMessages.stream().map(KafkaMessage::getConfiguration) + .collect(Collectors.toSet()); + assertThat(configChangeContent) + .anyMatch(el -> el.contains("new value: /pnf-simulator:config/itemValue1 = 100")); + assertThat(configChangeContent) + .anyMatch(el -> el.contains("new value: /pnf-simulator:config/itemValue2 = 200")); + } + } + + @Test + public void testShouldGetLastMessage() throws IOException { + client.updateConfig(); + + try (CloseableHttpResponse lastConfigChangesResponse = client.getLastConfigChanges(2)) { + String newAllConfigChangesString = client.getResponseContentAsString(lastConfigChangesResponse); + List<KafkaMessage> kafkaMessages = objectMapper + .readValue(newAllConfigChangesString, new TypeReference<List<KafkaMessage>>() { + }); + + assertThat(kafkaMessages).hasSize(2); + assertThat(kafkaMessages.get(0).getConfiguration()) + .contains("new value: /pnf-simulator:config/itemValue1 = 100"); + assertThat(kafkaMessages.get(1).getConfiguration()) + .contains("new value: /pnf-simulator:config/itemValue2 = 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"); + } + } + + @Test + 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( + "<config2 xmlns=\"http://onap.org/newyangmodel\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + + " <item1>100</item1>\n" + + "</config2>\n"); + } + + } + + private void assertResponseStatusCode(HttpResponse response, HttpStatus expectedStatus) { + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(expectedStatus.value()); + } + +} diff --git a/netconfsimulator/src/test/java/integration/NetconfSimulatorClient.java b/netconfsimulator/src/test/java/integration/NetconfSimulatorClient.java new file mode 100644 index 0000000..61f2ef1 --- /dev/null +++ b/netconfsimulator/src/test/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; + } + +} diff --git a/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/EmbeddedKafkaConfig.java b/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/EmbeddedKafkaConfig.java new file mode 100644 index 0000000..5ddf2b2 --- /dev/null +++ b/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/EmbeddedKafkaConfig.java @@ -0,0 +1,69 @@ +/*- + * ============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 org.onap.netconfsimulator.kafka; + + +import java.util.Map; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.core.ProducerFactory; +import org.springframework.kafka.test.utils.KafkaTestUtils; + +import static org.onap.netconfsimulator.kafka.StoreServiceTest.embeddedKafka; + +@Configuration +class EmbeddedKafkaConfig { + + @Bean + KafkaTemplate<String, String> kafkaTemplate(){ + return new KafkaTemplate<>(producerFactory()); + } + + @Bean + @Autowired + ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory(ConsumerFactory<String, String> consumerFactory){ + ConcurrentKafkaListenerContainerFactory<String, String> containerFactory = new ConcurrentKafkaListenerContainerFactory<>(); + containerFactory.setConsumerFactory(consumerFactory); + return containerFactory; + } + + @Bean + ConsumerFactory<String, String> consumerFactory(){ + Map<String, Object> consumerProperties = + KafkaTestUtils.consumerProps("sender", "false", embeddedKafka.getEmbeddedKafka()); + consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + return new DefaultKafkaConsumerFactory<>(consumerProperties); + } + + private ProducerFactory<String, String> producerFactory() { + Map<String, Object> senderProperties = + KafkaTestUtils.senderProps(embeddedKafka.getEmbeddedKafka().getBrokersAsString()); + return new DefaultKafkaProducerFactory<>(senderProperties); + } + +} diff --git a/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreControllerTest.java b/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreControllerTest.java new file mode 100644 index 0000000..02eec12 --- /dev/null +++ b/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreControllerTest.java @@ -0,0 +1,86 @@ +/*- + * ============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 org.onap.netconfsimulator.kafka; + +import java.time.Instant; +import java.util.List; +import org.assertj.core.api.Assertions; +import org.assertj.core.util.Lists; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static org.mockito.Mockito.when; + +@RunWith(SpringJUnit4ClassRunner.class) +public class StoreControllerTest { + + private static final String MESSAGE_3 = "message 3"; + private static final String MESSAGE_2 = "message 2"; + private static final String MESSAGE_1 = "message 1"; + + private static final List<MessageDTO> ALL_MESSAGES = Lists.newArrayList(new MessageDTO(Instant.now().getEpochSecond(), MESSAGE_1), + new MessageDTO(Instant.now().getEpochSecond(), MESSAGE_2), + new MessageDTO(Instant.now().getEpochSecond(), MESSAGE_3)); + + @Mock + private StoreService service; + + @InjectMocks + private StoreController storeController; + + + @Test + public void lessShouldTakeAllMessagesTest() { + when(service.getLastMessages(3)).thenReturn(ALL_MESSAGES); + + List<MessageDTO> lessResponse = storeController.less(3); + + assertResponseContainsExpectedMessages(lessResponse, 3, MESSAGE_1, MESSAGE_2, MESSAGE_3); + } + + @Test + public void lessShouldTakeTwoMessagesTest() { + when(service.getLastMessages(2)).thenReturn(Lists.newArrayList(new MessageDTO(Instant.now().getEpochSecond(), MESSAGE_1))); + + List<MessageDTO> lessResult = storeController.less(2); + + assertResponseContainsExpectedMessages(lessResult, 1, MESSAGE_1); + } + + @Test + public void shouldGetAllMessages(){ + when(service.getAllMessages()).thenReturn(ALL_MESSAGES); + + List<MessageDTO> allMsgResult = storeController.getAllConfigurationChanges(); + + assertResponseContainsExpectedMessages(allMsgResult, 3, MESSAGE_1, MESSAGE_2, MESSAGE_3); + } + + private void assertResponseContainsExpectedMessages(List<MessageDTO> actualMessages, int expectedMessageCount, String... expectedMessages){ + Assertions.assertThat(actualMessages.stream().map(MessageDTO::getConfiguration)) + .hasSize(expectedMessageCount) + .containsExactly(expectedMessages); + } + +} diff --git a/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreServiceTest.java b/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreServiceTest.java new file mode 100644 index 0000000..fd36116 --- /dev/null +++ b/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreServiceTest.java @@ -0,0 +1,103 @@ +/*- + * ============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 org.onap.netconfsimulator.kafka; + +import org.bitbucket.radistao.test.annotation.BeforeAllMethods; +import org.bitbucket.radistao.test.runner.BeforeAfterSpringTestRunner; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.test.context.EmbeddedKafka; +import org.springframework.kafka.test.rule.EmbeddedKafkaRule; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(BeforeAfterSpringTestRunner.class) +@SpringBootTest(classes = {StoreService.class, EmbeddedKafkaConfig.class}) +@EmbeddedKafka +public class StoreServiceTest { + + private static final String MESSAGE_1 = "message1"; + private static final String MESSAGE_2 = "message2"; + private static final String MESSAGE_3 = "message3"; + + @ClassRule + public static EmbeddedKafkaRule embeddedKafka = new EmbeddedKafkaRule(1, true, 1, "config"); + + @Autowired + StoreService service; + + @Autowired + KafkaTemplate<String, String> kafkaTemplate; + + @BeforeAllMethods + public void setupBeforeAll() { + prepareProducer(); + } + + @Test + public void testShouldReturnAllAvailableMessages(){ + + List<MessageDTO> actualMessages = service.getAllMessages(); + + assertResponseContainsExpectedMessages(actualMessages, 3, MESSAGE_1, MESSAGE_2, MESSAGE_3); + } + + @Test + public void testShouldGetLastMessagesRespectingOffset(){ + + List<MessageDTO> wantedLastMsg = service.getLastMessages(1L); + + assertResponseContainsExpectedMessages(wantedLastMsg, 1, MESSAGE_3); + } + + @Test + public void testShouldGetAll3Messages() { + List<MessageDTO> wantedLastMsgs = service.getLastMessages(3L); + + assertResponseContainsExpectedMessages(wantedLastMsgs, 3, MESSAGE_1, MESSAGE_2, MESSAGE_3); + } + + private void prepareProducer(){ + kafkaTemplate.send("config", "message1"); + kafkaTemplate.send("config", "message2"); + kafkaTemplate.send("config", "message3"); + } + + private void assertResponseContainsExpectedMessages(List<MessageDTO> actualMessages, int expectedMessageCount, String... expectedMessages){ + assertThat(actualMessages.stream().map(MessageDTO::getConfiguration)) + .hasSize(expectedMessageCount) + .containsExactly(expectedMessages); + } + +} + + + + + + + diff --git a/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandlerTest.java b/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandlerTest.java new file mode 100644 index 0000000..fcb7266 --- /dev/null +++ b/netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandlerTest.java @@ -0,0 +1,87 @@ +/*- + * ============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 org.onap.netconfsimulator.kafka.listener; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.MockitoAnnotations.initMocks; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.listener.ContainerProperties; +import org.springframework.kafka.listener.KafkaMessageListenerContainer; +import org.springframework.kafka.listener.MessageListener; + +class KafkaListenerHandlerTest { + + private static final String CLIENT_ID_REGEX = "[0-9]{10,}"; + private static final String SAMPLE_TOPIC = "sampleTopic"; + + @Mock + private ConsumerFactory<String, String> consumerFactory; + + @Mock + private KafkaMessageListenerContainer<String, String> kafkaMessageListenerContainer; + + @Mock + private MessageListener messageListener; + + @BeforeEach + void setUp() { + initMocks(this); + } + + + @Test + void shouldProperlyCreateKafkaListener() { + KafkaListenerHandler kafkaListenerHandler = spy(new KafkaListenerHandler(consumerFactory)); + doReturn(kafkaMessageListenerContainer).when(kafkaListenerHandler) + .createListenerContainer(any(ContainerProperties.class), eq(SAMPLE_TOPIC)); + + KafkaListenerEntry kafkaListenerEntry = kafkaListenerHandler + .createKafkaListener(messageListener, SAMPLE_TOPIC); + + assertThat(kafkaListenerEntry.getListenerContainer()).isEqualTo(kafkaMessageListenerContainer); + assertThat(kafkaListenerEntry.getClientId()).matches(CLIENT_ID_REGEX); + } + + @Test + void shouldProperlyCreateContainer() { + KafkaListenerHandler kafkaListenerHandler = spy(new KafkaListenerHandler(consumerFactory)); + ContainerProperties containerProperties = new ContainerProperties(SAMPLE_TOPIC); + containerProperties.setMessageListener(mock(MessageListener.class)); + + KafkaMessageListenerContainer<String, String> listenerContainer = kafkaListenerHandler + .createListenerContainer(containerProperties, SAMPLE_TOPIC); + + ContainerProperties actualProperties = listenerContainer.getContainerProperties(); + assertThat(actualProperties.getTopics()).isEqualTo(containerProperties.getTopics()); + assertThat(actualProperties.getMessageListener()).isEqualTo(containerProperties.getMessageListener()); + } + + +} diff --git a/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/NetconfControllerTest.java b/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/NetconfControllerTest.java new file mode 100644 index 0000000..73fb627 --- /dev/null +++ b/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/NetconfControllerTest.java @@ -0,0 +1,172 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 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 org.onap.netconfsimulator.netconfcore; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.tailf.jnc.JNCException; +import java.io.IOException; +import java.nio.file.Files; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.onap.netconfsimulator.netconfcore.configuration.NetconfConfigurationService; +import org.onap.netconfsimulator.netconfcore.model.LoadModelResponse; +import org.onap.netconfsimulator.netconfcore.model.NetconfModelLoaderService; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.util.ResourceUtils; +import org.springframework.web.multipart.MultipartFile; + +class NetconfControllerTest { + + private MockMvc mockMvc; + + @Mock + private NetconfConfigurationService netconfService; + + @Mock + private NetconfModelLoaderService netconfModelLoaderService; + + @InjectMocks + private NetconfController controller; + + private static final String SAMPLE_CONFIGURATION = "<config xmlns=\"http://onap.org/pnf-simulator\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><itemValue1>11</itemValue1><itemValue2>22</itemValue2></config>"; + + @BeforeEach + void setUp() { + initMocks(this); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + void testShouldDigestMultipartFile() throws Exception { + byte[] bytes = + Files.readAllBytes(ResourceUtils.getFile("classpath:updatedConfig.xml").toPath()); + MockMultipartFile file = new MockMultipartFile("editConfigXml", bytes); + + mockMvc + .perform(MockMvcRequestBuilders.multipart("/netconf/edit-config").file(file)) + .andExpect(status().isAccepted()); + + verify(netconfService).editCurrentConfiguration(any(MultipartFile.class)); + } + + @Test + void testShouldThrowExceptionWhenEditConfigFileWithIncorrectNameProvided() throws Exception { + MockMultipartFile file = new MockMultipartFile("wrongName", new byte[0]); + + mockMvc + .perform(MockMvcRequestBuilders.multipart("/netconf/edit-config").file(file)) + .andExpect(status().isBadRequest()); + + verify(netconfService, never()).editCurrentConfiguration(any(MultipartFile.class)); + } + + @Test + void testShouldReturnCurrentConfiguration() throws Exception { + when(netconfService.getCurrentConfiguration()).thenReturn(SAMPLE_CONFIGURATION); + + String contentAsString = + mockMvc + .perform(get("/netconf/get")) + .andExpect(status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + + verify(netconfService).getCurrentConfiguration(); + assertThat(contentAsString).isEqualTo(SAMPLE_CONFIGURATION); + } + + @Test + void testShouldReturnConfigurationForGivenPath() throws Exception { + when(netconfService.getCurrentConfiguration("sampleModel", "sampleContainer")) + .thenReturn(SAMPLE_CONFIGURATION); + + String contentAsString = + mockMvc + .perform(get("/netconf/get/sampleModel/sampleContainer")) + .andExpect(status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + + verify(netconfService).getCurrentConfiguration("sampleModel", "sampleContainer"); + assertThat(contentAsString).isEqualTo(SAMPLE_CONFIGURATION); + } + + @Test + void testShouldRaiseBadRequestWhenConfigurationIsNotPresent() throws Exception { + when(netconfService.getCurrentConfiguration("sampleModel", "sampleContainer2")) + .thenThrow(new JNCException(JNCException.ELEMENT_MISSING, "/sampleModel:sampleContainer2")); + + String contentAsString = + mockMvc + .perform(get("/netconf/get/sampleModel/sampleContainer2")) + .andExpect(status().isBadRequest()) + .andReturn() + .getResponse() + .getContentAsString(); + + assertThat(contentAsString).isEqualTo("Element does not exists: /sampleModel:sampleContainer2"); + } + + @Test + void shouldThrowExceptionWhenNoConfigurationPresent() throws IOException, JNCException { + when(netconfService.getCurrentConfiguration()).thenThrow(JNCException.class); + + assertThatThrownBy(() -> mockMvc.perform(get("/netconf/get"))) + .hasRootCauseExactlyInstanceOf(JNCException.class); + } + + @Test + void testShouldDeleteYangModel() throws Exception { + String responseOkString = "Alles klar"; + String yangModelName = "someModel"; + LoadModelResponse loadModelResponse = new LoadModelResponse(200, responseOkString); + String uri = String.format("/netconf/model/%s", yangModelName); + when(netconfModelLoaderService.deleteYangModel(yangModelName)).thenReturn(loadModelResponse); + + String contentAsString = + mockMvc + .perform(delete(uri)) + .andExpect(status().isOk()) + .andReturn() + .getResponse() + .getContentAsString(); + + verify(netconfModelLoaderService).deleteYangModel(yangModelName); + assertThat(contentAsString).isEqualTo(responseOkString); + } +} diff --git a/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditorTest.java b/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditorTest.java new file mode 100644 index 0000000..371bdd8 --- /dev/null +++ b/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditorTest.java @@ -0,0 +1,69 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 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 org.onap.netconfsimulator.netconfcore.configuration; + +import com.tailf.jnc.Element; +import com.tailf.jnc.JNCException; +import com.tailf.jnc.NetconfSession; +import com.tailf.jnc.XMLParser; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.onap.netconfsimulator.netconfcore.configuration.NetconfConfigurationEditor; +import org.springframework.util.ResourceUtils; +import org.xml.sax.InputSource; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.file.Files; + +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +class NetconfConfigurationEditorTest { + + @Mock + private NetconfSession session; + @Mock + private NetconfSessionHelper netconfSessionHelper; + + private NetconfConfigurationEditor editor; + + @BeforeEach + void setUp() throws IOException, JNCException { + initMocks(this); + NetconfConnectionParams params = null; + Mockito.when(netconfSessionHelper.createNetconfSession(params)).thenReturn(session); + editor = new NetconfConfigurationEditor(params, netconfSessionHelper); + } + + @Test + void testShouldEditConfigSuccessfully() throws IOException, JNCException { + byte[] bytes = + Files.readAllBytes(ResourceUtils.getFile("classpath:updatedConfig.xml").toPath()); + Element editConfigXml = new XMLParser().parse(new InputSource(new ByteArrayInputStream(bytes))); + + editor.editConfig(editConfigXml); + + verify(session).editConfig(editConfigXml); + } +} diff --git a/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReaderTest.java b/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReaderTest.java new file mode 100644 index 0000000..a0a15b9 --- /dev/null +++ b/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReaderTest.java @@ -0,0 +1,94 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 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 org.onap.netconfsimulator.netconfcore.configuration; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.tailf.jnc.Element; +import com.tailf.jnc.JNCException; +import com.tailf.jnc.NetconfSession; +import com.tailf.jnc.NodeSet; +import java.io.IOException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +class NetconfConfigurationReaderTest { + + private static final String NETCONF_MODEL_PATH = ""; + private static final String EXPECTED_STRING_XML = "<?xml version=\"1.0\"?>"; + private NetconfConfigurationReader reader; + + @Mock + private NetconfSession netconfSession; + + @Mock + private NetconfSessionHelper netconfSessionHelper; + + @Mock + private NodeSet nodeSet; + + @Mock + private Element element; + + @BeforeEach + void setUp() throws IOException, JNCException { + MockitoAnnotations.initMocks(this); + NetconfConnectionParams params = null; + Mockito.when(netconfSessionHelper.createNetconfSession(params)).thenReturn(netconfSession); + reader = new NetconfConfigurationReader(params, netconfSessionHelper); + } + + @Test + void properlyReadXML() throws IOException, JNCException { + when(netconfSession.getConfig()).thenReturn(nodeSet); + when(nodeSet.toXMLString()).thenReturn(EXPECTED_STRING_XML); + + String result = reader.getRunningConfig(); + + verify(netconfSession).getConfig(); + verify(nodeSet).toXMLString(); + assertThat(result).isEqualTo(EXPECTED_STRING_XML); + } + + @Test + void shouldProperlyReadXmlByName() throws IOException, JNCException { + when(netconfSession.getConfig("/sample:test")).thenReturn(nodeSet); + when(nodeSet.first()).thenReturn(element); + when(element.toXMLString()).thenReturn(EXPECTED_STRING_XML); + + String result = reader.getRunningConfig("/sample:test"); + + verify(netconfSession).getConfig("/sample:test"); + verify(nodeSet, times(2)).first(); + verify(element).toXMLString(); + + assertThat(result).isEqualTo(EXPECTED_STRING_XML); + } + +} diff --git a/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationServiceTest.java b/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationServiceTest.java new file mode 100644 index 0000000..6da6572 --- /dev/null +++ b/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationServiceTest.java @@ -0,0 +1,102 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 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 org.onap.netconfsimulator.netconfcore.configuration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.tailf.jnc.Element; +import com.tailf.jnc.JNCException; +import java.io.IOException; +import java.nio.file.Files; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.util.ResourceUtils; + +class NetconfConfigurationServiceTest { + + @Mock + NetconfConfigurationReader reader; + + @Mock + NetconfConfigurationEditor editor; + + @InjectMocks + NetconfConfigurationService service; + + private static String CURRENT_CONFIG_XML_STRING = + "<config xmlns=\"http://onap.org/pnf-simulator\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + + " <itemValue1>100</itemValue1>\n" + + " <itemValue2>200</itemValue2>\n" + + "</config>\n"; + + @BeforeEach + void setUp() { + initMocks(this); + } + + @Test + void testShouldReturnCorrectCurrentConfiguration() throws IOException, JNCException { + String expectedConfiguration = CURRENT_CONFIG_XML_STRING; + when(reader.getRunningConfig()).thenReturn(CURRENT_CONFIG_XML_STRING); + + String actualCurrentConfiguration = service.getCurrentConfiguration(); + + assertThat(actualCurrentConfiguration).isEqualToIgnoringCase(expectedConfiguration); + } + + @Test + void testShouldThrowExceptionWhenCurrentConfigurationDoesNotExists() throws IOException, JNCException{ + when(reader.getRunningConfig()).thenThrow(JNCException.class); + + assertThatThrownBy(() -> service.getCurrentConfiguration()).isInstanceOf(JNCException.class); + } + + @Test + void testShouldEditConfigurationSuccessfully() throws IOException, JNCException{ + byte[] bytes = + Files.readAllBytes(ResourceUtils.getFile("classpath:updatedConfig.xml").toPath()); + MockMultipartFile editConfigXmlContent = new MockMultipartFile("editConfigXml", bytes); + ArgumentCaptor<Element> elementCaptor = ArgumentCaptor.forClass(Element.class); + doNothing().when(editor).editConfig(elementCaptor.capture()); + + service.editCurrentConfiguration(editConfigXmlContent); + + assertThat(elementCaptor.getValue().toXMLString()).isEqualTo(CURRENT_CONFIG_XML_STRING); + } + + @Test + void testShouldRaiseExceptionWhenMultipartFileIsInvalidXmlFile() throws IOException { + byte[] bytes = + Files.readAllBytes(ResourceUtils.getFile("classpath:invalidXmlFile.xml").toPath()); + MockMultipartFile editConfigXmlContent = new MockMultipartFile("editConfigXml", bytes); + + assertThatThrownBy(() -> service.editCurrentConfiguration(editConfigXmlContent)).isInstanceOf(JNCException.class); + } + +} diff --git a/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderServiceTest.java b/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderServiceTest.java new file mode 100644 index 0000000..a10876b --- /dev/null +++ b/netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderServiceTest.java @@ -0,0 +1,121 @@ +/*- + * ============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 org.onap.netconfsimulator.netconfcore.model; + + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.web.multipart.MultipartFile; + +class NetconfModelLoaderServiceTest { + + @Mock + private HttpClient httpClient; + + private NetconfModelLoaderService modelLoaderService; + + @BeforeEach + void setUp() { + MockitoAnnotations.initMocks(this); + modelLoaderService = new NetconfModelLoaderService(httpClient); + } + + + @Test + void shouldSendMultipartToServer() throws IOException { + //given + String loadModelAddress = modelLoaderService.getBackendAddress(); + makeMockClientReturnStatusOk(httpClient, HttpPost.class); + ArgumentCaptor<HttpPost> postArgumentCaptor = ArgumentCaptor.forClass(HttpPost.class); + MultipartFile yangMmodel = mock(MultipartFile.class); + MultipartFile initialConfig = mock(MultipartFile.class); + String moduleName = "moduleName"; + when(yangMmodel.getInputStream()).thenReturn(getEmptyImputStream()); + when(initialConfig.getInputStream()).thenReturn(getEmptyImputStream()); + + //when + LoadModelResponse response = modelLoaderService.loadYangModel(yangMmodel, initialConfig, moduleName); + + //then + verify(httpClient).execute(postArgumentCaptor.capture()); + HttpPost sentPost = postArgumentCaptor.getValue(); + assertThat(response.getStatusCode()).isEqualTo(200); + assertThat(response.getMessage()).isEqualTo(""); + assertThat(sentPost.getURI().toString()).isEqualTo(loadModelAddress); + assertThat(sentPost.getEntity().getContentType().getElements()[0].getName()).isEqualTo("multipart/form-data"); + } + + @Test + void shouldSendDeleteRequestToServer() throws IOException { + //given + String yangModelName = "sampleModel"; + String deleteModelAddress = modelLoaderService.getDeleteAddress(yangModelName); + makeMockClientReturnStatusOk(httpClient, HttpDelete.class); + ArgumentCaptor<HttpDelete> deleteArgumentCaptor = ArgumentCaptor.forClass(HttpDelete.class); + + //when + LoadModelResponse response = modelLoaderService.deleteYangModel(yangModelName); + + //then + verify(httpClient).execute(deleteArgumentCaptor.capture()); + HttpDelete sendDelete = deleteArgumentCaptor.getValue(); + assertThat(response.getStatusCode()).isEqualTo(200); + assertThat(response.getMessage()).isEqualTo(""); + assertThat(sendDelete.getURI().toString()).isEqualTo(deleteModelAddress); + } + + private void makeMockClientReturnStatusOk(HttpClient client, + Class<? extends HttpRequestBase> httpMethodClass) throws IOException { + HttpResponse httpResponse = mock(HttpResponse.class); + StatusLine mockStatus = mock(StatusLine.class); + HttpEntity mockEntity = mock(HttpEntity.class); + + when(client.execute(any(httpMethodClass))).thenReturn(httpResponse); + when(httpResponse.getStatusLine()).thenReturn(mockStatus); + when(mockStatus.getStatusCode()).thenReturn(200); + when(httpResponse.getEntity()).thenReturn(mockEntity); + when(mockEntity.getContent()).thenReturn(getEmptyImputStream()); + } + + private InputStream getEmptyImputStream() { + return new ByteArrayInputStream("".getBytes()); + } + +} diff --git a/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/NetconfEndpointTest.java b/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/NetconfEndpointTest.java new file mode 100644 index 0000000..c1484d4 --- /dev/null +++ b/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/NetconfEndpointTest.java @@ -0,0 +1,135 @@ +/*- + * ============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 org.onap.netconfsimulator.websocket; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.util.Map; +import java.util.Optional; +import javax.websocket.CloseReason; +import javax.websocket.EndpointConfig; +import javax.websocket.RemoteEndpoint; +import javax.websocket.Session; +import org.apache.kafka.common.Metric; +import org.apache.kafka.common.MetricName; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.onap.netconfsimulator.kafka.listener.KafkaListenerEntry; +import org.onap.netconfsimulator.kafka.listener.KafkaListenerHandler; +import org.onap.netconfsimulator.websocket.message.NetconfMessageListener; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.listener.AbstractMessageListenerContainer; + +import org.springframework.kafka.listener.ContainerProperties; +import org.springframework.kafka.listener.GenericMessageListener; + +class NetconfEndpointTest { + + + @Mock + private KafkaListenerHandler kafkaListenerHandler; + + @Mock + private Session session; + + @Mock + private EndpointConfig endpointConfig; + + @Mock + private RemoteEndpoint.Basic remoteEndpoint; + + + @BeforeEach + void setUp() { + initMocks(this); + } + + + @Test + void shouldCreateKafkaListenerWhenClientInitializeConnection() { + NetconfEndpoint netconfEndpoint = new NetconfEndpoint(kafkaListenerHandler); + AbstractMessageListenerContainer abstractMessageListenerContainer = getListenerContainer(); + when(session.getBasicRemote()).thenReturn(remoteEndpoint); + KafkaListenerEntry kafkaListenerEntry = new KafkaListenerEntry("sampleGroupId", + abstractMessageListenerContainer); + when(kafkaListenerHandler.createKafkaListener(any(NetconfMessageListener.class), eq("config"))) + .thenReturn(kafkaListenerEntry); + + netconfEndpoint.onOpen(session, endpointConfig); + + assertThat(netconfEndpoint.getEntry().get().getClientId()).isEqualTo("sampleGroupId"); + assertThat(netconfEndpoint.getEntry().get().getListenerContainer()).isEqualTo(abstractMessageListenerContainer); + + verify(abstractMessageListenerContainer).start(); + } + + + @Test + void shouldCloseListenerWhenClientDisconnects() { + NetconfEndpoint netconfEndpoint = new NetconfEndpoint(kafkaListenerHandler); + AbstractMessageListenerContainer abstractMessageListenerContainer = getListenerContainer(); + netconfEndpoint.setEntry( Optional.of(new KafkaListenerEntry("sampleGroupId", abstractMessageListenerContainer)) ); + + netconfEndpoint.onClose(session, mock(CloseReason.class)); + + verify(abstractMessageListenerContainer).stop(); + } + + class TestAbstractMessageListenerContainer extends AbstractMessageListenerContainer { + + + TestAbstractMessageListenerContainer(ContainerProperties containerProperties) { + super(mock(ConsumerFactory.class),containerProperties); + } + + @Override + protected void doStart() { + + } + + @Override + protected void doStop(Runnable callback) { + + } + + @Override + public Map<String, Map<MetricName, ? extends Metric>> metrics() { + return null; + } + } + + private AbstractMessageListenerContainer getListenerContainer() { + ContainerProperties containerProperties = new ContainerProperties("config"); + containerProperties.setGroupId("sample"); + containerProperties.setMessageListener(mock(GenericMessageListener.class)); + TestAbstractMessageListenerContainer testAbstractMessageListenerContainer = new TestAbstractMessageListenerContainer( + containerProperties); + return spy(testAbstractMessageListenerContainer); + } +} diff --git a/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListenerTest.java b/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListenerTest.java new file mode 100644 index 0000000..bb040d1 --- /dev/null +++ b/netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListenerTest.java @@ -0,0 +1,73 @@ +/*- + * ============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 org.onap.netconfsimulator.websocket.message; + + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.io.IOException; +import javax.websocket.EncodeException; +import javax.websocket.RemoteEndpoint; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.onap.netconfsimulator.kafka.model.KafkaMessage; + + +class NetconfMessageListenerTest { + + private static final ConsumerRecord<String, String> KAFKA_RECORD = new ConsumerRecord<>("sampleTopic", 0, 0, + "sampleKey", "sampleValue"); + + @Mock + private RemoteEndpoint.Basic remoteEndpoint; + + @InjectMocks + private NetconfMessageListener netconfMessageListener; + + + @BeforeEach + void setUp() { + initMocks(this); + } + + + @Test + void shouldProperlyParseAndSendConsumerRecord() throws IOException, EncodeException { + netconfMessageListener.onMessage(KAFKA_RECORD); + + verify(remoteEndpoint).sendObject(any(KafkaMessage.class)); + } + + + + @Test + void shouldNotPropagateEncodeException() throws IOException, EncodeException { + doThrow(new EncodeException("","")).when(remoteEndpoint).sendObject(any(KafkaMessage.class)); + + netconfMessageListener.onMessage(KAFKA_RECORD); + } +} diff --git a/netconfsimulator/src/test/resources/application-it.properties b/netconfsimulator/src/test/resources/application-it.properties new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/netconfsimulator/src/test/resources/application-it.properties diff --git a/netconfsimulator/src/test/resources/initialConfig.xml b/netconfsimulator/src/test/resources/initialConfig.xml new file mode 100644 index 0000000..f28a1a0 --- /dev/null +++ b/netconfsimulator/src/test/resources/initialConfig.xml @@ -0,0 +1,23 @@ +<!-- + ============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========================================================= + --> + +<config2 xmlns="http://onap.org/newyangmodel"> + <item1>100</item1> +</config2> diff --git a/netconfsimulator/src/test/resources/invalidXmlFile.xml b/netconfsimulator/src/test/resources/invalidXmlFile.xml new file mode 100644 index 0000000..3debd8c --- /dev/null +++ b/netconfsimulator/src/test/resources/invalidXmlFile.xml @@ -0,0 +1,23 @@ +<!-- + ============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========================================================= + --> + +<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <itemValue1>100</itemValue1> +<config> diff --git a/netconfsimulator/src/test/resources/newYangModel.yang b/netconfsimulator/src/test/resources/newYangModel.yang new file mode 100644 index 0000000..bbe66c3 --- /dev/null +++ b/netconfsimulator/src/test/resources/newYangModel.yang @@ -0,0 +1,8 @@ +module newyangmodel { + namespace "http://onap.org/newyangmodel"; + prefix config2; + container config2 { + config true; + leaf item1 {type uint32;} + } +} diff --git a/netconfsimulator/src/test/resources/updatedConfig.xml b/netconfsimulator/src/test/resources/updatedConfig.xml new file mode 100644 index 0000000..628a710 --- /dev/null +++ b/netconfsimulator/src/test/resources/updatedConfig.xml @@ -0,0 +1,24 @@ +<!-- + ============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========================================================= + --> + +<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <itemValue1>100</itemValue1> + <itemValue2>200</itemValue2> +</config> diff --git a/netconfsimulator/src/test/resources/updatedConfigForCmHistory.xml b/netconfsimulator/src/test/resources/updatedConfigForCmHistory.xml new file mode 100644 index 0000000..5bc0e42 --- /dev/null +++ b/netconfsimulator/src/test/resources/updatedConfigForCmHistory.xml @@ -0,0 +1,24 @@ +<!-- + ============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========================================================= + --> + +<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <itemValue1>500</itemValue1> + <itemValue2>1000</itemValue2> +</config> |