aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBartosz Gardziejewski <bartosz.gardziejewski@nokia.com>2020-04-08 09:31:13 +0200
committerBogumil Zebek <bogumil.zebek@nokia.com>2020-04-08 09:43:31 +0000
commit3c494af52c476a86ae1389991b464914517774b8 (patch)
treee6d9b4f261eac5f7b3fd0f42e740840a106842e6
parent75496bfc5b2f7e03e49ab4929d1f20962b39c992 (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
-rw-r--r--.coafile4
-rw-r--r--.gitignore3
-rw-r--r--checkstyle-suppressions.xml46
-rw-r--r--deployment/assembly.xml35
-rw-r--r--deployment/pnf-pnp-simulators-resources.zipbin0 -> 2292 bytes
-rw-r--r--deployment/pom.xml52
-rw-r--r--deployment/src/MANIFEST.json17
-rw-r--r--deployment/src/simulators_heat_template.env10
-rw-r--r--deployment/src/simulators_heat_template.yaml147
-rw-r--r--netconfsimulator/Dockerfile_app4
-rw-r--r--netconfsimulator/Dockerfile_netopeer6
-rw-r--r--netconfsimulator/README.md297
-rw-r--r--netconfsimulator/apt.conf5
-rw-r--r--netconfsimulator/config/netconf.env5
-rw-r--r--netconfsimulator/docker-compose.yml93
-rw-r--r--netconfsimulator/docker/Dockerfile4
-rw-r--r--netconfsimulator/ftpes/files/ftpes-noone.txt0
-rw-r--r--netconfsimulator/ftpes/files/onap/ftpes-onap.txt0
-rwxr-xr-xnetconfsimulator/ftpes/tls/pure-ftpd.pem49
-rwxr-xr-xnetconfsimulator/ftpes/userpass/pureftpd.passwd1
-rw-r--r--netconfsimulator/netconf/__init__.py19
-rwxr-xr-xnetconfsimulator/netconf/initialize_netopeer.sh37
-rw-r--r--netconfsimulator/netconf/load_server_certs.xml44
-rw-r--r--netconfsimulator/netconf/netopeer_change_saver.py107
-rw-r--r--netconfsimulator/netconf/newmodel.xml24
-rw-r--r--netconfsimulator/netconf/newmodel.yang9
-rw-r--r--netconfsimulator/netconf/pnf-simulator.data.xml24
-rw-r--r--netconfsimulator/netconf/pnf-simulator.yang9
-rwxr-xr-xnetconfsimulator/netconf/set-up-xmls.py162
-rw-r--r--netconfsimulator/netconf/test_yang_loader_server.py121
-rw-r--r--netconfsimulator/netconf/tls_listen.xml49
-rw-r--r--netconfsimulator/netconf/yang_loader.log1
-rw-r--r--netconfsimulator/netconf/yang_loader_server.py172
-rw-r--r--netconfsimulator/pom.xml318
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/Configuration.java34
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/Main.java31
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/SwaggerConfig.java43
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/Config.java70
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/MessageDTO.java31
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreController.java59
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/StoreService.java91
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerEntry.java36
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandler.java67
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/kafka/model/KafkaMessage.java37
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/NetconfController.java111
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfBeanConfiguration.java60
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditor.java50
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReader.java57
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationService.java76
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationTO.java32
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConnectionParams.java37
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfSessionHelper.java37
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/LoadModelResponse.java40
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderService.java104
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/EndpointConfig.java46
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/NetconfEndpoint.java95
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageEncoder.java34
-rw-r--r--netconfsimulator/src/main/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListener.java51
-rw-r--r--netconfsimulator/src/main/resources/application.properties8
-rw-r--r--netconfsimulator/src/test/java/integration/NetconfFunctionsIT.java211
-rw-r--r--netconfsimulator/src/test/java/integration/NetconfSimulatorClient.java150
-rw-r--r--netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/EmbeddedKafkaConfig.java69
-rw-r--r--netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreControllerTest.java86
-rw-r--r--netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/StoreServiceTest.java103
-rw-r--r--netconfsimulator/src/test/java/org/onap/netconfsimulator/kafka/listener/KafkaListenerHandlerTest.java87
-rw-r--r--netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/NetconfControllerTest.java172
-rw-r--r--netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationEditorTest.java69
-rw-r--r--netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationReaderTest.java94
-rw-r--r--netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/configuration/NetconfConfigurationServiceTest.java102
-rw-r--r--netconfsimulator/src/test/java/org/onap/netconfsimulator/netconfcore/model/NetconfModelLoaderServiceTest.java121
-rw-r--r--netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/NetconfEndpointTest.java135
-rw-r--r--netconfsimulator/src/test/java/org/onap/netconfsimulator/websocket/message/NetconfMessageListenerTest.java73
-rw-r--r--netconfsimulator/src/test/resources/application-it.properties0
-rw-r--r--netconfsimulator/src/test/resources/initialConfig.xml23
-rw-r--r--netconfsimulator/src/test/resources/invalidXmlFile.xml23
-rw-r--r--netconfsimulator/src/test/resources/newYangModel.yang8
-rw-r--r--netconfsimulator/src/test/resources/updatedConfig.xml24
-rw-r--r--netconfsimulator/src/test/resources/updatedConfigForCmHistory.xml24
-rw-r--r--netconfsimulator/ssh/ssh_host_rsa_key49
-rw-r--r--netconfsimulator/ssh/ssh_host_rsa_key.pub1
-rw-r--r--netconfsimulator/tls/ca.crt32
-rw-r--r--netconfsimulator/tls/ca.key54
-rw-r--r--netconfsimulator/tls/client.crt30
-rw-r--r--netconfsimulator/tls/client.key51
-rw-r--r--netconfsimulator/tls/client.req28
-rw-r--r--netconfsimulator/tls/server.req28
-rw-r--r--netconfsimulator/tls/server_cert.crt30
-rw-r--r--netconfsimulator/tls/server_key.pem51
-rw-r--r--pnfsimulator/.gitignore4
-rw-r--r--pnfsimulator/Dockerfile8
-rw-r--r--pnfsimulator/README.md333
-rw-r--r--pnfsimulator/certificates/AAF_RootCA.crt31
-rw-r--r--pnfsimulator/certificates/dcaelocal.crt20
-rw-r--r--pnfsimulator/config/config.json9
-rw-r--r--pnfsimulator/db/pnf_simulator.js28
-rw-r--r--pnfsimulator/deployment/PnP_PNF_sim_heat_template.yml118
-rw-r--r--pnfsimulator/docker-compose.yml38
-rw-r--r--pnfsimulator/integration/pom.xml130
-rw-r--r--pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/Main.java35
-rw-r--r--pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/VesSimulatorController.java59
-rw-r--r--pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/VesSimulatorService.java36
-rw-r--r--pnfsimulator/integration/src/main/resources/application.properties1
-rw-r--r--pnfsimulator/integration/src/main/resources/keystorebin0 -> 2196 bytes
-rw-r--r--pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/BasicAvailabilityTest.java235
-rw-r--r--pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/OptionalTemplatesTest.java204
-rw-r--r--pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/SearchInTemplatesTest.java269
-rw-r--r--pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/TemplatesManagementTest.java175
-rw-r--r--pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/TestConfiguration.java36
-rw-r--r--pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/suites/DockerBasedTestsSuite.java46
-rw-r--r--pnfsimulator/integration/src/test/resources/application.properties6
-rw-r--r--pnfsimulator/integration/src/test/resources/templates/notification.json45
-rw-r--r--pnfsimulator/integration/src/test/resources/templates/overwrite_template.json6
-rw-r--r--pnfsimulator/integration/src/test/resources/templates/overwritten_template.json6
-rw-r--r--pnfsimulator/integration/src/test/resources/templates/registration.json36
-rw-r--r--pnfsimulator/integration/src/test/resources/templates/search/complicated_template.json43
-rw-r--r--pnfsimulator/integration/src/test/resources/templates/search/simple_template.json12
-rw-r--r--pnfsimulator/integration/src/test/resources/templates/search/template_with_array.json23
-rw-r--r--pnfsimulator/integration/src/test/resources/templates/search/template_with_booleans.json12
-rw-r--r--pnfsimulator/integration/src/test/resources/templates/search/template_with_floats.json13
-rw-r--r--pnfsimulator/integration/src/test/resources/templates/search/template_with_ints.json12
-rw-r--r--pnfsimulator/integration/src/test/resources/templates/upload_template.json6
-rw-r--r--pnfsimulator/pom.xml403
-rw-r--r--pnfsimulator/src/assembly/resources.xml57
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/Main.java57
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/SwaggerConfig.java46
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/db/Row.java34
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/db/Storage.java41
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/event/EventData.java76
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/event/EventDataRepository.java26
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/event/EventDataService.java63
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherConfig.java39
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherEventProcessor.java110
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherService.java44
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherThread.java81
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/logging/MDCVariables.java35
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/SimulatorController.java219
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/TemplateController.java105
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/FullEvent.java48
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/SearchExp.java42
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/SimulatorParams.java46
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/SimulatorRequest.java51
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/TemplateRequest.java42
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/util/DateUtil.java35
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/util/JsonObjectDeserializer.java42
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/util/ResponseBuilder.java62
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/DBTemplateReader.java49
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/EventNotFoundException.java28
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/FilesystemTemplateReader.java54
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/IncrementProvider.java26
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/IncrementProviderImpl.java47
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/JsonTokenProcessor.java134
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/KeywordsExtractor.java118
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/KeywordsHandler.java72
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/KeywordsValueProvider.java80
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/SimulatorService.java121
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/TemplatePatcher.java53
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/TemplateReader.java28
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapter.java26
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImpl.java104
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SSLAuthenticationHelper.java79
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SslSupportLevel.java120
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/Keyword.java74
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/NonParameterKeyword.java65
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/SingleParameterKeyword.java73
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/TwoParameterKeyword.java80
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventJob.java93
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventScheduler.java121
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/QuartzConfiguration.java38
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfig.java49
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfigRepository.java26
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfigService.java52
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/template/FsToDbTemplateSynchronizer.java74
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/template/Template.java92
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/template/TemplateRepository.java26
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/template/TemplateService.java81
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/IllegalJsonValueException.java28
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/JsonUtils.java104
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/TemplateSearchHelper.java95
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/handler/PrimitiveValueCriteriaBuilder.java103
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/viewmodel/FlatTemplateContent.java45
-rw-r--r--pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/viewmodel/KeyValuePair.java40
-rw-r--r--pnfsimulator/src/main/resources/application.properties18
-rw-r--r--pnfsimulator/src/main/resources/logback.xml70
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/event/EventDataServiceTest.java133
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/filesystem/InMemoryTemplateStorage.java71
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/filesystem/WatcherEventProcessorTest.java124
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/SimulatorControllerTest.java212
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/TemplateControllerTest.java256
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/util/DateUtilTest.java38
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/util/ResponseBuilderTest.java65
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/IncrementProviderImplTest.java78
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorInvalidRandomIntegerTest.java67
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorInvalidRandomStringTest.java67
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorInvalidTimestampTest.java65
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidRandomIntegerTest.java66
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidRandomPrimitiveIntegerTest.java66
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidRandomStringTest.java69
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidTimestampPrimitiveTest.java66
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidTimestampTest.java67
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsHandlerTest.java304
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsValueProviderTest.java81
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/SimulatorServiceTest.java227
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/TemplatePatcherTest.java164
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/TemplateReaderTest.java51
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImplTest.java99
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SslSupportLevelTest.java52
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/scheduler/EventJobTest.java90
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/scheduler/EventSchedulerTest.java149
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfigServiceTest.java104
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/template/TemplateServiceTest.java152
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/template/search/JsonUtilsTest.java166
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/template/search/TemplateSearchHelperTest.java160
-rw-r--r--pnfsimulator/src/test/java/org/onap/pnfsimulator/template/search/handler/PrimitiveValueCriteriaBuilderTest.java75
-rw-r--r--pnfsimulator/src/test/resources/application.properties2
-rw-r--r--pnfsimulator/src/test/resources/client.p12bin0 -> 2685 bytes
-rw-r--r--pnfsimulator/src/test/resources/logback-test.xml69
-rw-r--r--pnfsimulator/src/test/resources/org/onap/pnfsimulator/simulator/filesystem/test1.json12
-rw-r--r--pnfsimulator/src/test/resources/org/onap/pnfsimulator/simulator/invalidJsonStructureEvent.json1
-rw-r--r--pnfsimulator/src/test/resources/org/onap/pnfsimulator/simulator/validExampleMeasurementEvent.json89
-rw-r--r--pnfsimulator/src/test/resources/trustStorebin0 -> 1455 bytes
-rw-r--r--pnfsimulator/store/client.p12bin0 -> 2685 bytes
-rw-r--r--pnfsimulator/store/trustStorebin0 -> 1455 bytes
-rw-r--r--pnfsimulator/templates/measurement.json44
-rw-r--r--pnfsimulator/templates/notification.json42
-rw-r--r--pnfsimulator/templates/registration.json33
-rw-r--r--pom.xml79
-rw-r--r--simulator-cli/.gitignore7
-rw-r--r--simulator-cli/README.md304
-rw-r--r--simulator-cli/cli/__init__.py19
-rw-r--r--simulator-cli/cli/client/__init__.py19
-rw-r--r--simulator-cli/cli/client/tailf_client.py59
-rw-r--r--simulator-cli/cli/data/logging.ini20
-rwxr-xr-xsimulator-cli/cli/netconf_simulator.py278
-rwxr-xr-xsimulator-cli/cli/pnf_simulator.py374
-rw-r--r--simulator-cli/requirements.txt23
-rw-r--r--simulator-cli/setup.py34
-rw-r--r--simulator-cli/tests/__init__.py19
-rw-r--r--simulator-cli/tests/resources/notification.json17
-rw-r--r--simulator-cli/tests/test_netconf_simulator.py165
-rw-r--r--simulator-cli/tests/test_pnf_simulator.py270
-rw-r--r--simulator-cli/tests/test_tailf_client.py47
241 files changed, 16973 insertions, 1 deletions
diff --git a/.coafile b/.coafile
index 470c5ae..d6ae3b1 100644
--- a/.coafile
+++ b/.coafile
@@ -9,7 +9,9 @@ bears = JSONFormatBear
json_sort = False
indent_size = 2
ignore =
- .tox/**
+ .tox/**,
+ pnfsimulator/src/test/resources/**,
+ netconfsimulator/src/test/resources/**
[py]
bears = PyLintBear
diff --git a/.gitignore b/.gitignore
index 33defe4..8d81d9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
.tox/
+**/*.iml
+**/.idea
+**/target
diff --git a/checkstyle-suppressions.xml b/checkstyle-suppressions.xml
new file mode 100644
index 0000000..8d95607
--- /dev/null
+++ b/checkstyle-suppressions.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!--
+ ============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=========================================================
+ -->
+
+
+<!DOCTYPE suppressions PUBLIC
+ "-//Checkstyle//DTD SuppressionFilter Configuration 1.0//EN"
+ "https://checkstyle.org/dtds/suppressions_1_0.dtd">
+
+<suppressions>
+ <suppress checks=".*" files="\.java" lines="1-20"
+ />
+ <suppress checks=".*" files="\.properties"
+ />
+ <suppress checks="javadoc" files="\.java"
+ />
+ <suppress checks="LineLength" files="\.java"
+ />
+ <suppress checks="HiddenField" files="\.java"
+ />
+ <suppress checks="FinalClass" files="\.java"
+ />
+ <suppress checks="FinalParameters" files="\.java"
+ />
+ <suppress checks="AvoidInlineConditionals" files="\.java"
+ />
+ <suppress checks="DesignForExtension" files="\.java"
+ />
+</suppressions>
diff --git a/deployment/assembly.xml b/deployment/assembly.xml
new file mode 100644
index 0000000..a0609ef
--- /dev/null
+++ b/deployment/assembly.xml
@@ -0,0 +1,35 @@
+<!--
+ ============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=========================================================
+ -->
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+ <id>resources</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>zip</format>
+ </formats>
+ <fileSets>
+
+ <fileSet>
+ <directory>src</directory>
+ <outputDirectory>.</outputDirectory>
+ </fileSet>
+ </fileSets>
+</assembly> \ No newline at end of file
diff --git a/deployment/pnf-pnp-simulators-resources.zip b/deployment/pnf-pnp-simulators-resources.zip
new file mode 100644
index 0000000..e099cbb
--- /dev/null
+++ b/deployment/pnf-pnp-simulators-resources.zip
Binary files differ
diff --git a/deployment/pom.xml b/deployment/pom.xml
new file mode 100644
index 0000000..25fe08b
--- /dev/null
+++ b/deployment/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============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=========================================================
+ -->
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onap.simulator</groupId>
+ <artifactId>simulator-parent</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>onboarding-package</artifactId>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <descriptor>assembly.xml</descriptor>
+ <finalName>pnf-pnp-simulators</finalName>
+ <outputDirectory>.</outputDirectory>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/deployment/src/MANIFEST.json b/deployment/src/MANIFEST.json
new file mode 100644
index 0000000..1065b6e
--- /dev/null
+++ b/deployment/src/MANIFEST.json
@@ -0,0 +1,17 @@
+{
+ "name": "",
+ "description": "",
+ "data": [
+ {
+ "file": "simulators_heat_template.yaml",
+ "type": "HEAT",
+ "isBase": "true",
+ "data": [
+ {
+ "file": "simulators_heat_template.env",
+ "type": "HEAT_ENV"
+ }
+ ]
+ }
+ ]
+}
diff --git a/deployment/src/simulators_heat_template.env b/deployment/src/simulators_heat_template.env
new file mode 100644
index 0000000..cf2db52
--- /dev/null
+++ b/deployment/src/simulators_heat_template.env
@@ -0,0 +1,10 @@
+parameters:
+ image_name: ubuntu-14-04-cloud-amd64
+ flavor_name: m1.medium
+ public_net_id: 41eae12a-27f7-4ace-9fda-3cd55c7c0651
+ private_net_id: 41eae12a-27f7-4ace-9fda-3cd55c7c0651
+ private_subnet_id: 41eae12a-27f7-4ace-9fda-3cd55c7c0651
+ proxy: http://10.10.10.10:8080
+ vnf_id: simulators_vnf
+ vf_module_id: simulators_vsp
+ key_name: onap-dev \ No newline at end of file
diff --git a/deployment/src/simulators_heat_template.yaml b/deployment/src/simulators_heat_template.yaml
new file mode 100644
index 0000000..253fcc7
--- /dev/null
+++ b/deployment/src/simulators_heat_template.yaml
@@ -0,0 +1,147 @@
+description: Heat template that deploys PnP PNF simulators
+heat_template_version: '2013-05-23'
+parameters:
+ flavor_name: {description: Type of instance (flavor) to be used, label: Flavor,
+ type: string}
+ image_name: {description: Image to be used for compute instance, label: Image name
+ or ID, type: string}
+ key_name: {description: Public/Private key pair name, label: Key pair name, type: string}
+ public_net_id: {description: Public network that enables remote connection to VNF,
+ label: Public network name or ID, type: string}
+ private_net_id: {type: string, description: Private network id, label: Private network name or ID}
+ private_subnet_id: {type: string, description: Private subnet id, label: Private subnetwork name or ID}
+ proxy: {type: string, description: Proxy, label: Proxy, default: ""}
+ vnf_id: {type: string, label: VNF ID, description: The VNF ID is provided by ONAP}
+ vf_module_id: {type: string, label: vBase module ID, description: The vBase Module ID is provided by ONAP}
+resources:
+ PNF_PnP_simulator:
+ type: OS::Nova::Server
+ properties:
+ key_name: { get_param: key_name }
+ image: { get_param: image_name }
+ flavor: { get_param: flavor_name }
+ networks:
+ - port: { get_resource: PNF_PnP_simulator_port0 }
+ user_data_format: RAW
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+
+ set_versions () {
+ DOCKER_VERSION=17.03
+ DOCKER_COMPOSE_VERSION=1.22.0
+ }
+
+ set_proxy () {
+ HTTP_PROXY=$proxy
+ HTTPS_PROXY=$proxy
+ http_proxy=$proxy
+ https_proxy=$proxy
+ export HTTP_PROXY=$proxy
+ export HTTPS_PROXY=$proxy
+ export http_proxy=$proxy
+ export https_proxy=$proxy
+ }
+
+ enable_root_ssh () {
+ sed -i 's/PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
+ sed -i 's/PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
+ service sshd restart
+ echo -e "arthur\narthur" | passwd root
+ }
+
+ update_os () {
+ rm -rf /var/lib/apt/lists/*
+ apt-get clean
+ apt-get update
+ }
+
+ set_apt_get_proxy () {
+ cat > /etc/apt/apt.conf.d/proxy.conf << EOF
+ Acquire {
+ HTTP::proxy "$proxy";
+ HTTPS::proxy "$proxy";
+ }
+ EOF
+ }
+
+ docker_remove () {
+ dnf -y remove docker \
+ docker-client \
+ docker-client-latest \
+ docker-common \
+ docker-latest \
+ docker-latest-logrotate \
+ docker-logrotate \
+ docker-selinux \
+ docker-engine-selinux \
+ docker-engine
+ }
+
+ docker_install_and_configure () {
+ curl "https://releases.rancher.com/install-docker/$DOCKER_VERSION.sh" | sh
+ mkdir -p /etc/systemd/system/docker.service.d/
+ cat > /etc/systemd/system/docker.service.d/docker.conf << EOF
+ [Service]
+ ExecStart=
+ ExecStart=/usr/bin/dockerd -H fd:// --insecure-registry=nexus3.onap.org:10003
+ Environment="HTTP_PROXY=$proxy"
+ Environment="HTTPS_PROXY=$proxy"
+ EOF
+ systemctl daemon-reload
+ systemctl restart docker
+ apt-mark hold docker-ce
+ docker login -u docker -p docker nexus3.onap.org:10003
+ }
+ docker_compose_install () {
+ curl -L https://github.com/docker/compose/releases/download/$DOCKER_COMPOSE_VERSION/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
+ chmod +x /usr/local/bin/docker-compose
+ }
+ pnf_sim_file_checkout () {
+ cd /root; git clone https://gerrit.onap.org/r/integration
+ }
+
+ start_pnf_simulator () {
+ docker login -u docker -p docker nexus3.onap.org:10003
+ cd ~/integration/test/mocks/pnfsimulator/pnfsimulator
+ docker-compose up -d
+ }
+
+ start_netconf_simulator () {
+ docker login -u docker -p docker nexus3.onap.org:10003
+ cd ~/integration/test/mocks/pnfsimulator/netconfsimulator
+ docker-compose up -d
+ }
+
+ set_versions
+ set_proxy
+ enable_root_ssh
+ set_apt_get_proxy
+ docker_install_and_configure
+ docker_compose_install
+ pnf_sim_file_checkout
+ start_pnf_simulator
+ start_netconf_simulator
+ params:
+ $proxy: { get_param: proxy }
+ PNF_PnP_simulator_port0:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_param: private_net_id }
+ security_groups:
+ - default
+ fixed_ips:
+ - subnet_id: { get_param: private_subnet_id }
+ PNF_PnP_simulator_public:
+ type: OS::Neutron::FloatingIP
+ properties:
+ floating_network_id: { get_param: public_net_id }
+ port_id: { get_resource: PNF_PnP_simulator_port0 }
+outputs:
+ PNF_PnP_simulator_private_ip:
+ description: IP address of PNF_PnP_simulator in private network
+ value: { get_attr: [ PNF_PnP_simulator, first_address ] }
+ PNF_PnP_simulator_public_ip:
+ description: Floating IP address of PNF_PnP_simulator in public network
+ value: { get_attr: [ PNF_PnP_simulator_public, floating_ip_address ] }
diff --git a/netconfsimulator/Dockerfile_app b/netconfsimulator/Dockerfile_app
new file mode 100644
index 0000000..a775219
--- /dev/null
+++ b/netconfsimulator/Dockerfile_app
@@ -0,0 +1,4 @@
+FROM openjdk:8-jre-alpine
+ADD target/libs /app/libs
+ADD target/netconfsimulator-5.0.0-SNAPSHOT.jar /app/netconf-simulator.jar
+CMD java -cp /app/libs/*:/app/netconf-simulator.jar org.onap.netconfsimulator.Main \ No newline at end of file
diff --git a/netconfsimulator/Dockerfile_netopeer b/netconfsimulator/Dockerfile_netopeer
new file mode 100644
index 0000000..9fe56e1
--- /dev/null
+++ b/netconfsimulator/Dockerfile_netopeer
@@ -0,0 +1,6 @@
+FROM sysrepo/sysrepo-netopeer2:v0.7.7
+ADD apt.conf /etc/apt/apt.conf
+RUN apt-get update && apt-get install -y python3 python3-pip python-pip && pip3 install flask flask_restful kafka-python && pip install kafka-python
+RUN cd /opt/dev/sysrepo && cmake -DGEN_PYTHON_VERSION=2 -DREPOSITORY_LOC:PATH=/etc/sysrepo . && make install
+
+CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
diff --git a/netconfsimulator/README.md b/netconfsimulator/README.md
new file mode 100644
index 0000000..b825226
--- /dev/null
+++ b/netconfsimulator/README.md
@@ -0,0 +1,297 @@
+# Netconf Simulator
+A simulator that is able to receive and print history of CM configurations.
+
+## Required software
+To run the simulator, the following software should be installed:
+- JDK 1.8
+- Maven
+- docker
+- docker-compose
+
+### API
+Simulator exposes both HTTP and native netconf interface.
+
+### Running simulator
+In order to run simulator, invoke *mvn clean install docker:build* to build required images.
+Add executable permission to initialize_netopeer.sh (by executing `sudo chmod +x netconf/initialize_netopeer.sh`)
+and then invoke *docker-compose up* command.
+In case of copying simulator files to another location, keep in mind to copy also *docker-compose.yml* and directories: *config, templates, netopeer-change-saver-native and netconf*.
+
+#### Restarting
+Restarting simulator can be done by first typing *docker-compose restart* in terminal.
+
+#### Shutting down
+The command *docker-compose down* can be used to shut the simulator down.
+
+## Usage of simulator
+
+### Netconf TLS support
+Embedded netconf server supports connections over TLS on port 6513. Default server and CA certificate have been generated using method described below. Please proceed with these steps to recreate own certificates. Important is to fulfill all needed data during certificate preparation because Netconf verifies certs description pretty strictly.
+
+Mentioned Github repository contains sample client certificate, which works out of the box.
+
+#### Replacing server certificates
+In order to replace TLS certificates with third-party ones, the following naming schema must be followed:
+* CA certificate file should be named 'ca.crt'
+* Netconf server certificate file should be named 'server_cert.crt'
+* Netconf server keyfile file should be named 'server_key.pem'
+* Client certificate file should be named 'client.crt'
+* Client keyfile should be named 'client.key'
+
+Certificates and keys should follow PEM formatting guidelines.
+Prepared files should be placed under _tls/_ directory (existing files must be overwritten).
+After copying, it is necessary to restart the Netconf Simulator (please refer to [restarting simulator](restarting) guide).
+
+This is a sample curl command to test client connection (the example assumes that Netconf Simulator runs on 127.0.0.1):
+```
+curl --cacert ca.crt --cert client.crt --key client.key https://127.0.0.1:6513 -kv --http0.9
+```
+or using openssl:
+```
+openssl s_client -connect 127.0.0.1:6513 -cert client.crt -key client.key -CAfile ca.crt
+```
+
+To regenerate all required certificates follow steps:
+1. Generate your private key and public certificate: ```openssl req -newkey rsa:4096 -keyform PEM -keyout ca.key -x509 -days 3650 -outform PEM -out ca.crt```
+2. Create a private client key:```openssl genrsa -out client.key 4096```
+3. Generate certificate signing request:```openssl req -new -key client.key -out client.req```
+4. Generating signed client certificate: ```openssl x509 -req -in client.req -CA ca.crt -CAkey ca.key -set_serial 101 -extensions client -days 365 -outform PEM -out client.crt```
+5. Create a private server key:```openssl genrsa -out server_key.pem 4096```
+6. Generate certificate signing request:```openssl req -new -key server_key.pem -out server.req -sha256```
+7. Generating signed server certificate: ```openssl x509 -req -in server.req -CA ca.crt -CAkey ca.key -set_serial 100 -extensions server -days 1460 -outform PEM -out server_cert.crt -sha256```
+
+Client authenticates using described TLS configuration, their username will resolve to test (more information in tls_listen.xml under the cert-to-name section). It is required that this username exists on the local system (just like for SSH), so you will need to (temporarily) create this user. The simplest way is executing # useradd -MN test, which creates the user without a home directory and user group.
+
+Currently by default there is only a possibility to substitute existing certificates for single user.
+```
+
+
+### Capturing netconf configuration changes
+
+The netconfsimulator tool will intercept changes in netconf configuration, done by edit-config command (invoked through simulator's edit-configuration endpoint or directly through exposed netconf-compliant interface). The following changes are intercepted:
+- creating new item
+- moving an item
+- modifying an item
+- deleting an item
+
+Each captured change contains fully qualified parameter name (including xpath - namespace and container name)
+
+#### REST API usage with examples
+
+Application of native netconf operations on YANG model is covered by REST API layer.
+Example invocation of operations with its requests and results are presented below.
+For basic edit-config and get config actions, response is in plain XML format, whereas stored data that can be accessed via API is returned in JSON format.
+
+**Load new YANG model**
+http method: POST
+```
+URL: http:<simulator_ip>:9000/netconf/model/<moduleName>
+```
+request: file content to be sent as multipart (form data)
+```
+module pnf-simulator {
+ namespace "http://onap.org/pnf-simulator";
+ prefix config;
+ container config {
+ config true;
+ leaf itemValue1 {type uint32;}
+ leaf itemValue2 {type uint32;}
+ leaf itemValue3 {type uint32;}
+ leaf-list allow-user {
+ type string;
+ ordered-by user;
+ description "A sample list of user names.";
+ }
+ }
+}
+```
+
+**Delete existing YANG model**
+http method: DELETE
+```
+URL: http:<simulator_ip>:9000/netconf/model/<moduleName>
+```
+request body should be empty.
+response: a HTTP 200 code indicating successful operation or 400/500 in case of errors.
+
+**Get all running configurations**
+http method: GET
+```
+URL: http:<simulator_ip>:9000/netconf/get
+```
+response: plain XML
+```
+<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>2781</itemValue1>
+ <itemValue2>3782</itemValue2>
+ <itemValue3>3333</itemValue3>
+</config>
+<config2 xmlns="http://onap.org/pnf-simulator2" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>2781</itemValue1>
+ <itemValue2>3782</itemValue2>
+ <itemValue3>3333</itemValue3>
+</config2>
+```
+
+**Get running configuration**
+http method: GET
+```
+URL: http:<simulator_ip>:9000/netconf/get/'moduleName'/'container'
+```
+response: plain XML
+```
+<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>2781</itemValue1>
+ <itemValue2>3782</itemValue2>
+ <itemValue3>3333</itemValue3>
+</config>
+```
+
+**Edit configuration**
+To edit configuration XML file must be prepared. No plain request body is used here,
+request content must be passed as multipart file (form data) with file name/key='editConfigXml' and file content in XML format
+
+http method: POST
+```
+URL: http:<simulator_ip>:9000/netconf/edit-config
+```
+request: file content to be sent as multipart (form data)
+```
+<config xmlns="http://onap.org/pnf-simulator">
+ <itemValue1>2781</itemValue1>
+ <itemValue2>3782</itemValue2>
+ <itemValue3>3333</itemValue3>
+</config>
+```
+
+response: actual, running configuration after editing config:
+```
+<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>2781</itemValue1>
+ <itemValue2>3782</itemValue2>
+ <itemValue3>3333</itemValue3>
+</config>"
+```
+
+Captured change, that can be obtained from db also via REST API:
+
+http method: GET
+```
+URL: http://<simulator_ip>:9000/store/less?offset=1
+```
+response:
+```
+[{"timestamp": 1542877413979, "configuration": "CREATED: /pnf-simulator:config/itemValue3 = 3333"}]
+```
+
+Notice: if new value is the same as the old one, the change won’t be intercepted (because there is no state change). This is a limitation of used netconf implementation (Netopeer2).
+
+**Modify request**
+http method: POST
+```
+URL: http:<simulator_ip>:9000/netconf/edit-config
+```
+file content to be sent as multipart (form data):
+```
+<config xmlns="http://onap.org/pnf-simulator" >
+ <itemValue1>111</itemValue1>
+ <itemValue2>222</itemValue2>
+</config>
+```
+
+response: actual, running configuration after editing config:
+```
+<config xmlns="http://onap.org/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>111</itemValue1>
+ <itemValue2>222</itemValue2>
+</config>"
+```
+
+Captured change:
+http method: GET
+```
+URL: http://<simulator_ip>:9000/store/less?offset=2
+```
+```
+[{"timestamp": 1542877413979, "configuration": "MODIFIED: : old value: /pnf-simulator:config/itemValue1 = 2781, new value: /pnf-simulator:config/itemValue1 = 111",
+ {"timestamp": 1542877413979, "configuration": "MODIFIED: : old value: /pnf-simulator:config/itemValue2 = 3782, new value: /pnf-simulator:config/itemValue2 = 222"}]
+```
+
+**Move request** (inserting a value into leaf-list which in turn rearranges remaining elements)
+http method: POST
+```
+URL: http:<simulator_ip>:9000/netconf/edit-config
+```
+file content to be sent as multipart (form data):
+```
+<config xmlns="http://onap.org/pnf-simulator" xmlns:yang="urn:ietf:params:xml:ns:yang:1" xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <allow-user xc:operation="create" yang:insert="before" yang:value="bob">mike</allow-user>
+</config>
+```
+
+Captured change:
+http method: GET
+```
+URL: http://<simulator_ip>:9000/store/less?offset=2
+```
+```
+[{"timestamp": 1542877413979, "configuration": "CREATED: /pnf-simulator:config/allow-user = mike"},
+ {"timestamp": 1542877413979, "configuration": "MOVED: /pnf-simulator:config/allow-user = mike after /pnf-simulator:config/allow-user = alice"}]
+```
+
+**Delete request**
+http method: POST
+```
+URL: http:<simulator_ip>:9000/netconf/edit-config
+```
+file content to be sent as multipart (form data):
+```
+<config xmlns="http://onap.org/pnf-simulator">
+ <itemValue1>1111</itemValue1>
+ <itemValue2 xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0" xc:operation="delete"/>
+</config>
+```
+
+Captured change:
+http method: GET
+```
+URL: http://<simulator_ip>:9000/store/less?offset=1
+```
+```
+[{"timestamp": 1542877413979, "configuration": "DELETED: /pnf-simulator:config/itemValue2 = 222"}]
+```
+
+Getting all configuration changes:
+http method: GET
+```
+URL: http://<simulator_ip>:9000/store/cm-history
+```
+response:
+```
+[{"timestamp":1542877413979,"configuration":"MODIFIED: : old value: /pnf-simulator:config/itemValue1 = 2781, new value: /pnf-simulator:config/itemValue1 = 111"},
+ {"timestamp":1542877413979,"configuration":"MODIFIED: : old value: /pnf-simulator:config/itemValue2 = 3782, new value: /pnf-simulator:config/itemValue2 = 222"},
+ {"timestamp":1542877414000,"configuration":"CREATED: : /pnf-simulator:config/itemValue3 = 3333"},
+ {"timestamp":1542877414104,"configuration":"CREATED: : CREATED: /pnf-simulator:config/allow-user = mike"}
+ {"timestamp":1542877414107,"configuration":"MOVED: /pnf-simulator:config/allow-user = mike after /pnf-simulator:config/allow-user = alice"},
+ {"timestamp":1542877414275,"configuration":"DELETED: /pnf-simulator:config/itemValue2 = 222"}]
+```
+
+### Logging
+
+### Swagger
+
+## Developers Guide
+
+### Integration tests
+Integration tests use docker-compose for setting up cluster with all services.
+Those tests are not part of build pipeline, but can be run manually by invoking *mvn verify -DskipITs=false* from project command line.
+Tests can be found in netconfsimulator project in src/integration directory.
+
+## Troubleshooting
+Q: Simulator throws errors after shutting down with *docker-compose down* or *docker-compose restart*
+
+A: Remove docker containers that were left after stopping the simulator with the following commands:
+```
+docker stop $(docker ps | grep netconfsimulator | awk '{print $1;}')
+docker rm $(docker ps -a | grep netconfsimulator | awk '{print $1;}')
+```
diff --git a/netconfsimulator/apt.conf b/netconfsimulator/apt.conf
new file mode 100644
index 0000000..9146b6b
--- /dev/null
+++ b/netconfsimulator/apt.conf
@@ -0,0 +1,5 @@
+Acquire::http {
+ No-Cache "true";
+ No-Store "true";
+ Pipeline-Depth "0";
+ }; \ No newline at end of file
diff --git a/netconfsimulator/config/netconf.env b/netconfsimulator/config/netconf.env
new file mode 100644
index 0000000..6cf310a
--- /dev/null
+++ b/netconfsimulator/config/netconf.env
@@ -0,0 +1,5 @@
+NETCONF_ADDRESS=netopeer
+NETCONF_PORT=830
+NETCONF_MODEL=pnf-simulator
+NETCONF_MAIN_CONTAINER=config
+TZ=Europe/Warsaw
diff --git a/netconfsimulator/docker-compose.yml b/netconfsimulator/docker-compose.yml
new file mode 100644
index 0000000..5e9acbe
--- /dev/null
+++ b/netconfsimulator/docker-compose.yml
@@ -0,0 +1,93 @@
+version: '3'
+
+services:
+ zookeeper:
+ image: wurstmeister/zookeeper
+ ports:
+ - "2181:2181"
+ networks:
+ - netconfnetwork
+
+ kafka1:
+ image: wurstmeister/kafka:1.1.0
+ ports:
+ - "9092:9092"
+ hostname: kafka1
+ networks:
+ - netconfnetwork
+ environment:
+ KAFKA_ADVERTISED_PORT: 9092
+ KAFKA_ADVERTISED_HOST_NAME: kafka1
+ KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
+ KAFKA_CREATE_TOPICS: "config:1:1"
+ KAFKA_DELETE_RETENTION_MS: 604800000
+ KAFKA_LOG_CLEANER_DELETE_RETENTION_MS: 604800000
+ depends_on:
+ - zookeeper
+
+ netconf-simulator:
+ image: nexus3.onap.org:10003/onap/netconfsimulator
+ ports:
+ - "9000:8080"
+ restart: on-failure
+ hostname: netconf-simulator
+ networks:
+ - netconfnetwork
+ depends_on:
+ - zookeeper
+ - kafka1
+ - netopeer
+
+ netopeer:
+ image: nexus3.onap.org:10003/netopeer
+ ports:
+ - "830:830"
+ - "5002:5002"
+ - "6513:6513"
+ volumes:
+ - ./netconf:/netconf
+ - ./netopeer-change-saver-native:/netopeer-change-saver
+ - ./tls:/tls
+ env_file:
+ - ./config/netconf.env
+ restart: on-failure
+ networks:
+ - netconfnetwork
+ depends_on:
+ - sftp-server
+ - ftpes-server
+ command:
+ - /netconf/initialize_netopeer.sh
+
+ sftp-server:
+ image: atmoz/sftp:alpine
+ ports:
+ - "2222:22"
+ volumes:
+ - ./sftp:/home/sftp-user/sftp
+ - ./ssh/ssh_host_rsa_key.pub:/home/sftp-user/.ssh/keys/ssh_host_rsa_key.pub
+ networks:
+ - netconfnetwork
+ restart: on-failure
+ command: sftp-user::1001
+
+ ftpes-server:
+ image: stilliard/pure-ftpd:latest
+ ports:
+ - "2221:21"
+ - "30000-30009:30000-30009"
+ volumes:
+ - ./ftpes/files:/home/ftpusers/onap
+ - ./ftpes/userpass/:/etc/pure-ftpd/passwd/
+ - ./ftpes/tls/:/etc/ssl/private/
+ networks:
+ - netconfnetwork
+ environment:
+ PUBLICHOST: localhost
+ ADDED_FLAGS: --tls=2
+ FTP_USER_HOME: onap
+ restart: on-failure
+
+networks:
+ netconfnetwork:
+ driver: bridge
diff --git a/netconfsimulator/docker/Dockerfile b/netconfsimulator/docker/Dockerfile
new file mode 100644
index 0000000..0e25fd3
--- /dev/null
+++ b/netconfsimulator/docker/Dockerfile
@@ -0,0 +1,4 @@
+FROM openjdk:8-jre-alpine
+ADD libs /app/libs
+ADD netconfsimulator-5.0.0-SNAPSHOT.jar /app/netconf-simulator.jar
+CMD java -cp /app/libs/*:/app/netconf-simulator.jar org.onap.netconfsimulator.Main
diff --git a/netconfsimulator/ftpes/files/ftpes-noone.txt b/netconfsimulator/ftpes/files/ftpes-noone.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/netconfsimulator/ftpes/files/ftpes-noone.txt
diff --git a/netconfsimulator/ftpes/files/onap/ftpes-onap.txt b/netconfsimulator/ftpes/files/onap/ftpes-onap.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/netconfsimulator/ftpes/files/onap/ftpes-onap.txt
diff --git a/netconfsimulator/ftpes/tls/pure-ftpd.pem b/netconfsimulator/ftpes/tls/pure-ftpd.pem
new file mode 100755
index 0000000..0ce676e
--- /dev/null
+++ b/netconfsimulator/ftpes/tls/pure-ftpd.pem
@@ -0,0 +1,49 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHbSk5/cABTpCt
+q54QyTkhuhb84nEz5ztKL0hY56fsVtAA2gSAde+qV9YwUIuReOUhAF4RVVt2Lkn/
+1R0yX+0IjrXnO7jYzDj6QhgyqYKb3iQlvShZMMQ7qihn8qBxobk7+O10x6VLS2L8
+WYpQxGXu7T1qXbw10RhrqG8nbXYX+aHMsv9zMt9OYqKSI073OZR2vk3K49Uqcurj
+sXuRJOa10MRsxgA726pr8OLWAWejsoFaqP2fQS3HeT2RnAqPyAgPc0P6n7gxo0JU
+U5dPnrPbsvfdegIFxfc57oZXrLz7nYXkJEcjYTBFSQ+JAaRfx9kNXZ7Gft7EAMyF
+BLemY/0VAgMBAAECggEARD9bSHlKaCgW4xhEM8JpRt2EWG62BukvJSghPiupD/x1
+mpUBzWSO7GC68DXgTZxt7WlOx+fKMRuOP3sTTtX9LFyKa+PIUokxRpOv7EaOaAER
+pciiMkO6JCELSueBeOG7noaF3N0l+CqIaYvLBfDwYV/XELubWV+BV/aAc6HGNFWi
+4bjM+BOBLQstrEeJh2jVylzv4CTtlTs2pwiHFSyrHhudTk5nnATAHn1gi+X42v1A
+zk3UfqADZJmMI0/Roup4YPZ3+6zUzDN2i+qasHexL0OKIjRcSqpgqQoIeKEbKKfw
+sOgiWIR2Xvj7EJmhzJlWgKjk8OLs/7U4QpnD+s0agQKBgQDu3ojqKNWnPHy0Nupm
+tmAs28WLK76R0iZeAd2nwsU2K6lnm9z5o2ab3ffTJVB9kAetKJa3UerKskF/qF9C
+MtjlEn6F++uYFitpLjQevnyrKSqFqbzytDXrQlk+gZLglmi6YylT5k9qLSREAu55
+XS/wbm9XU2Q7sl8oTnZHXptT7QKBgQDVunvqdDn1FaNU9EwQCGPS3QGu+go22xkM
+4Rs2CoHWfqmhGOo8lJKBElDqsXvxggrZLWJe/1lgnELT/9aXS8QLWBnZxpTj9wfd
+igH+CJc3mWnLThmUGdSV/tuHon2IdQ8/1CiGSwIr9kYCnStidUtOXjIbgc6kUTTi
+5wtIGHh4yQKBgQDXJ/0dJbDklRgiX4CdCdLxNPfnlnxt7mN+s6GK1WY7l/JcD8ln
+1qW66aGrP2YT42L2tqOi9hdNgmh66xb6ksBI/XKXjsWz1Ow/Lk3mD2BN76OMh8pY
+trgGc1ndcmrw/qnQkTcNilqn4YdT92wER0rB/0cs2kFjgBQ0QxBI0s+INQKBgA6Y
+2fW9UmgGvk0DEl7V89tm9MJ6mU/9zswuY6lhNlTr+bHi/bx9eTQPiC8/R/PKqesD
+SoCqd/Q9N+M6yfEzX4RW1A0nnuui54qd7lznQUyu0abtApo22WoVKfEti91SAWSe
+nNXvMYrHGyj6iwgCcs47aLiwOOjIExCcLw0RfsjhAoGAc1zaRbrtjjh66FJYjLiJ
+Q6EXfm31ptaQQUn5rQyHMD2VRlajCYV+fv75tezf2dQvJcqHYWrEuY8U+OTbB1TB
+IEqN8ETUeLegl5RgvWoyWinqdbv/0d9LtwVBdtiEQLoYumD934mshEDgzCOOjrBe
+Salcd1vc6y6NiFooPlvloXQ=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDYDCCAkigAwIBAgIJAMH2upKd2yAJMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTgwOTEwMTI1ODE2WhcNMzgwOTA1MTI1ODE2WjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAx20pOf3AAU6QraueEMk5IboW/OJxM+c7Si9IWOen7FbQANoEgHXvqlfW
+MFCLkXjlIQBeEVVbdi5J/9UdMl/tCI615zu42Mw4+kIYMqmCm94kJb0oWTDEO6oo
+Z/KgcaG5O/jtdMelS0ti/FmKUMRl7u09al28NdEYa6hvJ212F/mhzLL/czLfTmKi
+kiNO9zmUdr5NyuPVKnLq47F7kSTmtdDEbMYAO9uqa/Di1gFno7KBWqj9n0Etx3k9
+kZwKj8gID3ND+p+4MaNCVFOXT56z27L33XoCBcX3Oe6GV6y8+52F5CRHI2EwRUkP
+iQGkX8fZDV2exn7exADMhQS3pmP9FQIDAQABo1MwUTAdBgNVHQ4EFgQUt51lQ+ab
+MTq+w2U/knCsIPb3wrkwHwYDVR0jBBgwFoAUt51lQ+abMTq+w2U/knCsIPb3wrkw
+DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAQ69AktYLQ+VRbojz
+zC0XQ2M1FAkfJI2P0LvPoYxZgId2CnZW3sMIdnJdF+KjvOqeGyFmw+hn8WkoKiWj
+0sxuGmrWt5t+5bF2wcq0CtTeF1/o6DsRhRiJBzmcLe81ItrN6emZSg96xCKzkHBZ
+3nF4fG88vtiYgD932lMStDqQzSTx0FsCGpGaKh9xDmKvlP24NWdM9gyOEsRbDvqd
+vS1Q45Jx0jzkp7X5d0casqBWIZak3z0EVdK7c8Y/GxxTcWfIMINCl9+F9kpTA/ZX
+uARYzrPWaBfDBi2r5acWi/AHJM3U+LgzO5nCKa+38vtjNw3NtbslA4InQ5cU2B8X
+QN8NlQ==
+-----END CERTIFICATE-----
diff --git a/netconfsimulator/ftpes/userpass/pureftpd.passwd b/netconfsimulator/ftpes/userpass/pureftpd.passwd
new file mode 100755
index 0000000..7961e71
--- /dev/null
+++ b/netconfsimulator/ftpes/userpass/pureftpd.passwd
@@ -0,0 +1 @@
+onap:$6$Guq6OMhBdNZ6nTk0$7dLt6hOrAv.in36jzWGd5UgWeDqN3CuKjrzJ.izRTdgZRTszeNYbT2dk7UDh9CLD7pohnB0.k1NSZmRIUB/ID/:1001:1001::/home/ftpusers/onap/./::::::::::::
diff --git a/netconfsimulator/netconf/__init__.py b/netconfsimulator/netconf/__init__.py
new file mode 100644
index 0000000..aa8b4f9
--- /dev/null
+++ b/netconfsimulator/netconf/__init__.py
@@ -0,0 +1,19 @@
+###
+# ============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=========================================================
+###
diff --git a/netconfsimulator/netconf/initialize_netopeer.sh b/netconfsimulator/netconf/initialize_netopeer.sh
new file mode 100755
index 0000000..59fc8a1
--- /dev/null
+++ b/netconfsimulator/netconf/initialize_netopeer.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+###
+# ============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=========================================================
+###
+
+cp /tls/* /usr/local/etc/keystored/keys/
+cp /netconf/*.xml /tmp/
+
+chmod +x /netconf/set-up-xmls.py
+/netconf/set-up-xmls.py /tls ca.crt server_cert.crt server_key.pem /tmp/load_server_certs.xml /tmp/tls_listen.xml client.crt
+
+/usr/bin/supervisord -c /etc/supervisord.conf &
+sysrepoctl --install --yang=/netconf/pnf-simulator.yang --owner=netconf:nogroup --permissions=777
+sysrepocfg --import=/netconf/pnf-simulator.data.xml --datastore=startup --format=xml --level=3 pnf-simulator
+sysrepocfg --merge=/tmp/load_server_certs.xml --format=xml --datastore=startup ietf-keystore
+sysrepocfg --merge=/tmp/tls_listen.xml --format=xml --datastore=startup ietf-netconf-server
+
+nohup python3 /netconf/yang_loader_server.py &
+
+python /netconf/netopeer_change_saver.py pnf-simulator kafka1:9092 config \ No newline at end of file
diff --git a/netconfsimulator/netconf/load_server_certs.xml b/netconfsimulator/netconf/load_server_certs.xml
new file mode 100644
index 0000000..b52f911
--- /dev/null
+++ b/netconfsimulator/netconf/load_server_certs.xml
@@ -0,0 +1,44 @@
+<!--
+ ============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=========================================================
+ -->
+
+<keystore xmlns="urn:ietf:params:xml:ns:yang:ietf-keystore">
+ <private-keys>
+ <private-key>
+ <name>SERVER_KEY_NAME</name>
+ <certificate-chains>
+ <certificate-chain>
+ <name>SERVER_CERT_NAME</name>
+ <certificate>SERVER_CERTIFICATE_HERE</certificate>
+ </certificate-chain>
+ </certificate-chains>
+ </private-key>
+ </private-keys>
+ <trusted-certificates>
+ <name>test_trusted_ca_list</name>
+ <trusted-certificate>
+ <name>CA_CERT_NAME</name>
+ <certificate>CA_CERTIFICATE_HERE</certificate>
+ </trusted-certificate>
+ <trusted-certificate>
+ <name>CLIENT_CERT_NAME</name>
+ <certificate>CLIENT_CERTIFICATE_HERE</certificate>
+ </trusted-certificate>
+ </trusted-certificates>
+</keystore>
diff --git a/netconfsimulator/netconf/netopeer_change_saver.py b/netconfsimulator/netconf/netopeer_change_saver.py
new file mode 100644
index 0000000..92f8846
--- /dev/null
+++ b/netconfsimulator/netconf/netopeer_change_saver.py
@@ -0,0 +1,107 @@
+###
+# ============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=========================================================
+###
+
+import sysrepo as sr
+import sys
+import json
+import time
+import logging
+from kafka import KafkaProducer
+from enum import Enum
+
+logging.basicConfig(filename='netopeer_change_saver.log', level=logging.DEBUG)
+
+kafka_producer = None
+topic = "config"
+
+
+class OperationType(Enum):
+ CREATED = sr.SR_OP_CREATED
+ DELETED = sr.SR_OP_DELETED
+ MODIFIED = sr.SR_OP_MODIFIED
+ MOVED = sr.SR_OP_MOVED
+
+
+def module_change_callback(session, name, event, private_ctx):
+ if sr.SR_EV_APPLY == event:
+ change_path = "/{}:*".format(name)
+ changes = session.get_changes_iter(change_path)
+ change = session.get_change_next(changes)
+ while change:
+ try:
+ process_change(change)
+ change = session.get_change_next(changes)
+ except Exception:
+ logging.exception("Exception occured")
+
+ return sr.SR_ERR_OK
+
+
+def process_change(change):
+ if change:
+ message = {"type": OperationType(change.oper()).name}
+ if change.old_val():
+ message["old"] = {"path": change.old_val().xpath(), "value": change.old_val().val_to_string()}
+ if change.new_val():
+ message["new"] = {"path": change.new_val().xpath(), "value": change.new_val().val_to_string()}
+ send_message(message)
+
+
+def send_message(message):
+ logging.debug("Message to kafka : %s", message)
+ response = kafka_producer.send(topic, message)
+ logging.info(response.get(timeout=90))
+
+
+def create_producer(server):
+ for i in range(10): # pylint: disable=W0612
+ try:
+ return KafkaProducer(bootstrap_servers=server, value_serializer=lambda v: json.dumps(v).encode('utf-8'))
+ except Exception:
+ time.sleep(15)
+ raise Exception("Could not connect to kafka server")
+
+
+def print_current_config(kafka_session, module):
+ name = "/{}:*//*".format(module)
+ logging.info("Retrieving current config for %s module", name)
+ values = kafka_session.get_items(name)
+ for i in range(values.val_cnt()):
+ logging.info(values.val(i).to_string())
+
+
+if __name__ == "__main__":
+ try:
+ module_name = sys.argv[1]
+ bootstrap_servers = sys.argv[2]
+ topic = sys.argv[3]
+ connection = sr.Connection("example_application2")
+ session = sr.Session(connection)
+ subscribe = sr.Subscribe(session)
+ subscribe.module_change_subscribe(module_name, module_change_callback)
+
+ print_current_config(session, module_name)
+
+ kafka_producer = create_producer(bootstrap_servers)
+
+ sr.global_loop()
+ except Exception as e:
+ logging.exception("Exception occured")
+ raise e
diff --git a/netconfsimulator/netconf/newmodel.xml b/netconfsimulator/netconf/newmodel.xml
new file mode 100644
index 0000000..90a3451
--- /dev/null
+++ b/netconfsimulator/netconf/newmodel.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=========================================================
+ -->
+
+<config2 xmlns="http://onap.org/pnf-simulator2">
+ <item1>500</item1>
+ <item2>1000</item2>
+</config2>
diff --git a/netconfsimulator/netconf/newmodel.yang b/netconfsimulator/netconf/newmodel.yang
new file mode 100644
index 0000000..544f467
--- /dev/null
+++ b/netconfsimulator/netconf/newmodel.yang
@@ -0,0 +1,9 @@
+module newmodel {
+ namespace "http://onap.org/pnf-simulator2";
+ prefix config2;
+ container config2 {
+ config true;
+ leaf item1 {type uint32;}
+ leaf item2 {type uint32;}
+ }
+}
diff --git a/netconfsimulator/netconf/pnf-simulator.data.xml b/netconfsimulator/netconf/pnf-simulator.data.xml
new file mode 100644
index 0000000..c235f64
--- /dev/null
+++ b/netconfsimulator/netconf/pnf-simulator.data.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">
+ <itemValue1>42</itemValue1>
+ <itemValue2>35</itemValue2>
+</config>
diff --git a/netconfsimulator/netconf/pnf-simulator.yang b/netconfsimulator/netconf/pnf-simulator.yang
new file mode 100644
index 0000000..ba11585
--- /dev/null
+++ b/netconfsimulator/netconf/pnf-simulator.yang
@@ -0,0 +1,9 @@
+module pnf-simulator {
+ namespace "http://onap.org/pnf-simulator";
+ prefix config;
+ container config {
+ config true;
+ leaf itemValue1 {type uint32;}
+ leaf itemValue2 {type uint32;}
+ }
+}
diff --git a/netconfsimulator/netconf/set-up-xmls.py b/netconfsimulator/netconf/set-up-xmls.py
new file mode 100755
index 0000000..2ec1cf2
--- /dev/null
+++ b/netconfsimulator/netconf/set-up-xmls.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python
+
+###
+# ============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=========================================================
+###
+
+import os
+import sys
+import logging
+import logging.config
+
+logging.basicConfig()
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
+
+# Placeholders definition - this needs to match placeholders in
+# load_server_certs_xml_file and tls_listen_xml_file
+SERVER_KEY_NAME = "SERVER_KEY_NAME"
+SERVER_CERT_NAME = "SERVER_CERT_NAME"
+SERVER_CERTIFICATE_HERE = "SERVER_CERTIFICATE_HERE"
+CA_CERT_NAME = "CA_CERT_NAME"
+CLIENT_CERT_NAME = "CLIENT_CERT_NAME"
+CLIENT_CERTIFICATE_HERE="CLIENT_CERTIFICATE_HERE"
+CA_CERTIFICATE_HERE = "CA_CERTIFICATE_HERE"
+CLIENT_FINGERPRINT_HERE = "CLIENT_FINGERPRINT_HERE"
+SERVER_CERTIFICATE_ENV = "SERVER_CERTIFICATE_ENV"
+CA_CERTIFICATE_ENV = "CA_CERTIFICATE_ENV"
+
+
+class FileHelper(object):
+ @classmethod
+ def get_file_contents(cls, filename):
+ with open(filename, "r") as f:
+ return f.read()
+
+ @classmethod
+ def write_file_contents(cls, filename, data):
+ with open(filename, "w+") as f:
+ f.write(data)
+
+
+class CertHelper(object):
+ @classmethod
+ def get_pem_content_stripped(cls, pem_dir, pem_filename):
+ cmd = "cat {}/{} | grep -v '^-'".format(pem_dir, pem_filename)
+ content = CertHelper.system(cmd)
+ return content
+
+ @classmethod
+ def get_cert_fingerprint(cls, directory, cert_filename):
+ cmd = "openssl x509 -fingerprint -noout -in {}/{} | sed -e " \
+ "'s/SHA1 Fingerprint//; s/=//; s/=//p'" \
+ .format(directory, cert_filename)
+ fingerprint = CertHelper.system(cmd)
+ return fingerprint
+
+ @classmethod
+ def print_certs_info(cls, ca_cert, ca_fingerprint, server_cert):
+ logger.info("Will use server certificate: " + server_cert)
+ logger.info("Will use CA certificate: " + ca_cert)
+ logger.info("CA certificate fingerprint: " + ca_fingerprint)
+
+ @classmethod
+ def system(cls, cmd):
+ return os.popen(cmd).read().replace("\n", "")
+
+
+class App(object):
+ @classmethod
+ def patch_server_certs(cls, data, server_key_filename_noext,
+ server_cert_filename_noext, ca_cert_filename_noext,
+ server_cert, ca_cert, client_cert_filename_noext, client_cert):
+ data = data.replace(SERVER_KEY_NAME, server_key_filename_noext)
+ data = data.replace(SERVER_CERT_NAME, server_cert_filename_noext)
+ data = data.replace(CA_CERT_NAME, ca_cert_filename_noext)
+ data = data.replace(CLIENT_CERT_NAME, client_cert_filename_noext)
+ data = data.replace(CLIENT_CERTIFICATE_HERE, client_cert)
+ data = data.replace(SERVER_CERTIFICATE_HERE, server_cert)
+ data = data.replace(CA_CERTIFICATE_HERE, ca_cert)
+ return data
+
+ @classmethod
+ def patch_tls_listen(cls, data, server_cert_filename_noext, client_fingerprint,
+ server_cert, ca_cert):
+ data = data.replace(SERVER_CERT_NAME, server_cert_filename_noext)
+ data = data.replace(CLIENT_FINGERPRINT_HERE, client_fingerprint)
+ data = data.replace(SERVER_CERTIFICATE_HERE, server_cert)
+ data = data.replace(CA_CERTIFICATE_HERE, ca_cert)
+ return data
+
+ @classmethod
+ def run(cls):
+ # name things
+ cert_dir = sys.argv[1]
+ ca_cert_filename = sys.argv[2]
+ server_cert_filename = sys.argv[3]
+ server_key_filename = sys.argv[4]
+ load_server_certs_xml_file = sys.argv[5]
+ tls_listen_xml_file = sys.argv[6]
+ client_cert_filename = sys.argv[7]
+
+
+ # strip extensions
+ ca_cert_filename_noext = ca_cert_filename.replace(".crt", "")
+ server_cert_filename_noext = server_cert_filename.replace(".crt", "")
+ server_key_filename_noext = server_key_filename.replace(".pem", "")
+ client_cert_filename_noext = client_cert_filename.replace(".crt", "")
+
+ # get certificates from files
+ server_cert = CertHelper.get_pem_content_stripped(cert_dir,
+ server_cert_filename)
+ ca_cert = CertHelper.get_pem_content_stripped(cert_dir,
+ ca_cert_filename)
+ client_fingerprint = CertHelper.get_cert_fingerprint(cert_dir,
+ client_cert_filename)
+ CertHelper.print_certs_info(ca_cert, client_fingerprint, server_cert)
+
+ client_cert = CertHelper.get_pem_content_stripped(cert_dir,
+ client_cert_filename)
+ # patch TLS configuration files
+ data_srv = FileHelper.get_file_contents(load_server_certs_xml_file)
+ patched_srv = App.patch_server_certs(data_srv, server_key_filename_noext,
+ server_cert_filename_noext,
+ ca_cert_filename_noext,
+ server_cert, ca_cert,
+ client_cert_filename_noext, client_cert)
+ FileHelper.write_file_contents(load_server_certs_xml_file, patched_srv)
+
+ data_tls = FileHelper.get_file_contents(tls_listen_xml_file)
+ patched_tls = App.patch_tls_listen(data_tls, server_cert_filename_noext,
+ client_fingerprint, server_cert, ca_cert)
+ FileHelper.write_file_contents(tls_listen_xml_file, patched_tls)
+
+
+def main():
+ if len(sys.argv) is not 8:
+ print("Usage: {1} <cert_dir> <ca_cert_filename> <server_cert_filename> "
+ "<server_key_filename> <load_server_certs_xml_full_path> "
+ "<tls_listen_full_path> <client_cert_filename>", sys.argv[0])
+ return 1
+ App.run()
+ logger.info("XML files patched successfully")
+
+
+if __name__ == '__main__':
+ main()
diff --git a/netconfsimulator/netconf/test_yang_loader_server.py b/netconfsimulator/netconf/test_yang_loader_server.py
new file mode 100644
index 0000000..a222087
--- /dev/null
+++ b/netconfsimulator/netconf/test_yang_loader_server.py
@@ -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=========================================================
+###
+
+import unittest
+
+from unittest import mock
+from werkzeug.datastructures import FileStorage
+
+from yang_loader_server import YangLoaderHelper, YangModelServer
+
+
+class TestYangLoaderHelper(unittest.TestCase):
+
+ def test_should_save_file_and_return_path(self):
+ helper = YangLoaderHelper()
+ mocked_file = mock.Mock(FileStorage)
+ mocked_file.filename = "sample"
+
+ path = helper.save_file(mocked_file)
+
+ self.assertEqual(path, "/tmp/sample")
+ mocked_file.save.assert_called_once_with("/tmp/sample")
+
+ @mock.patch('yang_loader_server.check_output')
+ def test_should_install_new_yang_model(self, mocked_output):
+ helper = YangLoaderHelper()
+
+ helper.install_new_model("path")
+
+ mocked_output.assert_called_with(
+ ['sysrepoctl', '--install', '--yang=path',
+ '--owner=netconf:nogroup', '--permissions=777'],
+ stderr=-2, universal_newlines=True)
+
+ @mock.patch('yang_loader_server.check_output')
+ def test_should_delete_yang_model(self, mocked_output):
+ helper = YangLoaderHelper()
+
+ helper.uninstall_a_model("modelName")
+
+ mocked_output.assert_called_with(
+ ['sysrepoctl', '--uninstall', '--module=modelName'],
+ stderr=-2, universal_newlines=True)
+
+ @mock.patch('yang_loader_server.check_output')
+ def test_should_set_default_configuration(self, mocked_output):
+ helper = YangLoaderHelper()
+
+ helper.set_default_configuration("samplePath", "sampleModuleName")
+
+ mocked_output.assert_called_with(
+ ['sysrepocfg', '--import=samplePath', '--datastore=startup',
+ '--format=xml', '--level=3', 'sampleModuleName'],
+ stderr=-2, universal_newlines=True)
+
+ @mock.patch('yang_loader_server.subprocess.Popen')
+ @mock.patch('yang_loader_server.check_output')
+ def test_should_verify_change_listener_for_model_properly(self, mocked_output, mocked_popen):
+ helper = YangLoaderHelper()
+
+ helper.start_change_listener_for_model("sampleModule")
+
+ mocked_output.assert_called_with(
+ ['pgrep', '-f', 'python /netconf/netopeer_change_saver.py sampleModule kafka1:9092 config'],
+ stderr=-2, universal_newlines=True)
+
+ @mock.patch('yang_loader_server.check_output')
+ def test_should_raise_exception_when_error_occurred_in_output(self,
+ mocked_output):
+ helper = YangLoaderHelper()
+ mocked_output.return_value = "abcd ERR"
+ with self.assertRaises(RuntimeError) as context:
+ helper._run_bash_command("sample command")
+
+ self.assertEqual('abcd ERR', str(context.exception))
+
+
+class TestYangModelServer(unittest.TestCase):
+
+ def __init__(self, methodName='runTest'):
+ super().__init__(methodName)
+ self._mocked_file = mock.Mock(FileStorage)
+
+ def test_should_properly_apply_and_start_new_model(self):
+ with mock.patch.object(YangModelServer, '_parse_request',
+ new=self._mock_request):
+ helper = mock.Mock(YangLoaderHelper)
+ helper.save_file.return_value = "sampleFile"
+ server = YangModelServer(helper)
+
+ server.post()
+
+ self.assertEqual(helper.save_file.call_count, 2)
+ helper.install_new_model.assert_called_once_with('sampleFile')
+ helper.set_default_configuration.assert_called_once_with(
+ 'sampleFile', 'sampleModuleName')
+ helper.start_change_listener_for_model.assert_called_once_with('sampleModuleName')
+
+ def _mock_request(self):
+ return {
+ 'yangModel': self._mocked_file,
+ 'initialConfig': self._mocked_file,
+ 'moduleName': "sampleModuleName"
+ }
diff --git a/netconfsimulator/netconf/tls_listen.xml b/netconfsimulator/netconf/tls_listen.xml
new file mode 100644
index 0000000..4f610b5
--- /dev/null
+++ b/netconfsimulator/netconf/tls_listen.xml
@@ -0,0 +1,49 @@
+<!--
+ ============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=========================================================
+ -->
+
+<netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server">
+ <listen>
+ <endpoint>
+ <name>test_tls_listen_endpt</name>
+ <tls>
+ <address>0.0.0.0</address>
+ <port>6513</port>
+ <certificates>
+ <certificate>
+ <name>SERVER_CERT_NAME</name>
+ </certificate>
+ </certificates>
+ <client-auth>
+ <trusted-ca-certs>test_trusted_ca_list</trusted-ca-certs>
+ <trusted-client-certs>test_trusted_ca_list</trusted-client-certs>
+ <cert-maps>
+ <cert-to-name>
+ <id>1</id>
+ <!-- This is not a typo - 0x02 should stay there -->
+ <fingerprint>02:CLIENT_FINGERPRINT_HERE</fingerprint>
+ <map-type xmlns:x509c2n="urn:ietf:params:xml:ns:yang:ietf-x509-cert-to-name">x509c2n:specified</map-type>
+ <name>test</name>
+ </cert-to-name>
+ </cert-maps>
+ </client-auth>
+ </tls>
+ </endpoint>
+ </listen>
+</netconf-server>
diff --git a/netconfsimulator/netconf/yang_loader.log b/netconfsimulator/netconf/yang_loader.log
new file mode 100644
index 0000000..61b95b3
--- /dev/null
+++ b/netconfsimulator/netconf/yang_loader.log
@@ -0,0 +1 @@
+INFO:werkzeug: * Running on http://0.0.0.0:5002/ (Press CTRL+C to quit)
diff --git a/netconfsimulator/netconf/yang_loader_server.py b/netconfsimulator/netconf/yang_loader_server.py
new file mode 100644
index 0000000..27d46ce
--- /dev/null
+++ b/netconfsimulator/netconf/yang_loader_server.py
@@ -0,0 +1,172 @@
+###
+# ============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=========================================================
+###
+
+import logging
+import subprocess
+import os
+from subprocess import check_output, CalledProcessError
+from flask import Flask
+from flask_restful import Resource, Api, reqparse
+from werkzeug.datastructures import FileStorage
+import time
+
+app = Flask(__name__)
+api = Api(app)
+logger = logging.getLogger("yang-loader")
+logger.addHandler(logging.StreamHandler())
+KAFKA_BROKER_NAME="kafka1:9092"
+KAFKA_TOPIC_NAME="config"
+
+
+class YangLoaderHelper(object):
+
+ @classmethod
+ def save_file(cls, yang_model_file: FileStorage) -> str:
+ path = "/tmp/" + yang_model_file.filename
+ yang_model_file.save(path)
+ return path
+
+ @classmethod
+ def install_new_model(cls, yang_model_path: str):
+ logger.info("Installing new model: %s", yang_model_path)
+ command = "sysrepoctl --install --yang={} --owner=netconf:nogroup --permissions=777" \
+ .format(yang_model_path)
+ cls._run_bash_command(command)
+
+ @classmethod
+ def uninstall_a_model(cls, yang_model_name: str):
+ logger.info("Uninstalling a model: %s", yang_model_name)
+ command = "sysrepoctl --uninstall --module={}" \
+ .format(yang_model_name)
+ cls._run_bash_command(command)
+
+
+ @classmethod
+ def set_default_configuration(cls, init_conf_path: str, module_name: str):
+ logger.info("Attempting to set default configuration %s for module %s", init_conf_path, module_name)
+ command = "sysrepocfg --import={} --datastore=startup --format=xml --level=3 {}" \
+ .format(init_conf_path, module_name)
+ cls._run_bash_command(command)
+
+ @classmethod
+ def start_change_listener_for_model(cls, module_name: str):
+ logger.info("Starting listener for model: %s", module_name)
+ command = "python /netconf/netopeer_change_saver.py {} {} {}" \
+ .format(module_name, KAFKA_BROKER_NAME, KAFKA_TOPIC_NAME)
+ try:
+ check_output(["pgrep", "-f" , command], stderr=subprocess.STDOUT, universal_newlines=True)
+ logger.info("Change listener for {} already exist.".format(module_name))
+ except CalledProcessError:
+ subprocess.Popen(command.split(), stdout=subprocess.PIPE)
+
+ @classmethod
+ def stop_change_listener_for_model(cls, model_name):
+ logger.info("Stopping listener for model %s", model_name)
+ pid = cls.get_pid_by_name(model_name)
+ logger.info("pid is %s", pid)
+ command = "kill -2 {}".format(pid)
+ cls._run_bash_command(command)
+
+ @classmethod
+ def _run_bash_command(cls, command: str):
+ try:
+ logger.info("Attempts to invoke %s", command)
+ output = check_output(command.split(), stderr=subprocess.STDOUT,
+ universal_newlines=True)
+ logger.info("Output: %s", output)
+ if "ERR" in output:
+ raise RuntimeError(str(output))
+ except subprocess.CalledProcessError as e:
+ raise RuntimeError(e, str(e.stdout))
+
+ @classmethod
+ def get_pid_by_name(cls, name):
+ for dirname in os.listdir('/proc'):
+ if not dirname.isdigit():
+ continue
+ try:
+ with open('/proc/{}/cmdline'.format(dirname), mode='rb') as fd:
+ content = fd.read().decode().split('\x00')
+ except Exception as e:
+ print(e)
+ continue
+
+ if name in content:
+ return dirname
+
+
+class YangModelServer(Resource):
+ logger = logging.getLogger('YangModelServer')
+
+ def __init__(self, yang_loader_helper: YangLoaderHelper = YangLoaderHelper()):
+ self._yang_loader_helper = yang_loader_helper
+
+ def post(self):
+ args = self._parse_request()
+ yang_model_file = args['yangModel']
+ initial_config_file = args['initialConfig']
+ module_name = args['moduleName']
+ model_path = self._yang_loader_helper.save_file(yang_model_file)
+ conf_path = self._yang_loader_helper.save_file(initial_config_file)
+
+ try:
+ self._yang_loader_helper.install_new_model(model_path)
+ self._yang_loader_helper.set_default_configuration(conf_path,
+ module_name)
+ self._yang_loader_helper.start_change_listener_for_model(module_name)
+ except RuntimeError as e:
+ self.logger.error(e.args, exc_info=True)
+ return str(e.args), 400
+ return "Successfully started"
+
+ def delete(self):
+ args = self._parse_request()
+ yang_model_name = args['yangModelName']
+
+ try:
+ self._yang_loader_helper.stop_change_listener_for_model(yang_model_name)
+ time.sleep(5)
+ self._yang_loader_helper.uninstall_a_model(yang_model_name)
+ except RuntimeError as e:
+ self.logger.error(e.args, exc_info=True)
+ return str(e.args), 400
+ return "Successfully deleted"
+
+ @classmethod
+ def _parse_request(cls) -> reqparse.Namespace:
+ parse = reqparse.RequestParser()
+ parse.add_argument('yangModel',
+ type=FileStorage,
+ location='files')
+ parse.add_argument('initialConfig',
+ type=FileStorage,
+ location='files')
+ parse.add_argument('moduleName', type=str)
+ parse.add_argument('yangModelName', type=str)
+ return parse.parse_args()
+
+
+api.add_resource(YangModelServer, '/model')
+
+if __name__ == '__main__':
+ logging.basicConfig(filename=os.path.dirname(__file__) + "/yang_loader.log",
+ filemode="w",
+ level=logging.DEBUG)
+ app.run(host='0.0.0.0', port='5002')
diff --git a/netconfsimulator/pom.xml b/netconfsimulator/pom.xml
new file mode 100644
index 0000000..ecf5845
--- /dev/null
+++ b/netconfsimulator/pom.xml
@@ -0,0 +1,318 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============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=========================================================
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onap.simulator</groupId>
+ <artifactId>simulator-parent</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>netconfsimulator</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ <maven.build.timestamp.format>yyyyMMdd'T'HHmmss</maven.build.timestamp.format>
+ <docker.registry>nexus3.onap.org:10003</docker.registry>
+ <docker.image.tag>latest</docker.image.tag>
+ <docker.image.name>onap/${project.artifactId}</docker.image.name>
+ <spring.boot.version>2.1.6.RELEASE</spring.boot.version>
+ <spring.kafka.version>2.2.7.RELEASE</spring.kafka.version>
+ <apache.httpclient.version>4.5.6</apache.httpclient.version>
+ <dependency.directory.name>libs</dependency.directory.name>
+ <dependency.directory.location>${project.build.directory}/${dependency.directory.name}
+ </dependency.directory.location>
+ <netopeer-saver-project-name>netopeer-change-saver</netopeer-saver-project-name>
+ <netopeer-saver-source-dir>${project.basedir}/netopeer-change-saver-native</netopeer-saver-source-dir>
+ <netopeer-saver-build-dir>${project.build.directory}/cmake</netopeer-saver-build-dir>
+ <netopeer-saver-executable-dir>${netopeer-saver-build-dir}/bin</netopeer-saver-executable-dir>
+ <skipITs>true</skipITs>
+ <proxy>""</proxy>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter</artifactId>
+ <version>${spring.boot.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ <version>${spring.boot.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ <version>1.18.2</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>jnc</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-websocket</artifactId>
+ <version>${spring.boot.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.websocket</groupId>
+ <artifactId>javax.websocket-api</artifactId>
+ <version>1.1</version>
+ </dependency>
+
+ <!-- Kafka -->
+
+ <dependency>
+ <groupId>org.springframework.kafka</groupId>
+ <artifactId>spring-kafka</artifactId>
+ <version>${spring.kafka.version}</version>
+ </dependency>
+
+ <!-- TEST DEPENDENCIES -->
+
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.9.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>2.18.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <version>5.3.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.vintage</groupId>
+ <artifactId>junit-vintage-engine</artifactId>
+ <version>5.3.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <version>${spring.boot.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.kafka</groupId>
+ <artifactId>spring-kafka-test</artifactId>
+ <version>${spring.kafka.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.bitbucket.radistao.test</groupId>
+ <artifactId>before-after-spring-test-runner</artifactId>
+ <version>0.1.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.palantir.docker.compose</groupId>
+ <artifactId>docker-compose-rule-junit4</artifactId>
+ <version>0.29.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.5.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpmime</artifactId>
+ <version>4.5.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>${apache.httpclient.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpmime</artifactId>
+ <version>${apache.httpclient.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger2</artifactId>
+ <version>2.9.2</version>
+ </dependency>
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger-ui</artifactId>
+ <version>2.9.2</version>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <configuration>
+ <outputDirectory>${dependency.directory.location}</outputDirectory>
+ <includeScope>runtime</includeScope>
+ <silent>true</silent>
+ </configuration>
+ <executions>
+ <execution>
+ <id>copy-external-dependencies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy-dependencies</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.19</version>
+ <dependencies>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-surefire-provider</artifactId>
+ <version>1.1.1</version>
+ </dependency>
+ </dependencies>
+ <configuration>
+ <detail>true</detail>
+ <printSummary>true</printSummary>
+ <useSystemClassLoader>false</useSystemClassLoader>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <version>2.19.1</version>
+ <configuration>
+ <skipITs>${skipITs}</skipITs>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <profiles>
+ <profile>
+ <id>docker</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <properties>
+ <os.detected.name>linux</os.detected.name>
+ <os.detected.arch>x86_64</os.detected.arch>
+ <os.detected.classifier>${os.detected.name}-${os.detected.arch}</os.detected.classifier>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ <version>${docker-maven-plugin.version}</version>
+ <executions>
+ <execution>
+ <id>docker-build-image</id>
+ <phase>package</phase>
+ <goals>
+ <goal>build</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>docker-push-image</id>
+ <phase>deploy</phase>
+ <goals>
+ <goal>push</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <skipPush>${skipDockerPush}</skipPush>
+ <verbose>true</verbose>
+ <imagePullPolicy>IfNotPresent</imagePullPolicy>
+ <images>
+ <image>
+ <alias>${project.artifactId}</alias>
+ <name>${onap.nexus.dockerregistry.daily}/${docker.image.name}</name>
+ <registry>${onap.nexus.dockerregistry.daily}</registry>
+ <build>
+ <contextDir>${project.basedir}</contextDir>
+ <dockerFile>${project.basedir}/Dockerfile_app</dockerFile>
+ <tags>
+ <tag>${project.version}-${maven.build.timestamp}Z</tag>
+ </tags>
+ </build>
+ </image>
+ <image>
+ <name>${onap.nexus.dockerregistry.daily}/netopeer</name>
+ <build>
+ <contextDir>${project.basedir}</contextDir>
+ <dockerFile>${project.basedir}/Dockerfile_netopeer</dockerFile>
+ <args>
+ <PROXY>${proxy}</PROXY>
+ </args>
+ </build>
+ </image>
+ </images>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+ <repositories>
+ <repository>
+ <id>Palantir</id>
+ <url>https://dl.bintray.com/palantir/releases/</url>
+ </repository>
+ </repositories>
+</project>
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>
diff --git a/netconfsimulator/ssh/ssh_host_rsa_key b/netconfsimulator/ssh/ssh_host_rsa_key
new file mode 100644
index 0000000..dbf8d76
--- /dev/null
+++ b/netconfsimulator/ssh/ssh_host_rsa_key
@@ -0,0 +1,49 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
+NhAAAAAwEAAQAAAgEA0Eve1v58bkLc4/6FvcBYWO8q6rOTJok7YyOHnc6QBJFuChSTbAaV
+nn+E8nPgyqN0/hH740+qjtWpaJdKh0+hJMw0vUeX6SQ1OpRgyXFbDSbFrjsBjB7N2eusrR
+M5FweDxVW/mfR7AHemrLulbNYHt/HmY+Hee09D/awICpuViLKLOsPp8ifmbHG1mXStLNSg
+OJgBvsrMvbsKYgJq0HdWPyfkzFf4SVdjKxeegQbkSogWGqRG7zkfu8bsYooUpIFRFMiJvW
+dr9tlegESAu1fmUx2Wz7EtN4Qq7xqHfAbT+Ruzr4rtCRRCsdEou49kpTSM75FaKlXmchRV
+tBQKndFhcPQCO+m9OkQ4+45VSeLL236MTvAFGnqfLks97TO7VLGinhtMvJA1gZI93bwVBS
+P1uaVuiUdzT8OgUq6xsLx2qg2aoau5Ivj65Hdj8tlIjeFHeGgUd1GZa434X3p8WCDFVrQK
+GpReaDTFttVFw7F2+biFmn68TO0bb4GE84r8ouaYVSGzWG1kEy8UtriXnx6GPOzncp8HKT
+YFn2etzs5EE9Ae64+1mIJTUEnWlpxwrjCqCIreibMz4/7AkSWrwc0YeAUERso+ESQkCU8R
+zpcYSRz50UTCaerS8K5cPE9N2XB3WYbsziNTpR568onQIkL+ZlTIbNNYBgdVNDLlQeabKS
+kAAAdILF3O/Sxdzv0AAAAHc3NoLXJzYQAAAgEA0Eve1v58bkLc4/6FvcBYWO8q6rOTJok7
+YyOHnc6QBJFuChSTbAaVnn+E8nPgyqN0/hH740+qjtWpaJdKh0+hJMw0vUeX6SQ1OpRgyX
+FbDSbFrjsBjB7N2eusrRM5FweDxVW/mfR7AHemrLulbNYHt/HmY+Hee09D/awICpuViLKL
+OsPp8ifmbHG1mXStLNSgOJgBvsrMvbsKYgJq0HdWPyfkzFf4SVdjKxeegQbkSogWGqRG7z
+kfu8bsYooUpIFRFMiJvWdr9tlegESAu1fmUx2Wz7EtN4Qq7xqHfAbT+Ruzr4rtCRRCsdEo
+u49kpTSM75FaKlXmchRVtBQKndFhcPQCO+m9OkQ4+45VSeLL236MTvAFGnqfLks97TO7VL
+GinhtMvJA1gZI93bwVBSP1uaVuiUdzT8OgUq6xsLx2qg2aoau5Ivj65Hdj8tlIjeFHeGgU
+d1GZa434X3p8WCDFVrQKGpReaDTFttVFw7F2+biFmn68TO0bb4GE84r8ouaYVSGzWG1kEy
+8UtriXnx6GPOzncp8HKTYFn2etzs5EE9Ae64+1mIJTUEnWlpxwrjCqCIreibMz4/7AkSWr
+wc0YeAUERso+ESQkCU8RzpcYSRz50UTCaerS8K5cPE9N2XB3WYbsziNTpR568onQIkL+Zl
+TIbNNYBgdVNDLlQeabKSkAAAADAQABAAACAQDFUyq+14T/W34wytzd/opzbddlUksTlavZ
+5j3CZH4Qpcjt6cIi8zXoWfuTR+1ramAZlOXf2IfGGmkLeU+UUf5hgsZvjZQ+vBtk7E2oaC
+eOlO1ued2kZUYzrMz/hRdvVqIhXnNNoMqpjbArMPSs3zGes53Df6UpgdTySnevvOZzAlld
+iV1mFyB2GV6lCmBH+QHzuyTkHvDIyJk0cf/Ij1T4LY3Ve3zt1chPeWeh6ep5JORzxq6gT6
+hdVjx3uUGG+i7aloPOF1yzFAcvUjX1xHagxIYrKTihwCaALsys1TcYZYLayKx3DmeEVpXU
+4SnCS7878KHPO2M9LUBngRjxmvpHtnaIyp4LugY48y+KtywjR39hOsKW3QawVp6CtTceNE
+QMlosaVIQuMJkeW14poYBclva0B/lCn3r9/3OrCI9qZPdD1RrCQqUyom56EU4kwRddSwHi
+LDj4xKoyzH022lfaTt+PwbXtVJGAOVTS86ZovJaIJwyKCE1T0p66qqIM/mo1GVMTfTKwUt
+A9v0AwTYFXmZ/9HJjhMNhCNclPilZHzuPI9VqiZ3tkKhS6kxZQA2cB1VdcFPIEeVOpZcJd
+yPzGfFuKYOh9CLQMxfMC46MeT1XoQi5bMBygy8ZajV9VM2xUMKft1IqyN65nA7d+covoxL
+PLjC6n3hUJJtSMTAohAQAAAQBf4lenniOgFycb1REtnUhyaCsopBmkuxlCOknkmFiMNW+B
+v/aeFUp03WluKGOVureJoooU3Z32Om2+YTxTOnCRL5Sn4gL7cAp7JfDSZpZPqynUAKFvKq
+QVbYyiEmFkUDFWes9Q0r0LAx2rvPrDIGhqx2ZgrzINlhrhxaQU2+fGbNRdI86PcWSYtc5u
+Oucd6nJM7eI3QL7/pVNlK3GMJ68eviKmhxP5HHnanNes2ORa15S5rRSOAZ5653pA1J1KxB
+J5vwwMvIXbEnBn8NilqWK50DHJMFXEoLaKlb0OoYMKbiLt4CjcCCIUUT1kAu/SFUpFdYI1
+DNXfrieu3ZTEwnU9AAABAQDrf9b8VnARNimzLqI15hp0Z5B8Sxf8Xw85KRRxg7cu6TJLoF
+K6VRK1jOzrKCrIzvBwuS2Er06ogEE/3N8eXC2ipzNOtDh7CQqoAq8IKNUt+2cvThNzfOFe
+BZ6lP9pQ60RGEPoeQyhlxHOUbV80K/ksiFqnAixOmOV7Uc7KZ+8clFvhOCm76vo80GaYYk
+NQtvMa1qxIsyUrOdhmIxF9dYN/sQMUr909o80kN69L7d+D1hG6WNskEJphrHSkPWTell5g
+4ekFna1+MjNQoCWhp6KPDKK9Y8AMrqWU/bFYw9CYwXo67p486qt487ZN+0cNItmSLMR3Ke
+MWmCmq37+v3viJAAABAQDibc4fU/xePaCjQa4VlAwk9ISd8Ufz/LcJWDiseKSzQnIdCaCG
+gO/SWTUuAzGEdrNnfJRcEUtrJ0UJAo2ntYZ3AlJLQvFF9KII3yuUn/RHMrkwslAIoqLhLO
+QX38nJeWR+hEWSFbpWP5N5biLRi2Qnwtv55hYgNqTLHRURzvin5/YeuwigBax1SwtN+V2D
+JSDhMiaFV85ZQggSoIjLIsunLK5XIDzTC82gJ9aaazvKBXwkf4Yfv5t1BgPATzqwjmXztQ
+T8WbTeqEO95cIu0zhfKIGo7Wvx7S9NPrNjyNO+JK9/qqdYGhErPiZPKOcHHywF77Yyv2cm
+gOrMYVCubUOhAAAAD21hcmNpbkBtYW5kaW5nbwECAw==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/netconfsimulator/ssh/ssh_host_rsa_key.pub b/netconfsimulator/ssh/ssh_host_rsa_key.pub
new file mode 100644
index 0000000..8f73911
--- /dev/null
+++ b/netconfsimulator/ssh/ssh_host_rsa_key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDQS97W/nxuQtzj/oW9wFhY7yrqs5MmiTtjI4edzpAEkW4KFJNsBpWef4Tyc+DKo3T+EfvjT6qO1alol0qHT6EkzDS9R5fpJDU6lGDJcVsNJsWuOwGMHs3Z66ytEzkXB4PFVb+Z9HsAd6asu6Vs1ge38eZj4d57T0P9rAgKm5WIsos6w+nyJ+ZscbWZdK0s1KA4mAG+ysy9uwpiAmrQd1Y/J+TMV/hJV2MrF56BBuRKiBYapEbvOR+7xuxiihSkgVEUyIm9Z2v22V6ARIC7V+ZTHZbPsS03hCrvGod8BtP5G7Oviu0JFEKx0Si7j2SlNIzvkVoqVeZyFFW0FAqd0WFw9AI76b06RDj7jlVJ4svbfoxO8AUaep8uSz3tM7tUsaKeG0y8kDWBkj3dvBUFI/W5pW6JR3NPw6BSrrGwvHaqDZqhq7ki+Prkd2Py2UiN4Ud4aBR3UZlrjfhfenxYIMVWtAoalF5oNMW21UXDsXb5uIWafrxM7RtvgYTzivyi5phVIbNYbWQTLxS2uJefHoY87OdynwcpNgWfZ63OzkQT0B7rj7WYglNQSdaWnHCuMKoIit6JszPj/sCRJavBzRh4BQRGyj4RJCQJTxHOlxhJHPnRRMJp6tLwrlw8T03ZcHdZhuzOI1OlHnryidAiQv5mVMhs01gGB1U0MuVB5pspKQ== marcin@mandingo
diff --git a/netconfsimulator/tls/ca.crt b/netconfsimulator/tls/ca.crt
new file mode 100644
index 0000000..a72e926
--- /dev/null
+++ b/netconfsimulator/tls/ca.crt
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFlTCCA32gAwIBAgIUYjGBXIujymdkS9A2MzssbetWn9gwDQYJKoZIhvcNAQEL
+BQAwWjELMAkGA1UEBhMCUEwxCzAJBgNVBAgMAkRTMRAwDgYDVQQHDAdXcm9jbGF3
+MQ4wDAYDVQQKDAVOb2tpYTENMAsGA1UECwwETWFubzENMAsGA1UEAwwET25hcDAe
+Fw0xOTExMDYxNTExNDBaFw0yOTExMDMxNTExNDBaMFoxCzAJBgNVBAYTAlBMMQsw
+CQYDVQQIDAJEUzEQMA4GA1UEBwwHV3JvY2xhdzEOMAwGA1UECgwFTm9raWExDTAL
+BgNVBAsMBE1hbm8xDTALBgNVBAMMBE9uYXAwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQC7zJGm+qODeBm0citmi+VKOwhVUg1QTyofXsqoib+yeiepYz+c
+MRMXMK20TA2m4ObAG5JoU0Ht1bT08f4K8BAoutq0rwb28YodSKiq8PCtD4Pn81Up
+FdP+Ymsl4YqXhydLEsqv/tpZgVvsCr/a6esxPfGSrScqw2PUf8qrHEXV48lX2Ho9
+19nZWjQTgqDzmOAShyMQabdrfuKII6/A6w/Q8eghzMQrJFsnfOS35RmH6MJyi1vl
+Hn37uEpZWjh0untRA4mtAHB6f3exw36gfpGyQgfMUiCBCbsr8SgJbHCaDpozD9ZE
+Ny+R+jRsqt0RlBoZsSPToNw1o6YjzBZZi1qYyDuNz0bQR/hedQxkwwKcj0Y7tfxF
+IfwUkb1XQ0e4mDia6R6tptphSfrSKLouVFGTVdVtoP5lpGhsw3+KWuTbwB64cZuS
+YuWW1BJ7gM6xj8b9cWRtOakHJ7WKt98Z3U7wDrQqZpnWk893kWoo0Q22X4uTipHX
++ETSdtTq5mVvYQQNub2CcA/4GQyODfdlC0gDkNeT9t6yfMJcOAJUBvW1JMoDAkED
+nWnWm/kS7aflFH6IHfqHbt1ZXUGJG94wTpvGJAHTnaiyX0mL4KK/r+ivdii+Tais
+VFKOqW7EqNZziOvZ0TMfkHtrBY89DgG8+XNwa0KUQIqVIQoJwOfQl4pAjQIDAQAB
+o1MwUTAdBgNVHQ4EFgQUQ9QHKoM/JUSm7ZnO+JvsHMVo6M0wHwYDVR0jBBgwFoAU
+Q9QHKoM/JUSm7ZnO+JvsHMVo6M0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
+AQsFAAOCAgEAK6bcNkkAVY11yUg1Vm3e9QGRWPeWNbKVIHR/uJIvq4R8AthvFcEG
+6GxApE9Zdd0oWpUG9cM/EZ3ZB5drDnpqUYURkQ8YNrIPJXByvqXFeQPej+m7DHOE
+vPtbm780cE+Z6abfctRFdujJX8hQ5s3UEKjpk0HsbZqEX4JbNeLT+LKgH+kwU/SA
+puVFZXN7dTACdX2gg2F1Fg52L54ZTWMlFMNn32b1a8zrKY0HaoLQd+yXS4RfrMpd
+jPsaytuOimU+OFRFb8cvSpuC+gkCEwQFVKb/B+0Rp1No3edEE4N92hpkiDh5/+ML
+/B8kSh0InaGxu6uTC3QzqTRgzbaRpOoWkdVRkDSpU6xNtyQN5jXDExpXtPe4LMP6
+Vk1jGpJ3E6kjSzzSvZFsH9Fko++aF6m5qqAHgG/Yc0j+7bmWCoMZWVqoxE9fHjfc
+mFs8N7R2HYA7G3qKs1fZy5SzAhuMkggys6z3c6ZW2BtD82GClSlXIlPwBu6KpCNy
+6cDeQ245wH1pO7pKfaIu3hbY6m5Z9jkbyOeW4I2Pj2s13345qjF9pRggkJ0MQtgi
+M6vU/13o/WhF0lEVz37pbriJ106412tF60uyyzZvJkAqhBCJyJ0jxqtwKr8GhL9g
+PnTRJkhFAE/5KvAi0ETbOIScRz/B+v+nm08ZGe1nHQ/Vu+NIY5gLp18=
+-----END CERTIFICATE-----
diff --git a/netconfsimulator/tls/ca.key b/netconfsimulator/tls/ca.key
new file mode 100644
index 0000000..1e87b90
--- /dev/null
+++ b/netconfsimulator/tls/ca.key
@@ -0,0 +1,54 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIJpDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIUwiDd8rZROYCAggA
+MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECJDuKSaXjEGWBIIJUImHlTdWbHhQ
+5/4oKETy31+ge3i0bBkk4HvfxaKlzsiHlG/WCLbUCSb0MR7KBdg1Xvl0sA9fE8ei
+6GH/iEBsJ/NDdwhQyPZiaLRKwQQaaT33zkBTY02fxHAxz5YCGmahFUa/UQYcBZKX
+FjHUWx1MBv0tNgwpcdOKhLSTviL92QNHdzuq2IlOfhZ/Ly3fTKlQbgRaTPfgtlWh
+8VkhtOUjikcNzoOUEQja2OUQ5scjmAo07iLx+ObR8znpOfg/ScYTUZ6fEEm8zykV
+1QFnuNr5RAUnldIqPUhZwmbNrlijvGcbQez0jQI/KZqKEMtBX8Hs224IiCrG1GPe
+AFvbs2Y3gc4hP9OSqlePVNhTSF8i6ALy8FZdCH70rJ3nHowxmuj5TnEWSZV2gqjv
+ruNy7u8KGK9YPSyL6dI/toE0vdNjvbDlqOEbLKJ6wg0spdQx1w6PBjImc/S+21oB
+pXTt3VEgo6AWlB2Q4sbIg+7Oh6iWwgzcrLjmkFndgWnxa74Ajzi8TylJdgLQ2wHg
+2w4cuFxdPdgDwYNUiJtC7FhnEerOndTxgTtAAwjPKtI7xhwVqd11aCw0MR/Zi7Pp
+GVN2EAEJjFUwRfnc37JwOiL03HfKZGdDCBggldyqJX82ECeMvfwSqlBVji4ouxKY
+ZRypStk8wQnB4BCOgymQXTWAzUIWtVjOpOyUIobRXkqX3vDTEYfKkBzHQSVgFqA9
+iOMJ4pB76wG/f7Y8X7rz7p9AVMrt8XhNeQP9/H4COZmhWYyOkJNcAM3S4Gq3jnpp
+sYy99Vw81BWHXmqCfY7omZSle7YstisB5syIdehDR7H3pqe84ZHrs3GP84sWbuyC
+jpmOWp5pt5PhJA3BmyUZZpGpwzGr3ZgRzo5O+l8LkDNa5tcz1qgyergzxyQWwurV
+YMTxSoBiJOMJdGD3ZwNgQ499naDUeHExncWWA1ghCOsEcTNSF0SjSINRk6NhP0bQ
+KsbMemqF1pr6qLtyVmWHv2pl8yunxvRxUM8VGReXhRoJGsm4U6lXY3pL5LumQ6TM
+7J0AKdaTl9qV3xL9LXVFfP4pcx6vstMVHzzkt8i6uN0tti0OkQzCk6tiPBi/pk3l
+vlLQ/IWrng4ddI6IObF+63JkDfpzGkFk1XG07qP/Urj88f3lEkh5D208JpMb5Zfu
+m3O5nM/SQddL4FWU63OvfVRISiJ9V93mE6Zc2z1+XeTEx8OKK720sCeYwaCkTv1f
+6FhVG9F8QvX+OP7eLPCXQKokY6/XgwK/C0gJHdrJcJYT1pM3rR+CTBoxHs+698xO
+utV0miEDTg8jG6CoU86tP9o1KP7eSAWII+8E7tWJlMphHTBdWJZbz/917LQMlCyF
+ObLwhSKCi8PPNGj09wkZBOt5CoMGFsVZkO+z+4m++g9BWS/hzYaQxwnNZ0JnxVci
+15QkZRl4deyKiJqcKjvPdLCXBGsejjAfaUfu3xuwq+ZNlcHqlMRGBx7RLngGciFK
+yRJxrUBoWcHCUNF5zpUiQq8UIU88ptCQyk1qRXAmIcXyzRZ8ge9J0mFoznd+vY6X
+Mlm1fHR1sR/CY+neZlWnVKCiR+3BLl/vVoavsOo8FBbPUiIuAvPuqSUK37IG/RAZ
+E4mfxP6K5B+zGaPSXXOiI1uqYj1RGRtRXYWKmoIpOQhwydylMOYGRksmfCL4ureA
+eMmsXAs0pLXjkcxv81pE7ax004OZtB/RnnlGmSgmvpzRt6usIxH8YksWT5ZyXZsD
+IcXtmOim4BpniZFjTAQb53u5gd5C1KvlLs3IZQ6n+1brPT2fwSiCI9SKJgln2exv
+0oKyYCs1MtZpLPi8tb3/iuZTc33cyyLkf/e27qREauYS+IlFtQrjG4EMslzNrcyU
+FcVUmqgMArxObcBmv6/yMTsu3gGUPxCR59y9EZwZ/iNo+AJqer1lUdZD5RurGBW+
+qEohqtZ1zeBWq7a5S/RPm6+UcdXzQNJd5Ipfw1geIjyT3Xqn2gSf/hHXfSmu7vqv
+xvIdDu5H7ZEfguhSxtmde/tWdckDXrBRqGATCr9iZ5Jp+DZ2YXaOSW0JuSfZFEAI
+1XsIPyGaGvH3qXdGN/xruUq/lGFLNPsLWzWFzV31tuTgsqE1ptkRUTmLMwz8tIFW
+hFCuGuEMp1wgsco5x7GOrwn6w6p7fDqgCnXBAI2Y5094j5UmtM4SsWWxiQO/muLk
+v01s5DoJh5oEhDjsHmwkvnHzuhjco/omZCcik1kbs7KRu8ha26kMZ6A28T29OvAL
+llQvOPzAVQFXCYWJIn7JFv185kna+twfGX78rXyxbfl9l640D0tlklKZZwxsvIFI
+Zb+jPFFQdKFlAnDXSYvhqr7UHbIM4jDOPyKNmkXB13ePuxisIZvO1QEQP/Jz21xj
+yF5VcHIImFpzXhJyqbUn6PlSJUPgkGsTLeIMAXBaYk+JfRnxcARdTqPAXXIOF+wf
+QTnAwsAI14nwiZ7skoPqBkKUvUOWg0nQF1+BpqUmbONbAjTetfJEzg8Qkkdj5poG
+h7w9vT5h/PyGjYa1dkAJVaFHCE/cW2Hy6jQ6b5dO3/HfAgEpZxaGKBri2fns7WhU
+iTzQb1RhD9JLFDnMRqNDWbwW7DDqN3hjevDs8MIoaxIaxW8zjpOYMRKAbkUWpr6i
+Tz1sa1vQewNPq+Pw14AbYlr7LMm7y6WJWW1vzXVstQFE4JU1drSwSqRP/CVPSWRb
+5l5mb7/eiERKN4/EFsHpoRB7EIsRdD5xNGy9rAmjFOUlO/CxzjIy5MYLcKT+liJf
+x8l+EbfsjqtNFFVnntduTL7joo9bi9OufFDTtJkoBJdx+YZITrsQ0szbrdQBOe0Q
+KcU3CmY7TsUlur/2BHQ8KARA3e1nhYYKJxlb0WDocVlQANcervZ8qf4EmlPSxi5n
+1E9AxLSrhPFW8wa3G2ygwZWPmNgLdPw8bD3aS60s1P+whsBdDq6oQXFMD+RIWm2n
+L293J8nml3PcP3RhphMTUUbNLepOYYRNOieHr2dt2V78M8Hp2Ue8lCfNYu7ZXjTB
+5XeUWa2b76vwiZiSiKYDIG59hHjeIsQOGtPq6CzrgtxsPORpmmDcBFmZ6HxszApB
+hr8rrcdPLmoI0zFF+tOBuGglrHswp1c98aZcM9lrErf62MfOG9DRa11Xiexmt4wt
+ETq+BbQtrvmZb6x5IrRU3Kt5QmkS/K/1
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/netconfsimulator/tls/client.crt b/netconfsimulator/tls/client.crt
new file mode 100644
index 0000000..3c0d20c
--- /dev/null
+++ b/netconfsimulator/tls/client.crt
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFKDCCAxACAWUwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UEBhMCUEwxCzAJBgNV
+BAgMAkRTMRAwDgYDVQQHDAdXcm9jbGF3MQ4wDAYDVQQKDAVOb2tpYTENMAsGA1UE
+CwwETWFubzENMAsGA1UEAwwET25hcDAeFw0xOTExMDYxNTEyMjlaFw0yMDExMDUx
+NTEyMjlaMFoxCzAJBgNVBAYTAlBMMQswCQYDVQQIDAJETjEQMA4GA1UEBwwHV3Jv
+Y2xhdzEOMAwGA1UECgwFTm9raWExDTALBgNVBAsMBE1hbm8xDTALBgNVBAMMBE9u
+YXAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC1kEo6cAykAqNE4R53
+8VJn5Rzbs5HDDk9+0HCxgEp/7suRMWLOgeG5/L+nRgrLLcFPscwnIwIHUUE1pzyj
+aDux6cXjpto+1APq/sd1ofVMMl3I/zqcrqojixjFJO1INgN/i1CaAeBRO+mNmIUN
+IJKy2KbTYvTYYJrJbzIRh1NBsUvuIzvj5Wkq3cri4oJmz82lsNDAh4BGedWQT4RU
+mjb/EC7D9WpfOlMhsgLVJgPdG5ESFqN+xx/n4xJUcM/HGqVlfZED5XDcfQjp56Ba
+FBpNhLW41qVy8UoeixYiduLaVvKDoJc2Ea3N/pyE3YU1LBOy7znpULX3UdMb+bX5
+jGn4uauKz77hLZA5nurgb8v1ofbj1GCM28JfaiPRtIuWVtWKI+8RyKjaXjKd/rcG
+W/5T9aM9um/lH2v/DteaTa4i8zebL7k/DJFTXP+DCtJTi8e23Xalkp+O+MWQQrGR
+O07NHdl1gjDmEoc3NMRO51Ux5CDyqCbhF4QdX0ND+vDJmD8GBuxb6A3HjdmT5/iB
+iH5/HO6FRHFhMtR9xABryT01gYkDWJHszTStNLx/ZzW4Dz3i2WyoEvDa2meLkSh5
+edE1skowZriSeoOuGWJlfpjjvn1p4kjk0msg3T8EgJqZu9AiKzuVgILa8u6Kny8/
+p12MN+yltB02hiJZyMlyJDCn3QIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQAf227R
+PWMNcdVKbDz8pKXOYUN5RtvvisabF25CrxVMNzH2+3nQIuICOEg7dFI1qKEEh+DJ
+51dAgwCC7d+levHBHximFaW7ED1rRFZJUoHlF9Ku74KA3eWPcWBJdzIAk2KzcQhu
+GXVQs2RUv5nJJogAPTtt9hFU9e3vqfpb+DNrzO0BTCfTsO3QSSojrtOpLOhH4EBV
+z6TyKDYJH7XCIvhApJjbuWsvVi3WTYFpFtF3dBNTMXjMZaHqMQBmMO4VzoQeu422
+fAcCVb+UUIrdU/Jdm+6tjuUqfQj3lVIZFPhTXrZ0MfBkowHoioos34u+JJ3Hjd+5
+mK27OhNA4SKaYQfi2DQ8jV1Jf/yA+lcmZddAiLZyOgHXPNZpP6glG802/k67i61J
+PIxaoaVmgxccZINzqov4Sn7A6hgt3S68izpsrIu1R9JqKjjgNA5HqtkMjyYqaMoX
+nQ0jIxkd5lkHL8MzAecaR37Y4hibpgsDl2SsuLTtfcO3NQO0bGFMRqYaIHN6DYd0
+4S1vOrsCWc3njvUGW8UcvB8GC4puwTi9en9SPaVcgSEBZ8gsJ4E1XWAdyTQZJWon
+aMn8iAeapfJu7N3UA5nM9L0f7dvODkLDqXOJuLl1IhhNFu6E864Ld+BVH8EQjIrr
+xCvjvlMQ4pnp7kjQdEekswN9ON6e3pxT/W3t7g==
+-----END CERTIFICATE-----
diff --git a/netconfsimulator/tls/client.key b/netconfsimulator/tls/client.key
new file mode 100644
index 0000000..cb80fca
--- /dev/null
+++ b/netconfsimulator/tls/client.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAtZBKOnAMpAKjROEed/FSZ+Uc27ORww5PftBwsYBKf+7LkTFi
+zoHhufy/p0YKyy3BT7HMJyMCB1FBNac8o2g7senF46baPtQD6v7HdaH1TDJdyP86
+nK6qI4sYxSTtSDYDf4tQmgHgUTvpjZiFDSCSstim02L02GCayW8yEYdTQbFL7iM7
+4+VpKt3K4uKCZs/NpbDQwIeARnnVkE+EVJo2/xAuw/VqXzpTIbIC1SYD3RuREhaj
+fscf5+MSVHDPxxqlZX2RA+Vw3H0I6eegWhQaTYS1uNalcvFKHosWInbi2lbyg6CX
+NhGtzf6chN2FNSwTsu856VC191HTG/m1+Yxp+Lmris++4S2QOZ7q4G/L9aH249Rg
+jNvCX2oj0bSLllbViiPvEcio2l4ynf63Blv+U/WjPbpv5R9r/w7Xmk2uIvM3my+5
+PwyRU1z/gwrSU4vHtt12pZKfjvjFkEKxkTtOzR3ZdYIw5hKHNzTETudVMeQg8qgm
+4ReEHV9DQ/rwyZg/BgbsW+gNx43Zk+f4gYh+fxzuhURxYTLUfcQAa8k9NYGJA1iR
+7M00rTS8f2c1uA894tlsqBLw2tpni5EoeXnRNbJKMGa4knqDrhliZX6Y4759aeJI
+5NJrIN0/BICambvQIis7lYCC2vLuip8vP6ddjDfspbQdNoYiWcjJciQwp90CAwEA
+AQKCAgBvwrq+T/yn9Gu12XDc2fIku9ZQsq81xxhN8lZ8HIE/UKmD3Ud54F8nEhqT
+B71bJfYJbWNCb/0ureia8y+TUnT0ZfcG080hwXYL7/qtQR1yYspyEBFvIuDd8iA0
+Mi/RNFZg60uZETCYMG3qWAkGdJd4eX3vPsmCa6y787N5MLiiBlW753pVJQDgfxgF
+IvKsAJwQss88w+vE9ffs1dWaEjdqN/bhbq6koY1Mf3vY4oacUvywirjnMAFGxvKg
+gbSUVaWb3HgoUtJvFpQnDwoYiKlMMDV/6rh7EWXTeUANxOqNBdpQBckuYiF/XIvS
+CV2WxmP1z+Xw5VqtO3Pck5BdZP5RLj/43CAtXt/1JV7QFMXohBOvFbcqBaWjfvl7
+6/CvtTnTjNlp4Z2mciFrClqPTgeLT67dCCiyUwXiqJGWJ50tuao5vpPX3JEbc0By
+FGDsdcnXKat99EhC72celO1NprqfvFGJubHSmF1Kce+faqXEQLWHkda1dJtW2+BY
+AWgtOS9Gaxb/hTZPrD/mkhnUYebLBDmnp9azacKzddlovJvnbRNrpJZ+D+wXkGQR
+XmtfpFAkGyBlT4RyYOmfbEXvas8aSZmOltsPwiFwk0rPoH2owuYCj0XXLTFODuOy
+Rs0zns8sdQsvdpKuykvdh7m4hDDdR8FgEDbzK39DndwGUk7+DQKCAQEA8P82Re74
+Ri02TYK6SaSpxauaJMajgfg6FtXMhZ+Ed0eUmtDI5OMWkoVgaRZGjTLzxGmQxd2I
+h6YV3KsQng0mMySIqnVAPLm2HK3Ahopd1R26XalBBXpE7OwGlLmv7/9hQHNscMyO
+i89WcQWQWuvMUxHc55byeI3cfchgwrR5uqZcBcTmaCu8SQXfyVZnM230532Gz4+P
+DvLicAEI79SCKGDtjgnjWzqGbMgUtvtYDJzOT9py9DjW/h3I7aMIswp0y5PVvzOq
+HIFaDRW2WyfZD6FTqhhtYodQ5Bpu1APdhFo+ELYg7YuqwF8N/+oEU+BtkHlOGgnN
+RXMaBurO9W0MRwKCAQEAwN3iefqYcz+TywUbDXyvWGZNIrU7d8hS2NXZPDgj7ysc
+RecaPAcnEOEv4PgQjj6w+WL4XrIqDk6Nigzq/8nFL8M+odZke7o+zYxOFU9vsj7P
+iqfvcG3Yzkmuw8Rl/9yw25t3tq4ftN3Lw70CeSnWOo8wGrkYmnoHi1Boq1E+PAHr
+co2A6HvJchNxZhrEYPZJk1zg0w2TAhnwGxngZX2dz3bVTDWCu1dU5Me5Xj2ZLrcv
+ypTqF8wGPOkqzrCtV+diLybaYc4pLtuYRKew85KXRd/kNKVRLwtfA9MaLGYIuqaP
++X/fxUTDvWX3d/za7rUC7mgA0wLbzLkg/3i72nnQuwKCAQBwzrXD9IFIsB8sFxZG
+JIrHmXTU5BpjWyMtzzQmFC44e0wnT7XJ1jaZrU8cwa2SqJgMh3GZ/QHMpUS0f2qO
+4Q7Rf7dtOucvxapmMDrff2M+v+SdtP1XqA0dbboC4gxl5f/K4HyRLF9JOGt5Ciax
+OJDzDXh++1xSa+0a/7GzUjFs2jRXCvzjJB3FfLdtEkHKhBzQQyTYgZVwZObzkLhz
+MuEeL4jjJOPEr1d/EgUIRgzIEppMLLAEe5q/ZofIHLmUvT9Aj9nM300wfKf/Wft7
+HeLv+w8jnr2NElQeAfWAzrJhIrDMt8QgDXGHDLeL5CnUbUg8CVwyugXZDXDQbhck
+MMA/AoIBAE/ShY1efgIU6iI4dr3aQOghTwNyZq5opoE/4Kzfv1+ZRBZaU61MeIqv
+6JT/ljqJD83nfEwVCPrq4AGm3wwGgioGcMGibewXZS7FRvvi6KOpA1SrtJmfnP+j
+10UG4ABJYplBewaMgmrk8RSOAZwMCLIr2Qj2Wox0vPKX0Po2ob9MPBopHpUZxoIE
+MgbERUqsJD9tTU+Maq+P9KQCzbCzkMDwuc7UxsqqlmQplTq/28pN3uXFRC2dL2e+
+SQ1KajPbe/Nv7SpVnQ3brOWdUcs9fIZa56QyV6tU1XpLwKVnhW1aynPEzORkVW7p
+6MYsTKBHInNxcAXlRPsyE8ooOfduO0kCggEBAI6yl6Cqv/Yc8wEkews2aIS0oUav
+HF9QGoSWP/yJcX7w4C09L1jemdqJxIzZRJ5dN298PIKdEDuQT59FwC0qgIzNgG9q
+7VI6XZXh3+W9jC7wmp89eUbrj7yPpkREJg3bP9C6d4cFxJQCUACukDfRz896WIUf
+VKJLsXeebs0Ond6+hDX+HKPoCKdpbvIDUZd5FUJkNFh5EoSwkuo5mCAyTTWYkKSl
+JuiUdViT+RkvX3XzxVVBgZsS70mu/Iur/Qbz74rbMeEoReAfOYtXC22rzMD3rQsZ
+/uzCpGWsh32ywI/C+ESWSLAo+HZGL0gbrILQbnBknX2Yq8iUvnkB7W8/igs=
+-----END RSA PRIVATE KEY----- \ No newline at end of file
diff --git a/netconfsimulator/tls/client.req b/netconfsimulator/tls/client.req
new file mode 100644
index 0000000..ace54c0
--- /dev/null
+++ b/netconfsimulator/tls/client.req
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIE0zCCArsCAQAwWjELMAkGA1UEBhMCUEwxCzAJBgNVBAgMAkROMRAwDgYDVQQH
+DAdXcm9jbGF3MQ4wDAYDVQQKDAVOb2tpYTENMAsGA1UECwwETWFubzENMAsGA1UE
+AwwET25hcDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALWQSjpwDKQC
+o0ThHnfxUmflHNuzkcMOT37QcLGASn/uy5ExYs6B4bn8v6dGCsstwU+xzCcjAgdR
+QTWnPKNoO7HpxeOm2j7UA+r+x3Wh9UwyXcj/OpyuqiOLGMUk7Ug2A3+LUJoB4FE7
+6Y2YhQ0gkrLYptNi9NhgmslvMhGHU0GxS+4jO+PlaSrdyuLigmbPzaWw0MCHgEZ5
+1ZBPhFSaNv8QLsP1al86UyGyAtUmA90bkRIWo37HH+fjElRwz8capWV9kQPlcNx9
+COnnoFoUGk2EtbjWpXLxSh6LFiJ24tpW8oOglzYRrc3+nITdhTUsE7LvOelQtfdR
+0xv5tfmMafi5q4rPvuEtkDme6uBvy/Wh9uPUYIzbwl9qI9G0i5ZW1Yoj7xHIqNpe
+Mp3+twZb/lP1oz26b+Ufa/8O15pNriLzN5svuT8MkVNc/4MK0lOLx7bddqWSn474
+xZBCsZE7Ts0d2XWCMOYShzc0xE7nVTHkIPKoJuEXhB1fQ0P68MmYPwYG7FvoDceN
+2ZPn+IGIfn8c7oVEcWEy1H3EAGvJPTWBiQNYkezNNK00vH9nNbgPPeLZbKgS8Nra
+Z4uRKHl50TWySjBmuJJ6g64ZYmV+mOO+fWniSOTSayDdPwSAmpm70CIrO5WAgtry
+7oqfLz+nXYw37KW0HTaGIlnIyXIkMKfdAgMBAAGgNDAYBgkqhkiG9w0BCQIxCwwJ
+Y29sbGVjdG9yMBgGCSqGSIb3DQEJBzELDAljb2xsZWN0b3IwDQYJKoZIhvcNAQEL
+BQADggIBAG4zPJVwotinJdDfBY8sGj6ARcWNtRuCmLj4Xri6pzzvUCaSLFAvIkd1
+Tdm7uIrfUMU+/stvu2E1myWJGnITL8njUahMqrTwI0yZF3Do8bw12kr9lf3wDNLT
+CvPApGpUEegcGQS/pp6ksa7zd4iDkWPLqf4CqnooDKprPQXz2HL2clBCmSrD2u61
+zTVey/G6FCU1tfAXisykmF2pElC8AJpY9EkY/ShSwk31n3ARe0XZAqo9NfcwvgAx
+isqevdXQ+D/FROjVwSKb8CB1rANn36ziZRMz02cyVj3FLbOgSBsrAfunbLNDyQ9z
+C3FYNA/tTtaPlwL8aOz0T5N6iyjmpqcvlFFFB+H5zg1tP0rcYwGnto3d85GgBYBF
+BK+ikAzYgZlpB48VceMQydZk3aMOcUIVSWpzsVhr9GkUYmYE6DxSZlzXGs75FkFf
+aP7/Qqf8Ut8G8fZV2QziOcuMbCnZ2fZl/lLLHO6A4e86bDyUjqPQK/LPylvJGgmY
+CivXpuBVA7j5SZZ4YnwqtWe4xh5vgbxZU7sq9omHnJaLfINHfQepeHcL4mWRo1oP
+xnacKz0ju2rlPNRN2jFciFQnZTUvmWO5KvoipadyjWqd7QzM0J4rdW56+a4tR1LA
+0JiV+yzsq9NB6/wKGGtwjYOEhr5AJOEb3qJPNJ45KI3nag2l3HWj
+-----END CERTIFICATE REQUEST----- \ No newline at end of file
diff --git a/netconfsimulator/tls/server.req b/netconfsimulator/tls/server.req
new file mode 100644
index 0000000..7e3e70a
--- /dev/null
+++ b/netconfsimulator/tls/server.req
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIEuTCCAqECAQAwWjELMAkGA1UEBhMCUEwxCzAJBgNVBAgMAkROMRAwDgYDVQQH
+DAdXcm9jbGF3MQ4wDAYDVQQKDAVOb2tpYTENMAsGA1UECwwETWFubzENMAsGA1UE
+AwwET25hcDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALFl14dsyahU
+bDm8WxD2VPGHOy8CccW77WFyxbUUM0JxfawM8cJC8AZlb2RRVNmwsq5mtQa5XWnp
+rZBx1BaMmN+ek201QSec5m3Nwesz6m7w8xIn+JJqrerdQ6vDqKTsCIINhekHgNAb
+3QdRnIE2dSk8ORZgVTPxNMyLG832U/8lGic2yi+P/jB3j4Ianx33k18GUb851H60
+DSDYkT1QDP97DSw/2k2YThljBcUKaKKuajJpA+IwL2s7vDbaJj1neIuE+tDSKZAS
+TPzUJSS9d0yFk090wI42idZ9szByP+v08hBQDc4Uo0CWUrSxebKJ8Z7k347U3uG6
+MEjY6FqjL7y4oZo670fjiKiOnJVRxFDdj/5DR37hFGjHg1x6BSRt4aJ4SSdYuNu5
+X2LGijqJ6FBeKc+TZfj4g1eH2bLvEI03Z0V1Q2p8qq59jVJAb5U2qwPRU30JRrFY
+R84kPj8xV1xi3tZke0T0nszCLnDa5oCImm1Q31Ncbe1ZruSNNmGhpjGqe+nGaLOg
+jSQaoYfe982i9hhsEIgLOtOLSOYSRsIwd6tWoH5WrsrkotTawby9dMDA7ghkQ8lL
+xkuLkcm/VIKrqCofkP1KNi0NzAa4F3HXhwN8xd/UrbYz5jjjh0s6DOxX5Eu5P1+7
+JIGnuSc2Trd2iHSob2Cf97a5OrQ7/farAgMBAAGgGjAYBgkqhkiG9w0BCQcxCwwJ
+Y29sbGVjdG9yMA0GCSqGSIb3DQEBCwUAA4ICAQCJb4dJS4ddeL/IzuOA6kKpqOz/
+YJUECLCRPSvdf5DCH/ezf5L+UE8WUpcvSkRo36rwBPR/R2hDnshfKUYYZoi5UqzT
++Y7E3Kdrim4K9MGJGVTqtIYqoNS0jgbmX2zw7FDXHBVfP2TQhalzBO4BMshB916o
+IWs8esTRNhXZ7P4hDOkYyWp4qPeOjzdUgqEQosz+OkB+RdVpKfs3sG0eqZRDVnHz
+Hk7cfpn6m9OZf+0aLlAlhs1d9KvvdmUn0vC3QVktPLXZpa1XNTrE7tml3crYNmU0
+JSuP7MBwgjJMBe400aIXIKssqVUJjlAzNARFWo/84Fzb6QuyfVwNp1tMzBAFFDc3
+BOv891fwIKAOKRSSui2Eq3nxt6+8JTTx8YeuppS79V50XNl6PolY5jDEkxED7sst
+ScpU7rOURDfgT2EdqjWzSmW7JzBuuTkAz4T/lg4dAspKacVi9hG28Jt+gX6xlM5Y
+qS43o2YhSze4PfOgjkUIvqSiDa6uORMJPi5iCwGVpomKfMtZCmXrPiCyQg6u8TqJ
+LXA9pm++mehgj79kl+yb8A2eQvIqEb8eOG/CzZjW5IZxLZgKk/AjAcBn0yhhIrVq
+6rBaIlVbXOdxMsxxVgyktziA7DFCt/1TvQylSn9hWaBkpdjVlbFmMyuNU+KHZA41
+8raJhp+mC5IUG/98UQ==
+-----END CERTIFICATE REQUEST-----
diff --git a/netconfsimulator/tls/server_cert.crt b/netconfsimulator/tls/server_cert.crt
new file mode 100644
index 0000000..5909026
--- /dev/null
+++ b/netconfsimulator/tls/server_cert.crt
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFKDCCAxACAWQwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UEBhMCUEwxCzAJBgNV
+BAgMAkRTMRAwDgYDVQQHDAdXcm9jbGF3MQ4wDAYDVQQKDAVOb2tpYTENMAsGA1UE
+CwwETWFubzENMAsGA1UEAwwET25hcDAeFw0xOTExMDYxNTEzMjJaFw0yMzExMDUx
+NTEzMjJaMFoxCzAJBgNVBAYTAlBMMQswCQYDVQQIDAJETjEQMA4GA1UEBwwHV3Jv
+Y2xhdzEOMAwGA1UECgwFTm9raWExDTALBgNVBAsMBE1hbm8xDTALBgNVBAMMBE9u
+YXAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCxZdeHbMmoVGw5vFsQ
+9lTxhzsvAnHFu+1hcsW1FDNCcX2sDPHCQvAGZW9kUVTZsLKuZrUGuV1p6a2QcdQW
+jJjfnpNtNUEnnOZtzcHrM+pu8PMSJ/iSaq3q3UOrw6ik7AiCDYXpB4DQG90HUZyB
+NnUpPDkWYFUz8TTMixvN9lP/JRonNsovj/4wd4+CGp8d95NfBlG/OdR+tA0g2JE9
+UAz/ew0sP9pNmE4ZYwXFCmiirmoyaQPiMC9rO7w22iY9Z3iLhPrQ0imQEkz81CUk
+vXdMhZNPdMCONonWfbMwcj/r9PIQUA3OFKNAllK0sXmyifGe5N+O1N7hujBI2Oha
+oy+8uKGaOu9H44iojpyVUcRQ3Y/+Q0d+4RRox4NcegUkbeGieEknWLjbuV9ixoo6
+iehQXinPk2X4+INXh9my7xCNN2dFdUNqfKqufY1SQG+VNqsD0VN9CUaxWEfOJD4/
+MVdcYt7WZHtE9J7Mwi5w2uaAiJptUN9TXG3tWa7kjTZhoaYxqnvpxmizoI0kGqGH
+3vfNovYYbBCICzrTi0jmEkbCMHerVqB+Vq7K5KLU2sG8vXTAwO4IZEPJS8ZLi5HJ
+v1SCq6gqH5D9SjYtDcwGuBdx14cDfMXf1K22M+Y444dLOgzsV+RLuT9fuySBp7kn
+Nk63doh0qG9gn/e2uTq0O/32qwIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQBqW8/0
+u85VTUWA8tpfjqgmMd9WdM5YVty/oRQ3sT+1k6mdPF2u8HgTD5vNR71XEkpQuNbZ
+9UrJ1UOgcbxcodDh2v2Jv5faSyQBnmkSjagyH+7goN/mcM+CtIZQsdp8drzlty/M
+x1CeVW54jVrInk01yS3cATCdxqkel9nhTT0qcPOmHxugTyTwApzwN4dRMf1WHQ9B
+U1K95yWvscZdY2cMKRw3Ps90+AU8eSoAbV1qQEl95qcWani7LPNfc8nii3nCINBR
+KTdbP1uG8hck4BFxOZye6mbNMx086T7O3Buzcve5O/3isCCZQhV6PZIE7zL5+hyY
+RLx7MxUUETy4QFapGCfOGvixK8QM/xXJone2CjnmptiRJjC11Imv84bL3KUziKPR
+DTyX2f4oKEwaVpbfJgyyxMmoNUAB2NaKNAW9NhvYjVqmCUvgrvL7L6WZTe9N5gkX
+H7cQZ7PyAdgGKet0b0Spr8NfBATyZKNI6ZRcGJduO7sOblqAsC6GToq7Jcz9xkYd
+ItNdhoDohZIDwW1XnHuNKgE1tEXm1KAbdlGrUEOTsyuK4bz4kDdVu58/nFT4ZYnC
+2HSyLwsNNeKChwdVFL8d/Rj9ojjVyUBH6wuQHRAcs0Ui2Cpy+OPWcNjDuHjkiw2J
+KEzFqRi6zQqj1y2Sjv1HQpn8dxppKOsV6rjoXQ==
+-----END CERTIFICATE-----
diff --git a/netconfsimulator/tls/server_key.pem b/netconfsimulator/tls/server_key.pem
new file mode 100644
index 0000000..b10a076
--- /dev/null
+++ b/netconfsimulator/tls/server_key.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAsWXXh2zJqFRsObxbEPZU8Yc7LwJxxbvtYXLFtRQzQnF9rAzx
+wkLwBmVvZFFU2bCyrma1BrldaemtkHHUFoyY356TbTVBJ5zmbc3B6zPqbvDzEif4
+kmqt6t1Dq8OopOwIgg2F6QeA0BvdB1GcgTZ1KTw5FmBVM/E0zIsbzfZT/yUaJzbK
+L4/+MHePghqfHfeTXwZRvznUfrQNINiRPVAM/3sNLD/aTZhOGWMFxQpooq5qMmkD
+4jAvazu8NtomPWd4i4T60NIpkBJM/NQlJL13TIWTT3TAjjaJ1n2zMHI/6/TyEFAN
+zhSjQJZStLF5sonxnuTfjtTe4bowSNjoWqMvvLihmjrvR+OIqI6clVHEUN2P/kNH
+fuEUaMeDXHoFJG3honhJJ1i427lfYsaKOonoUF4pz5Nl+PiDV4fZsu8QjTdnRXVD
+anyqrn2NUkBvlTarA9FTfQlGsVhHziQ+PzFXXGLe1mR7RPSezMIucNrmgIiabVDf
+U1xt7Vmu5I02YaGmMap76cZos6CNJBqhh973zaL2GGwQiAs604tI5hJGwjB3q1ag
+flauyuSi1NrBvL10wMDuCGRDyUvGS4uRyb9UgquoKh+Q/Uo2LQ3MBrgXcdeHA3zF
+39SttjPmOOOHSzoM7FfkS7k/X7skgae5JzZOt3aIdKhvYJ/3trk6tDv99qsCAwEA
+AQKCAgEAiN5OgAoLPHiGV7VffaLQvtjbbNWx+iGYcMsghylisyzlDU2faYZDsmuo
+mfm2QY73DV/o45s423KTmxJ31D395OpwTJ39+xYvIyDRMSsJATeeErAMNMtiDAdT
+Ir7+6t/BYLYWT0ietfIJgpAQ9BCULJBuazlAA6diWT4q4anf/McfeonnTPMH51UX
+wAXSJkjRx0sU3zk7HdbFGkZ9KJUOsj66Vx2czKlp+GRSG0iAmqN/BA3KFUWgEFgy
+Sw/c2RtwAhVDfsHnl2THSXpUfhhAZyXYFkG6Hv8tMfQhwIsljmOaL8PUIuABZN+W
+SBt2Da4oYbOsWIMcw4PnpQzVmLi3JtajPMI5vJJQtNbgEKQwl7Xewhyfjdy61vVZ
+k3UBKzeRX78pW9BXpBrLlp3AcU3/jE+HihWzkaz5mXD2raOPJRXGHr9di2egttQx
+Fxdc4YRWTH/k6Eo/M20A7q1RiIkwKthKru0Ju2U0g4Szwn0vuAqs1+QEMhbZ/Z79
+p9KzKg6g+X125W4JxNLWSli0uQmmyYkUYsBXXPMRmlpqjhXN35plRPdlLVm6WgOT
+QhGta69Az4+rVJis5zlYXVBF7P8ympP5IDKAeBWbEfm6ZW2dWF3R8KOD1LA86Ctw
++n5NUoLQutGl5qrpFHEYyQO0RhJApFe808Gj4XZ0NbRKWYZYNuECggEBAOQVG7LF
+LDRWMe8t2aYsblOkJqdz5Nv+wvN7t7rRGeUZJ0mcy8QbqbRbYCbpelNYJQaV/qUM
+pDJc6D0xmdocGmKO5gTy6RxXGynB0pQW6Z3BavpQiCsQy0SsNGtdrP7nJ6uKwgfI
+WBlzQxk1RhLet60jcik9bRvc0x4TI03qIAVKfXk5XrSuvXxDaEFFfBIwclucK00g
+qkWDmVLaZbKisSgAod8DHZC2HroNbePQphGpPZPu5MfUKRhD0cjJmc90NIW1Teph
+cbjcl4bMfjZgjZgxMu+EKweqwikMolzMeR6QNlwDOER9BnXOrW9RLIOa2KWDLHTu
+7BKubWYEr2G9eoUCggEBAMcci/OGkAeB+aw6GgqRHxQVzbV05ItO8OZa1TdP5/s9
+8ZcqbcxejhdTGw92OjI5mjlc4r8DPrJy9AHkZNoUYcuS8qPK3x1k+roqvtXnGFeP
+xvEMRXQIv+GQe+aF75qMS2+hUq11daP8KmkqDvife5sPcS0zf1vdkeoj2fPOIC/G
+NMw5EjQEMHFj5I9N5tQr47GkWgKDaaM24tW4jxMMuDAnVH70iXbZm6GdqBGTPGV1
+cwM5KJKbP4d0WMnAm1NLCtGpxXzFiRD6xLWdikntdaM5eGOthkhbENadiEXYPfyb
+fCdV3XtIDYTbs41VruI4I/QR9kqXJD4dTJpve9Ozq28CggEAOn+zd/mcadwFuKvQ
+3A8fSPKcO2y9MTO5esurhw3kj18RNY8ysBzMPQcGtVRC3KeJ75N/GYQRhR1RL4jO
+RiPZHJg8JN7MrUkmHWKqCPOLfbvDHWqUUj9fc0CbvUHsggB1q0jOlUJsXjKy7f88
+9tAK2ixyNmOb3X6Y42jTMEOxbMn7PqFZSgxba0i6r6sLtHqaiiaW8JRFN8kWBSsg
+1F1oBLOM0O8yoRAk92FEYx77ySHYOl4Wba5bz+5tGwuiGJRceELVNh1CFsD1WEyQ
+vo+rIXLU9pu2kaPZYr/r0NNc6Vrhpl3pC5/pjUOyNg/i0+peUK1BNMim4e+8/WKM
+PQxlJQKCAQEAmM9e1TpLpYdqD3TT8ZIo0Ohe5MlecU/XcGL7yiSIPHjwxfwpIjTb
+Rilg1j/9L/inVSjlWIVsA9ZXUHBxujD0USFpGK2lCFEsX95YcZ+XPfQ5UVwCnaft
+Y3bYsDXQisLrTjP4WOmRrJXmEqnVrZfwbDOwv2kLcHye9JwpNITYjPHIKYgPYV5p
+s0xf6F94LRO1/urMrasfUsTX2AB5xbv4S/STU4/nRBCD8cgmab6fFprJ9wpSChod
+6p4vWj94tbcdCYYK69RKen6Ko3vR2yTkKK39qDpPmY5SYPOr1za7c/f8k8/BWFEX
+FwTqSykcO7+sUC/M3rgad5VS6h9vYC0+/wKCAQA5ANtXDECYCLTor0PHFxnRswfo
+7qZNn7HrrisRcJjdHsnGyW2KNQjrVYmidEBzN/AWpmr2qfSRp3xh++oLswYeJ2cR
+HC87Ws6xy17Mcd39sB9poE6XWfoTb4YYotrp1A2rJMaNbFOvkoPpMWJRzvgOgR9I
+XNvUFBlIJ/iNr9r08XzElEcqlonR10RwxpASHgq9PnNd6kxNCIzMJVBDnW1lpYHn
+PucG5sjhMjpuDNXvo0rKUoJEs37N2KeTngTWscGhdPfcIFJrw4d6Yyo89ORcc+Sc
+G+vn3AHyzlIPXMfG8cDWTKA+rfGQhKSp6+OWBKX/ZatFD8a0X0L4aBoKXpUk
+-----END RSA PRIVATE KEY-----
diff --git a/pnfsimulator/.gitignore b/pnfsimulator/.gitignore
new file mode 100644
index 0000000..3fa204a
--- /dev/null
+++ b/pnfsimulator/.gitignore
@@ -0,0 +1,4 @@
+.idea
+target
+*.iml
+logs
diff --git a/pnfsimulator/Dockerfile b/pnfsimulator/Dockerfile
new file mode 100644
index 0000000..aafea0e
--- /dev/null
+++ b/pnfsimulator/Dockerfile
@@ -0,0 +1,8 @@
+FROM openjdk:8-jre-alpine
+ADD target/libs /app/libs
+ADD target/pnfsimulator-5.0.0-SNAPSHOT.jar /app/pnf-simulator.jar
+CMD apk update
+CMD apk add ca-certificates
+ADD certificates /usr/local/share/ca-certificates/
+RUN update-ca-certificates
+CMD java -Dspring.config.location=file:/app/application.properties -cp /app/libs/*:/app/pnf-simulator.jar org.onap.pnfsimulator.Main
diff --git a/pnfsimulator/README.md b/pnfsimulator/README.md
new file mode 100644
index 0000000..6caa644
--- /dev/null
+++ b/pnfsimulator/README.md
@@ -0,0 +1,333 @@
+# PNF Simulator
+Simulator that generates VES events related to PNF PNP integration.
+
+## Usage of simulator
+### Setting up
+Preferred way to start simulator is to use `docker-compose up -d` command.
+All required docker images will be downloaded from ONAP Nexus, however there is possibility to build those
+images locally. It can be achieve by invoking `mvn clean package docker:build` from top directory.
+
+### API
+Simulator provides REST endpoints which can be used to trigger sending events to VES.
+
+*Periodic event sending*
+To trigger sending use following endpoint *http://<simulator_ip>:5000/simulator/start*.
+Supported method: *POST*
+Headers:
+ - Content-Type - application/json
+Parameters:
+
+ simulatorParams:
+ repeatCount - determines how many events will be sent
+ repeatInterval - time (in seconds) between events
+ vesServerUrl - valid path to VES Collector
+ templateName - name of template file (check *Templates* section)
+ patch - part of event which will be merged into template
+
+
+Sample Request:
+
+ {
+ "simulatorParams": {
+ "repeatCount": 5,
+ "repeatInterval": 2,
+ "vesServerUrl": "http://VES-HOST:8080/eventListener/v7"
+ },
+ "templateName": "validExampleMeasurementEvent.json",
+ "patch": {
+ "event": {
+ "commonEventHeader": {
+ "eventId": "PATCHED_eventId",
+ "sourceName": "PATCHED_sourceName",
+ "version": 3.0
+ }
+ }
+ }
+ }
+
+*One-time event sending*
+Enables direct, immediate event sending without need to have template deployed on backend.
+Keywords are supported,thus once passed, will also be substituted with proper strings.
+Passed event body must be valid and complete event according to VES Collector interface.
+To trigger sending use following endpoint *http://<simulator_ip>:5000/simulator/event*.
+
+Supported method: *POST*
+Headers:
+ - Content-Type - application/json
+Parameters:
+
+ vesServerUrl - valid URL to VES Collector event listener
+ event - body of event to be sent directly to VES Collector (it can contain keyword expressions)
+
+
+Sample Request:
+
+ {
+ "vesServerUrl": "http://VES-HOST:8080/eventListener/v7",
+ "event": {
+ "commonEventHeader": {
+ "eventId": "#RandomString(20)",
+ "sourceName": "PATCHED_sourceName",
+ "version": 3.0
+ }
+ }
+ }
+
+### Changing simulator configuration
+Utility of default configuration has been introduced so as to facilitate sending requests. so far only vesServerUrl states default simulator configuration.
+On simulator startup, vesServerUrl is initialized with default value, but must be replaced with correct VES server url by user.
+Once vesServerUrl is properly set on simulator, this parameter does not need to be incorporated into every trigger event request.
+If user does not provide vesServerUrl in trigger request, default value will be used.
+If use does provide vesServerUrl in trigger request, then passed value will be used instead of default one (default value will not be overwritten by provided one).
+
+It is possible to get and update configuration (current target vesServerUrl) using offered REST API - */simulator/config* endpoint is exposed for that.
+To get current configuration *GET* method must be used.
+To update vesServerUrl *PUT* method is used, example request:
+
+ {
+ "vesServerUrl": "http://10.154.164.117:8080/eventListener/v7"
+ }
+
+Note: passed vesServerUrl must be wellformed URL.
+
+
+### Running simulator
+The recommended way is to checkout PNF Simulator project from ONAP Git repository and use *simulator*.sh script.
+If you copy *simulator.sh* script to another location, keep in mind to copy also *docker-compose.yml* and directories: *config and templates*.
+In order to run simulator, invoke ./simulator.sh -e build to build required images and then invoke ./simulator.sh -e start
+Script downloads if necessary needed Docker images and runs instances of these images.
+After simulator start it is advisable to setup default value for target vesServerUrl.
+
+Example request:
+
+ PUT to http://<simulator_ip>:<simulator_port>/simulator/config
+
+ {
+ "vesServerUrl": "PUT HERE VALID URL TO YOUR VES EVENT LISTENER"
+ }
+
+### Templates
+Template is a draft event. Merging event with patch will result in valid VES event. Template itself should be a correct VES event as well as valid json object.
+In order to apply custom template, just copy it to ./templates directory.
+*notification.json* and *registration.json* are available by default in *./templates* directory.
+
+#### Template management
+The simulator provides means for managing templates. Supported actions: adding, editing (overriding) and deleting are available via HTTP endpoint */template*
+
+```GET /template/list```
+Lists all templates known to the simulator.
+
+```GET /template/get-content/{name}```
+Gets template content based on *name* path variable.
+
+```POST /template/upload?override=true```
+Saves template content under *name* path variable. The non-mandatory parameter *override* allows overwriting an existing template.
+
+Sample payload:
+```
+{
+ "name": "someTemplate",
+ "template": {
+ "commonEventHeader": {
+ "domain": "notification",
+ "eventName": "vFirewallBroadcastPackets"
+ },
+ "notificationFields": {
+ "arrayOfNamedHashMap": [{
+ "name": "A20161221.1031-1041.bin.gz",
+ "hashMap": {
+ "fileformatType": "org.3GPP.32.435#measCollec"
+ }
+ }]
+ }
+ }
+}
+```
+
+### Searching for key-value conditions in stored templates
+Simulator allows to search through stored templates and retrieve names of those that satisfy given criteria passed in form of key-value pairs (See examples below).
+Following data types are supported in search as values:
+-integer
+-string
+-double
+-boolean
+Searching for null values as well as incorporating regex expression with intention to find a match is not supported.
+Search expression must be valid JSON, thus no duplicate keys are allowed - user could specify the same parameter multiple times, but only last occurrence will be applied to query.
+Example search expression:
+
+{"domain": "notification", "sequence": 1, "startEpochMicrosec": 1531616794, "sampleDouble": 2.5}
+
+will find all templates that contain all of passed key-value entries. There is an AND condition beetwen given criteria - all of them must be satisfied to qualify template as matching item.
+ Keys of search expressions are searched in case insensitive way as well as string values.
+Where it comes to values of numerical and boolean type exact match is expected.
+
+API usage:
+
+```POST /template/search```
+Produces query that returns templates that contain searched criteria
+
+Sample payload:
+```
+{
+ "searchExpr": {
+ "domain": "notification",
+ "sequence": 1,
+ "startEpochMicrosec": 1531616794,
+ "sampleDouble": 2.5
+ }
+}
+```
+Sample response:
+```
+[notification.json]
+```
+
+
+Note: Manually deployed templates, or actually existing ones, but modified inside the templates catalog '/app/templates', will be automatically synchronized with schemas stored inside the database. That means that a user can dynamically change the template content using vi editor at simulator container, as well as use any editor at any machine and then push the changes to the template folder. All the changes will be processed 'on the fly' and accessible via the rest API.
+
+### Periodic events
+Simulator has ability to send event periodically. Rest API support parameters:
+* repeatCount - count of times that event will be sent to VES
+* repeatInterval - interval (in second) between two events.
+(Checkout example to see how to use them)
+
+### Patching
+User is able to provide patch in request, which will be merged into template.
+
+Warning: Patch should be a valid json object (no json primitives nor json arrays are allowed as a full body of patch).
+
+This mechanism allows to override part of template.
+If in "patch" section there are additional parameters (absent in template), those parameters with values will be added to event.
+Patching mechanism supports also keywords that enables automatic value generation of appropriate type
+
+### Keyword support
+Simulator supports corresponding keywords:
+- \#RandomInteger(start,end) - substitutes keyword with random positive integer within given range (range borders inclusive)
+- \#RandomPrimitiveInteger(start,end) - the same as #RandomInteger(start,end), but returns long as result
+- \#RandomInteger - substitutes keyword with random positive integer
+- \#RandomString(length) - substitutes keyword with random ASCII string with specified length
+- \#RandomString - substitutes keyword with random ASCII string with length of 20 characters
+- \#Timestamp - substitutes keyword with current timestamp in epoch (calculated just before sending event)
+- \#TimestampPrimitive - the same as \#Timestamp, but returns long as result
+- \#Increment - substitutes keyword with positive integer starting from 1 - for each consecutive event, value of increment property is incremented by 1
+
+Additional hints and restrictions:
+All keywords without 'Primitive' in name return string as result. To specify keyword with 2 arguments e.g. #RandomInteger(start,end) no whitespaces between arguments are allowed.
+Maximal value of arguments for RandomInteger is limited to the java integer range. Minimal is always 0. (Negative values are prohibited and wont be treated as a correct parts of keyword).
+RandomInteger with parameters will automatically find minimal and maximal value form the given attributes so no particular order of those is expected.
+
+How does it work?
+When user do not want to fill in parameter values that are not relevant from user perspective but are mandatory by end system, then keyword feature should be used.
+In template, keyword strings are substituted in runtime with appropriate values autogenerated by simulator.
+Example can be shown below:
+
+Example template with keywords:
+
+ {
+ "event": {
+ "commonEventHeader": {
+ "eventId": "123#RandomInteger(8,8)",
+ "eventType": "pnfRegistration",
+ "startEpochMicrosec": "#Timestamp",
+ "vesEventListenerVersion": "7.0.1",
+ "lastEpochMicrosec": 1539239592379
+ },
+ "pnfRegistrationFields": {
+ "pnfRegistrationFieldsVersion":"2.0",
+ "serialNumber": "#RandomString(7)",
+ "vendorName": "Nokia",
+ "oamV4IpAddress": "val3",
+ "oamV6IpAddress": "val4"
+ }
+ }
+ }
+
+Corresponding result of keyword substitution (event that will be sent):
+
+ {
+ "event": {
+ "commonEventHeader": {
+ "eventId": "1238",
+ "eventType": "pnfRegistration",
+ "startEpochMicrosec": "154046405117",
+ "vesEventListenerVersion": "7.0.1",
+ "lastEpochMicrosec": 1539239592379
+ },
+ "pnfRegistrationFields": {
+ "pnfRegistrationFieldsVersion":"2.0",
+ "serialNumber": "6061ZW3",
+ "vendorName": "Nokia",
+ "oamV4IpAddress": "val3",
+ "oamV6IpAddress": "val4"
+ }
+ }
+ }
+
+
+### Logging
+Every start of simulator will generate new logs that can be found in docker pnf-simualtor container under path:
+/var/log/ONAP/pnfsimulator/pnfsimulator_output.log
+
+### Swagger
+Detailed view of simulator REST API is available via Swagger UI
+Swagger UI is available on *http://<simulator_ip>:5000/swagger-ui.html*
+
+### History
+User is able to view events history.
+In order to browse history, go to *http://<simulator_ip>:8081/db/pnf_simulator/eventData*
+
+### TLS Support
+Simulator is able to communicate with VES using HTTPS protocol.
+CA certificates are incorporated into simulator docker image, thus no additional actions are required from user.
+
+Certificates can be found in docker container under path: */usr/local/share/ca-certificates/*
+
+Simulator works with VES that uses both self-signed certificate (already present in keystore) and VES integrated to AAF.
+
+
+## Developers Guide
+
+### Integration tests
+Integration tests are located in folder 'integration'. Tests are using docker-compose from root folder.
+This docker-compose has pnfsimulator image set on nexus3.onap.org:10003/onap/pnf-simulator:5.0.0-SNAPSHOT.
+To test your local changes before running integration tests please build project using:
+
+ 'mvn clean package docker:build'
+
+then go to 'integration' folder and run:
+
+ 'mvn test'
+
+### Client certificate authentication
+Simulator can cooperate with VES server in different security types in particular ```auth.method=certBasicAuth``` which means that it needs to authenticate using client private certificate.
+
+Warning: according to VES implementation which uses certificate with Common Name set to DCAELOCAL we decided not to use strict hostname verification, so at least this parameter is skipped during checking of the client certificate.
+
+#### How to generate client correct keystore for pnf-simulator
+ The Root CA cert is available in certs folder in VES repository. The password for rootCA.key is collector.
+
+ The procedure of generating client's certificate:
+ 1. Generate a private key for the SSL client: ```openssl genrsa -out client.key 2048```
+ 2. Use the client’s private key to generate a cert request: ```openssl req -new -key client.key -out client.csr```
+ 3. Issue the client certificate using the cert request and the CA cert/key: ```openssl x509 -req -in client.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out client.crt -days 500 -sha256```
+ 4. Convert the client certificate and private key to pkcs#12 format: openssl pkcs12 -export -inkey client.key -in client.cer -out client.p12
+ 5. Copy pkcs file into pnf simulators folder: ```/app/store/```
+
+#### How to generate correct truststore for pnf-simulator
+ Create truststore with rootCA.crt:
+ 1. ```keytool -import -file rootCA.crt -alias firstCA -keystore trustStore```
+ 2. Copy truststore to ```/app/store/```
+
+#### How to refresh configuration of app
+Depends your needs, you are able to change client certificate, replace trustStore to accept new server certificate change keystore and truststore passwords or completely disable client cert authentication.
+
+For this purpose:
+1. Go to the pnf simulator container into the /app folder.
+2. If you want to replace keystore or truststore put them into the /app/store folder.
+3. Edit /app/application.properties file as follow:
+- ssl.clientCertificateEnabled=true (to disable/enable client authentication)
+- ssl.clientCertificateDir=/app/store/client.p12 (to replace keystore file)
+- ssl.clientCertificatePassword=collector (to replace password for keystore)
+- ssl.trustStoreDir=/app/store/trustStore (to replace truststore file)
+- ssl.trustStorePassword=collector (to replace password for truststore)
+4. Refresh configuration sending simple POST request to correct actuator endpoint at: ```curl http://localhost:5001/refresh -H 'Content-type: application/json' -X POST --data '{}'```
diff --git a/pnfsimulator/certificates/AAF_RootCA.crt b/pnfsimulator/certificates/AAF_RootCA.crt
new file mode 100644
index 0000000..e9a50d7
--- /dev/null
+++ b/pnfsimulator/certificates/AAF_RootCA.crt
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFPjCCAyagAwIBAgIJAJ6u7cCnzrWdMA0GCSqGSIb3DQEBCwUAMCwxDjAMBgNV
+BAsMBU9TQUFGMQ0wCwYDVQQKDARPTkFQMQswCQYDVQQGEwJVUzAeFw0xODA0MDUx
+NDE1MjhaFw0zODAzMzExNDE1MjhaMCwxDjAMBgNVBAsMBU9TQUFGMQ0wCwYDVQQK
+DARPTkFQMQswCQYDVQQGEwJVUzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBAMA5pkgRs7NhGG4ew5JouhyYakgYUyFaG121+/h8qbSdt0hVQv56+EA41Yq7
+XGie7RYDQK9NmAFF3gruE+6X7wvJiChp+Cyd7sFMnb65uWhxEdxWTM2BJFrgfzUn
+H8ZCxgaCo3XH4PzlKRy2LQQJEJECwl/RZmRCXijMt5e9h8XoZY/fKkKcZZUsWNCM
+pTo266wjvA9MXLmdgReRj0+vrCjrNqy+htwJDztoiHWiYPqT6o8EvGcgjNqjlZx7
+NUNf8MfLDByqKF6+wRbHv1GKjn3/Vijd45Fv8riyRYROiFanvbV6jIfBkv8PZbXg
+2VDWsYsgp8NAvMxK+iV8cO+Ck3lBI2GOPZbCEqpPVTYbLUz6sczAlCXwQoPzDIZY
+wYa3eR/gYLY1gP2iEVHORag3bLPap9ZX5E8DZkzTNTjovvLk8KaCmfcaUMJsBtDd
+ApcUitz10cnRyZc1sX3gE1f3DpzQM6t9C5sOVyRhDcSrKqqwb9m0Ss04XAS9FsqM
+P3UWYQyqDXSxlUAYaX892u8mV1hxnt2gjb22RloXMM6TovM3sSrJS0wH+l1nznd6
+aFXftS/G4ZVIVZ/LfT1is4StoyPWZCwwwly1z8qJQ/zhip5NgZTxQw4mi7ww35DY
+PdAQOCoajfSvFjqslQ/cPRi/MRCu079heVb5fQnnzVtnpFQRAgMBAAGjYzBhMB0G
+A1UdDgQWBBRTVTPyS+vQUbHBeJrBKDF77+rtSTAfBgNVHSMEGDAWgBRTVTPyS+vQ
+UbHBeJrBKDF77+rtSTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAN
+BgkqhkiG9w0BAQsFAAOCAgEAPx/IaK94n02wPxpnYTy+LVLIxwdq/kawNd6IbiMz
+L87zmNMDmHcGbfoRCj8OkhuggX9Lx1/CkhpXimuYsZOFQi5blr/u+v4mIbsgbmi9
+7j+cUHDP0zLycvSvxKHty51LwmaX9a4wkJl5zBU4O1sd/H9tWcEmwJ39ltKoBKBx
+c94Zc3iMm5ytRWGj+0rKzLDAXEWpoZ5bE5PLJauA6UDCxDLfs3FwhbS7uDggxYvf
+jySF5FCNET94oJ+m8s7VeHvoa8iPGKvXrIqdd7XDHnqJJlVKr7m9S0fMbyEB8ci2
+RtOXDt93ifY1uhoEtEykn4dqBSp8ezvNMnwoXdYPDvTd9uCAFeWFLVreBAWxd25h
+PsBTkZA5hpa/rA+mKv6Af4VBViYr8cz4dZCsFChuioVebe9ighrfjB//qKepFjPF
+CyjzKN1u0JKm/2x/ORqxkTONG8p3uDwoIOyimUcTtTMv42bfYD88RKakqSFXE9G+
+Z0LlaKABqfjK49o/tsAp+c5LoNlYllKhnetO3QAdraHwdmC36BhoghzR1jpX751A
+cZn2VH3Q4XKyp01cJNCJIrua+A+bx6zh3RyW6zIIkbRCbET+UD+4mr8WIcSE3mtR
+ZVlnhUDO4z9//WKMVzwS9Rh8/kuszrGFI1KQozXCHLrce3YP6RYZfOed79LXaRwX
+dYY=
+-----END CERTIFICATE-----
diff --git a/pnfsimulator/certificates/dcaelocal.crt b/pnfsimulator/certificates/dcaelocal.crt
new file mode 100644
index 0000000..1be5a6f
--- /dev/null
+++ b/pnfsimulator/certificates/dcaelocal.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDSDCCAjCgAwIBAgIEWAkZ5jANBgkqhkiG9w0BAQUFADBmMQswCQYDVQQGEwJV
+UzELMAkGA1UECBMCTkoxEzARBgNVBAcTCk1pZGRsZXRvd24xDTALBgNVBAoTBERD
+QUUxEjAQBgNVBAsTCU9QRU4tRENBRTESMBAGA1UEAxMJRENBRUxPQ0FMMB4XDTE2
+MTAyMDE5MjQyMloXDTE3MDExODE5MjQyMlowZjELMAkGA1UEBhMCVVMxCzAJBgNV
+BAgTAk5KMRMwEQYDVQQHEwpNaWRkbGV0b3duMQ0wCwYDVQQKEwREQ0FFMRIwEAYD
+VQQLEwlPUEVOLURDQUUxEjAQBgNVBAMTCURDQUVMT0NBTDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALoR0omXk1Xou3TdDydwhCF9V0ZSgQtMlMr8qYxe
+wLg/UGZbTea5HHqBmjWTVEKFGea7V7pnEiGWCPftIy/4D8ZSDaEQQ0WYCf6jNxeI
+T6PSiM+db/TwPToqPNMQwoftpsAEkJEJFB9rgUXoDD9FY5kUQW+fYOLyUeOb/rDE
+cRnSZX2qp1M/jAm1puIeNQcqiPExOMHqnN2bIgAZoHaFucdyOQUxuhIAgxoqe8Zt
+s451hm94g42F92I8KLy4EyOhXmSMjaLmgOqdjOPyBubOde4R39+6KAyQNGAnm5I4
+xq2J80R7qU0+4nnfjs2ScfAB/HUsWoVaGr9Ii4e34CW8JG8CAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEARqQmWg1gtwbgPbamWy0vdNWLQZm78y1ETR0ro9cazD25sD5/
+anDJ2RA97pGQFgncAI+Fzg4YygWBIext0OE92wQeKaHkxSujAe09F0wPcwc00Vyq
+NtBPcP17n18FkpFW1hkurlWiHOpGDRW2MmwhLj4lSFJmxMBAbzlfKrTBgj116UT+
+d4AGyuovS4LkmBWYFN//yoddGyrO26ar1M9ORdbDkjzOK4DkioTx3bwbdKPW7V4a
++Ns1KDFZ7FpjcWPZc6uDV4lBIhNUZuQZLy5hWBUeNhh4i2lL6xMrPzRa5kV1M7og
+ipv/ReVCixJqsGhx+8I4OPUbPoUYzRF+JmYUzA==
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/pnfsimulator/config/config.json b/pnfsimulator/config/config.json
new file mode 100644
index 0000000..af45632
--- /dev/null
+++ b/pnfsimulator/config/config.json
@@ -0,0 +1,9 @@
+{
+ "simulatorParams": {
+ "vesServerUrl": "http://VES-HOST:VES-PORT/eventListener/v7",
+ "repeatInterval": 10,
+ "repeatCount": 2
+ },
+ "templateName": "notification.json",
+ "patch": {}
+}
diff --git a/pnfsimulator/db/pnf_simulator.js b/pnfsimulator/db/pnf_simulator.js
new file mode 100644
index 0000000..f5a03c3
--- /dev/null
+++ b/pnfsimulator/db/pnf_simulator.js
@@ -0,0 +1,28 @@
+/*-
+ * ============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=========================================================
+ */
+
+const res = [
+ db.createUser({ user: 'pnf_simulator_user', pwd: 'zXcVbN123!', roles: ['readWrite', 'dbAdmin'] }),
+ db.simulatorConfig.insert({"vesServerUrl": "http://xdcae-ves-collector.onap:8080/eventListener/v7"}),
+ db.createCollection("template"),
+ db.createView("flatTemplatesView", "template", [{"$project":{"keyValues":{"$objectToArray": "$$ROOT.flatContent"}}}])
+];
+
+printjson(res);
diff --git a/pnfsimulator/deployment/PnP_PNF_sim_heat_template.yml b/pnfsimulator/deployment/PnP_PNF_sim_heat_template.yml
new file mode 100644
index 0000000..6f9b5f0
--- /dev/null
+++ b/pnfsimulator/deployment/PnP_PNF_sim_heat_template.yml
@@ -0,0 +1,118 @@
+description: Heat template that deploys PnP PNF simulator
+heat_template_version: '2013-05-23'
+parameters:
+ flavor_name: {description: Type of instance (flavor) to be used, label: Flavor,
+ type: string}
+ image_name: {description: Image to be used for compute instance, label: Image name
+ or ID, type: string}
+ key_name: {description: Public/Private key pair name, label: Key pair name, type: string}
+ public_net_id: {description: Public network that enables remote connection to VNF,
+ label: Public network name or ID, type: string}
+ private_net_id: {type: string, description: Private network id, label: Private network name or ID}
+ private_subnet_id: {type: string, description: Private subnet id, label: Private subnetwork name or ID}
+ proxy: {type: string, description: Proxy, label: Proxy, default: ""}
+resources:
+ PNF_PnP_simualtor:
+ type: OS::Nova::Server
+ properties:
+ key_name: { get_param: key_name }
+ image: { get_param: image_name }
+ flavor: { get_param: flavor_name }
+ networks:
+ - port: { get_resource: PNF_PnP_simualtor_port0 }
+ user_data_format: RAW
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+
+ set_versions () {
+ DOCKER_COMPOSE_VERSION=1.22.0
+ }
+
+
+ enable_root_ssh () {
+ sed -i 's/PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
+ sed -i 's/PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
+ service sshd restart
+ echo -e "arthur\narthur" | passwd root
+ }
+
+ update_os () {
+ dnf -y install fedora-upgrade
+ }
+
+ docker_remove () {
+ dnf -y remove docker \
+ docker-client \
+ docker-client-latest \
+ docker-common \
+ docker-latest \
+ docker-latest-logrotate \
+ docker-logrotate \
+ docker-selinux \
+ docker-engine-selinux \
+ docker-engine
+ }
+
+ docker_install_and_configure () {
+ dnf -y install dnf-plugins-core
+ dnf config-manager \
+ --add-repo \
+ https://download.docker.com/linux/fedora/docker-ce.repo
+ dnf -y install docker-ce
+ systemctl start docker
+ mkdir -p /etc/systemd/system/docker.service.d/
+ cat > /etc/systemd/system/docker.service.d/override.conf<< EOF
+ [Service]
+ Environment="HTTP_PROXY=$proxy"
+ Environment="HTTPS_PROXY=$proxy"
+ EOF
+ systemctl daemon-reload
+ systemctl restart docker
+ }
+ docker_compose_install () {
+ curl -L https://github.com/docker/compose/releases/download/$DOCKER_COMPOSE_VERSION/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
+ chmod +x /usr/local/bin/docker-compose
+ }
+ pnf_sim_file_checkout () {
+ cd /root; git clone https://gerrit.onap.org/r/integration
+ printf "{\n "simulatorParams": {\n "vesServerUrl": "http://VES-HOST:VES-PORT/eventListener/v7",\n "testDuration": "10",\n "messageInterval": "1"\n },\n "commonEventHeaderParams": {\n "eventName": "pnfRegistration_Nokia_5gDu",\n "nfNamingCode": "gNB",\n "nfcNamingCode": "oam",\n "sourceName": "NOK6061ZW3",\n "sourceId": "val13",\n "reportingEntityName": "NOK6061ZW3"\n },\n "pnfRegistrationParams": {\n "serialNumber": "6061ZW3",\n "vendorName": "Nokia",\n "oamV4IpAddress": "val3",\n "oamV6IpAddress": "val4",\n "unitFamily": "BBU",\n "modelNumber": "val6",\n "softwareVersion": "val7",\n "unitType": "val8"\n }\n}" > integration/test/mocks/pnfsimulator/config/config.json
+ }
+
+ start_simulator () {
+ docker login -u docker -p docker nexus3.onap.org:10003
+ cd ~/integration/test/mocks/pnfsimulator
+ ./simulator.sh start
+ }
+
+ set_versions
+ enable_root_ssh
+ update_os
+ docker_remove
+ docker_install_and_configure
+ docker_compose_install
+ pnf_sim_file_checkout
+ start_simulator
+ params:
+ $proxy: { get_param: proxy }
+ PNF_PnP_simualtor_port0:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_param: private_net_id }
+ security_groups:
+ - default
+ fixed_ips:
+ - subnet_id: { get_param: private_subnet_id }
+ PNF_PnP_simualtor_public:
+ type: OS::Neutron::FloatingIP
+ properties:
+ floating_network_id: { get_param: public_net_id }
+ port_id: { get_resource: PNF_PnP_simualtor_port0 }
+outputs:
+ PNF_PnP_simualtor_private_ip:
+ description: IP address of PNF_PnP_simualtor in private network
+ value: { get_attr: [ PNF_PnP_simualtor, first_address ] }
+ PNF_PnP_simualtor_public_ip:
+ description: Floating IP address of PNF_PnP_simualtor in public network
+ value: { get_attr: [ PNF_PnP_simualtor_public, floating_ip_address ] }
diff --git a/pnfsimulator/docker-compose.yml b/pnfsimulator/docker-compose.yml
new file mode 100644
index 0000000..23797fe
--- /dev/null
+++ b/pnfsimulator/docker-compose.yml
@@ -0,0 +1,38 @@
+version: '3'
+
+services:
+
+ mongo:
+ image: mongo
+ restart: always
+ environment:
+ MONGO_INITDB_ROOT_USERNAME: root
+ MONGO_INITDB_ROOT_PASSWORD: zXcVbN123!
+ MONGO_INITDB_DATABASE: pnf_simulator
+ volumes:
+ - ./db:/docker-entrypoint-initdb.d
+ ports:
+ - "27017:27017"
+
+ mongo-express:
+ image: mongo-express
+ restart: always
+ ports:
+ - 8081:8081
+ environment:
+ ME_CONFIG_MONGODB_ADMINUSERNAME: root
+ ME_CONFIG_MONGODB_ADMINPASSWORD: zXcVbN123!
+
+ pnf-simulator:
+ image: nexus3.onap.org:10003/onap/pnfsimulator
+ ports:
+ - "5000:5000"
+ volumes:
+ - ./logs:/var/log
+ - ./templates:/app/templates
+ - ./store:/app/store
+ - ./src/main/resources/application.properties:/app/application.properties
+ restart: on-failure
+ depends_on:
+ - mongo
+ - mongo-express
diff --git a/pnfsimulator/integration/pom.xml b/pnfsimulator/integration/pom.xml
new file mode 100644
index 0000000..21c2f3c
--- /dev/null
+++ b/pnfsimulator/integration/pom.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============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=========================================================
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onap.simulator</groupId>
+ <artifactId>simulator-parent</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>pnf-simulator-integration</artifactId>
+ <repositories>
+ <repository>
+ <id>dl.bintray.com</id>
+ <url>https://dl.bintray.com/palantir/releases</url>
+ </repository>
+ </repositories>
+
+ <properties>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.9.1</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter</artifactId>
+ <version>2.0.2.RELEASE</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ <version>2.0.2.RELEASE</version>
+ </dependency>
+
+ <dependency>
+ <groupId>io.rest-assured</groupId>
+ <artifactId>rest-assured</artifactId>
+ <version>3.2.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mongodb</groupId>
+ <artifactId>mongo-java-driver</artifactId>
+ <version>3.9.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <version>5.0.4.RELEASE</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <version>2.0.1.RELEASE</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.8.2</version>
+ </dependency>
+ <dependency>
+ <groupId>com.palantir.docker.compose</groupId>
+ <artifactId>docker-compose-rule-junit4</artifactId>
+ <version>0.34.0</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <executions>
+ <execution>
+ <id>onap-license</id>
+ <phase>none</phase>
+ </execution>
+ <execution>
+ <id>onap-java-style</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>2.17</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.12.1</version>
+ <configuration>
+ <includes>
+ <!--Execute only suites from the test folder-->
+ <include>**/*Suite.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/Main.java b/pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/Main.java
new file mode 100644
index 0000000..7288c2a
--- /dev/null
+++ b/pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/Main.java
@@ -0,0 +1,35 @@
+/*
+ * ============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.pnfsimulator.integration;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+@SpringBootApplication
+@EnableAsync
+public class Main {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Main.class, args);
+ }
+}
+
+
diff --git a/pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/VesSimulatorController.java b/pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/VesSimulatorController.java
new file mode 100644
index 0000000..70e0c60
--- /dev/null
+++ b/pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/VesSimulatorController.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.pnfsimulator.integration;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import org.springframework.beans.factory.annotation.Autowired;
+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.RestController;
+
+@RequestMapping("ves-simulator")
+@RestController
+public class VesSimulatorController {
+
+ private final VesSimulatorService vesSimulatorService;
+ private final Gson gson;
+
+ @Autowired
+ public VesSimulatorController(VesSimulatorService vesSimulatorService, Gson gson) {
+ this.vesSimulatorService = vesSimulatorService;
+ this.gson = gson;
+ }
+
+ @PostMapping("eventListener/v5")
+ String sendEventToDmaapV5(@RequestBody String body) {
+ System.out.println("Received event" + body);
+ JsonObject jsonObject = gson.fromJson(body, JsonObject.class);
+ vesSimulatorService.sendEventToDmaapV5(jsonObject);
+ return "MessageAccepted";
+ }
+
+ @PostMapping("eventListener/v7")
+ String sendEventToDmaapV7(@RequestBody String body) {
+ System.out.println("Received event" + body);
+ JsonObject jsonObject = gson.fromJson(body, JsonObject.class);
+ vesSimulatorService.sendEventToDmaapV7(jsonObject);
+ return "MessageAccepted";
+ }
+}
diff --git a/pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/VesSimulatorService.java b/pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/VesSimulatorService.java
new file mode 100644
index 0000000..65e5d3e
--- /dev/null
+++ b/pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/VesSimulatorService.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.pnfsimulator.integration;
+
+import com.google.gson.JsonObject;
+import org.springframework.stereotype.Service;
+
+@Service
+public class VesSimulatorService {
+
+ void sendEventToDmaapV5(JsonObject jsonObject) {
+ //JUST FOR TESTING PURPOSE
+ }
+
+ void sendEventToDmaapV7(JsonObject jsonObject) {
+ //JUST FOR TESTING PURPOSE
+ }
+}
diff --git a/pnfsimulator/integration/src/main/resources/application.properties b/pnfsimulator/integration/src/main/resources/application.properties
new file mode 100644
index 0000000..205ed95
--- /dev/null
+++ b/pnfsimulator/integration/src/main/resources/application.properties
@@ -0,0 +1 @@
+server.port=8000 \ No newline at end of file
diff --git a/pnfsimulator/integration/src/main/resources/keystore b/pnfsimulator/integration/src/main/resources/keystore
new file mode 100644
index 0000000..26a16f7
--- /dev/null
+++ b/pnfsimulator/integration/src/main/resources/keystore
Binary files differ
diff --git a/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/BasicAvailabilityTest.java b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/BasicAvailabilityTest.java
new file mode 100644
index 0000000..9f11a00
--- /dev/null
+++ b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/BasicAvailabilityTest.java
@@ -0,0 +1,235 @@
+/*-
+ * ============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.pnfsimulator.integration;
+
+import static io.restassured.RestAssured.given;
+import static io.restassured.RestAssured.when;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+
+import com.google.gson.JsonObject;
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.UUID;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.internal.util.Timer;
+import org.mockito.internal.verification.VerificationOverTimeImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = {Main.class, TestConfiguration.class}, webEnvironment = WebEnvironment.DEFINED_PORT)
+public class BasicAvailabilityTest {
+
+ @Autowired
+ VesSimulatorController vesSimulatorController;
+
+ @Autowired
+ VesSimulatorService vesSimulatorService;
+
+ private final String ACTION_START = "start";
+
+ private String currenVesSimulatorIp;
+
+ @Before
+ public void setUp() throws Exception {
+ currenVesSimulatorIp = getCurrentIpAddress();
+ }
+
+ @After
+ public void tearDown() {
+ Mockito.reset(vesSimulatorService);
+ }
+
+ @Test
+ public void simulatorShouldFailWhenTriggeredNonexistentTemplate(){
+ //given
+ String startUrl = prepareRequestUrl(ACTION_START);
+ String body = "{\n"
+ + "\"templateName\": \"any_nonexistent_template.json\",\n"
+ + "\"patch\":{},\n"
+ + "\"simulatorParams\": {\n"
+ + "\"vesServerUrl\": \"https://" + currenVesSimulatorIp + ":9443/ves-simulator/eventListener/v5\",\n"
+ + "\"repeatInterval\": 1,\n"
+ + "\"repeatCount\": 1\n"
+ + "}\n"
+ + "}";
+
+ //when
+ given()
+ .contentType("application/json")
+ .body(body)
+ .when()
+ .post(startUrl)
+ .then()
+ .statusCode(400)
+ .body("message", equalTo("Cannot start simulator - template any_nonexistent_template.json not found."));
+ }
+
+ @Test
+ public void whenTriggeredSimulatorShouldSendSingleEventToVes() {
+ //given
+ String startUrl = prepareRequestUrl(ACTION_START);
+ String body = "{\n"
+ + "\"templateName\": \"notification.json\",\n"
+ + "\"patch\":{},\n"
+ + "\"simulatorParams\": {\n"
+ + "\"vesServerUrl\": \"https://" + currenVesSimulatorIp + ":9443/ves-simulator/eventListener/v5\",\n"
+ + "\"repeatInterval\": 1,\n"
+ + "\"repeatCount\": 1\n"
+ + "}\n"
+ + "}";
+ ArgumentCaptor<JsonObject> parameterCaptor = ArgumentCaptor.forClass(JsonObject.class);
+
+ //when
+ given()
+ .contentType("application/json")
+ .body(body)
+ .when()
+ .post(startUrl)
+ .then()
+ .statusCode(200)
+ .body("message", equalTo("Request started"));
+
+ Mockito.verify(vesSimulatorService,
+ Mockito.timeout(3000))
+ .sendEventToDmaapV5(parameterCaptor.capture());
+
+ assertThat(parameterCaptor.getValue()
+ .getAsJsonObject("event")
+ .getAsJsonObject("commonEventHeader")
+ .get("domain").getAsString()).isEqualTo("notification");
+ }
+
+ @Test
+ public void simulatorShouldCorrectlyRespondOnCancellAllEvent() {
+ //given
+ String ACTION_CANCEL_ALL = "cancel";
+ String cancelAllUrl = prepareRequestUrl(ACTION_CANCEL_ALL);
+
+ //when
+ when()
+ .post(cancelAllUrl)
+ .then()
+ .statusCode(200)
+ .body("message", equalTo("Event(s) was cancelled"));
+
+ }
+
+ @Test
+ public void simulatorBeAbleToUseNewlyAddedTemplate() throws IOException {
+ //given
+ String templateBody = "{\"fake\":\"template\"}\n";
+ String fileName = UUID.randomUUID() + ".json";
+ String requestBody = "{\n"
+ + "\"templateName\": \"" + fileName + "\",\n"
+ + "\"patch\":{},\n"
+ + "\"simulatorParams\": {\n"
+ + "\"vesServerUrl\": \"https://" + currenVesSimulatorIp + ":9443/ves-simulator/eventListener/v5\",\n"
+ + "\"repeatInterval\": 1,\n"
+ + "\"repeatCount\": 1\n"
+ + "}\n"
+ + "}";
+ ArgumentCaptor<JsonObject> parameterCaptor = ArgumentCaptor.forClass(JsonObject.class);
+
+ //when
+ Path newFile = Files.createFile(Paths.get("..", "templates", fileName));
+ Files.write(newFile, templateBody.getBytes());
+
+ given()
+ .contentType("application/json")
+ .body(requestBody)
+ .when()
+ .post(prepareRequestUrl(ACTION_START));
+
+ Files.delete(newFile);
+
+ //then
+ Mockito.verify(vesSimulatorService, Mockito.timeout(3000))
+ .sendEventToDmaapV5(parameterCaptor.capture());
+ assertThat(parameterCaptor.getValue()
+ .get("fake").getAsString()).isEqualTo("template");
+
+ }
+
+ @Test
+ public void whenTriggeredSimulatorShouldSendGivenAmountOfEventsToVes() {
+ //given
+ String startUrl = prepareRequestUrl(ACTION_START);
+ String body = "{\n"
+ + "\"templateName\": \"notification.json\",\n"
+ + "\"patch\":{},\n"
+ + "\"simulatorParams\": {\n"
+ + "\"vesServerUrl\": \"https://" + currenVesSimulatorIp + ":9443/ves-simulator/eventListener/v5\",\n"
+ + "\"repeatInterval\": 1,\n"
+ + "\"repeatCount\": 4\n"
+ + "}\n"
+ + "}";
+ ArgumentCaptor<JsonObject> parameterCaptor = ArgumentCaptor.forClass(JsonObject.class);
+
+ //when
+ given()
+ .contentType("application/json")
+ .body(body)
+ .when()
+ .post(startUrl)
+ .then()
+ .statusCode(200)
+ .body("message", equalTo("Request started"));
+
+ VerificationOverTimeImpl verificator = new VerificationOverTimeImpl(100, Mockito.times(4), false, new Timer(6000));
+ Mockito.verify(vesSimulatorService, verificator).sendEventToDmaapV5(parameterCaptor.capture());
+
+ for (JsonObject value : parameterCaptor.getAllValues()) {
+ assertThat(value
+ .getAsJsonObject("event")
+ .getAsJsonObject("commonEventHeader")
+ .get("domain").getAsString()).isEqualTo("notification");
+ }
+ }
+
+ private String prepareRequestUrl(String action) {
+ return "http://0.0.0.0:5000/simulator/" + action;
+ }
+
+ private String getCurrentIpAddress() throws SocketException {
+ return Collections.list(NetworkInterface.getNetworkInterfaces()).stream()
+ .flatMap(i -> Collections.list(i.getInetAddresses()).stream())
+ .filter(ip -> ip instanceof Inet4Address)
+ .map(e -> (Inet4Address) e)
+ .findFirst()
+ .orElseThrow(RuntimeException::new)
+ .getHostAddress();
+ }
+}
diff --git a/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/OptionalTemplatesTest.java b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/OptionalTemplatesTest.java
new file mode 100644
index 0000000..a5ffe4d
--- /dev/null
+++ b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/OptionalTemplatesTest.java
@@ -0,0 +1,204 @@
+/*-
+ * ============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.pnfsimulator.integration;
+
+import static io.restassured.RestAssured.given;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+
+import com.google.gson.JsonObject;
+import com.mongodb.MongoClient;
+import com.mongodb.MongoClientOptions;
+import com.mongodb.MongoCredential;
+import com.mongodb.ServerAddress;
+import com.mongodb.client.FindIterable;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoCursor;
+import com.mongodb.client.MongoDatabase;
+import java.time.Instant;
+import java.net.Inet4Address;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Collections;
+import org.assertj.core.api.Assertions;
+import org.bson.Document;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = {Main.class, TestConfiguration.class}, webEnvironment = WebEnvironment.DEFINED_PORT)
+public class OptionalTemplatesTest {
+
+ private static final String PNF_SIMULATOR_DB = "pnf_simulator";
+ private static final String COMMON_EVENT_HEADER = "commonEventHeader";
+ private static final String PNF_SIMULATOR_DB_PSWD = "zXcVbN123!";
+ private static final String PNF_SIMULATOR_DB_USER = "pnf_simulator_user";
+ private static final String PATCHED = "patched";
+ private static final String SINGLE_EVENT_URL = "http://0.0.0.0:5000/simulator/event";
+
+ @Autowired
+ VesSimulatorController vesSimulatorController;
+
+ @Autowired
+ private VesSimulatorService vesSimulatorService;
+
+ private String currentVesSimulatorIp;
+
+ @Before
+ public void setUp() throws Exception {
+ currentVesSimulatorIp = getCurrentIpAddress();
+ }
+
+ @After
+ public void tearDown() {
+ Mockito.reset(vesSimulatorService);
+ }
+
+ @Test
+ public void whenTriggeredSimulatorWithoutTemplateShouldSendSingleEventToVes() {
+ //given
+ long currentTimestamp = Instant.now().getEpochSecond();
+
+ String body = "{\n"
+ + "\"vesServerUrl\": \"https://" + currentVesSimulatorIp + ":9443/ves-simulator/eventListener/v5\",\n"
+ + "\"event\": { \n"
+ + "\"commonEventHeader\": {\n"
+ + "\"eventId1\": \"#RandomString(20)\",\n"
+ + "\"eventId2\": \"#RandomInteger(10,10)\",\n"
+ + "\"eventId3\": \"#Increment\",\n"
+ + "\"eventId4\": \"#RandomPrimitiveInteger(10,10)\",\n"
+ + "\"eventId5\": \"#TimestampPrimitive\",\n"
+ + "\"sourceName\": \"Single_sourceName\",\n"
+ + "\"version\": 3"
+ + "}\n"
+ + "}\n"
+ + "}";
+ ArgumentCaptor<JsonObject> parameterCaptor = ArgumentCaptor.forClass(JsonObject.class);
+
+ //when
+ given()
+ .contentType("application/json")
+ .body(body)
+ .when()
+ .post(SINGLE_EVENT_URL)
+ .then()
+ .statusCode(202)
+ .body("message", equalTo("One-time direct event sent successfully"));
+
+ //then
+ long afterExecution = Instant.now().getEpochSecond();
+ Mockito.verify(vesSimulatorService,
+ Mockito.timeout(3000))
+ .sendEventToDmaapV5(parameterCaptor.capture());
+
+ JsonObject value = parameterCaptor.getValue();
+ assertThat(value
+ .getAsJsonObject(COMMON_EVENT_HEADER)
+ .get("sourceName").getAsString()).isEqualTo("Single_sourceName");
+ assertThat(value
+ .getAsJsonObject(COMMON_EVENT_HEADER)
+ .get("eventId1").getAsString().length()).isEqualTo(20);
+ assertThat(value
+ .getAsJsonObject(COMMON_EVENT_HEADER)
+ .get("eventId2").getAsString()).isEqualTo("10");
+ assertThat(value
+ .getAsJsonObject(COMMON_EVENT_HEADER)
+ .get("eventId3").getAsString()).isEqualTo("1");
+ assertThat(value
+ .getAsJsonObject(COMMON_EVENT_HEADER)
+ .get("eventId4").getAsInt()).isEqualTo(10);
+ assertThat(value
+ .getAsJsonObject(COMMON_EVENT_HEADER)
+ .get("eventId5").getAsLong()).isBetween(currentTimestamp, afterExecution);
+ }
+
+ @Test
+ public void whenTriggeredSimulatorWithoutTemplateEventShouldBeVisibleInDB() throws UnknownHostException {
+ //given
+ String body = "{\n"
+ + "\"vesServerUrl\": \"https://" + currentVesSimulatorIp + ":9443/ves-simulator/eventListener/v5\",\n"
+ + "\"event\": { \n"
+ + "\"commonEventHeader\": {\n"
+ + "\"sourceName\": \"HistoricalEvent\",\n"
+ + "\"version\": 3"
+ + "}\n"
+ + "}\n"
+ + "}";
+ ArgumentCaptor<JsonObject> parameterCaptor = ArgumentCaptor.forClass(JsonObject.class);
+
+ //when
+ given()
+ .contentType("application/json")
+ .body(body)
+ .when()
+ .post(SINGLE_EVENT_URL)
+ .then()
+ .statusCode(202)
+ .body("message", equalTo("One-time direct event sent successfully"));
+
+ //then
+ Mockito.verify(vesSimulatorService,
+ Mockito.timeout(3000))
+ .sendEventToDmaapV5(parameterCaptor.capture());
+
+ Document sourceNameInMongoDB = findSourceNameInMongoDB();
+ Assertions.assertThat(sourceNameInMongoDB.get(PATCHED))
+ .isEqualTo("{\"commonEventHeader\":{\"sourceName\":\"HistoricalEvent\",\"version\":3}}");
+ }
+
+ private Document findSourceNameInMongoDB() throws UnknownHostException {
+ MongoCredential credential = MongoCredential
+ .createCredential(PNF_SIMULATOR_DB_USER, PNF_SIMULATOR_DB, PNF_SIMULATOR_DB_PSWD.toCharArray());
+ MongoClient mongoClient = new MongoClient(new ServerAddress(Inet4Address.getLocalHost(), 27017),
+ credential, MongoClientOptions.builder().build());
+ MongoDatabase pnfSimulatorDb = mongoClient.getDatabase(PNF_SIMULATOR_DB);
+ MongoCollection<Document> table = pnfSimulatorDb.getCollection("eventData");
+ Document searchQuery = new Document();
+ searchQuery.put(PATCHED, new Document("$regex", ".*" + "HistoricalEvent" + ".*"));
+ FindIterable<Document> findOfPatched = table.find(searchQuery);
+ Document dbObject = null;
+ MongoCursor<Document> cursor = findOfPatched.iterator();
+ if (cursor.hasNext()) {
+ dbObject = cursor.next();
+ }
+ return dbObject;
+ }
+
+ private String getCurrentIpAddress() throws SocketException {
+ return Collections.list(NetworkInterface.getNetworkInterfaces()).stream()
+ .flatMap(i -> Collections.list(i.getInetAddresses()).stream())
+ .filter(ip -> ip instanceof Inet4Address)
+ .map(e -> (Inet4Address) e)
+ .findFirst()
+ .orElseThrow(RuntimeException::new)
+ .getHostAddress();
+ }
+
+}
diff --git a/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/SearchInTemplatesTest.java b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/SearchInTemplatesTest.java
new file mode 100644
index 0000000..9d4ff3b
--- /dev/null
+++ b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/SearchInTemplatesTest.java
@@ -0,0 +1,269 @@
+/*-
+ * ============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.pnfsimulator.integration;
+
+import static io.restassured.RestAssured.given;
+import static java.nio.file.Files.readAllBytes;
+
+import io.restassured.http.Header;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import org.hamcrest.Matchers;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.springframework.util.ResourceUtils;
+
+public class SearchInTemplatesTest {
+
+ private static final String UPLOAD = "upload";
+ private static final String SEARCH = "search";
+ private static final String APPLICATION_JSON = "application/json";
+ private static final String CONTENT_TYPE = "Content-Type";
+
+ @BeforeClass
+ public static void setUp() throws IOException {
+ for (File file : readFileFromTemplatesFolder()) {
+ byte[] body = readAllBytes(file.toPath());
+
+ given()
+ .body(body)
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(UPLOAD) + "?override=true")
+ .then()
+ .statusCode(201);
+ }
+ }
+
+ @Test
+ public void shouldFindNothingWhenNonexistentValueIsProvided(){
+ given()
+ .body("{\"searchExpr\": { \"child3\": \"nonexistentValue\" }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.empty());
+ }
+
+ @Test
+ public void shouldFindNothingWhenNonexistentKeyIsProvided(){
+ given()
+ .body("{\"searchExpr\": { \"nonexistentKey\": \"Any value 1\" }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.empty());
+ }
+
+ @Test
+ public void shouldFindNothingWhenPartOfKeyIsProvided(){
+ given()
+ .body("{\"searchExpr\": { \"child\": \"Any value 1\" }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.empty());
+ }
+
+ @Test
+ public void shouldFindNothingWhenPartOfValueIsProvided(){
+ given()
+ .body("{\"searchExpr\": { \"child5\": \"Any\" }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.empty());
+ }
+
+ @Test
+ public void shouldBeAbleToSearchForString(){
+ given()
+ .body("{\"searchExpr\": { \"child1\": \"Any value 1\" }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.hasItems("template_with_array.json", "complicated_template.json", "simple_template.json"));
+
+ given()
+ .body("{\"searchExpr\": { \"child2\": \"any value 4\" }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.hasItems("template_with_array.json"));
+ }
+
+ @Test
+ public void shouldBeAbleToSearchForManyStrings(){
+ given()
+ .body("{\"searchExpr\": { \"child1\": \"Any value 1\", \"child2\": \"any value 2\"}}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.hasItems("simple_template.json", "complicated_template.json"));
+ }
+
+ @Test
+ public void shouldBeAbleToSearchForStarSign(){
+ given()
+ .body("{\"searchExpr\": { \"child2\": \"*\" }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.hasItems("complicated_template.json"));
+ }
+
+ @Test
+ public void shouldBeAbleToSearchForQuestionMark(){
+ given()
+ .body("{\"searchExpr\": { \"child1\": \"?\" }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.hasItems("complicated_template.json"));
+ }
+
+ @Test
+ public void shouldBeAbleToSearchForBrackets(){
+ given()
+ .body("{\"searchExpr\": { \"parent2\": \"[]\" }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.hasItems("template_with_array.json"));
+ }
+
+ @Test
+ public void shouldInformThatSearchForNullsIsProhibited(){
+ given()
+ .body("{\"searchExpr\": { \"child3\": null }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(400);
+ }
+
+ @Test
+ public void shouldBeAbleToSearchForURI(){
+ given()
+ .body("{\"searchExpr\": { \"child3\": \"https://url.com?param1=test&param2=*\" }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.hasItems("complicated_template.json"));
+ }
+
+ @Test
+ public void shouldBeAbleToSearchForFloats(){
+ given()
+ .body("{\"searchExpr\": { \"child2\": 4.44 }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.hasItems("template_with_array.json"));
+
+ given()
+ .body("{\"searchExpr\": { \"child5\": 4.4 }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.hasItems("complicated_template.json", "template_with_floats.json"));
+ }
+
+ @Test
+ public void shouldBeAbleToSearchForIntegers(){
+ given()
+ .body("{\"searchExpr\": { \"child2\": 1 }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.hasItems("template_with_array.json", "template_with_ints.json"));
+
+ given()
+ .body("{\"searchExpr\": { \"child2\": 4 }}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.hasItems("template_with_array.json"));
+ }
+
+ @Test
+ public void shouldBeAbleToSearchForBooleans(){
+ given()
+ .body("{\"searchExpr\": { \"child4\": true}}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.hasItems("template_with_booleans.json"));
+
+ given()
+ .body("{\"searchExpr\": { \"parent2\": false}}")
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(SEARCH))
+ .then()
+ .statusCode(200)
+ .body("", Matchers.hasItems("template_with_booleans.json"));
+ }
+
+
+ private static String prepareRequestUrl(String action) {
+ return "http://0.0.0.0:5000/template/" + action;
+ }
+
+ private static File[] readFileFromTemplatesFolder() throws FileNotFoundException {
+ return ResourceUtils.getFile("classpath:templates/search").listFiles();
+ }
+
+}
diff --git a/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/TemplatesManagementTest.java b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/TemplatesManagementTest.java
new file mode 100644
index 0000000..7e74dd4
--- /dev/null
+++ b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/TemplatesManagementTest.java
@@ -0,0 +1,175 @@
+/*-
+ * ============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.pnfsimulator.integration;
+
+import static io.restassured.RestAssured.given;
+
+import io.restassured.http.Header;
+import io.restassured.path.json.JsonPath;
+import io.restassured.path.json.config.JsonPathConfig;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+import org.hamcrest.Matchers;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.util.ResourceUtils;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = {Main.class, TestConfiguration.class}, webEnvironment = WebEnvironment.DEFINED_PORT)
+public class TemplatesManagementTest {
+
+ private static final String LIST_URL = "list";
+ private static final String GET_URL = "get/";
+ private static final String UPLOAD = "upload";
+ private static final String NOTIFICATION_JSON = "notification.json";
+ private static final String REGISTRATION_JSON = "registration.json";
+ private static final String UPLOAD_TEMPLATE_JSON = "upload_template.json";
+ private static final String OVERWRITE_TEMPLATE_JSON = "overwrite_template.json";
+ private static final String OVERWRITTEN_TEMPLATE_JSON = "overwritten_template.json";
+ private static final String APPLICATION_JSON = "application/json";
+ private static final String CONTENT_TYPE = "Content-Type";
+ private static final String FORCE_FLAG = "?override=true";
+ private static final String CONTENT = "content";
+ private static final String TEMPLATE = "template";
+ private static final String ID = "id";
+
+ @Test
+ public void whenCallingGetShouldReceiveNotificationTemplate() throws IOException {
+ given()
+ .when()
+ .get(prepareRequestUrl(GET_URL) + NOTIFICATION_JSON)
+ .then()
+ .statusCode(200)
+ .body(ID, Matchers.equalTo(NOTIFICATION_JSON))
+ .body(CONTENT, Matchers.equalTo(readTemplateFromResources(NOTIFICATION_JSON).getMap(CONTENT)));
+ }
+
+ @Test
+ public void whenCallingGetShouldReceiveRegistrationTemplate() throws IOException {
+ given()
+ .when()
+ .get(prepareRequestUrl(GET_URL) + REGISTRATION_JSON)
+ .then()
+ .statusCode(200)
+ .body(ID, Matchers.equalTo(REGISTRATION_JSON))
+ .body(CONTENT, Matchers.equalTo(readTemplateFromResources(REGISTRATION_JSON).getMap(CONTENT)));
+ }
+
+ @Test
+ public void whenCallingListShouldReceiveAllPredefinedTemplates() throws IOException {
+ Map<Object, Object> registration = readTemplateFromResources(REGISTRATION_JSON).getMap(CONTENT);
+ Map<Object, Object> notification = readTemplateFromResources(NOTIFICATION_JSON).getMap(CONTENT);
+
+ given()
+ .when()
+ .get(prepareRequestUrl(LIST_URL))
+ .then()
+ .statusCode(200)
+ .body(CONTENT, Matchers.<Map>hasItems(
+ registration,
+ notification
+ ));
+ }
+
+ @Test
+ public void whenCallingUploadAndGetShouldReceiveNewTemplate() throws IOException {
+ byte[] body = Files.readAllBytes(readFileFromTemplatesFolder(UPLOAD_TEMPLATE_JSON));
+
+ given()
+ .body(body)
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(UPLOAD))
+ .then()
+ .statusCode(201);
+
+ given()
+ .when()
+ .get(prepareRequestUrl(GET_URL) + UPLOAD_TEMPLATE_JSON)
+ .then()
+ .statusCode(200)
+ .body(ID, Matchers.equalTo(UPLOAD_TEMPLATE_JSON))
+ .body(CONTENT, Matchers.equalTo(readTemplateFromResources(UPLOAD_TEMPLATE_JSON).getMap(TEMPLATE)));
+ }
+
+ @Test
+ public void whenCallingOverrideAndGetShouldReceiveNewTemplate() throws IOException, JSONException {
+ byte[] body = Files.readAllBytes(readFileFromTemplatesFolder(OVERWRITE_TEMPLATE_JSON));
+
+ given()
+ .body(body)
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(UPLOAD))
+ .then()
+ .statusCode(201);
+
+ JSONObject overwrittenBody = new JSONObject(new String(body));
+ JSONObject overwrittenTemplate = new JSONObject("{\"field1\": \"overwritten_field1\"}");
+ overwrittenBody.put(TEMPLATE, overwrittenTemplate);
+
+ given()
+ .body(overwrittenBody.toString().getBytes())
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(UPLOAD))
+ .then()
+ .statusCode(409);
+
+ given()
+ .body(overwrittenBody.toString().getBytes())
+ .header(new Header(CONTENT_TYPE, APPLICATION_JSON))
+ .when()
+ .post(prepareRequestUrl(UPLOAD + FORCE_FLAG))
+ .then()
+ .statusCode(201);
+
+ given()
+ .when()
+ .get(prepareRequestUrl(GET_URL) + OVERWRITE_TEMPLATE_JSON)
+ .then()
+ .statusCode(200)
+ .body(ID, Matchers.equalTo(OVERWRITE_TEMPLATE_JSON))
+ .body(CONTENT, Matchers.equalTo(readTemplateFromResources(OVERWRITTEN_TEMPLATE_JSON).getMap(CONTENT)));
+ }
+
+ private String prepareRequestUrl(String action) {
+ return "http://0.0.0.0:5000/template/" + action;
+ }
+
+ private JsonPath readTemplateFromResources(String templateName) throws IOException {
+ byte[] content = Files.readAllBytes(readFileFromTemplatesFolder(templateName));
+ return new JsonPath(new String(content)).using(new JsonPathConfig("UTF-8"));
+ }
+
+ private Path readFileFromTemplatesFolder(String templateName) throws FileNotFoundException {
+ return ResourceUtils.getFile("classpath:templates/"+templateName).toPath();
+ }
+
+}
diff --git a/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/TestConfiguration.java b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/TestConfiguration.java
new file mode 100644
index 0000000..19ae050
--- /dev/null
+++ b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/TestConfiguration.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.pnfsimulator.integration;
+
+import org.mockito.Mockito;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+@Configuration
+public class TestConfiguration {
+
+ @Bean
+ @Primary
+ VesSimulatorService provideVesSimulatorService() {
+ return Mockito.mock(VesSimulatorService.class);
+ }
+}
diff --git a/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/suites/DockerBasedTestsSuite.java b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/suites/DockerBasedTestsSuite.java
new file mode 100644
index 0000000..cc2ac58
--- /dev/null
+++ b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/suites/DockerBasedTestsSuite.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.pnfsimulator.integration.suites;
+
+import com.palantir.docker.compose.DockerComposeRule;
+import com.palantir.docker.compose.connection.waiting.HealthChecks;
+import org.junit.ClassRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+import org.onap.pnfsimulator.integration.BasicAvailabilityTest;
+import org.onap.pnfsimulator.integration.OptionalTemplatesTest;
+import org.onap.pnfsimulator.integration.SearchInTemplatesTest;
+import org.onap.pnfsimulator.integration.TemplatesManagementTest;
+
+@RunWith(Suite.class)
+@SuiteClasses({BasicAvailabilityTest.class, TemplatesManagementTest.class, OptionalTemplatesTest.class,
+ SearchInTemplatesTest.class})
+public class DockerBasedTestsSuite {
+
+ @ClassRule
+ public static DockerComposeRule docker = DockerComposeRule.builder()
+ .file("../docker-compose.yml")
+ .waitingForService("pnf-simulator", HealthChecks.toHaveAllPortsOpen())
+ .waitingForService("mongo", HealthChecks.toHaveAllPortsOpen())
+ .build();
+
+}
diff --git a/pnfsimulator/integration/src/test/resources/application.properties b/pnfsimulator/integration/src/test/resources/application.properties
new file mode 100644
index 0000000..c3e1472
--- /dev/null
+++ b/pnfsimulator/integration/src/test/resources/application.properties
@@ -0,0 +1,6 @@
+server.port=9443
+security.require-ssl=true
+server.ssl.key-store=src/main/resources/keystore
+server.ssl.key-store-password=collector
+server.ssl.keyStoreType=JKS
+server.ssl.keyAlias=tomcat \ No newline at end of file
diff --git a/pnfsimulator/integration/src/test/resources/templates/notification.json b/pnfsimulator/integration/src/test/resources/templates/notification.json
new file mode 100644
index 0000000..0b4ff2e
--- /dev/null
+++ b/pnfsimulator/integration/src/test/resources/templates/notification.json
@@ -0,0 +1,45 @@
+{
+ "id": "notification.json",
+ "content": {
+ "event": {
+ "commonEventHeader": {
+ "domain": "notification",
+ "eventName": "vFirewallBroadcastPackets",
+ "eventId": "4cfc-91cf-31a46",
+ "priority": "Normal",
+ "reportingEntityName": "myVNF",
+ "sequence": 1,
+ "sourceName": "ClosedLoopVNF",
+ "startEpochMicrosec": 1531616794,
+ "lastEpochMicrosec": 1531719042,
+ "vesEventListenerVersion": "7.0.1",
+ "version": "4.0.1"
+ },
+ "notificationFields": {
+ "changeIdentifier": "PM_MEAS_FILES",
+ "changeType": "FileReady",
+ "arrayOfNamedHashMap": [
+ {
+ "name": "A20161221.1031-1041.bin.gz",
+ "hashMap": {
+ "fileformatType": "org.3GPP.32.435#measCollec",
+ "fileFormatVersion": "V10",
+ "location": "ftpes://192.169.0.1:22/ftp/rop/A20161224.1030-1045.bin.gz",
+ "compression": "gzip"
+ }
+ },
+ {
+ "name": "A20161222.1042-1102.bin.gz",
+ "hashMap": {
+ "fileFormatType": "org.3GPP.32.435#measCollec",
+ "fileFormatVersion": "V10",
+ "location": "ftpes://192.168.0.102:22/ftp/rop/A20161224.1045-1100.bin.gz",
+ "compression": "gzip"
+ }
+ }
+ ],
+ "notificationFieldsVersion": "2.0"
+ }
+ }
+ }
+}
diff --git a/pnfsimulator/integration/src/test/resources/templates/overwrite_template.json b/pnfsimulator/integration/src/test/resources/templates/overwrite_template.json
new file mode 100644
index 0000000..55bf763
--- /dev/null
+++ b/pnfsimulator/integration/src/test/resources/templates/overwrite_template.json
@@ -0,0 +1,6 @@
+{
+ "name": "overwrite_template.json",
+ "template": {
+ "field1": "field1"
+ }
+}
diff --git a/pnfsimulator/integration/src/test/resources/templates/overwritten_template.json b/pnfsimulator/integration/src/test/resources/templates/overwritten_template.json
new file mode 100644
index 0000000..28e7381
--- /dev/null
+++ b/pnfsimulator/integration/src/test/resources/templates/overwritten_template.json
@@ -0,0 +1,6 @@
+{
+ "id": "overwrite_template.json",
+ "content": {
+ "field1": "overwritten_field1"
+ }
+}
diff --git a/pnfsimulator/integration/src/test/resources/templates/registration.json b/pnfsimulator/integration/src/test/resources/templates/registration.json
new file mode 100644
index 0000000..8b7908e
--- /dev/null
+++ b/pnfsimulator/integration/src/test/resources/templates/registration.json
@@ -0,0 +1,36 @@
+{
+ "id": "registration.json",
+ "content": {
+ "event": {
+ "commonEventHeader": {
+ "eventId": "registration_39239592",
+ "eventType": "pnfRegistration",
+ "reportingEntityName": "NOK6061ZW3",
+ "domain": "pnfRegistration",
+ "nfcNamingCode": "oam",
+ "sequence": 0,
+ "sourceId": "val13",
+ "internalHeaderFields": {},
+ "priority": "Normal",
+ "sourceName": "NOK6061ZW3",
+ "eventName": "pnfRegistration_Nokia_5gDu",
+ "version": "4.0.1",
+ "nfNamingCode": "gNB",
+ "startEpochMicrosec": 1539239592379,
+ "vesEventListenerVersion": "7.0.1",
+ "lastEpochMicrosec": 1539239592379
+ },
+ "pnfRegistrationFields": {
+ "pnfRegistrationFieldsVersion": "2.0",
+ "serialNumber": "6061ZW3",
+ "vendorName": "Nokia",
+ "oamV4IpAddress": "val3",
+ "oamV6IpAddress": "val4",
+ "unitFamily": "BBU",
+ "modelNumber": "val6",
+ "softwareVersion": "val7",
+ "unitType": "val8"
+ }
+ }
+ }
+}
diff --git a/pnfsimulator/integration/src/test/resources/templates/search/complicated_template.json b/pnfsimulator/integration/src/test/resources/templates/search/complicated_template.json
new file mode 100644
index 0000000..962600d
--- /dev/null
+++ b/pnfsimulator/integration/src/test/resources/templates/search/complicated_template.json
@@ -0,0 +1,43 @@
+{
+ "name": "complicated_template.json",
+ "template": {
+ "parent": {
+ "child1": "Any value 1",
+ "child2": {
+ "parent": {
+ "child1": "Any value 1",
+ "child2": "Any value 2",
+ "child3": {
+ "child4": "Any value 4"
+ },
+ "child4": [
+ "Any value 1",
+ "Any value 2, Any value 3"
+ ],
+ "child5": [
+ "Any value 4",
+ 1,
+ 2,
+ 4.4,
+ {
+ "child6": [
+ 1,
+ 2,
+ 4.4
+ ]
+ }
+ ]
+ }
+ },
+ "child3": {
+ "child4": "Any value 4"
+ }
+ },
+ "parent2": "Any value 2",
+ "parent3": {
+ "child1": "?",
+ "child2": "*",
+ "child3": "https://url.com?param1=test&param2=*"
+ }
+ }
+}
diff --git a/pnfsimulator/integration/src/test/resources/templates/search/simple_template.json b/pnfsimulator/integration/src/test/resources/templates/search/simple_template.json
new file mode 100644
index 0000000..d209a9e
--- /dev/null
+++ b/pnfsimulator/integration/src/test/resources/templates/search/simple_template.json
@@ -0,0 +1,12 @@
+{
+ "name": "simple_template.json",
+ "template": {
+ "parent": {
+ "child1": "Any value 1",
+ "child2": "Any value 2",
+ "child3": {
+ "child4": "Any value 4"
+ }
+ }
+ }
+}
diff --git a/pnfsimulator/integration/src/test/resources/templates/search/template_with_array.json b/pnfsimulator/integration/src/test/resources/templates/search/template_with_array.json
new file mode 100644
index 0000000..0ed4326
--- /dev/null
+++ b/pnfsimulator/integration/src/test/resources/templates/search/template_with_array.json
@@ -0,0 +1,23 @@
+{
+ "name": "template_with_array.json",
+ "template": {
+ "parent": {
+ "child1": [
+ {
+ "child1": "Any value 1",
+ "child2": [
+ 4,
+ 4.44
+ ]
+ }
+ ],
+ "child2": [
+ 1,
+ "Any value 4",
+ 3.3,
+ 5
+ ]
+ },
+ "parent2": "[]"
+ }
+}
diff --git a/pnfsimulator/integration/src/test/resources/templates/search/template_with_booleans.json b/pnfsimulator/integration/src/test/resources/templates/search/template_with_booleans.json
new file mode 100644
index 0000000..4954663
--- /dev/null
+++ b/pnfsimulator/integration/src/test/resources/templates/search/template_with_booleans.json
@@ -0,0 +1,12 @@
+{
+ "name": "template_with_booleans.json",
+ "template": {
+ "parent": {
+ "child1": true,
+ "child3": {
+ "child4": true
+ }
+ },
+ "parent2": false
+ }
+}
diff --git a/pnfsimulator/integration/src/test/resources/templates/search/template_with_floats.json b/pnfsimulator/integration/src/test/resources/templates/search/template_with_floats.json
new file mode 100644
index 0000000..2b8caf4
--- /dev/null
+++ b/pnfsimulator/integration/src/test/resources/templates/search/template_with_floats.json
@@ -0,0 +1,13 @@
+{
+ "name": "template_with_floats.json",
+ "template": {
+ "parent": {
+ "child1": 6.4,
+ "child2": 1.2,
+ "child3": {
+ "child5": 4.4,
+ "child2": "1"
+ }
+ }
+ }
+}
diff --git a/pnfsimulator/integration/src/test/resources/templates/search/template_with_ints.json b/pnfsimulator/integration/src/test/resources/templates/search/template_with_ints.json
new file mode 100644
index 0000000..369b0b4
--- /dev/null
+++ b/pnfsimulator/integration/src/test/resources/templates/search/template_with_ints.json
@@ -0,0 +1,12 @@
+{
+ "name": "template_with_ints.json",
+ "template": {
+ "parent": {
+ "child1": 6,
+ "child2": 1,
+ "child3": {
+ "child4": 4
+ }
+ }
+ }
+}
diff --git a/pnfsimulator/integration/src/test/resources/templates/upload_template.json b/pnfsimulator/integration/src/test/resources/templates/upload_template.json
new file mode 100644
index 0000000..df3ae28
--- /dev/null
+++ b/pnfsimulator/integration/src/test/resources/templates/upload_template.json
@@ -0,0 +1,6 @@
+{
+ "name": "upload_template.json",
+ "template": {
+ "field1": "field1"
+ }
+}
diff --git a/pnfsimulator/pom.xml b/pnfsimulator/pom.xml
new file mode 100644
index 0000000..9a8e796
--- /dev/null
+++ b/pnfsimulator/pom.xml
@@ -0,0 +1,403 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============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=========================================================
+ -->
+
+
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onap.simulator</groupId>
+ <artifactId>simulator-parent</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>pnfsimulator</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
+
+ <name>pnfsimulator</name>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ <maven.build.timestamp.format>yyyyMMdd'T'HHmmss</maven.build.timestamp.format>
+
+ <simulator.main.class>org.onap.pnfsimulator.Main</simulator.main.class>
+ <docker.image.tag>latest</docker.image.tag>
+ <junit.jupiter.version>5.1.0</junit.jupiter.version>
+ <junit.vintage.version>5.1.0</junit.vintage.version>
+ <spring.boot.version>2.1.0.RELEASE</spring.boot.version>
+ <docker.image.name>onap/${project.artifactId}</docker.image.name>
+
+ <dependency.directory.name>libs</dependency.directory.name>
+ <dependency.directory.location>${project.build.directory}/${dependency.directory.name}
+ </dependency.directory.location>
+
+ <onap.nexus.dockerregistry.daily>nexus3.onap.org:10003</onap.nexus.dockerregistry.daily>
+ <onap.nexus.url>http://nexus3.onap.org</onap.nexus.url>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter</artifactId>
+ <version>${spring.boot.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-logging</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ <version>${spring.boot.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-data-mongodb</artifactId>
+ <version>${spring.boot.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-actuator</artifactId>
+ <version>${spring.boot.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-config-client</artifactId>
+ <version>2.1.4.RELEASE</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>1.2.3</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <version>1.2.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.25</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.6</version>
+ </dependency>
+ <dependency>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ <version>20180130</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.8.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.5.5</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>21.0</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-cli</groupId>
+ <artifactId>commons-cli</artifactId>
+ <version>1.4</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.7</version>
+ </dependency>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ <version>1.18.2</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.datatype</groupId>
+ <artifactId>jackson-datatype-jdk8</artifactId>
+ <version>2.9.7</version>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.datatype</groupId>
+ <artifactId>jackson-datatype-jsr310</artifactId>
+ <version>2.9.7</version>
+ </dependency>
+ <dependency>
+ <groupId>io.vavr</groupId>
+ <artifactId>vavr-match</artifactId>
+ <version>0.9.2</version>
+ </dependency>
+ <dependency>
+ <groupId>io.vavr</groupId>
+ <artifactId>vavr</artifactId>
+ <version>0.9.2</version>
+ </dependency>
+
+
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <version>${junit.jupiter.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-migrationsupport</artifactId>
+ <version>${junit.jupiter.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.9.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>2.18.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <version>5.0.4.RELEASE</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <version>${spring.boot.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger2</artifactId>
+ <version>2.9.2</version>
+ </dependency>
+ <dependency>
+ <groupId>io.springfox</groupId>
+ <artifactId>springfox-swagger-ui</artifactId>
+ <version>2.9.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.quartz-scheduler</groupId>
+ <artifactId>quartz</artifactId>
+ <version>2.2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.quartz-scheduler</groupId>
+ <artifactId>quartz-jobs</artifactId>
+ <version>2.2.1</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <descriptor>src/assembly/resources.xml</descriptor>
+ <finalName>${project.artifactId}-${project.version}</finalName>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.7.0</version>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ <showWarnings>true</showWarnings>
+ <showDeprecation>true</showDeprecation>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.0.2</version>
+ <configuration>
+ <archive>
+ <manifestEntries>
+ <Main-Class>${simulator.main.class}</Main-Class>
+ <Build-Time>${maven.build.timestamp}</Build-Time>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>pl.project13.maven</groupId>
+ <artifactId>git-commit-id-plugin</artifactId>
+ <version>2.2.4</version>
+ <executions>
+ <execution>
+ <id>get-commit-info</id>
+ <goals>
+ <goal>revision</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
+ <generateGitPropertiesFile>true</generateGitPropertiesFile>
+ <includeOnlyProperties>git.commit.id.abbrev</includeOnlyProperties>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.19</version>
+ <dependencies>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-surefire-provider</artifactId>
+ <version>1.1.1</version>
+ </dependency>
+ </dependencies>
+ <configuration>
+ <detail>true</detail>
+ <printSummary>true</printSummary>
+ <useSystemClassLoader>false</useSystemClassLoader>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <configuration>
+ <outputDirectory>${dependency.directory.location}</outputDirectory>
+ <includeScope>runtime</includeScope>
+ <silent>true</silent>
+ </configuration>
+ <executions>
+ <execution>
+ <id>copy-external-dependencies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy-dependencies</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <version>0.8.1</version>
+ <configuration>
+ <excludes>
+ <exclude>org/onap/pnfsimulator/Main.class</exclude>
+ </excludes>
+ </configuration>
+ <executions>
+ <execution>
+ <id>default-prepare-agent</id>
+ <goals>
+ <goal>prepare-agent</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>report</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>report</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <profiles>
+ <profile>
+ <id>docker</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <properties>
+ <os.detected.name>linux</os.detected.name>
+ <os.detected.arch>x86_64</os.detected.arch>
+ <os.detected.classifier>${os.detected.name}-${os.detected.arch}</os.detected.classifier>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ <version>${docker-maven-plugin.version}</version>
+ <executions>
+ <execution>
+ <id>docker-build-image</id>
+ <phase>package</phase>
+ <goals>
+ <goal>build</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>docker-push-image</id>
+ <phase>deploy</phase>
+ <goals>
+ <goal>push</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <skipPush>${skipDockerPush}</skipPush>
+ <verbose>true</verbose>
+ <imagePullPolicy>IfNotPresent</imagePullPolicy>
+ <images>
+ <image>
+ <alias>${project.artifactId}</alias>
+ <name>${onap.nexus.dockerregistry.daily}/${docker.image.name}</name>
+ <registry>${onap.nexus.dockerregistry.daily}</registry>
+ <build>
+ <dockerFileDir>${project.basedir}</dockerFileDir>
+ <tags>
+ <tag>${project.version}-${maven.build.timestamp}Z</tag>
+ </tags>
+ </build>
+ </image>
+ </images>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/pnfsimulator/src/assembly/resources.xml b/pnfsimulator/src/assembly/resources.xml
new file mode 100644
index 0000000..35dd3b2
--- /dev/null
+++ b/pnfsimulator/src/assembly/resources.xml
@@ -0,0 +1,57 @@
+<!--
+ ============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=========================================================
+ -->
+
+<assembly>
+ <id>resources</id>
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <fileSet>
+ <includes>
+ <include>simulator.sh</include>
+ </includes>
+ <lineEnding>unix</lineEnding>
+ <fileMode>0755</fileMode>
+ </fileSet>
+ <fileSet>
+ <includes>
+ <include>docker-compose.yml</include>
+ </includes>
+ <lineEnding>unix</lineEnding>
+ <fileMode>0644</fileMode>
+ </fileSet>
+ <fileSet>
+ <directory>config</directory>
+ <outputDirectory>config</outputDirectory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>deployment</directory>
+ <outputDirectory>deployment</outputDirectory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/Main.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/Main.java
new file mode 100644
index 0000000..e0eace2
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/Main.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.pnfsimulator;
+
+import javax.annotation.PostConstruct;
+
+import org.onap.pnfsimulator.filesystem.WatcherService;
+import org.onap.pnfsimulator.template.FsToDbTemplateSynchronizer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+@SpringBootApplication
+@EnableAsync
+public class Main {
+
+ private final WatcherService watcherService;
+ private final FsToDbTemplateSynchronizer fsToDbTemplateSynchronizer;
+
+ @Autowired
+ public Main(WatcherService watcherService,
+ FsToDbTemplateSynchronizer fsToDbTemplateSynchronizer) {
+ this.watcherService = watcherService;
+ this.fsToDbTemplateSynchronizer = fsToDbTemplateSynchronizer;
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(Main.class, args);
+ }
+
+ @PostConstruct
+ public void createWatchers() {
+ fsToDbTemplateSynchronizer.synchronize();
+ watcherService.createWatcher();
+ }
+}
+
+
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/SwaggerConfig.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/SwaggerConfig.java
new file mode 100644
index 0000000..6e0e18e
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/SwaggerConfig.java
@@ -0,0 +1,46 @@
+/*
+ * ============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.pnfsimulator;
+
+import org.onap.pnfsimulator.simulator.client.utils.ssl.SSLAuthenticationHelper;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+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;
+
+@Configuration
+@EnableConfigurationProperties(SSLAuthenticationHelper.class)
+@EnableSwagger2
+public class SwaggerConfig {
+
+ @Bean
+ public Docket api() {
+ return new Docket(DocumentationType.SWAGGER_2)
+ .select()
+ .apis(RequestHandlerSelectors.basePackage("org.onap.pnfsimulator"))
+ .paths(PathSelectors.any())
+ .build();
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/db/Row.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/db/Row.java
new file mode 100644
index 0000000..f9a167b
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/db/Row.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.pnfsimulator.db;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Field;
+
+public abstract class Row {
+ @Id
+ @Field("_id")
+ protected String id;
+
+ public String getId() {
+ return id;
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/db/Storage.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/db/Storage.java
new file mode 100644
index 0000000..ad98ce0
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/db/Storage.java
@@ -0,0 +1,41 @@
+/*-
+ * ============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.pnfsimulator.db;
+
+import com.google.gson.JsonObject;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface Storage<T extends Row> {
+
+ List<T> getAll();
+
+ Optional<T> get(String rowId);
+
+ void persist(T row);
+
+ boolean tryPersistOrOverwrite(T row, boolean overwrite);
+
+ void delete(String rowId);
+
+ List<String> getIdsByContentCriteria(JsonObject queryJson);
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/event/EventData.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/event/EventData.java
new file mode 100644
index 0000000..23b1c21
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/event/EventData.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.pnfsimulator.event;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Field;
+
+@Builder
+@Getter
+@Setter
+public class EventData {
+ @Id
+ private String id;
+
+ @Field("template")
+ @JsonInclude
+ private String template;
+
+ @Field("patched")
+ @JsonInclude
+ private String patched;
+
+ @Field("input")
+ @JsonInclude
+ private String input;
+
+ @Field("keywords")
+ @JsonInclude
+ private String keywords;
+
+ @Field("incrementValue")
+ @JsonInclude
+ private int incrementValue;
+
+ protected EventData(String id, String template, String patched, String input, String keywords, int incrementValue) {
+ this.id = id;
+ this.template = template;
+ this.patched = patched;
+ this.input = input;
+ this.keywords = keywords;
+ this.incrementValue = incrementValue;
+ }
+
+ @Override
+ public String toString() {
+ return "EventData{"
+ + "id='" + id + '\''
+ + ", template='" + template + '\''
+ + ", patched='" + patched + '\''
+ + ", input='" + input + '\''
+ + ", keywords='" + keywords + '\''
+ + '}';
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/event/EventDataRepository.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/event/EventDataRepository.java
new file mode 100644
index 0000000..d1a66ab
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/event/EventDataRepository.java
@@ -0,0 +1,26 @@
+/*
+ * ============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.pnfsimulator.event;
+
+import org.springframework.data.mongodb.repository.MongoRepository;
+
+public interface EventDataRepository extends MongoRepository<EventData, String> {
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/event/EventDataService.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/event/EventDataService.java
new file mode 100644
index 0000000..3568f01
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/event/EventDataService.java
@@ -0,0 +1,63 @@
+/*
+ * ============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.pnfsimulator.event;
+
+import com.google.gson.JsonObject;
+import java.util.List;
+import java.util.Optional;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class EventDataService {
+ private final EventDataRepository repository;
+
+ @Autowired
+ public EventDataService(EventDataRepository repository) {
+ this.repository = repository;
+ }
+
+ private EventData persistEventData(String templateString, String patchedString, String inputString, String keywordsString) {
+ EventData eventData = EventData.builder()
+ .template(templateString)
+ .patched(patchedString)
+ .input(inputString)
+ .keywords(keywordsString)
+ .build();
+ return repository.save(eventData);
+ }
+
+ public EventData persistEventData(JsonObject templateJson, JsonObject patchedJson, JsonObject inputJson,
+ JsonObject keywordsJson) {
+ return persistEventData(templateJson.toString(),
+ patchedJson.toString(),
+ inputJson.toString(),
+ keywordsJson.toString());
+ }
+
+ public List<EventData> getAllEvents() {
+ return repository.findAll();
+ }
+
+ public Optional<EventData> getById(String id) {
+ return repository.findById(id);
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherConfig.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherConfig.java
new file mode 100644
index 0000000..3535e33
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherConfig.java
@@ -0,0 +1,39 @@
+/*-
+ * ============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.pnfsimulator.filesystem;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.task.TaskExecutor;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+@Configuration
+public class WatcherConfig {
+
+ @Bean
+ public TaskExecutor watcherTaskExecutor() {
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ executor.setThreadNamePrefix("pnfsimulator_fs_watcher");
+ executor.initialize();
+ return executor;
+ }
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherEventProcessor.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherEventProcessor.java
new file mode 100644
index 0000000..56a5696
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherEventProcessor.java
@@ -0,0 +1,110 @@
+/*-
+ * ============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.pnfsimulator.filesystem;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchEvent.Kind;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import lombok.extern.slf4j.Slf4j;
+import org.bson.json.JsonParseException;
+import org.onap.pnfsimulator.db.Storage;
+import org.onap.pnfsimulator.template.Template;
+import org.bson.Document;
+
+@Slf4j
+public enum WatcherEventProcessor {
+ CREATED(StandardWatchEventKinds.ENTRY_CREATE) {
+ @Override
+ public void processEvent(Path path, Storage<Template> storage) throws IOException {
+ String content = getContent(path);
+ String fileName = path.getFileName().toString();
+ Document documentsContent = Document.parse(content);
+ storage.persist(new Template(fileName, documentsContent, Instant.now().getNano()));
+ log.info("DB record created for template: " + fileName);
+ }
+ },
+ MODIFIED(StandardWatchEventKinds.ENTRY_MODIFY) {
+ @Override
+ public void processEvent(Path path, Storage<Template> storage) throws IOException {
+ String fileName = path.getFileName().toString();
+ String content = getContent(path);
+ Document documentsContent = Document.parse(content);
+ Template template = storage.get(fileName).orElse(new Template(fileName, documentsContent, Instant.now().getNano()));
+ template.setContent(documentsContent);
+ storage.persist(template);
+ log.info("DB record modified for template: " + fileName);
+ }
+ },
+ DELETED(StandardWatchEventKinds.ENTRY_DELETE) {
+ @Override
+ public void processEvent(Path path, Storage<Template> storage) {
+ String fileName = path.getFileName().toString();
+ storage.delete(fileName);
+ log.info("DB record deleted for template: " + fileName);
+ }
+ };
+
+ private final Kind<Path> pathKind;
+
+ String getContent(Path path) throws IOException {
+ try (Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8)) {
+ return lines.collect(Collectors.joining(System.lineSeparator()));
+ } catch (IOException e) {
+ log.error("Could not get content due to: " + e.getMessage() + " " + e.getCause(), e);
+ throw e;
+ }
+ }
+
+ WatcherEventProcessor(Kind<Path> pathKind) {
+ this.pathKind = pathKind;
+ }
+
+ public abstract void processEvent(Path templateName, Storage<Template> storage) throws IOException;
+
+ static void process(WatchEvent<?> event, Storage<Template> storage, Path templatesDir) {
+ Optional<WatcherEventProcessor> watcherEventProcessor = getWatcherEventProcessor(event);
+ watcherEventProcessor.ifPresent(processor -> {
+ try {
+ final Path templatePath = templatesDir.resolve((Path) event.context());
+ processor.processEvent(templatePath, storage);
+ } catch (IOException e) {
+ log.error("Error during processing DB record for template.", e);
+ } catch (JsonParseException e) {
+ log.error("Invalid JSON format provided for template.", e);
+ }
+ });
+ }
+
+ private static Optional<WatcherEventProcessor> getWatcherEventProcessor(WatchEvent<?> event) {
+ return Arrays.stream(values()).filter(value -> value.pathKind.equals(event.kind())).findFirst();
+ }
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherService.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherService.java
new file mode 100644
index 0000000..26b684d
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherService.java
@@ -0,0 +1,44 @@
+/*-
+ * ============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.pnfsimulator.filesystem;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.task.TaskExecutor;
+import org.springframework.stereotype.Service;
+
+@Service
+public class WatcherService {
+
+ private TaskExecutor taskExecutor;
+ private ApplicationContext applicationContext;
+
+ @Autowired
+ public WatcherService(ApplicationContext applicationContext, TaskExecutor taskExecutor) {
+ this.taskExecutor = taskExecutor;
+ this.applicationContext = applicationContext;
+ }
+
+ public void createWatcher() {
+ taskExecutor.execute(applicationContext.getBean(WatcherThread.class));
+ }
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherThread.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherThread.java
new file mode 100644
index 0000000..a202b1f
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/filesystem/WatcherThread.java
@@ -0,0 +1,81 @@
+/*-
+ * ============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.pnfsimulator.filesystem;
+
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.pnfsimulator.db.Storage;
+import org.onap.pnfsimulator.template.Template;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class WatcherThread implements Runnable {
+
+ private final WatchService watchService;
+ private final Storage<Template> storage;
+ private final Path templatesDir;
+
+ WatcherThread(String templatesDir, WatchService watchService, Storage<Template> storage) throws IOException {
+ this.watchService = watchService;
+ this.storage = storage;
+ this.templatesDir = Paths.get(templatesDir);
+ registerDirectory(this.templatesDir);
+ }
+
+ @Autowired
+ public WatcherThread(@Value("${templates.dir}") String templatesDir, Storage<Template> storage) throws IOException {
+ this(templatesDir, FileSystems.getDefault().newWatchService(), storage);
+ }
+
+ private void registerDirectory(Path path) throws IOException {
+ path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,
+ StandardWatchEventKinds.ENTRY_MODIFY);
+ }
+
+ @Override
+ public void run() {
+ while (true) {
+ WatchKey key;
+ try {
+ key = watchService.take();
+ for (WatchEvent<?> event : key.pollEvents()) {
+ WatcherEventProcessor.process(event, storage, templatesDir);
+ }
+ key.reset();
+ } catch (InterruptedException e) {
+ log.error("Watch service interrupted.", e.getMessage());
+ Thread.currentThread().interrupt();
+ return;
+ }
+
+ }
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/logging/MDCVariables.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/logging/MDCVariables.java
new file mode 100644
index 0000000..5678f4f
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/logging/MDCVariables.java
@@ -0,0 +1,35 @@
+/*
+ * ============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.pnfsimulator.logging;
+
+public final class MDCVariables {
+
+ public static final String X_ONAP_REQUEST_ID = "X-ONAP-RequestID";
+ public static final String X_INVOCATION_ID = "X-InvocationID";
+ public static final String REQUEST_ID = "RequestID";
+ public static final String INVOCATION_ID = "InvocationID";
+ public static final String INSTANCE_UUID = "InstanceUUID";
+ public static final String RESPONSE_CODE = "ResponseCode";
+ public static final String SERVICE_NAME = "ServiceName";
+
+ private MDCVariables() {
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/SimulatorController.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/SimulatorController.java
new file mode 100644
index 0000000..0e4bd56
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/SimulatorController.java
@@ -0,0 +1,219 @@
+/*
+ * ============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.pnfsimulator.rest;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.JsonSyntaxException;
+import org.json.JSONException;
+import org.onap.pnfsimulator.event.EventData;
+import org.onap.pnfsimulator.event.EventDataService;
+import org.onap.pnfsimulator.rest.model.FullEvent;
+import org.onap.pnfsimulator.rest.model.SimulatorRequest;
+import org.onap.pnfsimulator.rest.util.DateUtil;
+import org.onap.pnfsimulator.rest.util.ResponseBuilder;
+import org.onap.pnfsimulator.simulator.SimulatorService;
+import org.onap.pnfsimulator.simulatorconfig.SimulatorConfig;
+import org.quartz.SchedulerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+import org.slf4j.Marker;
+import org.slf4j.MarkerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+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.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.security.GeneralSecurityException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.onap.pnfsimulator.logging.MDCVariables.INSTANCE_UUID;
+import static org.onap.pnfsimulator.logging.MDCVariables.INVOCATION_ID;
+import static org.onap.pnfsimulator.logging.MDCVariables.REQUEST_ID;
+import static org.onap.pnfsimulator.logging.MDCVariables.RESPONSE_CODE;
+import static org.onap.pnfsimulator.logging.MDCVariables.SERVICE_NAME;
+import static org.onap.pnfsimulator.logging.MDCVariables.X_INVOCATION_ID;
+import static org.onap.pnfsimulator.logging.MDCVariables.X_ONAP_REQUEST_ID;
+import static org.onap.pnfsimulator.rest.util.ResponseBuilder.MESSAGE;
+import static org.onap.pnfsimulator.rest.util.ResponseBuilder.TIMESTAMP;
+import static org.springframework.http.HttpStatus.ACCEPTED;
+import static org.springframework.http.HttpStatus.BAD_REQUEST;
+import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
+import static org.springframework.http.HttpStatus.NOT_FOUND;
+import static org.springframework.http.HttpStatus.OK;
+
+@RestController
+@RequestMapping("/simulator")
+public class SimulatorController {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SimulatorController.class);
+ private static final Marker ENTRY = MarkerFactory.getMarker("ENTRY");
+ private static final String INCORRECT_TEMPLATE_MESSAGE = "Cannot start simulator, template %s is not in valid format: %s";
+ private static final String NOT_EXISTING_TEMPLATE = "Cannot start simulator - template %s not found.";
+ private final DateFormat responseDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss,SSS");
+ private final SimulatorService simulatorService;
+ private EventDataService eventDataService;
+
+ @Autowired
+ public SimulatorController(SimulatorService simulatorService,
+ EventDataService eventDataService) {
+ this.simulatorService = simulatorService;
+ this.eventDataService = eventDataService;
+ }
+
+ @PostMapping("test")
+ @Deprecated
+ public ResponseEntity test(@Valid @RequestBody SimulatorRequest simulatorRequest) {
+ MDC.put("test", "test");
+ LOGGER.info(ENTRY, simulatorRequest.toString());
+ return buildResponse(OK, ImmutableMap.of(MESSAGE, "message1234"));
+ }
+
+ @PostMapping(value = "start")
+ public ResponseEntity start(@RequestHeader HttpHeaders headers,
+ @Valid @RequestBody SimulatorRequest triggerEventRequest) {
+ logContextHeaders(headers, "/simulator/start");
+ LOGGER.info(ENTRY, "Simulator started");
+
+ try {
+ return processRequest(triggerEventRequest);
+
+ } catch (JSONException | JsonSyntaxException e) {
+ MDC.put(RESPONSE_CODE, BAD_REQUEST.toString());
+ LOGGER.warn("Cannot trigger event, invalid json format: {}", e.getMessage());
+ LOGGER.debug("Received json has invalid format", e);
+ return buildResponse(BAD_REQUEST, ImmutableMap.of(MESSAGE, String
+ .format(INCORRECT_TEMPLATE_MESSAGE, triggerEventRequest.getTemplateName(),
+ e.getMessage())));
+ } catch (GeneralSecurityException e ){
+ MDC.put(RESPONSE_CODE, INTERNAL_SERVER_ERROR.toString() );
+ LOGGER.error("Client certificate validation failed: {}", e.getMessage());
+ return buildResponse(INTERNAL_SERVER_ERROR,
+ ImmutableMap.of(MESSAGE, "Invalid or misconfigured client certificate"));
+ }
+ catch (IOException e) {
+ MDC.put(RESPONSE_CODE, BAD_REQUEST.toString());
+ LOGGER.warn("Json validation failed: {}", e.getMessage());
+ return buildResponse(BAD_REQUEST,
+ ImmutableMap.of(MESSAGE, String.format(NOT_EXISTING_TEMPLATE, triggerEventRequest.getTemplateName())));
+ } catch (Exception e) {
+ MDC.put(RESPONSE_CODE, INTERNAL_SERVER_ERROR.toString());
+ LOGGER.error("Cannot trigger event - unexpected exception", e);
+ return buildResponse(INTERNAL_SERVER_ERROR,
+ ImmutableMap.of(MESSAGE, "Unexpected exception: " + e.getMessage()));
+ } finally {
+ MDC.clear();
+ }
+ }
+
+ @GetMapping("all-events")
+ @Deprecated
+ public ResponseEntity allEvents() {
+ List<EventData> eventDataList = eventDataService.getAllEvents();
+ StringBuilder sb = new StringBuilder();
+ eventDataList.forEach(e -> sb.append(e).append(System.lineSeparator()));
+
+ return ResponseBuilder
+ .status(OK).put(MESSAGE, sb.toString())
+ .build();
+ }
+
+ @GetMapping("config")
+ public ResponseEntity getConfig() {
+ SimulatorConfig configToGet = simulatorService.getConfiguration();
+ return buildResponse(OK, ImmutableMap.of("simulatorConfig", configToGet));
+ }
+
+ @PutMapping("config")
+ public ResponseEntity updateConfig(@Valid @RequestBody SimulatorConfig newConfig) {
+ SimulatorConfig updatedConfig = simulatorService.updateConfiguration(newConfig);
+ return buildResponse(OK, ImmutableMap.of("simulatorConfig", updatedConfig));
+ }
+
+ @PostMapping("cancel/{jobName}")
+ public ResponseEntity cancelEvent(@PathVariable String jobName) throws SchedulerException {
+ LOGGER.info(ENTRY, "Cancel called on {}.", jobName);
+ boolean isCancelled = simulatorService.cancelEvent(jobName);
+ return createCancelEventResponse(isCancelled);
+ }
+
+ @PostMapping("cancel")
+ public ResponseEntity cancelAllEvent() throws SchedulerException {
+ LOGGER.info(ENTRY, "Cancel called on all jobs");
+ boolean isCancelled = simulatorService.cancelAllEvents();
+ return createCancelEventResponse(isCancelled);
+ }
+
+ @PostMapping("event")
+ public ResponseEntity sendEventDirectly(@RequestHeader HttpHeaders headers, @Valid @RequestBody FullEvent event)
+ throws IOException, GeneralSecurityException{
+ logContextHeaders(headers, "/simulator/event");
+ LOGGER.info(ENTRY, "Trying to send one-time event directly to VES Collector");
+ simulatorService.triggerOneTimeEvent(event);
+ return buildResponse(ACCEPTED, ImmutableMap.of(MESSAGE, "One-time direct event sent successfully"));
+ }
+
+ private ResponseEntity processRequest(SimulatorRequest triggerEventRequest)
+ throws IOException, SchedulerException, GeneralSecurityException {
+
+ String jobName = simulatorService.triggerEvent(triggerEventRequest);
+ MDC.put(RESPONSE_CODE, OK.toString());
+ return buildResponse(OK, ImmutableMap.of(MESSAGE, "Request started", "jobName", jobName));
+ }
+
+ private ResponseEntity buildResponse(HttpStatus endStatus, Map<String, Object> parameters) {
+ ResponseBuilder builder = ResponseBuilder
+ .status(endStatus)
+ .put(TIMESTAMP, DateUtil.getTimestamp(responseDateFormat));
+ parameters.forEach(builder::put);
+ return builder.build();
+ }
+
+ private void logContextHeaders(HttpHeaders headers, String serviceName) {
+ MDC.put(REQUEST_ID, headers.getFirst(X_ONAP_REQUEST_ID));
+ MDC.put(INVOCATION_ID, headers.getFirst(X_INVOCATION_ID));
+ MDC.put(INSTANCE_UUID, UUID.randomUUID().toString());
+ MDC.put(SERVICE_NAME, serviceName);
+ }
+
+ private ResponseEntity createCancelEventResponse(boolean isCancelled) {
+ if (isCancelled) {
+ return buildResponse(OK, ImmutableMap.of(MESSAGE, "Event(s) was cancelled"));
+ } else {
+ return buildResponse(NOT_FOUND, ImmutableMap.of(MESSAGE, "Simulator was not able to cancel event(s)"));
+ }
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/TemplateController.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/TemplateController.java
new file mode 100644
index 0000000..444e23b
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/TemplateController.java
@@ -0,0 +1,105 @@
+/*-
+ * ============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.pnfsimulator.rest;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+import javax.validation.Valid;
+
+import org.onap.pnfsimulator.db.Storage;
+import org.onap.pnfsimulator.rest.model.TemplateRequest;
+import org.onap.pnfsimulator.rest.model.SearchExp;
+import org.onap.pnfsimulator.template.Template;
+import org.onap.pnfsimulator.template.search.IllegalJsonValueException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.server.ResponseStatusException;
+
+
+@RestController
+@RequestMapping("/template")
+public class TemplateController {
+ static final String TEMPLATE_NOT_FOUND_MSG = "A template with given name does not exist";
+ static final String CANNOT_OVERRIDE_TEMPLATE_MSG = "Cannot overwrite existing template. Use override=true to override";
+ private final Storage<Template> service;
+ private static final Logger LOG = LoggerFactory.getLogger(TemplateController.class);
+
+ @Autowired
+ public TemplateController(Storage<Template> service) {
+ this.service = service;
+ }
+
+ @GetMapping("list")
+ public ResponseEntity<?> list() {
+ return new ResponseEntity<>(service.getAll(), HttpStatus.OK);
+ }
+
+ @GetMapping("get/{templateName}")
+ public ResponseEntity<?> get(@PathVariable String templateName) {
+ Optional<Template> template = service.get(templateName);
+ if (!template.isPresent()) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.TEXT_PLAIN);
+ return new ResponseEntity<>(TEMPLATE_NOT_FOUND_MSG, headers, HttpStatus.NOT_FOUND);
+ }
+ return new ResponseEntity<>(template, HttpStatus.OK);
+ }
+
+ @PostMapping("upload")
+ public ResponseEntity<?> upload(
+ @RequestBody @Valid TemplateRequest templateRequest,
+ @RequestParam(required = false) boolean override) {
+ String msg = "";
+ HttpStatus status = HttpStatus.CREATED;
+ Template template = new Template(templateRequest.getName(), templateRequest.getTemplate(), Instant.now().getNano());
+ if (!service.tryPersistOrOverwrite(template, override)) {
+ status = HttpStatus.CONFLICT;
+ msg = CANNOT_OVERRIDE_TEMPLATE_MSG;
+ }
+ return new ResponseEntity<>(msg, status);
+ }
+
+ @PostMapping("search")
+ public ResponseEntity<?> searchByCriteria(@RequestBody SearchExp queryJson) {
+ try {
+ List<String> templateNames = service.getIdsByContentCriteria(queryJson.getSearchExpr());
+ return new ResponseEntity<>(templateNames, HttpStatus.OK);
+ } catch (IllegalJsonValueException ex) {
+ throw new ResponseStatusException(HttpStatus.BAD_REQUEST, String.format("Try again with correct parameters. Cause: %s", ex.getMessage()), ex);
+ }
+
+ }
+
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/FullEvent.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/FullEvent.java
new file mode 100644
index 0000000..77d9b3d
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/FullEvent.java
@@ -0,0 +1,48 @@
+/*
+ * ============LICENSE_START=======================================================
+ * FULL-EVENT
+ * ================================================================================
+ * 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.pnfsimulator.rest.model;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import org.onap.pnfsimulator.rest.util.JsonObjectDeserializer;
+import org.springframework.lang.Nullable;
+
+import javax.validation.constraints.NotNull;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@ToString
+@EqualsAndHashCode
+public class FullEvent {
+
+ @Nullable
+ private String vesServerUrl;
+
+ @NotNull
+ @JsonDeserialize(using = JsonObjectDeserializer.class)
+ private JsonObject event;
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/SearchExp.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/SearchExp.java
new file mode 100644
index 0000000..41d112f
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/SearchExp.java
@@ -0,0 +1,42 @@
+/*-
+ * ============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.pnfsimulator.rest.model;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import org.onap.pnfsimulator.rest.util.JsonObjectDeserializer;
+
+import javax.validation.constraints.NotNull;
+
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString
+public class SearchExp {
+
+ @NotNull
+ @JsonDeserialize(using = JsonObjectDeserializer.class)
+ private JsonObject searchExpr;
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/SimulatorParams.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/SimulatorParams.java
new file mode 100644
index 0000000..787583e
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/SimulatorParams.java
@@ -0,0 +1,46 @@
+/*
+ * ============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.pnfsimulator.rest.model;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import javax.validation.constraints.NotNull;
+import org.springframework.lang.Nullable;
+
+@Getter
+@EqualsAndHashCode
+@AllArgsConstructor
+@NoArgsConstructor
+public class SimulatorParams {
+
+ @NotNull
+ private String vesServerUrl;
+
+ @Nullable
+ private Integer repeatInterval;
+
+ @Nullable
+ private Integer repeatCount;
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/SimulatorRequest.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/SimulatorRequest.java
new file mode 100644
index 0000000..2b06658
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/SimulatorRequest.java
@@ -0,0 +1,51 @@
+/*
+ * ============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.pnfsimulator.rest.model;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.gson.JsonObject;
+import javax.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import org.onap.pnfsimulator.rest.util.JsonObjectDeserializer;
+import org.springframework.lang.Nullable;
+
+@Getter
+@ToString
+@EqualsAndHashCode
+@AllArgsConstructor
+@NoArgsConstructor
+public class SimulatorRequest {
+
+ @NotNull
+ private SimulatorParams simulatorParams;
+
+ @NotNull
+ private String templateName;
+
+ @Nullable
+ @JsonDeserialize(using = JsonObjectDeserializer.class)
+ private JsonObject patch;
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/TemplateRequest.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/TemplateRequest.java
new file mode 100644
index 0000000..d5a77f0
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/model/TemplateRequest.java
@@ -0,0 +1,42 @@
+/*-
+ * ============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.pnfsimulator.rest.model;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.bson.Document;
+
+@Getter
+@Setter
+@ToString
+public class TemplateRequest {
+ private String name;
+ private Document template;
+
+ public TemplateRequest(String name, Document template) {
+ this.name = name;
+ this.template = template;
+ }
+
+ public TemplateRequest() {
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/util/DateUtil.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/util/DateUtil.java
new file mode 100644
index 0000000..9a5c9ca
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/util/DateUtil.java
@@ -0,0 +1,35 @@
+/*
+ * ============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.pnfsimulator.rest.util;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+public final class DateUtil {
+
+ private DateUtil() {
+ }
+
+ public static String getTimestamp(DateFormat dateFormat) {
+
+ return dateFormat.format(new Date());
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/util/JsonObjectDeserializer.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/util/JsonObjectDeserializer.java
new file mode 100644
index 0000000..f89c4a7
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/util/JsonObjectDeserializer.java
@@ -0,0 +1,42 @@
+/*
+ * ============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.pnfsimulator.rest.util;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+
+import java.io.IOException;
+
+public class JsonObjectDeserializer extends JsonDeserializer<JsonObject> {
+ private Gson gson = new Gson();
+
+ @Override
+ public JsonObject deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
+ ObjectCodec oc = jsonParser.getCodec();
+ JsonNode node = oc.readTree(jsonParser);
+ return gson.fromJson(node.toString(), JsonObject.class);
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/util/ResponseBuilder.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/util/ResponseBuilder.java
new file mode 100644
index 0000000..5fca25a
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/util/ResponseBuilder.java
@@ -0,0 +1,62 @@
+/*
+ * ============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.pnfsimulator.rest.util;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+public class ResponseBuilder {
+
+ public static final String TIMESTAMP = "timestamp";
+ public static final String MESSAGE = "message";
+ public static final String SIMULATOR_STATUS = "simulatorStatus";
+ public static final String REMAINING_TIME = "remainingTime";
+
+ private HttpStatus httpStatus;
+ private Map<String, Object> body = new LinkedHashMap<>();
+
+ private ResponseBuilder(HttpStatus httpStatus) {
+ this.httpStatus = httpStatus;
+ }
+
+ public static ResponseBuilder status(HttpStatus httpStatus) {
+
+ return new ResponseBuilder(httpStatus);
+ }
+
+ public ResponseBuilder put(String key, Object value) {
+
+ body.put(key, value);
+ return this;
+ }
+
+ public ResponseEntity build() {
+
+ if (body.isEmpty()) {
+ return ResponseEntity.status(httpStatus).build();
+ }
+
+ return ResponseEntity.status(httpStatus).body(body);
+ }
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/DBTemplateReader.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/DBTemplateReader.java
new file mode 100644
index 0000000..6c11254
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/DBTemplateReader.java
@@ -0,0 +1,49 @@
+/*-
+ * ============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.pnfsimulator.simulator;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.io.IOException;
+import org.onap.pnfsimulator.template.Template;
+import org.onap.pnfsimulator.template.TemplateService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DBTemplateReader implements TemplateReader {
+ private final TemplateService service;
+ private final Gson gson;
+
+ @Autowired
+ public DBTemplateReader(TemplateService service, Gson gson) {
+ this.service = service;
+ this.gson = gson;
+ }
+
+ @Override
+ public JsonObject readTemplate(String templateName) throws IOException {
+ Template template = service.get(templateName).orElseThrow(() -> new IOException("Template does not exist"));
+ JsonElement jsonElement = gson.toJsonTree(template.getContent());
+ return jsonElement.getAsJsonObject();
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/EventNotFoundException.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/EventNotFoundException.java
new file mode 100644
index 0000000..4f43d8c
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/EventNotFoundException.java
@@ -0,0 +1,28 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+public class EventNotFoundException extends RuntimeException {
+ private static final String NOT_FOUND = "Not found an event with id: ";
+ public EventNotFoundException(String eventId) {
+ super(NOT_FOUND + eventId);
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/FilesystemTemplateReader.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/FilesystemTemplateReader.java
new file mode 100644
index 0000000..a405a2e
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/FilesystemTemplateReader.java
@@ -0,0 +1,54 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+class FilesystemTemplateReader implements TemplateReader {
+
+ private final Path templatesDir;
+ private final Gson gson;
+
+ @Autowired
+ FilesystemTemplateReader(@Value("${templates.dir}") String templatesDir, Gson gson) {
+ this.templatesDir = Paths.get(templatesDir);
+ this.gson = gson;
+ }
+
+ public JsonObject readTemplate(String templateFileName) throws IOException {
+ Path absTemplateFilePath = templatesDir.resolve(templateFileName);
+ try (Stream<String> lines = Files.lines(absTemplateFilePath)) {
+ String content = lines.collect(Collectors.joining("\n"));
+ return gson.fromJson(content, JsonObject.class);
+ }
+ }
+}
+
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/IncrementProvider.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/IncrementProvider.java
new file mode 100644
index 0000000..ea87ae6
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/IncrementProvider.java
@@ -0,0 +1,26 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+@FunctionalInterface
+public interface IncrementProvider {
+ int getAndIncrement(String id);
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/IncrementProviderImpl.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/IncrementProviderImpl.java
new file mode 100644
index 0000000..16c0a0e
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/IncrementProviderImpl.java
@@ -0,0 +1,47 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import org.onap.pnfsimulator.event.EventData;
+import org.onap.pnfsimulator.event.EventDataRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class IncrementProviderImpl implements IncrementProvider {
+ private final EventDataRepository repository;
+
+ @Autowired
+ public IncrementProviderImpl(EventDataRepository repository) {
+ this.repository = repository;
+ }
+
+ @Override
+ public int getAndIncrement(String id) {
+ EventData eventData = repository.findById(id)
+ .orElseThrow(() -> new EventNotFoundException(id));
+ int value = eventData.getIncrementValue() + 1;
+ eventData.setIncrementValue(value);
+ repository.save(eventData);
+ return value;
+ }
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/JsonTokenProcessor.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/JsonTokenProcessor.java
new file mode 100644
index 0000000..da0026a
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/JsonTokenProcessor.java
@@ -0,0 +1,134 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+public enum JsonTokenProcessor {
+ STRING(JsonToken.STRING) {
+ @Override
+ void process(JsonReader reader, JsonWriter writer, int incrementValue, KeywordsExtractor keywordsExtractor)
+ throws IOException {
+ String originalString = reader.nextString();
+ if (keywordsExtractor.isPrimitive(originalString)) {
+ writer.value(keywordsExtractor.substitutePrimitiveKeyword(originalString));
+ } else {
+ String possibleSubstitution = Arrays.stream(originalString.split(" "))
+ .map(singleWord -> keywordsExtractor.substituteStringKeyword(singleWord, incrementValue)).collect(
+ Collectors.joining(" "));
+ writer.value(originalString.equals(possibleSubstitution) ? originalString : possibleSubstitution);
+ }
+ }
+ },
+ BEGIN_ARRAY(JsonToken.BEGIN_ARRAY) {
+ @Override
+ void process(JsonReader reader, JsonWriter writer, int incrementValue, KeywordsExtractor keywordsExtractor)
+ throws IOException {
+ reader.beginArray();
+ writer.beginArray();
+ }
+ },
+ END_ARRAY(JsonToken.END_ARRAY) {
+ @Override
+ void process(JsonReader reader, JsonWriter writer, int incrementValue, KeywordsExtractor keywordsExtractor)
+ throws IOException {
+ reader.endArray();
+ writer.endArray();
+ }
+ },
+ BEGIN_OBJECT(JsonToken.BEGIN_OBJECT) {
+ @Override
+ void process(JsonReader reader, JsonWriter writer, int incrementValue, KeywordsExtractor keywordsExtractor)
+ throws IOException {
+ reader.beginObject();
+ writer.beginObject();
+ }
+ },
+ END_OBJECT(JsonToken.END_OBJECT) {
+ @Override
+ void process(JsonReader reader, JsonWriter writer, int incrementValue, KeywordsExtractor keywordsExtractor)
+ throws IOException {
+ reader.endObject();
+ writer.endObject();
+ }
+ },
+ NAME(JsonToken.NAME) {
+ @Override
+ void process(JsonReader reader, JsonWriter writer, int incrementValue, KeywordsExtractor keywordsExtractor)
+ throws IOException {
+ writer.name(reader.nextName());
+ }
+ },
+ NUMBER(JsonToken.NUMBER) {
+ @Override
+ void process(JsonReader reader, JsonWriter writer, int incrementValue, KeywordsExtractor keywordsExtractor)
+ throws IOException {
+ writer.value(new BigDecimal(reader.nextString()));
+ }
+ },
+ BOOLEAN(JsonToken.BOOLEAN) {
+ @Override
+ void process(JsonReader reader, JsonWriter writer, int incrementValue, KeywordsExtractor keywordsExtractor)
+ throws IOException {
+ writer.value(reader.nextBoolean());
+ }
+ },
+ NULL(JsonToken.NULL) {
+ @Override
+ void process(JsonReader reader, JsonWriter writer, int incrementValue, KeywordsExtractor keywordsExtractor)
+ throws IOException {
+ reader.nextNull();
+ writer.nullValue();
+ }
+ },
+ END_DOCUMENT(JsonToken.END_DOCUMENT) {
+ @Override
+ void process(JsonReader reader, JsonWriter writer, int incrementValue, KeywordsExtractor keywordsExtractor)
+ throws IOException {
+ // do nothing
+ }
+ };
+
+ private JsonToken jsonToken;
+
+ JsonTokenProcessor(JsonToken jsonToken) {
+ this.jsonToken = jsonToken;
+ }
+
+ boolean isProcessorFor(JsonToken jsonToken) {
+ return this.jsonToken == jsonToken;
+ }
+
+ abstract void process(JsonReader reader, JsonWriter writer, int incrementValue, KeywordsExtractor keywordsExtractor) throws IOException;
+
+ private static final String INVALID_JSON_BODY_UNSUPPORTED_JSON_TOKEN = "Invalid json body. Unsupported JsonToken.";
+
+ static JsonTokenProcessor getProcessorFor(JsonToken jsonToken) throws IOException {
+ return Arrays.stream(JsonTokenProcessor.values()).filter(processor -> processor.isProcessorFor(jsonToken)).findFirst()
+ .orElseThrow(() -> new IOException(INVALID_JSON_BODY_UNSUPPORTED_JSON_TOKEN));
+ }
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/KeywordsExtractor.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/KeywordsExtractor.java
new file mode 100644
index 0000000..23c383f
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/KeywordsExtractor.java
@@ -0,0 +1,118 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import static io.vavr.API.$;
+import static io.vavr.API.Case;
+import static io.vavr.API.Match;
+import static org.onap.pnfsimulator.simulator.KeywordsValueProvider.getEpochSecond;
+import static org.onap.pnfsimulator.simulator.KeywordsValueProvider.getRandomLimitedInteger;
+import static org.onap.pnfsimulator.simulator.KeywordsValueProvider.getRandomInteger;
+import static org.onap.pnfsimulator.simulator.KeywordsValueProvider.getRandomLimitedString;
+import static org.onap.pnfsimulator.simulator.KeywordsValueProvider.getRandomString;
+import static org.onap.pnfsimulator.simulator.KeywordsValueProvider.getRandomPrimitiveInteger;
+import static org.onap.pnfsimulator.simulator.KeywordsValueProvider.getTimestampPrimitive;
+import static org.onap.pnfsimulator.simulator.keywords.NonParameterKeywordPatterns.$nonParameterKeyword;
+import static org.onap.pnfsimulator.simulator.keywords.SingleParameterKeywordPatterns.$singleParameterKeyword;
+import static org.onap.pnfsimulator.simulator.keywords.TwoParameterKeywordPatterns.$twoParameterKeyword;
+import io.vavr.API.Match.Pattern1;
+import org.onap.pnfsimulator.simulator.keywords.Keyword;
+import org.onap.pnfsimulator.simulator.keywords.NonParameterKeyword;
+import org.onap.pnfsimulator.simulator.keywords.SingleParameterKeyword;
+import org.onap.pnfsimulator.simulator.keywords.TwoParameterKeyword;
+import org.springframework.stereotype.Component;
+
+@Component
+public class KeywordsExtractor {
+
+ String substituteStringKeyword(String text, int increment) {
+ return Match(text).of(
+ Case(isRandomStringParamKeyword(),
+ spk -> spk.substituteKeyword(getRandomString().apply(spk.getAdditionalParameter()))),
+ Case(isRandomStringNonParamKeyword(),
+ npk -> npk.substituteKeyword(getRandomLimitedString().apply())),
+ Case(isRandomIntegerParamKeyword(),
+ tpk -> tpk.substituteKeyword(getRandomInteger().apply(tpk.getAdditionalParameter1(), tpk.getAdditionalParameter2()))),
+ Case(isRandomIntegerNonParamKeyword(),
+ npk -> npk.substituteKeyword(getRandomLimitedInteger().apply())),
+ Case(isIncrementKeyword(),
+ ik -> ik.substituteKeyword(String.valueOf(increment))),
+ Case(isTimestampNonParamKeyword(),
+ npk -> npk.substituteKeyword(getEpochSecond().apply())),
+ Case(
+ $(),
+ () -> text
+ ));
+ }
+
+ Long substitutePrimitiveKeyword(String text) {
+ return Match(text).of(
+ Case(isRandomPrimitiveIntegerParamKeyword(),
+ tpk ->
+ getRandomPrimitiveInteger().apply(tpk.getAdditionalParameter1(), tpk.getAdditionalParameter2())),
+ Case(isTimestampPrimitiveNonParamKeyword(),
+ tpk ->
+ getTimestampPrimitive().apply()),
+ Case(
+ $(),
+ () -> 0L
+ ));
+ }
+
+ boolean isPrimitive(String text) {
+ return Match(text).of(
+ Case(isRandomPrimitiveIntegerParamKeyword(), () -> true),
+ Case(isTimestampPrimitiveNonParamKeyword(), () -> true),
+ Case($(), () -> false));
+ }
+
+ private Pattern1<String, SingleParameterKeyword> isRandomStringParamKeyword() {
+ return $singleParameterKeyword($(spk -> Keyword.IS_MATCHING_KEYWORD_NAME.apply(spk, "RandomString")));
+ }
+
+ private Pattern1<String, NonParameterKeyword> isRandomStringNonParamKeyword() {
+ return $nonParameterKeyword($(npk -> Keyword.IS_MATCHING_KEYWORD_NAME.apply(npk, "RandomString")));
+ }
+
+ private Pattern1<String, NonParameterKeyword> isIncrementKeyword() {
+ return $nonParameterKeyword($(npk -> Keyword.IS_MATCHING_KEYWORD_NAME.apply(npk, "Increment")));
+ }
+
+ private Pattern1<String, TwoParameterKeyword> isRandomIntegerParamKeyword() {
+ return $twoParameterKeyword($(tpk -> Keyword.IS_MATCHING_KEYWORD_NAME.apply(tpk, "RandomInteger")));
+ }
+
+ private Pattern1<String, TwoParameterKeyword> isRandomPrimitiveIntegerParamKeyword() {
+ return $twoParameterKeyword($(tpk -> Keyword.IS_MATCHING_KEYWORD_NAME.apply(tpk, "RandomPrimitiveInteger")));
+ }
+
+ private Pattern1<String, NonParameterKeyword> isTimestampPrimitiveNonParamKeyword() {
+ return $nonParameterKeyword($(npk -> Keyword.IS_MATCHING_KEYWORD_NAME.apply(npk, "TimestampPrimitive")));
+ }
+
+ private Pattern1<String, NonParameterKeyword> isRandomIntegerNonParamKeyword() {
+ return $nonParameterKeyword($(npk -> Keyword.IS_MATCHING_KEYWORD_NAME.apply(npk, "RandomInteger")));
+ }
+
+ private Pattern1<String, NonParameterKeyword> isTimestampNonParamKeyword() {
+ return $nonParameterKeyword($(npk -> Keyword.IS_MATCHING_KEYWORD_NAME.apply(npk, "Timestamp")));
+ }
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/KeywordsHandler.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/KeywordsHandler.java
new file mode 100644
index 0000000..51e0c1f
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/KeywordsHandler.java
@@ -0,0 +1,72 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import org.springframework.stereotype.Component;
+
+@Component
+public class KeywordsHandler {
+
+ private KeywordsExtractor keywordsExtractor;
+ private IncrementProvider incrementProvider;
+
+ public KeywordsHandler(KeywordsExtractor keywordsExtractor, IncrementProvider incrementProvider) {
+ this.keywordsExtractor = keywordsExtractor;
+ this.incrementProvider = incrementProvider;
+ }
+
+ public JsonElement substituteKeywords(JsonElement jsonBody, String jobId) {
+ int counter = incrementProvider.getAndIncrement(jobId);
+ try (
+ JsonReader reader = new JsonReader(new StringReader(jsonBody.toString()));
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ ) {
+ modify(reader, jsonWriter, counter);
+ return new Gson().fromJson(stringWriter.getBuffer().toString(), JsonElement.class);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void modify(JsonReader reader, JsonWriter writer, int incrementValue) throws IOException {
+ JsonTokenProcessor jsonTokenProcessor;
+ do {
+ JsonToken token = reader.peek();
+ jsonTokenProcessor = JsonTokenProcessor.getProcessorFor(token);
+ jsonTokenProcessor.process(reader, writer, incrementValue, keywordsExtractor);
+ } while (isJsonProcessingFinished(jsonTokenProcessor));
+ }
+
+ private boolean isJsonProcessingFinished(JsonTokenProcessor jsonTokenProcessor) {
+ return !jsonTokenProcessor.isProcessorFor(JsonToken.END_DOCUMENT);
+ }
+
+}
+
+
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/KeywordsValueProvider.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/KeywordsValueProvider.java
new file mode 100644
index 0000000..3bcfa5b
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/KeywordsValueProvider.java
@@ -0,0 +1,80 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import io.vavr.Function0;
+import io.vavr.Function1;
+import io.vavr.Function2;
+
+import java.time.Instant;
+import java.util.Random;
+
+import org.apache.commons.lang3.RandomStringUtils;
+
+class KeywordsValueProvider {
+
+ private KeywordsValueProvider() {
+ }
+
+ static final int DEFAULT_STRING_LENGTH = 20;
+ public static final int RANDOM_INTEGER_MAX_LIMITATION = 9;
+ public static final int RANDOM_INTEGER_MIN_LIMITATION = 0;
+
+ private static Function2<Integer, Integer, Integer> bigger = (left, right) -> left >= right ? left : right;
+ private static Function2<Integer, Integer, Integer> smaller = (left, right) -> left < right ? left : right;
+ private static Function2<Integer, Integer, Integer> randomPrimitiveIntegerFromSortedRange = (min, max) -> new Random().nextInt(max - min + 1) + min;
+ private static Function2<Integer, Integer, String> randomIntegerFromSortedRange = (min, max) -> Integer.toString(new Random().nextInt(max - min + 1) + min);
+
+ private static Function1<Integer, String> randomString = RandomStringUtils::randomAscii;
+ private static Function2<Integer, Integer, String> randomInteger = (left, right) -> randomIntegerFromSortedRange.apply(smaller.apply(left, right), bigger.apply(left, right));
+ private static Function0<String> randomLimitedInteger = () -> randomInteger.apply(RANDOM_INTEGER_MIN_LIMITATION, RANDOM_INTEGER_MAX_LIMITATION);
+ private static Function0<String> randomLimitedString = () -> RandomStringUtils.randomAscii(DEFAULT_STRING_LENGTH);
+ private static Function0<String> epochSecond = () -> Long.toString(Instant.now().getEpochSecond());
+ private static Function2<Integer, Integer, Long> randomPrimitiveInteger = (left, right) -> randomPrimitiveIntegerFromSortedRange.apply(smaller.apply(left, right), bigger.apply(left, right)).longValue();
+ private static Function0<Long> timestampPrimitive = () -> Instant.now().getEpochSecond();
+
+ public static Function1<Integer, String> getRandomString() {
+ return randomString;
+ }
+
+ public static Function2<Integer, Integer, String> getRandomInteger() {
+ return randomInteger;
+ }
+
+ public static Function0<String> getRandomLimitedInteger() {
+ return randomLimitedInteger;
+ }
+
+ public static Function0<String> getRandomLimitedString() {
+ return randomLimitedString;
+ }
+
+ public static Function0<String> getEpochSecond() {
+ return epochSecond;
+ }
+
+ public static Function2<Integer, Integer, Long> getRandomPrimitiveInteger() {
+ return randomPrimitiveInteger;
+ }
+
+ public static Function0<Long> getTimestampPrimitive() {
+ return timestampPrimitive;
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/SimulatorService.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/SimulatorService.java
new file mode 100644
index 0000000..155c0ff
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/SimulatorService.java
@@ -0,0 +1,121 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import com.google.common.base.Strings;
+import com.google.gson.JsonObject;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.security.GeneralSecurityException;
+import java.util.Optional;
+import org.onap.pnfsimulator.event.EventData;
+import org.onap.pnfsimulator.event.EventDataService;
+import org.onap.pnfsimulator.rest.model.FullEvent;
+import org.onap.pnfsimulator.rest.model.SimulatorParams;
+import org.onap.pnfsimulator.rest.model.SimulatorRequest;
+import org.onap.pnfsimulator.simulator.client.HttpClientAdapter;
+import org.onap.pnfsimulator.simulator.client.HttpClientAdapterImpl;
+import org.onap.pnfsimulator.simulator.client.utils.ssl.SSLAuthenticationHelper;
+import org.onap.pnfsimulator.simulator.scheduler.EventScheduler;
+import org.onap.pnfsimulator.simulatorconfig.SimulatorConfig;
+import org.onap.pnfsimulator.simulatorconfig.SimulatorConfigService;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class SimulatorService {
+
+ private final TemplatePatcher templatePatcher;
+ private final TemplateReader templateReader;
+ private final EventDataService eventDataService;
+ private final EventScheduler eventScheduler;
+ private final SSLAuthenticationHelper sslAuthenticationHelper;
+ private SimulatorConfigService simulatorConfigService;
+ private static final JsonObject EMPTY_JSON_OBJECT = new JsonObject();
+
+ @Autowired
+ public SimulatorService(TemplatePatcher templatePatcher, TemplateReader templateReader,
+ EventScheduler eventScheduler, EventDataService eventDataService,
+ SimulatorConfigService simulatorConfigService, SSLAuthenticationHelper sslAuthenticationHelper) {
+ this.templatePatcher = templatePatcher;
+ this.templateReader = templateReader;
+ this.eventDataService = eventDataService;
+ this.eventScheduler = eventScheduler;
+ this.simulatorConfigService = simulatorConfigService;
+ this.sslAuthenticationHelper = sslAuthenticationHelper;
+ }
+
+ public String triggerEvent(SimulatorRequest simulatorRequest) throws IOException, SchedulerException, GeneralSecurityException {
+ String templateName = simulatorRequest.getTemplateName();
+ SimulatorParams simulatorParams = simulatorRequest.getSimulatorParams();
+ JsonObject template = templateReader.readTemplate(templateName);
+ JsonObject input = Optional.ofNullable(simulatorRequest.getPatch()).orElse(new JsonObject());
+ JsonObject patchedJson = templatePatcher
+ .mergeTemplateWithPatch(template, input);
+ JsonObject keywords = new JsonObject();
+
+ EventData eventData = eventDataService.persistEventData(template, patchedJson, input, keywords);
+
+ String targetVesUrl = getDefaultUrlIfNotProvided(simulatorParams.getVesServerUrl());
+ return eventScheduler
+ .scheduleEvent(targetVesUrl, Optional.ofNullable(simulatorParams.getRepeatInterval()).orElse(1),
+ Optional.ofNullable(simulatorParams.getRepeatCount()).orElse(1), simulatorRequest.getTemplateName(),
+ eventData.getId(),
+ patchedJson);
+ }
+
+ public void triggerOneTimeEvent(FullEvent event) throws IOException, GeneralSecurityException {
+ KeywordsHandler keywordsHandler = new KeywordsHandler(new KeywordsExtractor(), id -> 1);
+ JsonObject withKeywordsSubstituted = keywordsHandler.substituteKeywords(event.getEvent(), "").getAsJsonObject();
+
+ HttpClientAdapter client = createHttpClientAdapter(event.getVesServerUrl());
+ eventDataService.persistEventData(EMPTY_JSON_OBJECT, withKeywordsSubstituted, event.getEvent(), EMPTY_JSON_OBJECT);
+
+ client.send(withKeywordsSubstituted.toString());
+ }
+
+ public SimulatorConfig getConfiguration() {
+ return simulatorConfigService.getConfiguration();
+ }
+
+ public SimulatorConfig updateConfiguration(SimulatorConfig newConfig) {
+ return simulatorConfigService.updateConfiguration(newConfig);
+ }
+
+ public boolean cancelAllEvents() throws SchedulerException {
+ return eventScheduler.cancelAllEvents();
+ }
+
+ public boolean cancelEvent(String jobName) throws SchedulerException {
+ return eventScheduler.cancelEvent(jobName);
+ }
+
+ HttpClientAdapter createHttpClientAdapter(String vesServerUrl) throws IOException, GeneralSecurityException {
+ String targetVesUrl = getDefaultUrlIfNotProvided(vesServerUrl);
+ return new HttpClientAdapterImpl(targetVesUrl, sslAuthenticationHelper);
+ }
+
+ private String getDefaultUrlIfNotProvided(String vesUrlSimulatorParam) {
+ return Strings.isNullOrEmpty(vesUrlSimulatorParam)
+ ? simulatorConfigService.getConfiguration().getVesServerUrl().toString() : vesUrlSimulatorParam;
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/TemplatePatcher.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/TemplatePatcher.java
new file mode 100644
index 0000000..1114d3c
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/TemplatePatcher.java
@@ -0,0 +1,53 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+@Component
+class TemplatePatcher {
+
+ JsonObject mergeTemplateWithPatch(JsonObject templateJson, JsonObject patchJson) {
+ JsonObject template = templateJson.deepCopy();
+ patchTemplateNode(template, patchJson);
+ return template;
+ }
+
+ private void patchTemplateNode(JsonObject templateJson, JsonObject patchJson) {
+ for (Map.Entry<String, JsonElement> stringJsonElementEntry : patchJson.entrySet()) {
+ String patchKey = stringJsonElementEntry.getKey();
+ JsonElement patchValue = stringJsonElementEntry.getValue();
+ JsonElement templateElement = templateJson.get(patchKey);
+
+ if (!patchValue.isJsonObject() || templateElement == null || !templateElement.isJsonObject()) {
+ templateJson.remove(patchKey);
+ templateJson.add(patchKey, patchValue);
+ } else {
+ patchTemplateNode(templateElement.getAsJsonObject(), patchValue.getAsJsonObject());
+ }
+
+ }
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/TemplateReader.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/TemplateReader.java
new file mode 100644
index 0000000..bf06381
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/TemplateReader.java
@@ -0,0 +1,28 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import com.google.gson.JsonObject;
+import java.io.IOException;
+
+public interface TemplateReader {
+ JsonObject readTemplate(String templateName) throws IOException;
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapter.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapter.java
new file mode 100644
index 0000000..e7d113d
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapter.java
@@ -0,0 +1,26 @@
+/*
+ * ============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.pnfsimulator.simulator.client;
+
+public interface HttpClientAdapter {
+
+ void send(String content);
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImpl.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImpl.java
new file mode 100644
index 0000000..6ea1157
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImpl.java
@@ -0,0 +1,104 @@
+/*
+ * ============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.pnfsimulator.simulator.client;
+
+import static org.onap.pnfsimulator.logging.MDCVariables.REQUEST_ID;
+import static org.onap.pnfsimulator.logging.MDCVariables.X_INVOCATION_ID;
+import static org.onap.pnfsimulator.logging.MDCVariables.X_ONAP_REQUEST_ID;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.GeneralSecurityException;
+import java.util.UUID;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.util.EntityUtils;
+import org.onap.pnfsimulator.simulator.client.utils.ssl.SSLAuthenticationHelper;
+import org.onap.pnfsimulator.simulator.client.utils.ssl.SslSupportLevel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+import org.slf4j.Marker;
+import org.slf4j.MarkerFactory;
+
+public class HttpClientAdapterImpl implements HttpClientAdapter {
+
+ private static final int CONNECTION_TIMEOUT = 1000;
+ private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientAdapterImpl.class);
+ private static final String CONTENT_TYPE = "Content-Type";
+ private static final String APPLICATION_JSON = "application/json";
+ private static final RequestConfig CONFIG = RequestConfig.custom()
+ .setConnectTimeout(CONNECTION_TIMEOUT)
+ .setConnectionRequestTimeout(CONNECTION_TIMEOUT)
+ .setSocketTimeout(CONNECTION_TIMEOUT)
+ .build();
+ private static final Marker INVOKE = MarkerFactory.getMarker("INVOKE");
+ private SslSupportLevel sslSupportLevel;
+ private HttpClient client;
+ private final String targetUrl;
+
+ public HttpClientAdapterImpl(String targetUrl, SSLAuthenticationHelper sslAuthenticationHelper)
+ throws IOException, GeneralSecurityException {
+ this.sslSupportLevel = sslAuthenticationHelper.isClientCertificateEnabled() ?
+ SslSupportLevel.CLIENT_CERT_AUTH : SslSupportLevel.getSupportLevelBasedOnProtocol(targetUrl);
+ this.client = sslSupportLevel.getClient(CONFIG, sslAuthenticationHelper);
+ this.targetUrl = targetUrl;
+ }
+
+ HttpClientAdapterImpl(HttpClient client, String targetUrl) {
+ this.client = client;
+ this.targetUrl = targetUrl;
+ }
+
+ @Override
+ public void send(String content) {
+ try {
+ HttpPost request = createRequest(content);
+ HttpResponse response = client.execute(request);
+
+ //response has to be fully consumed otherwise apache won't release connection
+ EntityUtils.consumeQuietly(response.getEntity());
+ LOGGER.info(INVOKE, "Message sent, ves response code: {}", response.getStatusLine());
+ } catch (IOException e) {
+ LOGGER.warn("Error sending message to ves: " + e.getMessage(), e.getCause());
+ }
+ }
+
+ public SslSupportLevel getSslSupportLevel(){
+ return sslSupportLevel;
+ }
+
+ private HttpPost createRequest(String content) throws UnsupportedEncodingException {
+ HttpPost request = new HttpPost(this.targetUrl);
+ StringEntity stringEntity = new StringEntity(content);
+ request.addHeader(CONTENT_TYPE, APPLICATION_JSON);
+ request.addHeader(X_ONAP_REQUEST_ID, MDC.get(REQUEST_ID));
+ request.addHeader(X_INVOCATION_ID, UUID.randomUUID().toString());
+ request.setEntity(stringEntity);
+ return request;
+ }
+
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SSLAuthenticationHelper.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SSLAuthenticationHelper.java
new file mode 100644
index 0000000..eda17ef
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SSLAuthenticationHelper.java
@@ -0,0 +1,79 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * 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.pnfsimulator.simulator.client.utils.ssl;
+
+import java.io.Serializable;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "ssl")
+@RefreshScope
+@Primary
+public class SSLAuthenticationHelper implements Serializable {
+
+ private boolean clientCertificateEnabled;
+ private String clientCertificateDir;
+ private String clientCertificatePassword;
+ private String trustStoreDir;
+ private String trustStorePassword;
+
+ public boolean isClientCertificateEnabled() {
+ return clientCertificateEnabled;
+ }
+
+ public void setClientCertificateEnabled(boolean clientCertificateEnabled) {
+ this.clientCertificateEnabled = clientCertificateEnabled;
+ }
+
+ public String getClientCertificateDir() {
+ return clientCertificateDir;
+ }
+
+ public void setClientCertificateDir(String clientCertificateDir) {
+ this.clientCertificateDir = clientCertificateDir;
+ }
+
+ public String getClientCertificatePassword() {
+ return clientCertificatePassword;
+ }
+
+ public void setClientCertificatePassword(String clientCertificatePassword) {
+ this.clientCertificatePassword = clientCertificatePassword;
+ }
+
+ public String getTrustStoreDir() {
+ return trustStoreDir;
+ }
+
+ public void setTrustStoreDir(String trustStoreDir) {
+ this.trustStoreDir = trustStoreDir;
+ }
+
+ public String getTrustStorePassword() {
+ return trustStorePassword;
+ }
+
+ public void setTrustStorePassword(String trustStorePassword) {
+ this.trustStorePassword = trustStorePassword;
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SslSupportLevel.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SslSupportLevel.java
new file mode 100644
index 0000000..264a7d1
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SslSupportLevel.java
@@ -0,0 +1,120 @@
+/*
+ * ============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.pnfsimulator.simulator.client.utils.ssl;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.TrustAllStrategy;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.ssl.SSLContexts;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLContext;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Optional;
+
+public enum SslSupportLevel {
+
+ NONE {
+ public HttpClient getClient(RequestConfig requestConfig, SSLAuthenticationHelper sslAuthenticationHelper) {
+ LOGGER.info("<!-----IN SslSupportLevel.NONE, Creating BasicHttpClient for http protocol----!>");
+ return HttpClientBuilder
+ .create()
+ .setDefaultRequestConfig(requestConfig)
+ .build();
+ }
+ },
+ ALWAYS_TRUST {
+ public HttpClient getClient(RequestConfig requestConfig, SSLAuthenticationHelper sslAuthenticationHelper)
+ throws GeneralSecurityException, IOException {
+ LoggerFactory.getLogger(SslSupportLevel.class).info("<!-----IN SslSupportLevel.ALWAYS_TRUST, Creating client with SSL support for https protocol----!>");
+ HttpClient client;
+ try {
+ SSLContext alwaysTrustSslContext = SSLContextBuilder.create().loadTrustMaterial(TRUST_STRATEGY_ALWAYS).build();
+ client = HttpClients.custom()
+ .setSSLContext(alwaysTrustSslContext)
+ .setSSLHostnameVerifier(new NoopHostnameVerifier())
+ .setDefaultRequestConfig(requestConfig)
+ .build();
+
+ } catch (GeneralSecurityException e) {
+ LOGGER.error("Could not initialize client due to SSL exception: {}. Default client without SSL support will be used instead.\nCause: {}", e.getMessage(), e.getCause());
+ client = NONE.getClient(requestConfig, sslAuthenticationHelper);
+ }
+ return client;
+ }
+ },
+ CLIENT_CERT_AUTH {
+ @Override
+ public HttpClient getClient(RequestConfig requestConfig, SSLAuthenticationHelper sslAuthenticationHelper)
+ throws GeneralSecurityException, IOException {
+
+ SSLContext sslContext = SSLContexts.custom()
+ .loadKeyMaterial(readCertificate(sslAuthenticationHelper.getClientCertificateDir(), sslAuthenticationHelper.getClientCertificatePassword(), "PKCS12"), getPasswordAsCharArray(sslAuthenticationHelper.getClientCertificatePassword()))
+ .loadTrustMaterial(readCertificate(sslAuthenticationHelper.getTrustStoreDir(), sslAuthenticationHelper.getTrustStorePassword(), "JKS"), new TrustSelfSignedStrategy())
+ .build();
+
+ return HttpClients.custom()
+ .setSSLContext(sslContext)
+ .setSSLHostnameVerifier(new NoopHostnameVerifier())
+ .setDefaultRequestConfig(requestConfig)
+ .build();
+ }
+
+ private KeyStore readCertificate(String certificate, String password, String type) throws GeneralSecurityException, IOException {
+ try (InputStream keyStoreStream = new FileInputStream(certificate)) {
+ KeyStore keyStore = KeyStore.getInstance(type);
+ keyStore.load(keyStoreStream, getPasswordAsCharArray(password));
+ return keyStore;
+ }
+ }
+
+ private char[] getPasswordAsCharArray(String clientCertificatePassword) {
+ return Optional.ofNullable(clientCertificatePassword).map(String::toCharArray).orElse(null);
+ }
+ };
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SslSupportLevel.class);
+ private static final TrustStrategy TRUST_STRATEGY_ALWAYS = new TrustAllStrategy();
+
+ public static SslSupportLevel getSupportLevelBasedOnProtocol(String url) throws MalformedURLException {
+ return "https".equals(new URL(url).getProtocol()) ? SslSupportLevel.ALWAYS_TRUST : SslSupportLevel.NONE;
+ }
+
+ public abstract HttpClient getClient(RequestConfig config, SSLAuthenticationHelper sslAuthenticationHelper)
+ throws GeneralSecurityException, IOException;
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/Keyword.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/Keyword.java
new file mode 100644
index 0000000..edafe8f
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/Keyword.java
@@ -0,0 +1,74 @@
+/*
+ * ============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.pnfsimulator.simulator.keywords;
+
+import io.vavr.Function1;
+import io.vavr.Function2;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.stream.Collectors;
+import lombok.Getter;
+
+@Getter
+public class Keyword {
+
+ protected static final String LETTERS_REGEX = "([a-zA-Z]+)";
+ protected static final String NONLETTERS_REGEX = "([^a-zA-Z]+)";
+
+ protected static final Function1<String, String> OPTIONAL =
+ (regex) -> regex + "?";
+
+ private final String name;
+ private final List<String> meaningfulParts;
+
+ public static final Function2<Keyword, String, Boolean> IS_MATCHING_KEYWORD_NAME = (keyword, key) ->
+ keyword != null && keyword.getName() != null && keyword.getName().equals(key);
+
+ /**
+ * Returns list of independent parts inside the keyword. Current implementation assumes that customer can join keywords with integer values, so
+ * keyword is decomposed to parts then some parts of the keyword is skipped because of replacement process.
+ *
+ * @param matcher - Matcher to check find independent groups inside the keyword
+ * @param skipGroups Informs this method about which groups should be consider as part of the replacement process
+ * @return list of independent parts inside the keywords
+ */
+ static List<String> extractPartsFrom(Matcher matcher, List skipGroups) {
+ List<String> parts = new ArrayList<String>();
+ for (int i = 1; i <= matcher.groupCount(); i++) {
+ if (matcher.group(i) != null && !skipGroups.contains(i)) {
+ parts.add(matcher.group(i));
+ }
+ }
+ return parts;
+ }
+
+ Keyword(String name, List<String> meaningfulParts) {
+ this.name = name;
+ this.meaningfulParts = meaningfulParts;
+ }
+
+ public String substituteKeyword(String substitution) {
+ return meaningfulParts.stream()
+ .map(part -> part.equals(name) ? substitution : part)
+ .collect(Collectors.joining());
+ }
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/NonParameterKeyword.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/NonParameterKeyword.java
new file mode 100644
index 0000000..5e44550
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/NonParameterKeyword.java
@@ -0,0 +1,65 @@
+/*
+ * ============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.pnfsimulator.simulator.keywords;
+
+import io.vavr.Tuple;
+import io.vavr.Tuple1;
+import io.vavr.match.annotation.Patterns;
+import io.vavr.match.annotation.Unapply;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.val;
+
+@Patterns
+@Getter
+@Setter
+public class NonParameterKeyword extends Keyword {
+
+ public static final int KEYWORD_NAME_GROUP = 2;
+
+ private static final String KEYWORD_REGEX = new StringBuilder()
+ .append(OPTIONAL.apply(NONLETTERS_REGEX))
+ .append("#")
+ .append(LETTERS_REGEX)
+ .append("(?!\\()")
+ .append(OPTIONAL.apply(NONLETTERS_REGEX))
+ .toString();
+
+ private NonParameterKeyword(String name, List<String> meaningfulParts) {
+ super(name, meaningfulParts);
+ }
+
+ @Unapply
+ static Tuple1<NonParameterKeyword> nonParameterKeyword(String keyword) {
+ val matcher = Pattern.compile(KEYWORD_REGEX).matcher(keyword);
+ NonParameterKeyword npk = null;
+ if (matcher.find()) {
+ npk = new NonParameterKeyword(
+ matcher.group(KEYWORD_NAME_GROUP),
+ extractPartsFrom(matcher, Collections.emptyList())
+ );
+ }
+ return Tuple.of(npk);
+ }
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/SingleParameterKeyword.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/SingleParameterKeyword.java
new file mode 100644
index 0000000..b1c38c8
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/SingleParameterKeyword.java
@@ -0,0 +1,73 @@
+/*
+ * ============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.pnfsimulator.simulator.keywords;
+
+import io.vavr.Tuple;
+import io.vavr.Tuple1;
+import io.vavr.match.annotation.Patterns;
+import io.vavr.match.annotation.Unapply;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.val;
+
+@Patterns
+@Getter
+@Setter
+public class SingleParameterKeyword extends Keyword {
+
+ public static final int KEYWORD_NAME_GROUP = 2;
+ public static final int ADDITIONAL_PARAMETER_GROUP = 3;
+
+ private static final String KEYWORD_REGEX = new StringBuilder()
+ .append(OPTIONAL.apply(NONLETTERS_REGEX))
+ .append("#")
+ .append(LETTERS_REGEX)
+ .append("\\((\\d+)\\)")
+ .append(OPTIONAL.apply(NONLETTERS_REGEX))
+ .toString();
+ public static final int SKIPPED_GROUP_NUMBER = 3;
+
+ private Integer additionalParameter;
+
+ private SingleParameterKeyword(String name, List<String> meaningfulParts,
+ Integer additionalParameter) {
+ super(name, meaningfulParts);
+ this.additionalParameter = additionalParameter;
+ }
+
+ @Unapply
+ static Tuple1<SingleParameterKeyword> singleParameterKeyword(String keyword) {
+ val matcher = Pattern.compile(KEYWORD_REGEX).matcher(keyword);
+ SingleParameterKeyword spk = null;
+ if (matcher.find()) {
+ spk = new SingleParameterKeyword(
+ matcher.group(KEYWORD_NAME_GROUP),
+ extractPartsFrom(matcher, Collections.singletonList(SKIPPED_GROUP_NUMBER)),
+ Integer.parseInt(matcher.group(ADDITIONAL_PARAMETER_GROUP))
+ );
+ }
+ return Tuple.of(spk);
+ }
+}
+
+
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/TwoParameterKeyword.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/TwoParameterKeyword.java
new file mode 100644
index 0000000..6fecfa6
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/keywords/TwoParameterKeyword.java
@@ -0,0 +1,80 @@
+/*
+ * ============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.pnfsimulator.simulator.keywords;
+
+import io.vavr.Tuple;
+import io.vavr.Tuple1;
+import io.vavr.match.annotation.Patterns;
+import io.vavr.match.annotation.Unapply;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.val;
+
+@Patterns
+@Getter
+@Setter
+public class TwoParameterKeyword extends Keyword {
+
+ public static final int ADDITIONAL_PARAMETER_1_GROUP = 3;
+ public static final int ADDITIONAL_PARAMETER_2_GROUP = 4;
+ public static final int KEYWORD_NAME_GROUP = 2;
+ protected static final List<Integer> ADDITIONAL_PARAMETERS_GROUPS = Arrays.asList(ADDITIONAL_PARAMETER_1_GROUP, ADDITIONAL_PARAMETER_2_GROUP);
+
+ private static final String NON_LIMITED_NUMBER_REGEX = "\\((\\d+)";
+ private static final String COLON_REGEX = "\\s?,\\s?";
+ private static final String OPTIONAL_NUMBER_PARAM_REGEX = "(\\d+)\\)";
+
+ private static final String KEYWORD_REGEX = OPTIONAL.apply(NONLETTERS_REGEX)
+ + "#"
+ + LETTERS_REGEX
+ + NON_LIMITED_NUMBER_REGEX
+ + COLON_REGEX
+ + OPTIONAL_NUMBER_PARAM_REGEX
+ + OPTIONAL.apply(NONLETTERS_REGEX);
+
+ private Integer additionalParameter1;
+ private Integer additionalParameter2;
+
+ private TwoParameterKeyword(String name, List<String> meaningfulParts, Integer additionalParameter1,
+ Integer additionalParameter2) {
+ super(name, meaningfulParts);
+ this.additionalParameter1 = additionalParameter1;
+ this.additionalParameter2 = additionalParameter2;
+ }
+
+ @Unapply
+ static Tuple1<TwoParameterKeyword> twoParameterKeyword(String keyword) {
+ val matcher = Pattern.compile(KEYWORD_REGEX).matcher(keyword);
+ TwoParameterKeyword tpk = null;
+ if (matcher.find()) {
+ tpk = new TwoParameterKeyword(
+ matcher.group(KEYWORD_NAME_GROUP),
+ extractPartsFrom(matcher, ADDITIONAL_PARAMETERS_GROUPS),
+ Integer.parseInt(matcher.group(ADDITIONAL_PARAMETER_1_GROUP)),
+ Integer.parseInt(matcher.group(ADDITIONAL_PARAMETER_2_GROUP))
+ );
+ }
+ return Tuple.of(tpk);
+ }
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventJob.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventJob.java
new file mode 100644
index 0000000..c4b40fc
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventJob.java
@@ -0,0 +1,93 @@
+/*
+ * ============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.pnfsimulator.simulator.scheduler;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import org.onap.pnfsimulator.simulator.KeywordsHandler;
+import org.onap.pnfsimulator.simulator.client.HttpClientAdapter;
+import org.onap.pnfsimulator.simulator.client.HttpClientAdapterImpl;
+import org.onap.pnfsimulator.simulator.client.utils.ssl.SSLAuthenticationHelper;
+import org.quartz.Job;
+import org.quartz.JobDataMap;
+import org.quartz.JobExecutionContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.security.GeneralSecurityException;
+import java.util.Optional;
+
+public class EventJob implements Job {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EventJob.class);
+
+ static final String TEMPLATE_NAME = "TEMPLATE_NAME";
+ static final String VES_URL = "VES_URL";
+ static final String BODY = "BODY";
+ static final String CLIENT_ADAPTER = "CLIENT_ADAPTER";
+ static final String KEYWORDS_HANDLER = "KEYWORDS_HANDLER";
+ static final String EVENT_ID = "EVENT_ID";
+
+ @Override
+ public void execute(JobExecutionContext jobExecutionContext) {
+ JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
+ String templateName = jobDataMap.getString(TEMPLATE_NAME);
+ String vesUrl = jobDataMap.getString(VES_URL);
+ JsonObject body = (JsonObject) jobDataMap.get(BODY);
+ String id = jobDataMap.getString(EVENT_ID);
+ Optional<HttpClientAdapter> httpClientAdapter = getHttpClientAdapter(jobDataMap, vesUrl);
+
+ if (httpClientAdapter.isPresent()) {
+ KeywordsHandler keywordsHandler = (KeywordsHandler) jobDataMap.get(KEYWORDS_HANDLER);
+ JsonElement processedBody = keywordsHandler.substituteKeywords(body, id);
+ String processedBodyString = processedBody.toString();
+ String jobKey = jobExecutionContext.getJobDetail().getKey().toString();
+
+ logEventDetails(templateName, vesUrl, body.toString(), jobKey);
+ httpClientAdapter.get().send(processedBodyString);
+ } else {
+ LOGGER.error("Could not send event as client is not available");
+ }
+ }
+ private Optional<HttpClientAdapter> getHttpClientAdapter(JobDataMap jobDataMap, String vesUrl) {
+ HttpClientAdapter adapter = null;
+ try {
+ adapter = (HttpClientAdapter) (jobDataMap.containsKey(CLIENT_ADAPTER) ? jobDataMap.get(CLIENT_ADAPTER) :
+ new HttpClientAdapterImpl(vesUrl, new SSLAuthenticationHelper()));
+ } catch (MalformedURLException e) {
+ LOGGER.error("Invalid format of vesServerUr: {}", vesUrl);
+ } catch (IOException | GeneralSecurityException e){
+ LOGGER.error("Invalid configuration of client certificate");
+ }
+ return Optional.ofNullable(adapter);
+ }
+
+ private void logEventDetails(String templateName, String vesUrl, String body, String jobKey) {
+ LOGGER.info(String.format("Job %s:Sending event to %s from template %s",
+ jobKey, vesUrl, templateName));
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug(String.format("Job %s: Request body %s", jobKey, body));
+ }
+ }
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventScheduler.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventScheduler.java
new file mode 100644
index 0000000..08e24f8
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/EventScheduler.java
@@ -0,0 +1,121 @@
+/*
+ * ============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.pnfsimulator.simulator.scheduler;
+
+
+import static org.onap.pnfsimulator.simulator.scheduler.EventJob.BODY;
+import static org.onap.pnfsimulator.simulator.scheduler.EventJob.CLIENT_ADAPTER;
+import static org.onap.pnfsimulator.simulator.scheduler.EventJob.EVENT_ID;
+import static org.onap.pnfsimulator.simulator.scheduler.EventJob.KEYWORDS_HANDLER;
+import static org.onap.pnfsimulator.simulator.scheduler.EventJob.TEMPLATE_NAME;
+import static org.onap.pnfsimulator.simulator.scheduler.EventJob.VES_URL;
+import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
+
+import com.google.gson.JsonObject;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.security.GeneralSecurityException;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.onap.pnfsimulator.simulator.KeywordsHandler;
+import org.onap.pnfsimulator.simulator.client.HttpClientAdapterImpl;
+import org.onap.pnfsimulator.simulator.client.utils.ssl.SSLAuthenticationHelper;
+import org.quartz.JobBuilder;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.SimpleTrigger;
+import org.quartz.TriggerBuilder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class EventScheduler {
+
+
+ private final Scheduler scheduler;
+ private final KeywordsHandler keywordsHandler;
+ private final SSLAuthenticationHelper SSLAuthenticationHelper;
+
+ @Autowired
+ public EventScheduler(Scheduler scheduler, KeywordsHandler keywordsHandler, SSLAuthenticationHelper SSLAuthenticationHelper) {
+ this.scheduler = scheduler;
+ this.keywordsHandler = keywordsHandler;
+ this.SSLAuthenticationHelper = SSLAuthenticationHelper;
+ }
+
+ public String scheduleEvent(String vesUrl, Integer repeatInterval, Integer repeatCount,
+ String templateName, String eventId, JsonObject body)
+ throws SchedulerException, IOException, GeneralSecurityException {
+
+ JobDetail jobDetail = createJobDetail(vesUrl, templateName, eventId, body);
+ SimpleTrigger trigger = createTrigger(repeatInterval, repeatCount);
+
+ scheduler.scheduleJob(jobDetail, trigger);
+ return jobDetail.getKey().getName();
+ }
+
+ public boolean cancelAllEvents() throws SchedulerException {
+ List<JobKey> jobKeys = getActiveJobsKeys();
+ return scheduler.deleteJobs(jobKeys);
+ }
+
+ public boolean cancelEvent(String jobName) throws SchedulerException {
+ Optional<JobKey> activeJobKey = getActiveJobsKeys().stream().filter(e -> e.getName().equals(jobName)).findFirst();
+ return activeJobKey.isPresent() && scheduler.deleteJob(activeJobKey.get());
+ }
+
+ private SimpleTrigger createTrigger(int interval, int repeatCount) {
+ return TriggerBuilder.newTrigger()
+ .withSchedule(simpleSchedule()
+ .withIntervalInSeconds(interval)
+ .withRepeatCount(repeatCount - 1))
+ .build();
+ }
+
+ private JobDetail createJobDetail(String vesUrl, String templateName, String eventId, JsonObject body) throws IOException, GeneralSecurityException {
+ JobDataMap jobDataMap = new JobDataMap();
+ jobDataMap.put(TEMPLATE_NAME, templateName);
+ jobDataMap.put(VES_URL, vesUrl);
+ jobDataMap.put(EVENT_ID, eventId);
+ jobDataMap.put(KEYWORDS_HANDLER, keywordsHandler);
+ jobDataMap.put(BODY, body);
+ jobDataMap.put(CLIENT_ADAPTER, new HttpClientAdapterImpl(vesUrl, SSLAuthenticationHelper));
+
+ return JobBuilder
+ .newJob(EventJob.class)
+ .withDescription(templateName)
+ .usingJobData(jobDataMap)
+ .build();
+ }
+
+ private List<JobKey> getActiveJobsKeys() throws SchedulerException {
+ return scheduler.getCurrentlyExecutingJobs()
+ .stream()
+ .map(JobExecutionContext::getJobDetail)
+ .map(JobDetail::getKey)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/QuartzConfiguration.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/QuartzConfiguration.java
new file mode 100644
index 0000000..2beb9dc
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/scheduler/QuartzConfiguration.java
@@ -0,0 +1,38 @@
+/*
+ * ============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.pnfsimulator.simulator.scheduler;
+
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.impl.StdSchedulerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+class QuartzConfiguration {
+
+ @Bean
+ Scheduler provideScheduler() throws SchedulerException {
+ StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
+ Scheduler scheduler = stdSchedulerFactory.getScheduler();
+ scheduler.start();
+ return scheduler;
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfig.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfig.java
new file mode 100644
index 0000000..0baa477
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfig.java
@@ -0,0 +1,49 @@
+/*
+ * ============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.pnfsimulator.simulatorconfig;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Field;
+
+import javax.validation.constraints.NotNull;
+import java.net.URL;
+
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+@ToString
+public class SimulatorConfig {
+
+ @JsonIgnore
+ @Id
+ private String id;
+
+ @NotNull
+ @Field("vesServerUrl")
+ private URL vesServerUrl;
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfigRepository.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfigRepository.java
new file mode 100644
index 0000000..5e63ee4
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfigRepository.java
@@ -0,0 +1,26 @@
+/*
+ * ============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.pnfsimulator.simulatorconfig;
+
+import org.springframework.data.mongodb.repository.MongoRepository;
+
+public interface SimulatorConfigRepository extends MongoRepository<SimulatorConfig, String> {
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfigService.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfigService.java
new file mode 100644
index 0000000..2063351
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfigService.java
@@ -0,0 +1,52 @@
+/*
+ * ============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.pnfsimulator.simulatorconfig;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class SimulatorConfigService {
+
+ private final SimulatorConfigRepository repository;
+
+ @Autowired
+ public SimulatorConfigService(SimulatorConfigRepository repository) {
+ this.repository = repository;
+ }
+
+
+ public SimulatorConfig getConfiguration() {
+ List<SimulatorConfig> configs = repository.findAll();
+ if (configs.isEmpty()) {
+ throw new IllegalStateException("No configuration found in db");
+ }
+ return configs.get(0);
+ }
+
+ public SimulatorConfig updateConfiguration(SimulatorConfig configuration) {
+ SimulatorConfig currentConfig = getConfiguration();
+ currentConfig.setVesServerUrl(configuration.getVesServerUrl());
+ return repository.save(currentConfig);
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/FsToDbTemplateSynchronizer.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/FsToDbTemplateSynchronizer.java
new file mode 100644
index 0000000..881585b
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/FsToDbTemplateSynchronizer.java
@@ -0,0 +1,74 @@
+/*-
+ * ============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.pnfsimulator.template;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.stream.Stream;
+
+import org.bson.json.JsonParseException;
+import org.onap.pnfsimulator.db.Storage;
+import org.onap.pnfsimulator.filesystem.WatcherEventProcessor;
+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.stereotype.Service;
+
+@Service
+public class FsToDbTemplateSynchronizer {
+
+ private static final String CANNOT_SYNC = "Cannot synchronize templates. Check whether the proper folder exists.";
+ private static final Logger LOGGER = LoggerFactory.getLogger(FsToDbTemplateSynchronizer.class);
+
+ private final String templatesDir;
+ private final Storage<Template> storage;
+
+ @Autowired
+ public FsToDbTemplateSynchronizer(@Value("${templates.dir}") String templatesDir,
+ Storage<Template> storage) {
+ this.templatesDir = templatesDir;
+ this.storage = storage;
+ }
+
+ public void synchronize() {
+ try {
+ processTemplatesFolder();
+ } catch (IOException e) {
+ LOGGER.error(CANNOT_SYNC, e);
+ }
+ }
+
+ private void processTemplatesFolder() throws IOException {
+ try (Stream<Path> walk = Files.walk(Paths.get(templatesDir))) {
+ walk.filter(Files::isRegularFile).forEach(path -> {
+ try {
+ WatcherEventProcessor.MODIFIED.processEvent(path, storage);
+ } catch (IOException | JsonParseException e) {
+ LOGGER
+ .error("Cannot synchronize template: " + path.getFileName().toString(), e);
+ }
+ });
+ }
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/Template.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/Template.java
new file mode 100644
index 0000000..c84b8d0
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/Template.java
@@ -0,0 +1,92 @@
+/*-
+ * ============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.pnfsimulator.template;
+
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import org.onap.pnfsimulator.db.Row;
+import org.bson.Document;
+import org.onap.pnfsimulator.template.search.JsonUtils;
+import org.springframework.data.mongodb.core.mapping.Field;
+
+@NoArgsConstructor
+@ToString
+public class Template extends Row {
+
+ @Field("content")
+ private Document content;
+
+ @Field("flatContent")
+ private Document flatContent;
+
+ @Field("lmod")
+ private long lmod;
+
+ public Template(String name, Document content, long lmod) {
+ this.id = name;
+ this.content = content;
+ this.lmod = lmod;
+ this.flatContent = new JsonUtils().flatten(content);
+ }
+
+ public Template(String name, String template, long lmod) {
+ this.id = name;
+ this.content = Document.parse(template);
+ this.lmod = lmod;
+ this.flatContent = new JsonUtils().flatten(this.content);
+ }
+
+ public void setContent(Document content) {
+ this.content = content;
+ this.flatContent = new JsonUtils().flatten(content);
+ }
+
+ public Document getContent() {
+ return new Document(this.content);
+ }
+
+ @JsonIgnore
+ public Document getFlatContent() {
+ return new Document(this.flatContent);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Template template = (Template) o;
+ return Objects.equals(content, template.content)
+ && Objects.equals(id, template.id)
+ && Objects.equals(lmod, template.lmod);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(content, id);
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/TemplateRepository.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/TemplateRepository.java
new file mode 100644
index 0000000..78c9c77
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/TemplateRepository.java
@@ -0,0 +1,26 @@
+/*-
+ * ============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.pnfsimulator.template;
+
+import org.springframework.data.mongodb.repository.MongoRepository;
+
+public interface TemplateRepository extends MongoRepository<Template, String> {
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/TemplateService.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/TemplateService.java
new file mode 100644
index 0000000..3e245e1
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/TemplateService.java
@@ -0,0 +1,81 @@
+/*-
+ * ============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.pnfsimulator.template;
+
+import java.util.List;
+import java.util.Optional;
+
+import com.google.gson.JsonObject;
+import org.onap.pnfsimulator.db.Storage;
+import org.onap.pnfsimulator.template.search.TemplateSearchHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Service;
+
+@Primary
+@Service
+public class TemplateService implements Storage<Template> {
+
+ private final TemplateRepository templateRepository;
+ private TemplateSearchHelper searchHelper;
+
+
+ @Autowired
+ public TemplateService(TemplateRepository templateRepository, TemplateSearchHelper searchHelper) {
+ this.templateRepository = templateRepository;
+ this.searchHelper = searchHelper;
+ }
+
+ @Override
+ public List<Template> getAll() {
+ return templateRepository.findAll();
+ }
+
+ @Override
+ public Optional<Template> get(String name) {
+ return templateRepository.findById(name);
+ }
+
+ @Override
+ public void persist(Template template) {
+ templateRepository.save(template);
+ }
+
+ @Override
+ public boolean tryPersistOrOverwrite(Template template, boolean overwrite) {
+ if (templateRepository.existsById(template.getId()) && !overwrite) {
+ return false;
+ }
+ templateRepository.save(template);
+ return true;
+ }
+
+ @Override
+ public void delete(String templateName) {
+ templateRepository.deleteById(templateName);
+ }
+
+ @Override
+ public List<String> getIdsByContentCriteria(JsonObject stringQueryJson) {
+ return searchHelper.getIdsOfDocumentMatchingCriteria(stringQueryJson);
+ }
+
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/IllegalJsonValueException.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/IllegalJsonValueException.java
new file mode 100644
index 0000000..6890382
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/IllegalJsonValueException.java
@@ -0,0 +1,28 @@
+/*-
+ * ============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.pnfsimulator.template.search;
+
+public class IllegalJsonValueException extends IllegalArgumentException {
+
+ IllegalJsonValueException(String s) {
+ super(s);
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/JsonUtils.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/JsonUtils.java
new file mode 100644
index 0000000..b595b4f
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/JsonUtils.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.pnfsimulator.template.search;
+
+import com.google.common.base.Strings;
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import org.bson.Document;
+
+/**
+ * This util flattens nested json and produces json with keys transformed to form of json path
+ * where default separator between parent object key and object key is ':'
+ * For easing searching of boolean values, they are converted to its string representation
+ */
+public class JsonUtils {
+
+ private static final String DEFAULT_PARENT_KEY_TO_OBJECT_KEY_SEPARATOR = ":";
+ private static final String SEED_PREFIX = "";
+ private static final Gson GSON = new Gson();
+
+ public JsonObject flatten(JsonObject original) {
+ return flattenWithPrefixedKeys(DEFAULT_PARENT_KEY_TO_OBJECT_KEY_SEPARATOR, original.deepCopy(), SEED_PREFIX, new JsonObject());
+ }
+
+ public JsonObject flatten(String parentKeyToKeySeparator, JsonObject original) {
+ return flattenWithPrefixedKeys(parentKeyToKeySeparator, original.deepCopy(), SEED_PREFIX, new JsonObject());
+ }
+
+ public Document flatten(Document original) {
+ return flatten(DEFAULT_PARENT_KEY_TO_OBJECT_KEY_SEPARATOR, original);
+ }
+
+ public Document flatten(String parentKeyToKeySeparator, Document original) {
+ JsonObject originalJsonObject = GSON.fromJson(original.toJson(), JsonObject.class);
+ JsonObject flattenedJson = flatten(parentKeyToKeySeparator, originalJsonObject);
+ return Document.parse(flattenedJson.toString());
+ }
+
+ private JsonObject flattenWithPrefixedKeys(String parentKeyToKeySeparator, JsonElement topLevelElem, String prefix, JsonObject acc) {
+ if (topLevelElem.isJsonPrimitive()) {
+ handleJsonPrimitive(topLevelElem, prefix, acc);
+ } else if (topLevelElem.isJsonArray()) {
+ handleJsonArray(parentKeyToKeySeparator, topLevelElem, prefix, acc);
+ } else if (topLevelElem.isJsonObject()) {
+ handleJsonObject(parentKeyToKeySeparator, topLevelElem, prefix, acc);
+ } else {
+ acc.add(prefix, topLevelElem.getAsJsonNull());
+ }
+ return acc.deepCopy();
+ }
+
+ private void handleJsonObject(String parentKeyToKeySeparator, JsonElement topLevelElem, String prefix, JsonObject acc) {
+ boolean isEmpty = true;
+ JsonObject thisToplevelObj = topLevelElem.getAsJsonObject();
+ for (String key : thisToplevelObj.keySet()) {
+ isEmpty = false;
+ String keyPrefix = String.format("%s%s%s", prefix, parentKeyToKeySeparator, key);
+ flattenWithPrefixedKeys(parentKeyToKeySeparator, thisToplevelObj.get(key), keyPrefix, acc);
+ }
+ if (isEmpty && !Strings.isNullOrEmpty(prefix)) {
+ acc.add(prefix, new JsonObject());
+ }
+ }
+
+ private void handleJsonArray(String parentKeyToKeySeparator, JsonElement topLevelElem, String prefix, JsonObject acc) {
+ JsonArray asJsonArray = topLevelElem.getAsJsonArray();
+ if (asJsonArray.size() == 0) {
+ acc.add(prefix, new JsonArray());
+ }
+ for (int i = 0; i < asJsonArray.size(); i++) {
+ flattenWithPrefixedKeys(parentKeyToKeySeparator, asJsonArray.get(i), String.format("%s[%s]", prefix, i), acc);
+ }
+ }
+
+ private void handleJsonPrimitive(JsonElement topLevelElem, String prefix, JsonObject acc) {
+ JsonPrimitive jsonPrimitive = topLevelElem.getAsJsonPrimitive();
+ if (jsonPrimitive.isBoolean()) {
+ acc.add(prefix, new JsonPrimitive(jsonPrimitive.getAsString()));
+ } else {
+ acc.add(prefix, topLevelElem.getAsJsonPrimitive());
+ }
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/TemplateSearchHelper.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/TemplateSearchHelper.java
new file mode 100644
index 0000000..3f22b1a
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/TemplateSearchHelper.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.pnfsimulator.template.search;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import org.onap.pnfsimulator.template.search.handler.PrimitiveValueCriteriaBuilder;
+import org.onap.pnfsimulator.template.search.viewmodel.FlatTemplateContent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+@Component
+public class TemplateSearchHelper {
+ private static final String PARENT_TO_CHILD_KEY_SEPARATOR = ":"; //compliant with flat json stored in db
+ private static final String FLATTENED_JSON_KEY_REGEX = PARENT_TO_CHILD_KEY_SEPARATOR + "%s(?:(\\[[\\d]+\\]))?$";
+ private static final String FLATTENED_TEMPLATES_VIEW = "flatTemplatesView";
+
+ private MongoTemplate mongoTemplate;
+ private PrimitiveValueCriteriaBuilder criteriaBuilder;
+
+ @Autowired
+ public TemplateSearchHelper(MongoTemplate mongoTemplate) {
+ this.mongoTemplate = mongoTemplate;
+ this.criteriaBuilder = new PrimitiveValueCriteriaBuilder();
+ }
+
+ public List<String> getIdsOfDocumentMatchingCriteria(JsonObject jsonCriteria) {
+ if (isNullValuePresentInCriteria(jsonCriteria)) {
+ throw new IllegalJsonValueException("Null values in search criteria are not supported.");
+ }
+ Criteria mongoDialectCriteria = composeCriteria(jsonCriteria);
+ Query query = new Query(mongoDialectCriteria);
+ List<FlatTemplateContent> flatTemplateContents = mongoTemplate.find(query, FlatTemplateContent.class, FLATTENED_TEMPLATES_VIEW);
+ return flatTemplateContents
+ .stream()
+ .map(FlatTemplateContent::getId)
+ .collect(Collectors.toList());
+ }
+
+
+ private Criteria composeCriteria(JsonObject criteria) {
+ Criteria[] criteriaArr = criteria.entrySet()
+ .stream()
+ .map(this::mapEntryCriteriaWithRegex)
+ .toArray(Criteria[]::new);
+ return criteriaArr.length > 0 ? new Criteria().andOperator(criteriaArr) : new Criteria();
+ }
+
+ private Criteria mapEntryCriteriaWithRegex(Map.Entry<String, JsonElement> entry) {
+ Pattern primitiveOrArrayElemKeyRegex = getCaseInsensitive(String.format(FLATTENED_JSON_KEY_REGEX, entry.getKey()));
+ Criteria criteriaForJsonKey = Criteria.where("k").regex(primitiveOrArrayElemKeyRegex);
+ Criteria criteriaWithValue = criteriaBuilder.applyValueCriteriaBasedOnPrimitiveType(criteriaForJsonKey.and("v"), entry.getValue().getAsJsonPrimitive());
+ return Criteria.where("keyValues").elemMatch(criteriaWithValue);
+
+ }
+
+ private boolean isNullValuePresentInCriteria(JsonObject jsonObject) {
+ return jsonObject.entrySet()
+ .stream()
+ .map(Map.Entry::getValue)
+ .anyMatch(JsonElement::isJsonNull);
+ }
+
+ static Pattern getCaseInsensitive(String base) {
+ return Pattern.compile(base, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
+ }
+}
+
+
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/handler/PrimitiveValueCriteriaBuilder.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/handler/PrimitiveValueCriteriaBuilder.java
new file mode 100644
index 0000000..79d64b7
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/handler/PrimitiveValueCriteriaBuilder.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.pnfsimulator.template.search.handler;
+
+import com.google.common.collect.Lists;
+import com.google.gson.JsonPrimitive;
+import org.springframework.data.mongodb.core.query.Criteria;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * This class is a helper class for constructing apropriate criteria for query send to mongodb based on type of value.
+ * Query is build to search mongodb for templates that contains key-value pairs that satisfy given criteria.
+ * Value is oftype JsonPrimitive, based on its primitive java type following criteria are build to get proper document:
+ * -for string - there is a regex expression that ignores every meta character inside passed argument and searches for exact literal match ignoring case;
+ * -for number - all numbers are treated as double (mongodb number type equivalent)
+ * -for boolean - exact match, used string representation of boolean in search
+ **/
+
+public class PrimitiveValueCriteriaBuilder {
+
+ private final List<ValueTypeHandler> typeHandlers;
+
+ public PrimitiveValueCriteriaBuilder() {
+ typeHandlers = Lists.newArrayList(new StringValueHandler(), new NumberValueHandler(), new BoolValueHandler());
+ }
+
+ public Criteria applyValueCriteriaBasedOnPrimitiveType(Criteria baseCriteria, JsonPrimitive jsonPrimitive) {
+ ValueTypeHandler typeHandler = typeHandlers.stream()
+ .filter(el -> el.isProperTypeHandler(jsonPrimitive))
+ .findFirst()
+ .orElseThrow(() ->
+ new IllegalArgumentException(String.format(
+ "Expected json primitive, but given value: %s is of type: %s and could not be decoded",
+ jsonPrimitive, jsonPrimitive.getClass().toString())));
+ return typeHandler.chainCriteriaForValue(baseCriteria, jsonPrimitive);
+ }
+
+ private interface ValueTypeHandler {
+ boolean isProperTypeHandler(JsonPrimitive value);
+
+ Criteria chainCriteriaForValue(Criteria criteria, JsonPrimitive value);
+ }
+
+ private class BoolValueHandler implements ValueTypeHandler {
+ public boolean isProperTypeHandler(JsonPrimitive value) {
+ return value.isBoolean();
+ }
+
+ public Criteria chainCriteriaForValue(Criteria criteria, JsonPrimitive value) {
+ return criteria.is(value.getAsString());
+ }
+
+ }
+
+ private class NumberValueHandler implements ValueTypeHandler {
+ public boolean isProperTypeHandler(JsonPrimitive value) {
+ return value.isNumber();
+ }
+
+ public Criteria chainCriteriaForValue(Criteria baseCriteria, JsonPrimitive value) {
+ return baseCriteria.is(value.getAsDouble());
+ }
+ }
+
+ private class StringValueHandler implements ValueTypeHandler {
+ public boolean isProperTypeHandler(JsonPrimitive value) {
+ return value.isString();
+ }
+
+ public Criteria chainCriteriaForValue(Criteria baseCriteria, JsonPrimitive value) {
+ return baseCriteria.regex(makeRegexCaseInsensitive(value.getAsString()));
+ }
+
+ private Pattern makeRegexCaseInsensitive(String base) {
+ String metaCharEscaped = convertToIgnoreMetaChars(base);
+ return Pattern.compile("^" + metaCharEscaped + "$", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
+ }
+
+ private String convertToIgnoreMetaChars(String valueWithMetaChars) {
+ return Pattern.quote(valueWithMetaChars);
+ }
+ }
+}
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/viewmodel/FlatTemplateContent.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/viewmodel/FlatTemplateContent.java
new file mode 100644
index 0000000..84235f7
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/viewmodel/FlatTemplateContent.java
@@ -0,0 +1,45 @@
+/*-
+ * ============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.pnfsimulator.template.search.viewmodel;
+
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import org.onap.pnfsimulator.db.Row;
+
+import java.util.List;
+
+@Getter
+@NoArgsConstructor
+@ToString
+public class FlatTemplateContent extends Row {
+
+ private List<KeyValuePair> keyValues;
+
+
+ public FlatTemplateContent(String name, List<KeyValuePair> keyValues) {
+ this.id = name;
+ this.keyValues = keyValues;
+ }
+}
+
+
diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/viewmodel/KeyValuePair.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/viewmodel/KeyValuePair.java
new file mode 100644
index 0000000..5e44452
--- /dev/null
+++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/template/search/viewmodel/KeyValuePair.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.pnfsimulator.template.search.viewmodel;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+
+@Getter
+@ToString
+@NoArgsConstructor
+@AllArgsConstructor
+/**
+ * POJO for mongo structure after $objectToArray mapping where object consists of fields: k and v
+ */
+public class KeyValuePair {
+
+ private String k;
+ private String v;
+
+}
diff --git a/pnfsimulator/src/main/resources/application.properties b/pnfsimulator/src/main/resources/application.properties
new file mode 100644
index 0000000..e2c7639
--- /dev/null
+++ b/pnfsimulator/src/main/resources/application.properties
@@ -0,0 +1,18 @@
+server.port=5000
+templates.dir=/app/templates
+spring.data.mongodb.host=mongo
+spring.data.mongodb.port=27017
+spring.data.mongodb.username=pnf_simulator_user
+spring.data.mongodb.password=zXcVbN123!
+spring.data.mongodb.database=pnf_simulator
+management.endpoints.enabled-by-default=true
+management.endpoint.configprops.enabled=true
+management.endpoints.web.base-path=/
+management.server.port=5001
+management.endpoints.web.exposure.include=refresh
+
+ssl.clientCertificateEnabled=true
+ssl.clientCertificateDir=/app/store/client.p12
+ssl.clientCertificatePassword=collector
+ssl.trustStoreDir=/app/store/trustStore
+ssl.trustStorePassword=collector
diff --git a/pnfsimulator/src/main/resources/logback.xml b/pnfsimulator/src/main/resources/logback.xml
new file mode 100644
index 0000000..8569b56
--- /dev/null
+++ b/pnfsimulator/src/main/resources/logback.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============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=========================================================
+ -->
+
+<Configuration complete="true" compact="true">
+
+ <Property name="outputFilename" value="pnfsimulator_output"/>
+ <Property name="log-path" value="/var/log/ONAP/pnfsimulator"/>
+ <Property name="archive" value="/var/log/ONAP/pnfsimulator/archive"/>
+ <property name="maxFileSize" value="50MB"/>
+ <property name="maxHistory" value="30"/>
+ <property name="totalSizeCap" value="10GB"/>
+
+ <appender name="Console" target="SYSTEM_OUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <Pattern>%nopexception%logger
+ |%date{yyyy-MM-dd'T'HH:mm:ss.SSSXXX,UTC}
+ |%level
+ |%replace(%replace(%message){'\t','\\\\t'}){'\n','\\\\n'}
+ |%replace(%replace(%mdc){'\t','\\\\t'}){'\n','\\\\n'}
+ |%replace(%replace(%rootException){'\t','\\\\t'}){'\n','\\\\n'}
+ |%replace(%replace(%marker){'\t','\\\\t'}){'\n','\\\\n'}
+ |%thread
+ |%n</Pattern>
+ </encoder>
+ </appender>
+
+ <appender name="ROLLING-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <encoder>
+ <pattern>%nopexception%logger
+ |%date{yyyy-MM-dd'T'HH:mm:ss.SSSXXX,UTC}
+ |%level
+ |%replace(%replace(%message){'\t','\\\\t'}){'\n','\\\\n'}
+ |%replace(%replace(%mdc){'\t','\\\\t'}){'\n','\\\\n'}
+ |%replace(%replace(%rootException){'\t','\\\\t'}){'\n','\\\\n'}
+ |%replace(%replace(%marker){'\t','\\\\t'}){'\n','\\\\n'}
+ |%thread
+ |%n</pattern>
+ </encoder>
+ <File>${log-path}/${outputFilename}.log</File>
+ <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <FileNamePattern>${archive}/${outputFilename}.%d{yyyy-MM-dd}.%i.log.zip</FileNamePattern>
+ <MaxFileSize>${maxFileSize}</MaxFileSize>
+ <MaxHistory>${maxHistory}</MaxHistory>
+ <TotalSizeCap>${totalSizeCap}</TotalSizeCap>
+ </rollingPolicy>
+ </appender>
+
+ <root level="info">
+ <appender-ref ref="Console" />
+ <appender-ref ref="ROLLING-FILE" />
+ </root>
+</Configuration>
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/event/EventDataServiceTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/event/EventDataServiceTest.java
new file mode 100644
index 0000000..5ed51cc
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/event/EventDataServiceTest.java
@@ -0,0 +1,133 @@
+/*
+ * ============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.pnfsimulator.event;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import org.hamcrest.collection.IsIterableContainingInOrder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+public class EventDataServiceTest {
+
+ @Mock
+ private EventDataRepository repositoryMock;
+
+ @InjectMocks
+ private EventDataService service;
+
+ private static EventData sampleEventData(String id, String template,
+ String patched, String input, String keywords) {
+ return EventData.builder()
+ .id(id)
+ .template(template)
+ .patched(patched)
+ .input(input)
+ .keywords(keywords)
+ .build();
+ }
+
+ @BeforeEach
+ void resetMocks() {
+ initMocks(this);
+ }
+
+ @Test
+ void persistEventDataJsonObjectTest() {
+ JsonParser parser = new JsonParser();
+ JsonObject template = parser.parse("{ \"bla1\": \"bla2\"}").getAsJsonObject();
+ JsonObject patched = parser.parse("{ \"bla3\": \"bla4\"}").getAsJsonObject();
+ JsonObject input = parser.parse("{ \"bla5\": \"bla6\"}").getAsJsonObject();
+ JsonObject keywords = parser.parse("{ \"bla7\": \"bla8\"}").getAsJsonObject();
+ ArgumentCaptor<EventData> argumentCaptor = ArgumentCaptor.forClass(EventData.class);
+
+ service.persistEventData(template, patched, input, keywords);
+
+ verify(repositoryMock).save(argumentCaptor.capture());
+ EventData captured = argumentCaptor.getValue();
+
+ assertEquals(captured.getTemplate(), template.toString());
+ assertEquals(captured.getPatched(), patched.toString());
+ assertEquals(captured.getInput(), input.toString());
+ assertEquals(captured.getKeywords(), keywords.toString());
+ }
+
+ @Test
+ void getAllEventsTest() {
+
+ List<EventData> eventDataList = new ArrayList<>();
+ EventData ed1 = sampleEventData("id1", "t1", "p1", "i1", "k1");
+ EventData ed2 = sampleEventData("id2", "t2", "p2", "i2", "k2");
+ eventDataList.add(ed1);
+ eventDataList.add(ed2);
+
+ when(repositoryMock.findAll()).thenReturn(eventDataList);
+ List<EventData> actualList = service.getAllEvents();
+
+ assertEquals(eventDataList.size(), actualList.size());
+ assertThat(actualList, IsIterableContainingInOrder.contains(ed1, ed2));
+ }
+
+ @Test
+ void findByIdPresentTest() {
+ String id = "some_object";
+ EventData eventData = sampleEventData(id, "template", "patched", "input", "keywords");
+ Optional<EventData> optional = Optional.of(eventData);
+
+ when(repositoryMock.findById(id)).thenReturn(optional);
+
+ Optional<EventData> actualOptional = service.getById(id);
+ assertTrue(actualOptional.isPresent());
+ EventData actualObject = actualOptional.get();
+ assertEquals(eventData.getId(), actualObject.getId());
+ assertEquals(eventData.getTemplate(), actualObject.getTemplate());
+ assertEquals(eventData.getPatched(), actualObject.getPatched());
+ assertEquals(eventData.getInput(), actualObject.getInput());
+ assertEquals(eventData.getKeywords(), actualObject.getKeywords());
+
+ }
+
+ @Test
+ void findByIdNotPresentTest() {
+ String id = "some_object";
+ Optional<EventData> optional = Optional.empty();
+
+ when(repositoryMock.findById(id)).thenReturn(optional);
+
+ Optional<EventData> actualOptional = service.getById(id);
+ assertTrue(!actualOptional.isPresent());
+ }
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/filesystem/InMemoryTemplateStorage.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/filesystem/InMemoryTemplateStorage.java
new file mode 100644
index 0000000..98c4bc5
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/filesystem/InMemoryTemplateStorage.java
@@ -0,0 +1,71 @@
+/*-
+ * ============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.pnfsimulator.filesystem;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import com.google.gson.JsonObject;
+import org.onap.pnfsimulator.db.Storage;
+import org.onap.pnfsimulator.template.Template;
+
+public class InMemoryTemplateStorage implements Storage<Template> {
+
+ private List<Template> storage = new ArrayList<>();
+
+ @Override
+ public List<Template> getAll() {
+ return new ArrayList<>(storage);
+ }
+
+ @Override
+ public Optional<Template> get(String name) {
+ return storage.stream().filter(template -> template.getId().equals(name)).findFirst();
+ }
+
+ @Override
+ public void persist(Template template) {
+ if (!storage.contains(template)){
+ storage.add(template);
+ }
+ }
+
+ @Override
+ public boolean tryPersistOrOverwrite(Template template, boolean overwrite) {
+ if (!storage.contains(template) || overwrite){
+ storage.add(template);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void delete(String templateName) {
+ get(templateName).ifPresent(template -> storage.remove(template));
+ }
+
+ @Override
+ public List<String> getIdsByContentCriteria(JsonObject queryJson) {
+ throw new RuntimeException("Method is not implemented.");
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/filesystem/WatcherEventProcessorTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/filesystem/WatcherEventProcessorTest.java
new file mode 100644
index 0000000..42ed4d3
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/filesystem/WatcherEventProcessorTest.java
@@ -0,0 +1,124 @@
+/*-
+ * ============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.pnfsimulator.filesystem;
+
+import static junit.framework.TestCase.fail;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Optional;
+import org.bson.Document;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.onap.pnfsimulator.db.Storage;
+import org.onap.pnfsimulator.template.Template;
+
+class WatcherEventProcessorTest {
+
+ @Mock
+ private WatchEvent watchEvent;
+ @Mock
+ private Path templatesDir;
+
+ private Storage<Template> storage;
+ private static Path jsonFilePath;
+
+ @BeforeAll
+ static void init() {
+ jsonFilePath = Paths.get("src/test/resources/org/onap/pnfsimulator/simulator/filesystem/test1.json");
+ }
+
+ @BeforeEach
+ void resetMocks() {
+ initMocks(this);
+ storage = new InMemoryTemplateStorage();
+ initStubs();
+ }
+
+ @Test
+ void shouldProcessCreatedEventTest() {
+ // when
+ Mockito.when(watchEvent.kind()).thenReturn(StandardWatchEventKinds.ENTRY_CREATE);
+ WatcherEventProcessor.process(watchEvent, storage, templatesDir);
+ // then
+ verifyPersistedValue();
+ }
+
+ @Test
+ void shouldProcessModifiedEventTest() {
+ //given
+ storage.persist(new Template("test1.json", new Document(Collections.emptyMap()), Instant.now().getNano()));
+ // when
+ Mockito.when(watchEvent.kind()).thenReturn(StandardWatchEventKinds.ENTRY_MODIFY);
+ WatcherEventProcessor.process(watchEvent, storage, templatesDir);
+ // then
+ verifyPersistedValue();
+ }
+
+ private void verifyPersistedValue() {
+ Assertions.assertEquals(storage.getAll().size(), 1);
+ Optional<Template> templateFromStorage = storage.get("test1.json");
+ if (templateFromStorage.isPresent()) {
+ Template retrievedTemplate = templateFromStorage.get();
+ Document templateContent = retrievedTemplate.getContent();
+ Document flatContent = retrievedTemplate.getFlatContent();
+ Assertions.assertEquals(templateContent.getString("field1"), "value1");
+ Assertions.assertEquals(templateContent.getInteger("field2", 0), 2);
+ Assertions.assertEquals(flatContent.getInteger(":nested:key1[0]", 0), 1);
+ Assertions.assertEquals(flatContent.getInteger(":nested:key1[1]", 0), 2);
+ Assertions.assertEquals(flatContent.getInteger(":nested:key1[2]", 0), 3);
+ Assertions.assertEquals(flatContent.getString(":nested:key2"), "sampleValue2");
+ } else {
+ fail();
+ }
+ }
+
+ @Test
+ void shouldProcessDeletedEventTest() {
+ //given
+ HashMap<String, Object> legacyObject = new HashMap<>();
+ legacyObject.put("field1", "value1");
+ legacyObject.put("field2", 2);
+
+ storage.persist(new Template("test1.json", new Document(legacyObject), Instant.now().getNano()));
+ // when
+ Mockito.when(watchEvent.kind()).thenReturn(StandardWatchEventKinds.ENTRY_DELETE);
+ WatcherEventProcessor.process(watchEvent, storage, templatesDir);
+ // then
+ Assertions.assertEquals(storage.getAll().size(), 0);
+ }
+
+ private void initStubs() {
+ Mockito.when(templatesDir.resolve(jsonFilePath)).thenReturn(jsonFilePath);
+ Mockito.when(watchEvent.context()).thenReturn(jsonFilePath);
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/SimulatorControllerTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/SimulatorControllerTest.java
new file mode 100644
index 0000000..dae16c7
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/SimulatorControllerTest.java
@@ -0,0 +1,212 @@
+/*
+ * ============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.pnfsimulator.rest;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.onap.pnfsimulator.rest.model.FullEvent;
+import org.onap.pnfsimulator.rest.model.SimulatorParams;
+import org.onap.pnfsimulator.rest.model.SimulatorRequest;
+import org.onap.pnfsimulator.rest.util.JsonObjectDeserializer;
+import org.onap.pnfsimulator.simulator.SimulatorService;
+import org.onap.pnfsimulator.simulatorconfig.SimulatorConfig;
+import org.quartz.SchedulerException;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+class SimulatorControllerTest {
+
+ private static final String START_ENDPOINT = "/simulator/start";
+ private static final String CONFIG_ENDPOINT = "/simulator/config";
+ private static final String EVENT_ENDPOINT = "/simulator/event";
+ private static final String JSON_MSG_EXPRESSION = "$.message";
+
+ private static final String NEW_URL = "http://0.0.0.0:8090/eventListener/v7";
+ private static final String UPDATE_SIM_CONFIG_VALID_JSON = "{\"vesServerUrl\": \"" + NEW_URL + "\"}";
+ private static final String SAMPLE_ID = "sampleId";
+ private static final Gson GSON_OBJ = new Gson();
+ private static String simulatorRequestBody;
+ private MockMvc mockMvc;
+ @InjectMocks
+ private SimulatorController controller;
+ @Mock
+ private SimulatorService simulatorService;
+
+ @BeforeAll
+ static void beforeAll() {
+ SimulatorParams simulatorParams = new SimulatorParams("http://0.0.0.0:8080", 1, 1);
+ SimulatorRequest simulatorRequest = new SimulatorRequest(simulatorParams,
+ "testTemplate.json", new JsonObject());
+
+ simulatorRequestBody = GSON_OBJ.toJson(simulatorRequest);
+ }
+
+ @BeforeEach
+ void setup() throws IOException, SchedulerException, GeneralSecurityException {
+ MockitoAnnotations.initMocks(this);
+ when(simulatorService.triggerEvent(any())).thenReturn("jobName");
+ mockMvc = MockMvcBuilders
+ .standaloneSetup(controller)
+ .build();
+ }
+
+ @Test
+ void shouldStartSimulatorProperly() throws Exception {
+ startSimulator();
+ SimulatorRequest simulatorRequest = new Gson().fromJson(simulatorRequestBody, SimulatorRequest.class);
+
+ verify(simulatorService).triggerEvent(eq(simulatorRequest));
+ }
+
+ @Test
+ void testShouldGetConfigurationWhenRequested() throws Exception {
+ String newUrl = "http://localhost:8090/eventListener/v7";
+ SimulatorConfig expectedConfig = new SimulatorConfig(SAMPLE_ID, new URL(newUrl));
+ when(simulatorService.getConfiguration()).thenReturn(expectedConfig);
+
+ MvcResult getResult = mockMvc
+ .perform(get(CONFIG_ENDPOINT)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(UPDATE_SIM_CONFIG_VALID_JSON))
+ .andExpect(status().isOk())
+ .andReturn();
+
+ String expectedVesUrlJsonPart = createStringReprOfJson("vesServerUrl", newUrl);
+ assertThat(getResult.getResponse().getContentAsString()).contains(expectedVesUrlJsonPart);
+ }
+
+ @Test
+ void testShouldSuccessfullyUpdateConfigurationWithNewVesUrl() throws Exception {
+ String oldUrl = "http://localhost:8090/eventListener/v7";
+ SimulatorConfig expectedConfigBeforeUpdate = new SimulatorConfig(SAMPLE_ID, new URL(oldUrl));
+ SimulatorConfig expectedConfigAfterUpdate = new SimulatorConfig(SAMPLE_ID, new URL(NEW_URL));
+
+ when(simulatorService.getConfiguration()).thenReturn(expectedConfigBeforeUpdate);
+ when(simulatorService.updateConfiguration(any(SimulatorConfig.class))).thenReturn(expectedConfigAfterUpdate);
+
+ MvcResult postResult = mockMvc
+ .perform(put(CONFIG_ENDPOINT)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(UPDATE_SIM_CONFIG_VALID_JSON))
+ .andExpect(status().isOk())
+ .andReturn();
+
+ String expectedVesUrlJsonPart = createStringReprOfJson("vesServerUrl", expectedConfigAfterUpdate.getVesServerUrl().toString());
+ assertThat(postResult.getResponse().getContentAsString()).contains(expectedVesUrlJsonPart);
+ }
+
+ @Test
+ void testShouldRaiseExceptionWhenUpdateConfigWithIncorrectPayloadWasSent() throws Exception {
+ mockMvc
+ .perform(put(CONFIG_ENDPOINT)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("{\"vesUrl\": \"" + NEW_URL + "\"}"))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ void testShouldRaiseExceptionWhenUrlInInvalidFormatIsSent() throws Exception {
+ mockMvc
+ .perform(put(CONFIG_ENDPOINT)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("{\"vesUrl\": \"http://0.0.0.0:VES-PORT/eventListener/v7\"}"))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ void testShouldSendEventDirectly() throws Exception {
+ String contentAsString = mockMvc
+ .perform(post(EVENT_ENDPOINT)
+ .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
+ .content("{\"vesServerUrl\":\"http://0.0.0.0:8080/simulator/v7\",\n" +
+ " \"event\":{ \n" +
+ " \"commonEventHeader\":{ \n" +
+ " \"domain\":\"notification\",\n" +
+ " \"eventName\":\"vFirewallBroadcastPackets\"\n" +
+ " },\n" +
+ " \"notificationFields\":{ \n" +
+ " \"arrayOfNamedHashMap\":[ \n" +
+ " { \n" +
+ " \"name\":\"A20161221.1031-1041.bin.gz\",\n" +
+ " \"hashMap\":{ \n" +
+ " \"fileformatType\":\"org.3GPP.32.435#measCollec\"}}]}}}"))
+ .andExpect(status().isAccepted()).andReturn().getResponse().getContentAsString();
+ assertThat(contentAsString).contains("One-time direct event sent successfully");
+ }
+
+ @Test
+ void testShouldReplaceKeywordsAndSendEventDirectly() throws Exception {
+ String contentAsString = mockMvc
+ .perform(post(EVENT_ENDPOINT)
+ .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
+ .content("{\"vesServerUrl\": \"http://localhost:9999/eventListener\",\n" +
+ " \"event\": {\n" +
+ " \"commonEventHeader\": {\n" +
+ " \"eventId\": \"#RandomString(20)\",\n" +
+ " \"sourceName\": \"PATCHED_sourceName\",\n" +
+ " \"version\": 3.0\n}}}"))
+ .andExpect(status().isAccepted()).andReturn().getResponse().getContentAsString();
+ assertThat(contentAsString).contains("One-time direct event sent successfully");
+
+ verify(simulatorService, Mockito.times(1)).triggerOneTimeEvent(any(FullEvent.class));
+ }
+
+
+ private void startSimulator() throws Exception {
+ mockMvc
+ .perform(post(START_ENDPOINT)
+ .content(simulatorRequestBody)
+ .contentType(MediaType.APPLICATION_JSON).characterEncoding("utf-8"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath(JSON_MSG_EXPRESSION).value("Request started"));
+
+ }
+
+ private String createStringReprOfJson(String key, String value) {
+ return GSON_OBJ.toJson(ImmutableMap.of(key, value));
+ }
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/TemplateControllerTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/TemplateControllerTest.java
new file mode 100644
index 0000000..f34d73c
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/TemplateControllerTest.java
@@ -0,0 +1,256 @@
+/*-
+ * ============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.pnfsimulator.rest;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.onap.pnfsimulator.rest.TemplateController.CANNOT_OVERRIDE_TEMPLATE_MSG;
+import static org.onap.pnfsimulator.rest.TemplateController.TEMPLATE_NOT_FOUND_MSG;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonObject;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import org.assertj.core.util.Lists;
+import org.bson.Document;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import static org.mockito.Mockito.times;
+import org.mockito.MockitoAnnotations;
+import org.onap.pnfsimulator.db.Storage;
+import org.onap.pnfsimulator.rest.model.SearchExp;
+import org.onap.pnfsimulator.template.Template;
+import org.onap.pnfsimulator.template.search.IllegalJsonValueException;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+class TemplateControllerTest {
+
+ private static final String LIST_URL = "/template/list";
+ private static final String GET_FORMAT_STR = "/template/get/%s";
+ private static final String SEARCH_ENDPOINT = "/template/search";
+ private static final String UPLOAD_URL_NOFORCE = "/template/upload";
+ private static final String UPLOAD_URL_FORCE = "/template/upload?override=true";
+ private static final String SAMPLE_TEMPLATE_JSON = "{\"event\": {\n"
+ + " \"commonEventHeader\": {\n"
+ + " \"domain\": \"measurementsForVfScaling\",\n"
+ + " \"eventName\": \"vFirewallBroadcastPackets\",\n"
+ + " }"
+ + "}}";
+
+ public static final String TEMPLATE_REQUEST = "{\n"
+ + " \"name\": \"someTemplate\",\n"
+ + " \"template\": {\n"
+ + " \"commonEventHeader\": {\n"
+ + " \"domain\": \"notification\",\n"
+ + " \"eventName\": \"vFirewallBroadcastPackets\"\n"
+ + " },\n"
+ + " \"notificationFields\": {\n"
+ + " \"arrayOfNamedHashMap\": [{\n"
+ + " \"name\": \"A20161221.1031-1041.bin.gz\",\n"
+ + "\n"
+ + " \"hashMap\": {\n"
+ + " \"fileformatType\": \"org.3GPP.32.435#measCollec\"\n"
+ + " }\n"
+ + " }]\n"
+ + " }\n"
+ + " }\n"
+ + "}";
+ private static final Document SAMPLE_TEMPLATE_BSON = Document.parse(SAMPLE_TEMPLATE_JSON);
+ private static final List<String> SAMPLE_TEMPLATE_NAME_LIST = Lists.newArrayList("notification.json", "registration.json");
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+ private static final Gson GSON_OBJ = new GsonBuilder().create();
+ private MockMvc mockMvc;
+
+ @Mock
+ private Storage<Template> templateService;
+ @InjectMocks
+ private TemplateController controller;
+
+ @BeforeEach
+ void setup() {
+ MockitoAnnotations.initMocks(this);
+ mockMvc = MockMvcBuilders
+ .standaloneSetup(controller)
+ .build();
+ }
+
+ @Test
+ void shouldGetAllTemplates() throws Exception {
+ List<Template> templateList = createTemplatesList();
+ when(templateService.getAll()).thenReturn(templateList);
+
+ MvcResult getResult = mockMvc
+ .perform(get(LIST_URL)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
+ .andReturn();
+
+ Type listType = new TypeToken<ArrayList<Template>>() {}.getType();
+ List<Template> resultList = GSON_OBJ.fromJson(getResult.getResponse().getContentAsString(), listType);
+ assertThat(resultList).containsExactlyInAnyOrderElementsOf(templateList);
+ }
+
+ @Test
+ void shouldListEmptyCollectionWhenNoTemplatesAvailable() throws Exception {
+ List<Template> templateList = Collections.emptyList();
+ when(templateService.getAll()).thenReturn(templateList);
+
+ MvcResult getResult = mockMvc
+ .perform(get(LIST_URL))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
+ .andReturn();
+
+ String templatesAsString = GSON_OBJ.toJson(templateList);
+ assertThat(getResult.getResponse().getContentAsString()).containsSequence(templatesAsString);
+ }
+
+ @Test
+ void shouldSuccessfullyGetExisitngTemplateByName() throws Exception {
+ String sampleTemplateName = "someTemplate";
+ String requestUrl = String.format(GET_FORMAT_STR, sampleTemplateName);
+ Template sampleTemplate = new Template(sampleTemplateName, SAMPLE_TEMPLATE_BSON, 0L);
+
+ when(templateService.get(sampleTemplateName)).thenReturn(Optional.of(sampleTemplate));
+
+ MvcResult getResult = mockMvc
+ .perform(get(requestUrl))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
+ .andReturn();
+
+ Template result = new Gson().fromJson(getResult.getResponse().getContentAsString(), Template.class);
+ assertThat(result).isEqualTo(sampleTemplate);
+ }
+
+ @Test
+ void shouldReturnNotFoundWhenGetNonExisitngTemplateByName() throws Exception {
+ String sampleTemplateName = "doesNotExist";
+ String requestUrl = String.format(GET_FORMAT_STR, sampleTemplateName);
+
+ when(templateService.get(sampleTemplateName)).thenReturn(Optional.empty());
+
+ MvcResult getResult = mockMvc
+ .perform(get(requestUrl))
+ .andExpect(status().isNotFound())
+ .andExpect(content().contentType(MediaType.TEXT_PLAIN_VALUE))
+ .andReturn();
+
+ assertThat(getResult.getResponse().getContentLength()).isEqualTo(TEMPLATE_NOT_FOUND_MSG.length());
+ }
+
+
+ @Test
+ void shouldReturnNamesOfTemplatesThatSatisfyGivenCriteria() throws Exception {
+ when(templateService.getIdsByContentCriteria(any(JsonObject.class))).thenReturn(SAMPLE_TEMPLATE_NAME_LIST);
+ SearchExp expr = new SearchExp(new JsonObject());
+
+ String responseContent = mockMvc
+ .perform(post(SEARCH_ENDPOINT).content(GSON_OBJ.toJson(expr)).contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
+ .andReturn().getResponse().getContentAsString();
+
+ List<String> actualTemplates = OBJECT_MAPPER.readValue(responseContent, new TypeReference<List<String>>() {});
+ verify(templateService, times(1)).getIdsByContentCriteria(any(JsonObject.class));
+ assertThat(actualTemplates).isEqualTo(SAMPLE_TEMPLATE_NAME_LIST);
+ }
+
+ @Test
+ void shouldRaiseBadRequestWhenNullValueProvidedInSearchJsonAsJsonValue() throws Exception {
+ when(templateService.getIdsByContentCriteria(any(JsonObject.class))).thenThrow(IllegalJsonValueException.class);
+ SearchExp expr = new SearchExp(new JsonObject());
+
+ mockMvc.perform(post(SEARCH_ENDPOINT)
+ .content(GSON_OBJ.toJson(expr))
+ .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
+ .andExpect(status().isBadRequest());
+ }
+
+
+ @Test
+ void testTryUploadNewTemplate() throws Exception {
+ when(templateService.tryPersistOrOverwrite(any(Template.class), eq(false))).thenReturn(true);
+
+ MvcResult postResult = mockMvc
+ .perform(post(UPLOAD_URL_NOFORCE)
+ .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
+ .content(TEMPLATE_REQUEST))
+ .andExpect(status().isCreated())
+ .andReturn();
+ }
+
+ @Test
+ void testTryUploadNewTemplateWithForce() throws Exception {
+ when(templateService.tryPersistOrOverwrite(any(Template.class), eq(true))).thenReturn(true);
+
+ MvcResult postResult = mockMvc
+ .perform(post(UPLOAD_URL_FORCE)
+ .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
+ .content(TEMPLATE_REQUEST))
+ .andExpect(status().isCreated())
+ .andReturn();
+ }
+
+ @Test
+ void testOverrideExistingTemplateWithoutForceShouldFail() throws Exception {
+ when(templateService.tryPersistOrOverwrite(any(Template.class), eq(true))).thenReturn(false);
+
+ MvcResult postResult = mockMvc
+ .perform(post(UPLOAD_URL_FORCE)
+ .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
+ .content(TEMPLATE_REQUEST))
+ .andExpect(status().isConflict())
+ .andReturn();
+
+ assertThat(postResult.getResponse().getContentAsString()).isEqualTo(CANNOT_OVERRIDE_TEMPLATE_MSG);
+ }
+
+ private List<Template> createTemplatesList() {
+ return Arrays.asList(
+ new Template("1", SAMPLE_TEMPLATE_BSON, 0L),
+ new Template("2", SAMPLE_TEMPLATE_BSON, 0L),
+ new Template("3", SAMPLE_TEMPLATE_BSON, 0L));
+ }
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/util/DateUtilTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/util/DateUtilTest.java
new file mode 100644
index 0000000..1591a59
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/util/DateUtilTest.java
@@ -0,0 +1,38 @@
+/*
+ * ============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.pnfsimulator.rest.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import org.junit.jupiter.api.Test;
+
+class DateUtilTest {
+
+ @Test
+ void getFormattedDate() {
+ Calendar currentCalendar = Calendar.getInstance();
+ String expectedResult = String.valueOf(currentCalendar.get(Calendar.YEAR));
+
+ assertEquals(expectedResult, DateUtil.getTimestamp(new SimpleDateFormat("yyyy")));
+ }
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/util/ResponseBuilderTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/util/ResponseBuilderTest.java
new file mode 100644
index 0000000..0d62ee9
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/util/ResponseBuilderTest.java
@@ -0,0 +1,65 @@
+/*
+ * ============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.pnfsimulator.rest.util;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+class ResponseBuilderTest {
+
+
+ private static final HttpStatus SAMPLE_STATUS = HttpStatus.OK;
+
+ @Test
+ void response_should_have_empty_body_when_built_immediately() {
+ ResponseEntity responseEntity = ResponseBuilder.status(SAMPLE_STATUS).build();
+
+ assertAll(
+ () -> assertEquals(responseEntity.getStatusCode(), SAMPLE_STATUS),
+ () -> assertNull(responseEntity.getBody())
+ );
+ }
+
+ @Test
+ void builder_should_set_response_status_and_body() {
+ String key = "key";
+ String value = "value";
+ ResponseEntity response = ResponseBuilder
+ .status(SAMPLE_STATUS)
+ .put(key, value)
+ .build();
+
+ Map<String, Object> body = (Map<String, Object>) response.getBody();
+
+ assertAll(
+ () -> assertEquals(SAMPLE_STATUS, response.getStatusCode()),
+ () -> assertEquals(value, body.get(key))
+ );
+ }
+
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/IncrementProviderImplTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/IncrementProviderImplTest.java
new file mode 100644
index 0000000..53f02da
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/IncrementProviderImplTest.java
@@ -0,0 +1,78 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Optional;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.onap.pnfsimulator.event.EventData;
+import org.onap.pnfsimulator.event.EventDataRepository;
+
+public class IncrementProviderImplTest {
+ private IncrementProvider incrementProvider;
+
+ @Mock
+ private EventDataRepository eventDataRepositoryMock;
+
+ @BeforeEach
+ void setUp() {
+ eventDataRepositoryMock = mock(EventDataRepository.class);
+ incrementProvider = new IncrementProviderImpl(eventDataRepositoryMock);
+ }
+
+ @Test
+ public void getAndIncrementTest() {
+ ArgumentCaptor<EventData> eventDataArgumentCaptor = ArgumentCaptor.forClass(EventData.class);
+ String eventId = "1";
+ int initialIncrementValue = 0;
+ int expectedValue = initialIncrementValue + 1;
+ EventData eventData = EventData.builder().id(eventId).incrementValue(initialIncrementValue).build();
+ Optional<EventData> optional = Optional.of(eventData);
+
+ when(eventDataRepositoryMock.findById(eventId)).thenReturn(optional);
+
+ int value = incrementProvider.getAndIncrement(eventId);
+
+ verify(eventDataRepositoryMock).save(eventDataArgumentCaptor.capture());
+
+ assertThat(value).isEqualTo(expectedValue);
+ assertThat(eventDataArgumentCaptor.getValue().getIncrementValue()).isEqualTo(expectedValue);
+
+ }
+
+ @Test
+ public void shouldThrowOnNonExistingEvent() {
+ Optional<EventData> emptyOptional = Optional.empty();
+ String nonExistingEventId = "THIS_DOES_NOT_EXIST";
+ when(eventDataRepositoryMock.findById(nonExistingEventId)).thenReturn(emptyOptional);
+
+ assertThrows(EventNotFoundException.class,
+ () -> incrementProvider.getAndIncrement(nonExistingEventId));
+ }
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorInvalidRandomIntegerTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorInvalidRandomIntegerTest.java
new file mode 100644
index 0000000..8198e95
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorInvalidRandomIntegerTest.java
@@ -0,0 +1,67 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeywordsExtractorInvalidRandomIntegerTest {
+
+ private final String keyword;
+ private KeywordsExtractor keywordsExtractor;
+
+ private static final Collection INVALID_INTEGER_KEYWORDS = Arrays.asList(new Object[][]{
+ {"#RandoInteger"},
+ {"#Randominteger(23,11)"},
+ {"#randomInteger(11,34)"},
+ {"#Random_Integer(11,13)"},
+ {"#RandomInteger(11)"},
+ {"RandomInteger(11)"},
+ {"RandomInteger"}
+ });
+
+ public KeywordsExtractorInvalidRandomIntegerTest(String keyword) {
+ this.keyword = keyword;
+ }
+
+ @Before
+ public void setUp() {
+ this.keywordsExtractor = new KeywordsExtractor();
+ }
+
+ @Parameterized.Parameters
+ public static Collection data() {
+ return INVALID_INTEGER_KEYWORDS;
+ }
+
+ @Test
+ public void checkValidRandomStringKeyword() {
+ assertEquals(keywordsExtractor.substituteStringKeyword(this.keyword, 1), this.keyword);
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorInvalidRandomStringTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorInvalidRandomStringTest.java
new file mode 100644
index 0000000..6834c0d
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorInvalidRandomStringTest.java
@@ -0,0 +1,67 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeywordsExtractorInvalidRandomStringTest {
+
+ private final String keyword;
+ private KeywordsExtractor keywordsExtractor;
+
+ private static final Collection INVALID_STRING_KEYWORDS = Arrays.asList(new Object[][]{
+ {"#RandoString"},
+ {"#Randomstring(23)"},
+ {"#randomString(11)"},
+ {"#Random_String(11)"},
+ {"#RandomString(11,10)"},
+ {"RandomString(11)"},
+ {"RandomString"}
+ });
+
+ public KeywordsExtractorInvalidRandomStringTest(String keyword) {
+ this.keyword = keyword;
+ }
+
+ @Before
+ public void setUp() {
+ this.keywordsExtractor = new KeywordsExtractor();
+ }
+
+ @Parameterized.Parameters
+ public static Collection data() {
+ return INVALID_STRING_KEYWORDS;
+ }
+
+ @Test
+ public void checkValidRandomStringKeyword() {
+ assertEquals(keywordsExtractor.substituteStringKeyword(this.keyword, 1).length(), this.keyword.length());
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorInvalidTimestampTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorInvalidTimestampTest.java
new file mode 100644
index 0000000..eda4070
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorInvalidTimestampTest.java
@@ -0,0 +1,65 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeywordsExtractorInvalidTimestampTest {
+
+ private final String keyword;
+ private KeywordsExtractor keywordsExtractor;
+
+ private static final Collection INVALID_TIMESTAMP_KEYWORDS = Arrays.asList(new Object[][]{
+ {"#Timesamp"},
+ {"#Timestamp(10)"},
+ {"#timestamp"},
+ {"#Timestamp(11,13)"},
+ {"Timestamp"}
+ });
+
+ public KeywordsExtractorInvalidTimestampTest(String keyword) {
+ this.keyword = keyword;
+ }
+
+ @Before
+ public void setUp() {
+ this.keywordsExtractor = new KeywordsExtractor();
+ }
+
+ @Parameterized.Parameters
+ public static Collection data() {
+ return INVALID_TIMESTAMP_KEYWORDS;
+ }
+
+ @Test
+ public void checkValidRandomStringKeyword() {
+ assertEquals(keywordsExtractor.substituteStringKeyword(this.keyword, 1), this.keyword);
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidRandomIntegerTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidRandomIntegerTest.java
new file mode 100644
index 0000000..be79488
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidRandomIntegerTest.java
@@ -0,0 +1,66 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeywordsExtractorValidRandomIntegerTest {
+
+ private final String keyword;
+ private final String shouldParseTo;
+ private KeywordsExtractor keywordsExtractor;
+
+ private static final Collection VALID_INTEGER_KEYWORDS = Arrays.asList(new Object[][]{
+ {"#RandomInteger(23,23)", "23"},
+ {"#RandomInteger(6, 6)12", "612"},
+ {"1#RandomInteger(11,11)", "111"},
+ {"1#RandomInteger(11,11)2", "1112"}
+ });
+
+ public KeywordsExtractorValidRandomIntegerTest(String keyword, String shouldParseTo) {
+ this.keyword = keyword;
+ this.shouldParseTo = shouldParseTo;
+ }
+
+ @Before
+ public void setUp() {
+ this.keywordsExtractor = new KeywordsExtractor();
+ }
+
+ @Parameterized.Parameters
+ public static Collection data() {
+ return VALID_INTEGER_KEYWORDS;
+ }
+
+ @Test
+ public void checkValidRandomStringKeyword() {
+ assertEquals(keywordsExtractor.substituteStringKeyword(this.keyword, 1), this.shouldParseTo);
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidRandomPrimitiveIntegerTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidRandomPrimitiveIntegerTest.java
new file mode 100644
index 0000000..0843ad1
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidRandomPrimitiveIntegerTest.java
@@ -0,0 +1,66 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeywordsExtractorValidRandomPrimitiveIntegerTest {
+
+ private final String keyword;
+ private final Integer shouldParseTo;
+ private KeywordsExtractor keywordsExtractor;
+
+ private static final Collection VALID_INTEGER_KEYWORDS = Arrays.asList(new Object[][]{
+ {"#RandomPrimitiveInteger(23,23)", 23},
+ {"#RandomPrimitiveInteger(6, 6)12", 6},
+ {"1#RandomPrimitiveInteger(11,11)", 11},
+ {"1#RandomPrimitiveInteger(11,11)2", 11}
+ });
+
+ public KeywordsExtractorValidRandomPrimitiveIntegerTest(String keyword, Integer shouldParseTo) {
+ this.keyword = keyword;
+ this.shouldParseTo = shouldParseTo;
+ }
+
+ @Before
+ public void setUp() {
+ this.keywordsExtractor = new KeywordsExtractor();
+ }
+
+ @Parameterized.Parameters
+ public static Collection data() {
+ return VALID_INTEGER_KEYWORDS;
+ }
+
+ @Test
+ public void checkValidRandomStringKeyword() {
+ assertEquals(keywordsExtractor.substitutePrimitiveKeyword(this.keyword), Long.valueOf(this.shouldParseTo));
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidRandomStringTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidRandomStringTest.java
new file mode 100644
index 0000000..f0fdc0f
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidRandomStringTest.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.pnfsimulator.simulator;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.onap.pnfsimulator.simulator.KeywordsValueProvider.DEFAULT_STRING_LENGTH;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeywordsExtractorValidRandomStringTest {
+
+ private final String keyword;
+ private final int length;
+ private KeywordsExtractor keywordsExtractor;
+
+ private static final Collection VALID_STRING_KEYWORDS = Arrays.asList(new Object[][]{
+ {"#RandomString", DEFAULT_STRING_LENGTH},
+ {"1#RandomString2", 1 + DEFAULT_STRING_LENGTH + 1},
+ {"#RandomString(23)", 23},
+ {"#RandomString(11)12", 11 + 2},
+ {"1#RandomString(11)", 1 + 11},
+ {"1#RandomString(11)2", 1 + 11 + 1}
+ });
+
+ public KeywordsExtractorValidRandomStringTest(String keyword, int length) {
+ this.keyword = keyword;
+ this.length = length;
+ }
+
+ @Before
+ public void setUp() {
+ this.keywordsExtractor = new KeywordsExtractor();
+ }
+
+ @Parameterized.Parameters
+ public static Collection data() {
+ return VALID_STRING_KEYWORDS;
+ }
+
+ @Test
+ public void checkValidRandomStringKeyword() {
+ assertEquals(keywordsExtractor.substituteStringKeyword(this.keyword, 1).length(), this.length);
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidTimestampPrimitiveTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidTimestampPrimitiveTest.java
new file mode 100644
index 0000000..7743e55
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidTimestampPrimitiveTest.java
@@ -0,0 +1,66 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * 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.pnfsimulator.simulator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(Parameterized.class)
+public class KeywordsExtractorValidTimestampPrimitiveTest {
+ private final String keyword;
+ private KeywordsExtractor keywordsExtractor;
+
+ private static final Collection VALID_TIMESTAMP_KEYWORDS = Arrays.asList(new Object[][]{
+ {"#TimestampPrimitive"}
+ });
+
+ public KeywordsExtractorValidTimestampPrimitiveTest(String keyword) {
+ this.keyword = keyword;
+ }
+
+ @Before
+ public void setUp() {
+ this.keywordsExtractor = new KeywordsExtractor();
+ }
+
+ @Parameterized.Parameters
+ public static Collection data() {
+ return VALID_TIMESTAMP_KEYWORDS;
+ }
+
+ @Test
+ public void checkValidRandomStringKeyword() {
+ long currentTimestamp = Instant.now().getEpochSecond();
+ Long timestamp = keywordsExtractor.substitutePrimitiveKeyword(this.keyword);
+ long afterExecution = Instant.now().getEpochSecond();
+
+ assertThat(timestamp).isBetween(currentTimestamp, afterExecution);
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidTimestampTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidTimestampTest.java
new file mode 100644
index 0000000..f5c12c3
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsExtractorValidTimestampTest.java
@@ -0,0 +1,67 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeywordsExtractorValidTimestampTest {
+
+ private final String keyword;
+ private final int length;
+ private KeywordsExtractor keywordsExtractor;
+
+ private static final Collection VALID_TIMESTAMP_KEYWORDS = Arrays.asList(new Object[][]{
+ {"#Timestamp", 10},
+ {"#Timestamp12", 10 + 2},
+ {"1#Timestamp", 1 + 10},
+ {"1#Timestamp2", 1 + 10 +1}
+ });
+
+ public KeywordsExtractorValidTimestampTest(String keyword, Integer length) {
+ this.keyword = keyword;
+ this.length = length;
+ }
+
+ @Before
+ public void setUp() {
+ this.keywordsExtractor = new KeywordsExtractor();
+ }
+
+ @Parameterized.Parameters
+ public static Collection data() {
+ return VALID_TIMESTAMP_KEYWORDS;
+ }
+
+ @Test
+ public void checkValidRandomStringKeyword() {
+ String substitution = keywordsExtractor.substituteStringKeyword(this.keyword, 1);
+ assertEquals(substitution.length(), this.length);
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsHandlerTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsHandlerTest.java
new file mode 100644
index 0000000..e67d4a3
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsHandlerTest.java
@@ -0,0 +1,304 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.onap.pnfsimulator.simulator.KeywordsValueProvider.DEFAULT_STRING_LENGTH;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.Queue;
+import org.junit.jupiter.api.Test;
+
+class KeywordsHandlerTest {
+
+ private static final String TEMPLATE_JSON = "{\n" +
+ " \"event\": {\n" +
+ " \"commonEventHeader\": {\n" +
+ " \"domain\": \"#RandomString\"\n" +
+ " },\n" +
+ " \"measurementsForVfScalingFields\": {\n" +
+ " \"measurementsForVfSclaingFieldsVersion\": 2.0,\n" +
+ " \"additionalMeasurements\": {\n" +
+ " \"name\": \"licenseUsage\",\n" +
+ " \"extraFields\": {\n" +
+ " \"name\": \"#RandomString(4)\",\n" +
+ " \"value\": \"1\"\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ "}";
+
+ private static final String TEMPLATE_JSON_WITH_MANY_KEYWORDS_INSIDE_SINGLE_VALUE = "{\n" +
+ " \"event\": {\n" +
+ " \"commonEventHeader\": {\n" +
+ " \"domain1\": \"#RandomString(1) #RandomString(2) #RandomString(3)\",\n" +
+ " \"domain2\": \"1 #RandomString(1) 2\"\n" +
+ " },\n" +
+ " \"measurementsForVfScalingFields\": {\n" +
+ " \"measurementsForVfSclaingFieldsVersion\": 2.0,\n" +
+ " \"additionalMeasurements\": {\n" +
+ " \"name\": \"licenseUsage\",\n" +
+ " \"extraFields\": {\n" +
+ " \"value\": \"1\"\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ "}";
+
+ private static final String TEMPLATE_JSON_WITH_ARRAY = "{\n"
+ + " \"event\": {\n"
+ + " \"commonEventHeader\": {\n"
+ + " \"domain\": \"#RandomString(1)\",\n"
+ + " \"version\": 2.0\n"
+ + " },\n"
+ + " \"measurementsForVfScalingFields\": {\n"
+ + " \"additionalMeasurements\": [\n"
+ + " {\n"
+ + " \"name\": \"licenseUsage\",\n"
+ + " \"arrayOfFields\": [\n"
+ + " {\n"
+ + " \"name\": \"G711AudioPort\",\n"
+ + " \"value\": \"1\"\n"
+ + " },\n"
+ + " {\n"
+ + " \"name\": [\"1\",\"2\"],\n"
+ + " \"value\": \"#RandomString(2)\"\n"
+ + " },\n"
+ + " {\n"
+ + " \"name\": \"G722AudioPort\",\n"
+ + " \"value\": \"1\"\n"
+ + " }\n"
+ + " ]\n"
+ + " }\n"
+ + " ]\n"
+ + " }\n"
+ + " }\n"
+ + "}";
+
+ private static final String TEMPLATE_ONE_INCREMENT_JSON = "{\n" +
+ " \"event\": {\n" +
+ " \"commonEventHeader\": {\n" +
+ " \"domain\": \"#RandomString\"\n" +
+ " },\n" +
+ " \"measurementsForVfScalingFields\": {\n" +
+ " \"measurementsForVfSclaingFieldsVersion\": 2.0,\n" +
+ " \"additionalMeasurements\": {\n" +
+ " \"name\": \"licenseUsage\",\n" +
+ " \"extraFields\": {\n" +
+ " \"name\": \"#RandomString(4)\",\n" +
+ " \"value\": \"#Increment\"\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ "}";
+
+ private static final String TEMPLATE_WITH_SIMPLE_VALUE= "\"#RandomString(4)\"";
+
+ private static final String TEMPLATE_WITH_ARRAY_OF_PRIMITIVES = "[ 1, \"#RandomString(5)\", 3]";
+
+ private static final String TEMPLATE_TWO_INCREMENT_JSON = "{\n" +
+ " \"event\": {\n" +
+ " \"commonEventHeader\": {\n" +
+ " \"domain\": \"#RandomString\"\n" +
+ " },\n" +
+ " \"measurementsForVfScalingFields\": {\n" +
+ " \"measurementsForVfSclaingFieldsVersion\": 2.0,\n" +
+ " \"additionalMeasurements\": {\n" +
+ " \"name\": \"licenseUsage\",\n" +
+ " \"extraFields\": {\n" +
+ " \"name\": \"#RandomString(4)\",\n" +
+ " \"value\": \"#Increment\",\n" +
+ " \"otherValue\": \"#Increment\"\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ "}";
+
+ private Gson gson = new Gson();
+
+ @Test
+ void shouldReplaceRandomStringKeyword() {
+ // given
+ JsonObject templateJson = gson.fromJson(TEMPLATE_JSON, JsonObject.class);
+ KeywordsHandler keywordsHandler = new KeywordsHandler(new KeywordsExtractor(), (id) -> 1);
+
+ // when
+ JsonObject resultJson = keywordsHandler.substituteKeywords(templateJson, "").getAsJsonObject();
+
+ // then
+ String extraFields = resultJson
+ .get("event").getAsJsonObject()
+ .get("measurementsForVfScalingFields").getAsJsonObject()
+ .get("additionalMeasurements").getAsJsonObject()
+ .get("extraFields").getAsJsonObject()
+ .get("name").getAsString();
+ String newDomain = resultJson
+ .get("event").getAsJsonObject()
+ .get("commonEventHeader").getAsJsonObject()
+ .get("domain").getAsString();
+
+ assertThat(extraFields.length()).isEqualTo(4);
+ assertThat(newDomain.length()).isEqualTo(DEFAULT_STRING_LENGTH);
+ }
+
+ @Test
+ void shouldReplaceRandomStringKeywordsInsideSingleValue() {
+ // given
+ JsonObject templateJson = gson.fromJson(TEMPLATE_JSON_WITH_MANY_KEYWORDS_INSIDE_SINGLE_VALUE, JsonObject.class);
+ KeywordsHandler keywordsHandler = new KeywordsHandler(new KeywordsExtractor(), (id) -> 1);
+
+ // when
+ JsonObject resultJson = keywordsHandler.substituteKeywords(templateJson, "").getAsJsonObject();
+
+ // then
+ String newDomain1 = resultJson
+ .get("event").getAsJsonObject()
+ .get("commonEventHeader").getAsJsonObject()
+ .get("domain1").getAsString();
+ String newDomain2 = resultJson
+ .get("event").getAsJsonObject()
+ .get("commonEventHeader").getAsJsonObject()
+ .get("domain2").getAsString();
+
+ assertThat(newDomain1.length()).isEqualTo(1+1+2+1+3);
+ assertThat(newDomain2.length()).isEqualTo(1+1+1+1+1);
+ }
+
+ @Test
+ void shouldReplaceRandomStringKeywordInTeplateAsArrayWithPrimitves() {
+ // given
+ JsonElement templateJson = gson.fromJson(TEMPLATE_WITH_ARRAY_OF_PRIMITIVES, JsonElement.class);
+ KeywordsHandler keywordsHandler = new KeywordsHandler(new KeywordsExtractor(), (id) -> 1);
+
+ // when
+ JsonElement resultJson = keywordsHandler.substituteKeywords(templateJson, "");
+ assertThat(resultJson.getAsJsonArray().get(1).getAsString().length()).isEqualTo(5);
+ }
+
+ @Test
+ void shouldReplaceRandomStringKeywordInTeplateAsSimpleValue() {
+ // given
+ JsonElement templateJson = gson.fromJson(TEMPLATE_WITH_SIMPLE_VALUE, JsonElement.class);
+ KeywordsHandler keywordsHandler = new KeywordsHandler(new KeywordsExtractor(), (id) -> 1);
+
+ // when
+ JsonElement resultJson = keywordsHandler.substituteKeywords(templateJson, "");
+
+ // then
+ assertThat(resultJson.getAsString().length()).isEqualTo(4);
+ }
+
+ @Test
+ void shouldReplaceRandomStringKeywordInTeplateWithJsonArray() {
+ // given
+ JsonElement templateJson = gson.fromJson(TEMPLATE_JSON_WITH_ARRAY, JsonElement.class);
+ KeywordsHandler keywordsHandler = new KeywordsHandler(new KeywordsExtractor(), (id) -> 1);
+
+ // when
+ JsonObject resultJson = keywordsHandler.substituteKeywords(templateJson, "").getAsJsonObject();
+
+ // then
+ String actualValue = resultJson
+ .get("event").getAsJsonObject()
+ .get("measurementsForVfScalingFields").getAsJsonObject()
+ .get("additionalMeasurements").getAsJsonArray()
+ .get(0).getAsJsonObject()
+ .get("arrayOfFields").getAsJsonArray()
+ .get(1).getAsJsonObject()
+ .get("value").getAsString();
+ String otherActualValue = resultJson
+ .get("event").getAsJsonObject()
+ .get("commonEventHeader").getAsJsonObject()
+ .get("domain").getAsString();
+
+ assertThat(otherActualValue.length()).isEqualTo(1);
+ assertThat(actualValue.length()).isEqualTo(2);
+ }
+
+ @Test
+ void shouldReplaceOneIncrementKeyword() {
+ // given
+ final Integer newIncrementedValue = 2;
+ JsonObject templateJson = gson.fromJson(TEMPLATE_ONE_INCREMENT_JSON, JsonObject.class);
+ KeywordsHandler keywordsHandler = new KeywordsHandler(new KeywordsExtractor(), (id) -> newIncrementedValue);
+
+ // when
+ JsonObject resultJson = keywordsHandler.substituteKeywords(templateJson, "some random id").getAsJsonObject();
+
+ // then
+ String actualValue = resultJson
+ .get("event").getAsJsonObject()
+ .get("measurementsForVfScalingFields").getAsJsonObject()
+ .get("additionalMeasurements").getAsJsonObject()
+ .get("extraFields").getAsJsonObject()
+ .get("value").getAsString();
+
+ assertThat(actualValue).isEqualTo(newIncrementedValue.toString());
+ }
+
+ @Test
+ void shouldReplaceTwoIncrementKeyword() {
+ // given
+ final Integer firstIncrementValue = 2;
+ final Integer secondIncrementValue = 3;
+ JsonObject templateJson = gson.fromJson(TEMPLATE_TWO_INCREMENT_JSON, JsonObject.class);
+ KeywordsHandler keywordsHandler = new KeywordsHandler(new KeywordsExtractor(), new IncrementProvider() {
+ Queue<Integer> sequenceOfValues = new LinkedList<>(
+ Arrays.asList(firstIncrementValue, secondIncrementValue));
+
+ @Override
+ public int getAndIncrement(String id) {
+ return sequenceOfValues.poll();
+ }
+ });
+
+ // when
+ JsonObject resultJson = keywordsHandler.substituteKeywords(templateJson, "some random id").getAsJsonObject();
+ resultJson = keywordsHandler.substituteKeywords(templateJson, "some random id").getAsJsonObject();
+
+ // then
+ String actualValue = resultJson
+ .get("event").getAsJsonObject()
+ .get("measurementsForVfScalingFields").getAsJsonObject()
+ .get("additionalMeasurements").getAsJsonObject()
+ .get("extraFields").getAsJsonObject()
+ .get("value").getAsString();
+
+ String actualOtherValue = resultJson
+ .get("event").getAsJsonObject()
+ .get("measurementsForVfScalingFields").getAsJsonObject()
+ .get("additionalMeasurements").getAsJsonObject()
+ .get("extraFields").getAsJsonObject()
+ .get("otherValue").getAsString();
+
+ assertThat(actualValue).isEqualTo(secondIncrementValue.toString());
+ assertThat(actualOtherValue).isEqualTo(secondIncrementValue.toString());
+
+ }
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsValueProviderTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsValueProviderTest.java
new file mode 100644
index 0000000..73e4c31
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/KeywordsValueProviderTest.java
@@ -0,0 +1,81 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.onap.pnfsimulator.simulator.KeywordsValueProvider.DEFAULT_STRING_LENGTH;
+
+import java.util.Random;
+import org.junit.jupiter.api.RepeatedTest;
+import org.junit.jupiter.api.Test;
+
+class KeywordsValueProviderTest {
+
+ @RepeatedTest(10)
+ void randomLimitedStringTest() {
+ String supplierResult = KeywordsValueProvider.getRandomLimitedString().apply();
+ assertEquals(supplierResult.length(), DEFAULT_STRING_LENGTH);
+ }
+
+ @RepeatedTest(10)
+ void randomStringTest() {
+ int length = new Random().nextInt(15) + 1;
+ String supplierResult = KeywordsValueProvider.getRandomString().apply(length);
+ assertEquals(supplierResult.length(), length);
+ }
+
+ @RepeatedTest(10)
+ void randomIntegerTest(){
+ int min = new Random().nextInt(10) + 1;
+ int max = new Random().nextInt(1000) + 20;
+ String supplierResult = KeywordsValueProvider.getRandomInteger().apply(min, max);
+ assertTrue(Integer.parseInt(supplierResult)>=min);
+ assertTrue(Integer.parseInt(supplierResult)<=max);
+ }
+
+ @Test
+ void randomIntegerContainsMaximalAndMinimalValuesTest(){
+ int anyNumber = new Random().nextInt(10) + 1;
+ String supplierResult = KeywordsValueProvider.getRandomInteger().apply(anyNumber, anyNumber);
+ assertEquals(Integer.parseInt(supplierResult), anyNumber);
+ }
+
+ @Test
+ void randomIntegerFromNegativeRangeTest(){
+ String supplierResult = KeywordsValueProvider.getRandomInteger().apply(-20, -20);
+ assertEquals(Integer.parseInt(supplierResult), -20);
+ }
+
+ @RepeatedTest(10)
+ void randomIntegerFromParametersWithDifferentOrdersTest(){
+ String supplierResult = KeywordsValueProvider.getRandomInteger().apply(-20, -10);
+ assertTrue(Integer.parseInt(supplierResult)>=-20);
+ assertTrue(Integer.parseInt(supplierResult)<=-10);
+ }
+
+ @RepeatedTest(10)
+ void epochSecondGeneratedInCorrectFormatTest(){
+ String supplierResult = KeywordsValueProvider.getEpochSecond().apply();
+ assertEquals(supplierResult.length(), 10);
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/SimulatorServiceTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/SimulatorServiceTest.java
new file mode 100644
index 0000000..0196eb0
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/SimulatorServiceTest.java
@@ -0,0 +1,227 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSyntaxException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.onap.pnfsimulator.event.EventData;
+import org.onap.pnfsimulator.event.EventDataService;
+import org.onap.pnfsimulator.rest.model.FullEvent;
+import org.onap.pnfsimulator.rest.model.SimulatorParams;
+import org.onap.pnfsimulator.rest.model.SimulatorRequest;
+import org.onap.pnfsimulator.simulator.client.HttpClientAdapter;
+import org.onap.pnfsimulator.simulator.client.utils.ssl.SSLAuthenticationHelper;
+import org.onap.pnfsimulator.simulator.scheduler.EventScheduler;
+import org.onap.pnfsimulator.simulatorconfig.SimulatorConfig;
+import org.onap.pnfsimulator.simulatorconfig.SimulatorConfigService;
+import org.quartz.SchedulerException;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+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.internal.verification.VerificationModeFactory.times;
+
+class SimulatorServiceTest {
+
+ private static final String VES_URL = "http://0.0.0.0:8080";
+ private static final Gson GSON = new Gson();
+ private static final JsonObject VALID_PATCH = GSON.fromJson("{\"event\": {\n" +
+ " \"commonEventHeader\": {\n" +
+ " \"sourceName\": \"SomeCustomSource\"}}}\n", JsonObject.class);
+ private static JsonObject VALID_FULL_EVENT = GSON.fromJson("{\"event\": {\n" +
+ " \"commonEventHeader\": {\n" +
+ " \"domain\": \"notification\",\n" +
+ " \"eventName\": \"vFirewallBroadcastPackets\"\n" +
+ " },\n" +
+ " \"notificationFields\": {\n" +
+ " \"arrayOfNamedHashMap\": [{\n" +
+ " \"name\": \"A20161221.1031-1041.bin.gz\",\n" +
+ " \"hashMap\": {\n" +
+ " \"fileformatType\": \"org.3GPP.32.435#measCollec\"}}]}}}", JsonObject.class);
+ private static JsonObject FULL_EVENT_WITH_KEYWORDS = GSON.fromJson("{\"event\":{ \n" +
+ " \"commonEventHeader\":{ \n" +
+ " \"domain\":\"notification\",\n" +
+ " \"eventName\":\"#RandomString(20)\",\n" +
+ " \"eventOrderNo\":\"#Increment\"}}}", JsonObject.class);
+ private static final String SOME_CUSTOM_SOURCE = "SomeCustomSource";
+ private static final String CLOSED_LOOP_VNF ="ClosedLoopVNF";
+ private static final String SAMPLE_ID = "sampleId";
+ private static final EventData SAMPLE_EVENT = EventData.builder().id("1").build();
+ private final ArgumentCaptor<JsonObject> bodyCaptor = ArgumentCaptor.forClass(JsonObject.class);
+ private final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
+ private final ArgumentCaptor<Integer> repeatCountCaptor = ArgumentCaptor
+ .forClass(Integer.class);
+ private final ArgumentCaptor<String> templateNameCaptor = ArgumentCaptor.forClass(String.class);
+ private final ArgumentCaptor<String> eventIdCaptor = ArgumentCaptor.forClass(String.class);
+ private final ArgumentCaptor<String> vesUrlCaptor = ArgumentCaptor.forClass(String.class);
+ private final ArgumentCaptor<String> eventContentCaptor = ArgumentCaptor.forClass(String.class);
+ private SimulatorService simulatorService;
+ private EventDataService eventDataService;
+ private EventScheduler eventScheduler;
+ private SimulatorConfigService simulatorConfigService;
+ private static TemplatePatcher templatePatcher = new TemplatePatcher();
+ private static TemplateReader templateReader = new FilesystemTemplateReader(
+ "src/test/resources/org/onap/pnfsimulator/simulator/", GSON);
+
+ @BeforeEach
+ void setUp() {
+ eventDataService = mock(EventDataService.class);
+ eventScheduler = mock(EventScheduler.class);
+ simulatorConfigService = mock(SimulatorConfigService.class);
+
+ simulatorService = new SimulatorService(templatePatcher, templateReader,
+ eventScheduler, eventDataService, simulatorConfigService, new SSLAuthenticationHelper());
+ }
+
+ @Test
+ void shouldTriggerEventWithGivenParams() throws IOException, SchedulerException, GeneralSecurityException {
+ String templateName = "validExampleMeasurementEvent.json";
+ SimulatorParams simulatorParams = new SimulatorParams(VES_URL, 1, 1);
+ SimulatorRequest simulatorRequest = new SimulatorRequest(simulatorParams,
+ templateName, VALID_PATCH);
+
+ doReturn(SAMPLE_EVENT).when(eventDataService).persistEventData(any(JsonObject.class), any(JsonObject.class), any(JsonObject.class), any(JsonObject.class));
+
+ simulatorService.triggerEvent(simulatorRequest);
+
+ assertEventHasExpectedStructure(VES_URL, templateName, SOME_CUSTOM_SOURCE);
+ }
+
+ @Test
+ void shouldTriggerEventWithDefaultVesUrlWhenNotProvidedInRequest() throws IOException, SchedulerException, GeneralSecurityException {
+ String templateName = "validExampleMeasurementEvent.json";
+ SimulatorRequest simulatorRequest = new SimulatorRequest(
+ new SimulatorParams("", 1, 1),
+ templateName, VALID_PATCH);
+
+ URL inDbVesUrl = new URL("http://0.0.0.0:8080/eventListener/v6");
+ doReturn(SAMPLE_EVENT).when(eventDataService).persistEventData(any(JsonObject.class), any(JsonObject.class), any(JsonObject.class), any(JsonObject.class));
+ when(simulatorConfigService.getConfiguration()).thenReturn(new SimulatorConfig(SAMPLE_ID, inDbVesUrl));
+
+ simulatorService.triggerEvent(simulatorRequest);
+
+ assertEventHasExpectedStructure(inDbVesUrl.toString(), templateName, SOME_CUSTOM_SOURCE);
+ }
+
+ @Test
+ void shouldThrowJsonSyntaxWhenInvalidJson() {
+ //given
+ JsonObject patch = GSON.fromJson("{\n" +
+ " \"event\": {\n" +
+ " \"commonEventHeader\": {\n" +
+ " \"sourceName\": \"" + SOME_CUSTOM_SOURCE + "\"\n" +
+ " }\n" +
+ " }\n" +
+ "}\n", JsonObject.class);
+ EventData eventData = EventData.builder().id("1").build();
+
+ SimulatorParams simulatorParams = new SimulatorParams(VES_URL, 1, 1);
+ SimulatorRequest simulatorRequest = new SimulatorRequest(simulatorParams,
+ "invalidJsonStructureEvent.json", patch);
+ doReturn(eventData).when(eventDataService).persistEventData(any(JsonObject.class), any(JsonObject.class), any(JsonObject.class), any(JsonObject.class));
+
+ //when
+ assertThrows(JsonSyntaxException.class,
+ () -> simulatorService.triggerEvent(simulatorRequest));
+ }
+
+ @Test
+ void shouldHandleNonExistingPatchSection() throws IOException, SchedulerException, GeneralSecurityException {
+ String templateName = "validExampleMeasurementEvent.json";
+ SimulatorRequest simulatorRequest = new SimulatorRequest(
+ new SimulatorParams("", 1, 1),
+ templateName, null);
+
+ URL inDbVesUrl = new URL("http://0.0.0.0:8080/eventListener/v6");
+ doReturn(SAMPLE_EVENT).when(eventDataService).persistEventData(any(JsonObject.class), any(JsonObject.class), any(JsonObject.class), any(JsonObject.class));
+ doReturn(new SimulatorConfig(SAMPLE_ID, inDbVesUrl)).when(simulatorConfigService).getConfiguration();
+
+ simulatorService.triggerEvent(simulatorRequest);
+
+ assertEventHasExpectedStructure(inDbVesUrl.toString(), templateName, CLOSED_LOOP_VNF);
+ }
+
+ @Test
+ void shouldSuccessfullySendOneTimeEventWithVesUrlWhenPassed() throws IOException, GeneralSecurityException {
+ SimulatorService spiedTestedService = spy(new SimulatorService(templatePatcher,templateReader, eventScheduler, eventDataService, simulatorConfigService, new SSLAuthenticationHelper()));
+
+ HttpClientAdapter adapterMock = mock(HttpClientAdapter.class);
+ doNothing().when(adapterMock).send(eventContentCaptor.capture());
+ doReturn(adapterMock).when(spiedTestedService).createHttpClientAdapter(any(String.class));
+ FullEvent event = new FullEvent(VES_URL, VALID_FULL_EVENT);
+
+ spiedTestedService.triggerOneTimeEvent(event);
+
+ assertThat(eventContentCaptor.getValue()).isEqualTo(VALID_FULL_EVENT.toString());
+ verify(eventDataService, times(1)).persistEventData(any(JsonObject.class), any(JsonObject.class), any(JsonObject.class), any(JsonObject.class));
+ verify(adapterMock, times(1)).send(VALID_FULL_EVENT.toString());
+ }
+
+ @Test
+ void shouldSubstituteKeywordsAndSuccessfullySendOneTimeEvent() throws IOException, GeneralSecurityException {
+ SimulatorService spiedTestedService = spy(new SimulatorService(templatePatcher,templateReader, eventScheduler, eventDataService, simulatorConfigService, new SSLAuthenticationHelper()));
+
+ HttpClientAdapter adapterMock = mock(HttpClientAdapter.class);
+ doNothing().when(adapterMock).send(eventContentCaptor.capture());
+ doReturn(adapterMock).when(spiedTestedService).createHttpClientAdapter(any(String.class));
+ FullEvent event = new FullEvent(VES_URL, FULL_EVENT_WITH_KEYWORDS);
+
+ spiedTestedService.triggerOneTimeEvent(event);
+
+ JsonObject sentContent = GSON.fromJson(eventContentCaptor.getValue(), JsonElement.class).getAsJsonObject();
+ assertThat(sentContent.getAsJsonObject("event").getAsJsonObject("commonEventHeader").get("eventOrderNo").getAsString()).isEqualTo("1");
+ assertThat(sentContent.getAsJsonObject("event").getAsJsonObject("commonEventHeader").get("eventName").getAsString()).hasSize(20);
+ }
+
+
+ private void assertEventHasExpectedStructure(String expectedVesUrl, String templateName, String sourceNameString) throws SchedulerException, IOException, GeneralSecurityException {
+ verify(eventScheduler, times(1)).scheduleEvent(vesUrlCaptor.capture(), intervalCaptor.capture(),
+ repeatCountCaptor.capture(), templateNameCaptor.capture(), eventIdCaptor.capture(), bodyCaptor.capture());
+ assertThat(vesUrlCaptor.getValue()).isEqualTo(expectedVesUrl);
+ assertThat(intervalCaptor.getValue()).isEqualTo(1);
+ assertThat(repeatCountCaptor.getValue()).isEqualTo(1);
+ assertThat(templateNameCaptor.getValue()).isEqualTo(templateName);
+ String actualSourceName = GSON.fromJson(bodyCaptor.getValue(), JsonObject.class)
+ .get("event").getAsJsonObject()
+ .get("commonEventHeader").getAsJsonObject()
+ .get("sourceName").getAsString();
+ assertThat(actualSourceName).isEqualTo(sourceNameString);
+ verify(eventDataService)
+ .persistEventData(any(JsonObject.class), any(JsonObject.class), any(JsonObject.class),
+ any(JsonObject.class));
+ }
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/TemplatePatcherTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/TemplatePatcherTest.java
new file mode 100644
index 0000000..52e0d6a
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/TemplatePatcherTest.java
@@ -0,0 +1,164 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import org.assertj.core.api.AssertionsForInterfaceTypes;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+class TemplatePatcherTest {
+
+ private static final String TEMPLATE_JSON = "{\n" +
+ " \"event\": {\n" +
+ " \"commonEventHeader\": {\n" +
+ " \"domain\": \"measurementsForVfScaling\"\n" +
+ " },\n" +
+ " \"measurementsForVfScalingFields\": {\n" +
+ " \"measurementsForVfSclaingFieldsVersion\": 2.0,\n" +
+ " \"additionalMeasurements\": {\n" +
+ " \"name\": \"licenseUsage\",\n" +
+ " \"extraFields\": {\n" +
+ " \"name\": \"G711AudioPort\",\n" +
+ " \"value\": \"1\"\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ "}";
+
+ private TemplatePatcher templatePatcher;
+ private Gson gson = new Gson();
+ private JsonObject templateJson;
+
+ @BeforeEach
+ void setUp() {
+ templatePatcher = new TemplatePatcher();
+ templateJson = gson.fromJson(TEMPLATE_JSON, JsonObject.class);
+ }
+
+ @Test
+ void shouldReplaceJsonElementsInTemplate() {
+ //given
+ String patchJsonString = "{\n"
+ + " \"event\": {\n"
+ + " \"commonEventHeader\": {\n"
+ + " \"domain\": \"newDomain\"\n"
+ + " }\n"
+ + " }\n"
+ + "}";
+ JsonObject patchJson = gson.fromJson(patchJsonString, JsonObject.class);
+
+ //when
+ JsonObject requestJson = templatePatcher.mergeTemplateWithPatch(templateJson, patchJson);
+
+ //then
+ String newDomain = requestJson
+ .get("event").getAsJsonObject()
+ .get("commonEventHeader").getAsJsonObject()
+ .get("domain").getAsString();
+ assertThat(newDomain).isEqualTo("newDomain");
+ }
+
+ @Test
+ void shouldAddWholeJsonObjectToTemplateWhenItFinished() {
+ //given
+ String patchJsonString =
+ "{\n"
+ + " \"event\": {\n"
+ + " \"commonEventHeader\": {\n"
+ + " \"domain\": {\n"
+ + " \"extraFields\": {\n"
+ + " \"name\": \"G711AudioPort\",\n"
+ + " \"value\": \"1\"\n"
+ + " }\n"
+ + " }\n"
+ + " }\n"
+ + " }\n"
+ + "}";
+ JsonObject patchJson = gson.fromJson(patchJsonString, JsonObject.class);
+
+ //when
+ JsonObject requestJson = templatePatcher.mergeTemplateWithPatch(templateJson, patchJson);
+
+ //then
+ JsonElement newDomain = requestJson
+ .get("event").getAsJsonObject()
+ .get("commonEventHeader").getAsJsonObject()
+ .get("domain");
+ assertThat(newDomain.isJsonObject()).isTrue();
+ JsonObject newDomainJO = newDomain.getAsJsonObject();
+ AssertionsForInterfaceTypes.assertThat(newDomainJO.keySet()).containsExactly("extraFields");
+ JsonObject newDomainExtraFields = newDomainJO.get("extraFields").getAsJsonObject();
+ AssertionsForInterfaceTypes.assertThat(newDomainExtraFields.keySet()).containsExactly("name", "value");
+ }
+
+ @Test
+ void shouldReplaceJsonObjectWithJsonElementFromPatch() {
+ //given
+ String patchJsonString = "{ \"event\": \"test\" }";
+ JsonObject patchJson = gson.fromJson(patchJsonString, JsonObject.class);
+
+ //when
+ JsonObject requestJson = templatePatcher.mergeTemplateWithPatch(templateJson, patchJson);
+
+ //then
+ assertThat(requestJson.get("event").isJsonObject()).isFalse();
+ assertThat(requestJson.get("event").getAsString()).isEqualTo("test");
+ }
+
+ @Test
+ void shouldAddNewKeyIfPatchHasItAndTempleteDoesnt() {
+ //given
+ String patchJsonString = "{ \"newTestKey\": { \"newTestKeyChild\":\"newTestValue\" }}";
+ JsonObject patchJson = gson.fromJson(patchJsonString, JsonObject.class);
+
+ //when
+ JsonObject requestJson = templatePatcher.mergeTemplateWithPatch(templateJson, patchJson);
+
+ //then
+ assertThat(requestJson.get("event").isJsonObject()).isTrue();
+ assertThat(requestJson.get("newTestKey").isJsonObject()).isTrue();
+ JsonObject newTestKey = requestJson.get("newTestKey").getAsJsonObject();
+ AssertionsForInterfaceTypes.assertThat(newTestKey.keySet()).containsExactly("newTestKeyChild");
+ assertThat(newTestKey.get("newTestKeyChild").getAsString()).isEqualTo("newTestValue");
+
+ }
+
+
+ @Test
+ void shouldNotChangeInputTemplateParam() {
+ //given
+ String patchJsonString = "{ \"newTestKey\": { \"newTestKeyChild\":\"newTestValue\" }}";
+ JsonObject patchJson = gson.fromJson(patchJsonString, JsonObject.class);
+
+ //when
+ templatePatcher.mergeTemplateWithPatch(templateJson, patchJson);
+
+ //then
+ assertThat(templateJson).isEqualTo(gson.fromJson(TEMPLATE_JSON, JsonObject.class));
+
+ }
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/TemplateReaderTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/TemplateReaderTest.java
new file mode 100644
index 0000000..f029fce
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/TemplateReaderTest.java
@@ -0,0 +1,51 @@
+/*
+ * ============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.pnfsimulator.simulator;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSyntaxException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.context.TestPropertySource;
+
+import java.io.IOException;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+
+@TestPropertySource
+class TemplateReaderTest {
+
+ private FilesystemTemplateReader templateReader = new FilesystemTemplateReader("src/test/resources/org/onap/pnfsimulator/simulator/", new Gson());
+
+ @Test
+ void testShouldReadJsonFromFile() throws IOException {
+ JsonObject readJson = templateReader.readTemplate("validExampleMeasurementEvent.json");
+ assertThat(readJson.keySet()).containsOnly("event");
+ assertThat(readJson.get("event").getAsJsonObject().keySet()).containsExactlyInAnyOrder("commonEventHeader", "measurementsForVfScalingFields");
+ }
+
+ @Test
+ void testShouldRaiseExceptionWhenInvalidJsonIsRead() {
+ Assertions.assertThrows(JsonSyntaxException.class, () -> templateReader.readTemplate("invalidJsonStructureEvent.json"));
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImplTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImplTest.java
new file mode 100644
index 0000000..63c1b72
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImplTest.java
@@ -0,0 +1,99 @@
+/*
+ * ============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.pnfsimulator.simulator.client;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.onap.pnfsimulator.simulator.client.utils.ssl.SSLAuthenticationHelper;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.security.GeneralSecurityException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+class HttpClientAdapterImplTest {
+
+ private static final String HTTPS_URL = "https://0.0.0.0:8443/";
+ private static final String HTTP_URL = "http://0.0.0.0:8000/";
+
+ private HttpClient httpClient;
+ private HttpResponse httpResponse;
+
+ @BeforeEach
+ void setup() {
+ httpClient = mock(HttpClient.class);
+ httpResponse = mock(HttpResponse.class);
+ }
+
+ @Test
+ void sendShouldSuccessfullySendRequestGivenValidUrl() throws IOException {
+ assertAdapterSentRequest("http://valid-url:8080");
+ }
+
+ @Test
+ void sendShouldSuccessfullySendRequestGivenValidUrlUsingHTTPS() throws IOException {
+ assertAdapterSentRequest("https://valid-url:8443");
+ }
+
+ @Test
+ void shouldThrowExceptionWhenMalformedVesUrlPassed(){
+ assertThrows(MalformedURLException.class, () -> new HttpClientAdapterImpl("http://blablabla:VES-PORT", new SSLAuthenticationHelper()));
+ }
+ @Test
+ void shouldCreateAdapterWithClientNotSupportingSSLConnection() throws IOException, GeneralSecurityException {
+ HttpClientAdapter adapterWithHttps = new HttpClientAdapterImpl(HTTPS_URL, new SSLAuthenticationHelper());
+ try {
+ adapterWithHttps.send("sample");
+ } catch (Exception actualException) {
+ assertThat(actualException).hasStackTraceContaining(SSLConnectionSocketFactory.class.toString());
+ }
+ }
+
+ @Test
+ void shouldCreateAdapterWithClientSupportingPlainConnectionOnly() throws IOException, GeneralSecurityException {
+ HttpClientAdapter adapterWithHttps = new HttpClientAdapterImpl(HTTP_URL, new SSLAuthenticationHelper());
+ try {
+ adapterWithHttps.send("sample");
+ } catch (Exception actualException) {
+ assertThat(actualException).hasStackTraceContaining(PlainConnectionSocketFactory.class.toString());
+ }
+ }
+
+ private void assertAdapterSentRequest(String targetUrl) throws IOException {
+ HttpClientAdapter adapter = new HttpClientAdapterImpl(httpClient, targetUrl);
+ doReturn(httpResponse).when(httpClient).execute(any());
+
+ adapter.send("test-msg");
+
+ verify(httpClient).execute(any());
+ verify(httpResponse).getStatusLine();
+ }
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SslSupportLevelTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SslSupportLevelTest.java
new file mode 100644
index 0000000..ff41c44
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/utils/ssl/SslSupportLevelTest.java
@@ -0,0 +1,52 @@
+/*
+ * ============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.pnfsimulator.simulator.client.utils.ssl;
+
+import org.junit.jupiter.api.Test;
+
+import java.net.MalformedURLException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class SslSupportLevelTest {
+
+ private static final String HTTPS_URL = "https://127.0.0.1:8443/";
+ private static final String HTTP_URL = "http://127.0.0.1:8080/";
+
+ @Test
+ void testShouldReturnAlwaysTrustSupportLevelForHttpsUrl() throws MalformedURLException {
+ SslSupportLevel actualSupportLevel = SslSupportLevel.getSupportLevelBasedOnProtocol(HTTPS_URL);
+ assertEquals(actualSupportLevel, SslSupportLevel.ALWAYS_TRUST);
+ }
+
+ @Test
+ void testShouldReturnNoneSupportLevelForHttpUrl() throws MalformedURLException {
+ SslSupportLevel actualSupportLevel = SslSupportLevel.getSupportLevelBasedOnProtocol(HTTP_URL);
+ assertEquals(actualSupportLevel, SslSupportLevel.NONE);
+ }
+
+ @Test
+ void testShouldRaiseExceptionWhenInvalidUrlPassed(){
+ assertThrows(MalformedURLException.class, () -> SslSupportLevel.getSupportLevelBasedOnProtocol("http://bla:VES-PORT/"));
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/scheduler/EventJobTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/scheduler/EventJobTest.java
new file mode 100644
index 0000000..25ed84c
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/scheduler/EventJobTest.java
@@ -0,0 +1,90 @@
+/*
+ * ============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.pnfsimulator.simulator.scheduler;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.onap.pnfsimulator.simulator.scheduler.EventJob.BODY;
+import static org.onap.pnfsimulator.simulator.scheduler.EventJob.CLIENT_ADAPTER;
+import static org.onap.pnfsimulator.simulator.scheduler.EventJob.EVENT_ID;
+import static org.onap.pnfsimulator.simulator.scheduler.EventJob.KEYWORDS_HANDLER;
+import static org.onap.pnfsimulator.simulator.scheduler.EventJob.TEMPLATE_NAME;
+import static org.onap.pnfsimulator.simulator.scheduler.EventJob.VES_URL;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.onap.pnfsimulator.simulator.KeywordsExtractor;
+import org.onap.pnfsimulator.simulator.KeywordsHandler;
+import org.onap.pnfsimulator.simulator.client.HttpClientAdapter;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobKey;
+
+class EventJobTest {
+
+ @Test
+ void shouldSendEventWhenExecuteCalled() {
+ //given
+ EventJob eventJob = new EventJob();
+ String templateName = "template name";
+ String vesUrl = "http://someurl:80/";
+ String eventId = "1";
+ JsonParser parser = new JsonParser();
+ JsonObject body = parser.parse("{\"a\": \"A\"}").getAsJsonObject();
+ HttpClientAdapter clientAdapter = mock(HttpClientAdapter.class);
+ JobExecutionContext jobExecutionContext =
+ createMockJobExecutionContext(templateName, eventId, vesUrl, body, clientAdapter);
+
+ ArgumentCaptor<String> vesUrlCaptor = ArgumentCaptor.forClass(String.class);
+ ArgumentCaptor<String> bodyCaptor = ArgumentCaptor.forClass(String.class);
+
+ //when
+ eventJob.execute(jobExecutionContext);
+
+ //then
+ verify(clientAdapter).send(bodyCaptor.capture());
+ assertThat(bodyCaptor.getValue()).isEqualTo(body.toString());
+ }
+
+ private JobExecutionContext createMockJobExecutionContext(String templateName, String eventId, String vesURL,
+ JsonObject body, HttpClientAdapter clientAdapter) {
+
+ JobDataMap jobDataMap = new JobDataMap();
+ jobDataMap.put(TEMPLATE_NAME, templateName);
+ jobDataMap.put(KEYWORDS_HANDLER, new KeywordsHandler(new KeywordsExtractor(), (id) -> 1));
+ jobDataMap.put(EVENT_ID, eventId);
+ jobDataMap.put(VES_URL, vesURL);
+ jobDataMap.put(BODY, body);
+ jobDataMap.put(CLIENT_ADAPTER, clientAdapter);
+
+ JobExecutionContext jobExecutionContext = mock(JobExecutionContext.class);
+ JobDetail jobDetail = mock(JobDetail.class);
+ when(jobExecutionContext.getJobDetail()).thenReturn(jobDetail);
+ when(jobDetail.getJobDataMap()).thenReturn(jobDataMap);
+ when(jobDetail.getKey()).thenReturn(new JobKey("jobId", "group"));
+ return jobExecutionContext;
+ }
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/scheduler/EventSchedulerTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/scheduler/EventSchedulerTest.java
new file mode 100644
index 0000000..84df5e9
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/scheduler/EventSchedulerTest.java
@@ -0,0 +1,149 @@
+/*
+ * ============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.pnfsimulator.simulator.scheduler;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.gson.JsonObject;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+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.mockito.MockitoAnnotations;
+import org.onap.pnfsimulator.simulator.client.utils.ssl.SSLAuthenticationHelper;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.SimpleTrigger;
+
+class EventSchedulerTest {
+
+ @InjectMocks
+ EventScheduler eventScheduler;
+
+ @Mock
+ Scheduler quartzScheduler;
+
+ @Mock
+ SSLAuthenticationHelper sslAuthenticationHelper;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ void shouldTriggerEventWithGivenConfiguration() throws SchedulerException, IOException, GeneralSecurityException {
+ //given
+ ArgumentCaptor<JobDetail> jobDetailCaptor = ArgumentCaptor.forClass(JobDetail.class);
+ ArgumentCaptor<SimpleTrigger> triggerCaptor = ArgumentCaptor.forClass(SimpleTrigger.class);
+
+ String vesUrl = "http://some:80/";
+ int repeatInterval = 1;
+ int repeatCount = 4;
+ String testName = "testName";
+ String eventId = "1";
+ JsonObject body = new JsonObject();
+
+ //when
+ eventScheduler.scheduleEvent(vesUrl, repeatInterval, repeatCount, testName, eventId, body);
+
+ //then
+ verify(quartzScheduler).scheduleJob(jobDetailCaptor.capture(), triggerCaptor.capture());
+ JobDataMap actualJobDataMap = jobDetailCaptor.getValue().getJobDataMap();
+ assertThat(actualJobDataMap.get(EventJob.BODY)).isEqualTo(body);
+ assertThat(actualJobDataMap.get(EventJob.TEMPLATE_NAME)).isEqualTo(testName);
+ assertThat(actualJobDataMap.get(EventJob.VES_URL)).isEqualTo(vesUrl);
+
+ SimpleTrigger actualTrigger = triggerCaptor.getValue();
+ // repeat count adds 1 to given value
+ assertThat(actualTrigger.getRepeatCount()).isEqualTo(repeatCount - 1);
+
+ //getRepeatInterval returns interval in ms
+ assertThat(actualTrigger.getRepeatInterval()).isEqualTo(repeatInterval * 1000);
+ }
+
+ @Test
+ void shouldCancelAllEvents() throws SchedulerException {
+ //given
+ List<JobKey> jobsKeys = Arrays.asList(new JobKey("jobName1"), new JobKey("jobName2"),
+ new JobKey("jobName3"), new JobKey("jobName4"));
+ List<JobExecutionContext> jobExecutionContexts = createExecutionContextWithKeys(jobsKeys);
+ when(quartzScheduler.getCurrentlyExecutingJobs()).thenReturn(jobExecutionContexts);
+ when(quartzScheduler.deleteJobs(jobsKeys)).thenReturn(true);
+
+ //when
+ boolean isCancelled = eventScheduler.cancelAllEvents();
+
+ //then
+ assertThat(isCancelled).isTrue();
+ }
+
+ @Test
+ void shouldCancelSingleEvent() throws SchedulerException {
+ //given
+ JobKey jobToRemove = new JobKey("jobName3");
+ List<JobKey> jobsKeys = Arrays.asList(new JobKey("jobName1"), new JobKey("jobName2"),
+ jobToRemove, new JobKey("jobName4"));
+ List<JobExecutionContext> jobExecutionContexts = createExecutionContextWithKeys(jobsKeys);
+
+ when(quartzScheduler.getCurrentlyExecutingJobs()).thenReturn(jobExecutionContexts);
+ when(quartzScheduler.deleteJob(jobToRemove)).thenReturn(true);
+
+ //when
+ boolean isCancelled = eventScheduler.cancelEvent("jobName3");
+
+ //then
+ assertThat(isCancelled).isTrue();
+ }
+
+ private List<JobExecutionContext> createExecutionContextWithKeys(List<JobKey> jobsKeys) {
+ List<JobExecutionContext> contexts = new ArrayList<>();
+ for (JobKey key : jobsKeys) {
+ contexts.add(createExecutionContextFromKey(key));
+ }
+ return contexts;
+ }
+
+ private JobExecutionContext createExecutionContextFromKey(JobKey key) {
+ JobExecutionContext context = mock(JobExecutionContext.class);
+ JobDetail jobDetail = mock(JobDetail.class);
+ when(context.getJobDetail()).thenReturn(jobDetail);
+ when(jobDetail.getKey()).thenReturn(key);
+ return context;
+ }
+
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfigServiceTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfigServiceTest.java
new file mode 100644
index 0000000..4ed0972
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulatorconfig/SimulatorConfigServiceTest.java
@@ -0,0 +1,104 @@
+/*
+ * ============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.pnfsimulator.simulatorconfig;
+
+import org.assertj.core.util.Lists;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+import static org.assertj.core.api.Java6Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+class SimulatorConfigServiceTest {
+
+ private static final String SAMPLE_ID = "sampleId";
+ private static final String SAMPLE_NEW_VES_URL = "http://localhost:8090/eventListener/v7";
+ @Mock
+ private SimulatorConfigRepository repository;
+
+ @InjectMocks
+ private SimulatorConfigService service;
+
+ @BeforeEach
+ void resetMocks() {
+ initMocks(this);
+ }
+
+ @Test
+ void testShouldReturnConfiguration() throws MalformedURLException {
+ List<SimulatorConfig> expectedConfig = getExpectedConfig();
+ when(repository.findAll()).thenReturn(expectedConfig);
+
+ SimulatorConfig configs = service.getConfiguration();
+
+ assertThat(configs).isNotNull();
+ }
+
+ @Test
+ void testShouldRaiseExceptionWhenNoConfigurationPresent() {
+ when(repository.findAll()).thenReturn(Lists.emptyList());
+
+ assertThatThrownBy(() -> service.getConfiguration())
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("No configuration found in db");
+ }
+
+ @Test
+ void testShouldUpdateConfigurationWithVesUrl() throws MalformedURLException {
+ URL updatedUrl = new URL("http://localhost:8090/listener/v8");
+ SimulatorConfig configWithUpdates = new SimulatorConfig("sampleId", updatedUrl);
+ List<SimulatorConfig> expectedConfig = getExpectedConfig();
+
+ when(repository.findAll()).thenReturn(expectedConfig);
+ when(repository.save(any(SimulatorConfig.class))).thenReturn(configWithUpdates);
+
+ SimulatorConfig updatedConfig = service.updateConfiguration(configWithUpdates);
+
+ assertThat(updatedConfig).isEqualToComparingFieldByField(configWithUpdates);
+ }
+
+ @Test
+ void testShouldRaiseExceptionWhenNoConfigInDbPresentOnUpdate() throws MalformedURLException {
+ when(repository.findAll()).thenReturn(Lists.emptyList());
+
+ SimulatorConfig configWithUpdates = new SimulatorConfig(SAMPLE_ID, new URL(SAMPLE_NEW_VES_URL));
+
+ assertThatThrownBy(() -> service.updateConfiguration(configWithUpdates))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("No configuration found in db");
+ }
+
+ private List<SimulatorConfig> getExpectedConfig() throws MalformedURLException {
+ URL sampleVesUrl = new URL("http://localhost:8080/eventListener/v7");
+ SimulatorConfig config = new SimulatorConfig(SAMPLE_ID, sampleVesUrl);
+ return Lists.newArrayList(config);
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/template/TemplateServiceTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/template/TemplateServiceTest.java
new file mode 100644
index 0000000..0746960
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/template/TemplateServiceTest.java
@@ -0,0 +1,152 @@
+/*-
+ * ============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.pnfsimulator.template;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import org.assertj.core.util.Lists;
+import org.bson.Document;
+import org.junit.Assert;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;;
+import org.onap.pnfsimulator.template.search.viewmodel.FlatTemplateContent;
+import org.onap.pnfsimulator.template.search.TemplateSearchHelper;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Query;
+
+import java.time.Instant;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+class TemplateServiceTest {
+ private static final Gson GSON = new Gson();
+ private static final Template SAMPLE_TEMPLATE = new Template("sample name", new Document(), Instant.now().getNano());
+ private static final List<Template> SAMPLE_TEMPLATE_LIST = Collections.singletonList(SAMPLE_TEMPLATE);
+
+ @Mock
+ private TemplateRepository templateRepositoryMock;
+
+ @Mock
+ private MongoTemplate mongoTemplate;
+
+ @InjectMocks
+ private TemplateService service;
+
+ @BeforeEach
+ void setUp() {
+ initMocks(this);
+ TemplateSearchHelper searchHelper = new TemplateSearchHelper(mongoTemplate);
+ service = new TemplateService(templateRepositoryMock, searchHelper);
+ }
+
+ @Test
+ void testShouldReturnAllTemplates() {
+ when(templateRepositoryMock.findAll()).thenReturn(SAMPLE_TEMPLATE_LIST);
+
+ List<Template> actual = service.getAll();
+ assertThat(actual).containsExactly(SAMPLE_TEMPLATE_LIST.get(0));
+ }
+
+
+ @Test
+ void testShouldGetTemplateBySpecifiedName() {
+ when(templateRepositoryMock.findById("sample name")).thenReturn(Optional.of(SAMPLE_TEMPLATE));
+
+ Optional<Template> actualTemplate = service.get("sample name");
+ assertThat(actualTemplate).isPresent();
+ assertThat(actualTemplate.get()).isEqualTo(SAMPLE_TEMPLATE);
+ }
+
+ @Test
+ void testShouldSaveTemplate() {
+ service.persist(SAMPLE_TEMPLATE);
+
+ verify(templateRepositoryMock, times(1)).save(SAMPLE_TEMPLATE);
+ }
+
+ @Test
+ void testShouldDeleteTemplateByName() {
+ service.delete("sample name");
+
+ verify(templateRepositoryMock, times(1)).deleteById("sample name");
+ }
+
+
+ @Test
+ void testShouldReturnTemplatesAccordingToGivenSearchCriteria() {
+ doReturn(Lists.emptyList()).when(mongoTemplate).find(any(Query.class), anyObject(), any(String.class));
+
+ List<String> idsByContentCriteria = service.getIdsByContentCriteria(GSON.fromJson("{\"domain\": \"notification.json\"}", JsonObject.class));
+
+ assertThat(idsByContentCriteria).isEmpty();
+ }
+
+ @Test
+ void shouldReturnNamesForGivenComposedSearchCriteria(){
+ JsonObject composedCriteriaObject = GSON.fromJson("{\"eventName\": \"pnfRegistration_Nokia_5gDu\", \"sequence\": 1}", JsonObject.class);
+ List<FlatTemplateContent> arr = Lists.newArrayList(new FlatTemplateContent("sampleId", null));
+
+ doReturn(arr).when(mongoTemplate).find(any(Query.class), anyObject(), any(String.class));
+
+ List<String> idsByContentCriteria = service.getIdsByContentCriteria(composedCriteriaObject);
+ assertThat(idsByContentCriteria).containsOnly("sampleId");
+ }
+
+ @Test
+ void shouldReturnFalseWhenOverwritingWithoutForce() {
+ String id = "someTemplate";
+ Template template = new Template(id, new Document(), Instant.now().getNano());
+ when(templateRepositoryMock.existsById(id)).thenReturn(true);
+ boolean actual = service.tryPersistOrOverwrite(template, false);
+ Assert.assertFalse(actual);
+ }
+
+ @Test
+ void shouldReturnTrueWhenOverwritingWithForce() {
+ String id = "someTemplate";
+ Template template = new Template(id, new Document(), Instant.now().getNano());
+ when(templateRepositoryMock.existsById(id)).thenReturn(true);
+ boolean actual = service.tryPersistOrOverwrite(template, true);
+ Assert.assertTrue(actual);
+ }
+
+ @Test
+ void shouldReturnTrueWhenSavingNonExistingTemplate() {
+ String id = "someTemplate";
+ Template template = new Template(id, new Document(), Instant.now().getNano());
+ when(templateRepositoryMock.existsById(id)).thenReturn(false);
+ boolean actual = service.tryPersistOrOverwrite(template, false);
+ Assert.assertTrue(actual);
+ }
+
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/template/search/JsonUtilsTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/template/search/JsonUtilsTest.java
new file mode 100644
index 0000000..fa0bed1
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/template/search/JsonUtilsTest.java
@@ -0,0 +1,166 @@
+/*-
+ * ============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.pnfsimulator.template.search;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import org.bson.Document;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+
+class JsonUtilsTest {
+
+ private static final Gson GSON_HELPER = new Gson();
+ private JsonUtils utils;
+
+ @BeforeEach
+ void setUp() {
+ utils = new JsonUtils();
+ }
+
+ private static final String NOTIFICATION_JSON = "{\n\"event\": {\n" +
+ " \"commonEventHeader\": {\n" +
+ " \"domain\": \"notification\",\n" +
+ " \"eventName\": \"vFirewallBroadcastPackets\"\n" +
+ " },\n" +
+ " \"notificationFields\": {\n" +
+ " \"changeIdentifier\": \"PM_MEAS_FILES\",\n" +
+ " \"arrayOfNamedHashMap\": [{\n" +
+ " \"name\": \"A20161221.1031-1041.bin.gz\",\n" +
+ " \"hashMap\": {\n" +
+ " \"fileformatType\": \"org.3GPP.32.435#measCollec\",\n" +
+ " \"fileFormatVersion\": \"V10\"\n"+
+ " }\n" +
+ " }, {\n" +
+ " \"name\": \"A20161222.1042-1102.bin.gz\",\n" +
+ " \"hashMap\": {\n" +
+ " \"fileFormatType\": \"org.3GPP.32.435#measCollec\",\n" +
+ " \"fileFormatVersion\": \"1.0.0\"\n" +
+ " }\n" +
+ " }],\n" +
+ " \"notificationFieldsVersion\": \"2.0\"\n}\n\n}}";
+ private static final String EXPECTED_FLATTENED_NOTIFICATION = "{" +
+ " \":event:commonEventHeader:domain\" : \"notification\"," +
+ " \":event:commonEventHeader:eventName\" : \"vFirewallBroadcastPackets\"," +
+ " \":event:notificationFields:changeIdentifier\" : \"PM_MEAS_FILES\"," +
+ " \":event:notificationFields:arrayOfNamedHashMap[0]:name\" : \"A20161221.1031-1041.bin.gz\"," +
+ " \":event:notificationFields:arrayOfNamedHashMap[0]:hashMap:fileformatType\" : \"org.3GPP.32.435#measCollec\"," +
+ " \":event:notificationFields:arrayOfNamedHashMap[0]:hashMap:fileFormatVersion\" : \"V10\"," +
+ " \":event:notificationFields:arrayOfNamedHashMap[1]:name\" : \"A20161222.1042-1102.bin.gz\"," +
+ " \":event:notificationFields:arrayOfNamedHashMap[1]:hashMap:fileFormatType\" : \"org.3GPP.32.435#measCollec\"," +
+ " \":event:notificationFields:arrayOfNamedHashMap[1]:hashMap:fileFormatVersion\" : \"1.0.0\"," +
+ " \":event:notificationFields:notificationFieldsVersion\" : \"2.0\" }";
+
+ @Test
+ void shouldFlattenNestedJsonAndSeparateKeysWithDoubleHash(){
+ JsonObject templateJson = GSON_HELPER.fromJson(NOTIFICATION_JSON, JsonObject.class);
+
+ JsonObject result = utils.flatten(templateJson);
+
+ assertThat(result).isEqualTo(GSON_HELPER.fromJson(EXPECTED_FLATTENED_NOTIFICATION, JsonObject.class));
+ }
+
+ @Test
+ void shouldWorkOnEmptyJsonObject(){
+ JsonObject result = utils.flatten(new JsonObject());
+
+ assertThat(result.toString()).isEqualTo("{}");
+ }
+
+ @Test
+ void shouldFlattenObjectWithArrayValue(){
+ String expectedFlattenedObjectWithArray = "{" +
+ " \":sample[0]\": 1," +
+ " \":sample[1]\": 2," +
+ " \":sample[2]\": 3}";
+ JsonObject jsonWithPrimitivesArray = GSON_HELPER.fromJson("{\"sample\": [1, 2, 3]}", JsonObject.class);
+
+ JsonObject result = utils.flatten(jsonWithPrimitivesArray);
+
+ assertThat(result).isEqualTo(GSON_HELPER.fromJson(expectedFlattenedObjectWithArray, JsonObject.class));
+ }
+
+ @Test
+ void shouldFlattenObjectWithEmptyArrayValue(){
+ String expectedFlattenedObjectWithEmptyArray = "{\":sample\": []}";
+ JsonObject jsonWithEmptyArrayValue = GSON_HELPER.fromJson("{\"sample\": []}", JsonObject.class);
+
+ JsonObject result = utils.flatten(jsonWithEmptyArrayValue);
+
+ assertThat(result).isEqualTo(GSON_HELPER.fromJson(expectedFlattenedObjectWithEmptyArray, JsonObject.class));
+ }
+
+ @Test
+ void shouldFlattenNestedObjectWithEmptyObjectValue(){
+ String expectedFlattenedNestedObjectWithEmptyObject = "{\":sample:key\": {}}";
+ JsonObject nestedJsonWithEmptyObject = GSON_HELPER.fromJson("{\"sample\": {\"key\":{}}}", JsonObject.class);
+
+ JsonObject result = utils.flatten(nestedJsonWithEmptyObject);
+
+ assertThat(result).isEqualTo(GSON_HELPER.fromJson(expectedFlattenedNestedObjectWithEmptyObject, JsonObject.class));
+ }
+
+ @Test
+ void shouldFlattenObjectWithDifferentDataTypes(){
+ String jsonWithDifferentDataTypes = "{ \"topLevelKey\": {\"sampleInt\": 1, \"sampleBool\": false, \"sampleDouble\": 10.0, \"sampleString\": \"str\"}}";
+ String expectedResult = "{\":topLevelKey:sampleInt\": 1," +
+ " \":topLevelKey:sampleBool\": \"false\"," +
+ " \":topLevelKey:sampleDouble\": 10.0," +
+ " \":topLevelKey:sampleString\": \"str\"}";
+ JsonObject templateJson = GSON_HELPER.fromJson(jsonWithDifferentDataTypes, JsonObject.class);
+
+ JsonObject result = utils.flatten(templateJson);
+
+ assertThat(result).isEqualTo(GSON_HELPER.fromJson(expectedResult, JsonObject.class));
+ }
+
+ @Test
+ void shouldHandleNullValues(){
+ String jsonWithNullValue = "{ \"topLevelKey\": {\"sampleNull\": null, \"sampleString\": \"str\"}}";
+ String expectedResult = "{\":topLevelKey:sampleNull\": null," +
+ " \":topLevelKey:sampleString\": \"str\"}";
+ JsonObject templateJson = GSON_HELPER.fromJson(jsonWithNullValue, JsonObject.class);
+
+ JsonObject result = utils.flatten(templateJson);
+
+ assertThat(result).isEqualTo(GSON_HELPER.fromJson(expectedResult, JsonObject.class));
+ }
+
+ @Test
+ void shouldFlattenBsonDocument(){
+ Document documentInput = Document.parse(NOTIFICATION_JSON);
+
+ Document result = utils.flatten(documentInput);
+
+ assertThat(result.toJson()).isEqualTo(EXPECTED_FLATTENED_NOTIFICATION);
+ }
+
+ @Test
+ void shouldNotChangeEmptyBsonDocument(){
+ Document input = Document.parse("{}");
+
+ Document result = utils.flatten(input);
+
+ assertThat(result.toJson()).isEqualTo("{ }");
+ }
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/template/search/TemplateSearchHelperTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/template/search/TemplateSearchHelperTest.java
new file mode 100644
index 0000000..aeef870
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/template/search/TemplateSearchHelperTest.java
@@ -0,0 +1,160 @@
+/*-
+ * ============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.pnfsimulator.template.search;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.mongodb.BasicDBList;
+import org.assertj.core.util.Lists;
+import org.bson.Document;
+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.onap.pnfsimulator.template.search.viewmodel.FlatTemplateContent;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.BasicQuery;
+import org.springframework.data.mongodb.core.query.Query;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+
+class TemplateSearchHelperTest {
+
+ private static final Gson GSON = new Gson();
+ private static final String FLATTENED_TEMPLATES_VIEW = "flatTemplatesView";
+
+ @Mock
+ private MongoTemplate mongoTemplate;
+
+ @InjectMocks
+ private TemplateSearchHelper helper;
+
+ private static final ArgumentCaptor<Query> QUERY_CAPTOR = ArgumentCaptor.forClass(Query.class);
+ private static final ArgumentCaptor<String> COLLECTION_NAME_CAPTOR = ArgumentCaptor.forClass(String.class);
+ private static final ArgumentCaptor<Class<FlatTemplateContent>> CLASS_TYPE_CAPTOR = ArgumentCaptor.forClass((Class) FlatTemplateContent.class);
+
+
+ @BeforeEach
+ void setUp() {
+ initMocks(this);
+ }
+
+ @Test
+ void shouldReturnNamesForGivenComposedSearchCriteria(){
+ String expectedComposedQueryString = "{\"$and\":[{\"keyValues\":{\"$elemMatch\":{\"k\":{\"$regex\":\":eventName(?:(\\\\[[\\\\d]+\\\\]))?$\",\"$options\":\"iu\"},\"v\":{\"$regex\":\"^\\\\QpnfRegistration_Nokia_5gDu\\\\E$\",\"$options\":\"iu\"}}}},{\"keyValues\":{\"$elemMatch\":{\"k\":{\"$regex\":\":sequence(?:(\\\\[[\\\\d]+\\\\]))?$\",\"$options\":\"iu\"},\"v\":1.0}}}]}";
+ Query expectedQuery = new BasicQuery(expectedComposedQueryString);
+
+ String composedCriteriaInputJson = "{\"eventName\": \"pnfRegistration_Nokia_5gDu\", \"sequence\": 1}";
+ JsonObject composedCriteriaObject = GSON.fromJson(composedCriteriaInputJson, JsonObject.class);
+
+ when(mongoTemplate.find(any(Query.class), anyObject(), any(String.class))).thenReturn(Lists.newArrayList(new FlatTemplateContent("sampleId1", null), new FlatTemplateContent("sampleId2", null)));
+
+ List<String> idsOfDocumentMatchingCriteria = helper.getIdsOfDocumentMatchingCriteria(composedCriteriaObject);
+
+ assertThat(idsOfDocumentMatchingCriteria).containsOnly("sampleId1", "sampleId2");
+ verify(mongoTemplate, times(1)).find(QUERY_CAPTOR.capture(), CLASS_TYPE_CAPTOR.capture(), COLLECTION_NAME_CAPTOR.capture());
+ assertThat(QUERY_CAPTOR.getValue().toString()).isEqualTo(expectedQuery.toString());
+ assertThat(COLLECTION_NAME_CAPTOR.getValue()).isEqualTo(FLATTENED_TEMPLATES_VIEW);
+ assertThat(CLASS_TYPE_CAPTOR.getValue()).isEqualTo(FlatTemplateContent.class);
+ }
+
+ @Test
+ void shouldReturnTemplatesAccordingToGivenSearchCriteria() {
+ Query expectedQueryStructure = new BasicQuery("{\"$and\":[{\"keyValues\": { \"$elemMatch\" : { \"k\" : { \"$regex\" : \":domain(?:(\\\\[[\\\\d]+\\\\]))?$\", \"$options\" : \"iu\" }, \"v\" : { \"$regex\" : \"^\\\\Qnotification\\\\E$\", \"$options\" : \"iu\" }}}}]}");
+
+ helper.getIdsOfDocumentMatchingCriteria(GSON.fromJson("{\"domain\": \"notification\"}", JsonObject.class));
+
+
+ verify(mongoTemplate, times(1)).find(QUERY_CAPTOR.capture(), CLASS_TYPE_CAPTOR.capture(), COLLECTION_NAME_CAPTOR.capture());
+
+ assertThat(QUERY_CAPTOR.getValue().toString()).isEqualTo(expectedQueryStructure.toString());
+ assertThat(COLLECTION_NAME_CAPTOR.getValue()).isEqualTo(FLATTENED_TEMPLATES_VIEW);
+ assertThat(CLASS_TYPE_CAPTOR.getValue()).isEqualTo(FlatTemplateContent.class);
+ }
+
+ @Test
+ void shouldGetQueryForEmptyJson(){
+ JsonObject jsonObject = GSON.fromJson("{}", JsonObject.class);
+
+ String expectedComposedQueryString = "{}";
+ Query expectedQuery = new BasicQuery(expectedComposedQueryString);
+
+ helper.getIdsOfDocumentMatchingCriteria(jsonObject);
+
+ verify(mongoTemplate, times(1)).find(QUERY_CAPTOR.capture(), CLASS_TYPE_CAPTOR.capture(), COLLECTION_NAME_CAPTOR.capture());
+ Query queryBasedOnCriteria = QUERY_CAPTOR.getValue();
+
+ assertThat(QUERY_CAPTOR.getValue().toString()).isEqualTo(expectedQuery.toString());
+ assertThat(COLLECTION_NAME_CAPTOR.getValue()).isEqualTo(FLATTENED_TEMPLATES_VIEW);
+ assertThat(CLASS_TYPE_CAPTOR.getValue()).isEqualTo(FlatTemplateContent.class);
+ }
+
+
+ @Test
+ void shouldGetQueryWithAllTypeValues(){
+ JsonObject jsonObject = GSON.fromJson("{\"stringKey\": \"stringValue\", \"numberKey\": 16.00, \"boolKey\": false}", JsonObject.class);
+
+ helper.getIdsOfDocumentMatchingCriteria(jsonObject);
+
+ verify(mongoTemplate, times(1)).find(QUERY_CAPTOR.capture(), CLASS_TYPE_CAPTOR.capture(), COLLECTION_NAME_CAPTOR.capture());
+ Query queryBasedOnCriteria = QUERY_CAPTOR.getValue();
+
+ assertThat(queryBasedOnCriteria.getQueryObject().get("$and")).isInstanceOf(List.class);
+ List<Document> conditionDocuments = new ArrayList<>((List<Document>) queryBasedOnCriteria.getQueryObject().get("$and"));
+ List<Document> conditions = conditionDocuments.stream().map(el -> (Document) el.get("keyValues")).map(el -> (Document) el.get("$elemMatch")).collect(Collectors.toList());
+
+ assertThat(conditionDocuments).hasSize(3);
+ assertJsonPreparedKeyHasCorrectStructure(conditions.get(0), "stringKey");
+ assertThat(conditions.get(0).get("v").toString()).isEqualTo(TemplateSearchHelper.getCaseInsensitive("^\\QstringValue\\E$").toString());
+
+ assertJsonPreparedKeyHasCorrectStructure(conditions.get(1), "numberKey");
+ assertThat(conditions.get(1).get("v")).isEqualTo(16.0);
+
+ assertJsonPreparedKeyHasCorrectStructure(conditions.get(2), "boolKey");
+ assertThat(conditions.get(2).get("v")).isEqualTo("false");
+ }
+
+ @Test
+ void shouldThrowExceptionWhenNullIsPresentAsCriteriaValue(){
+ JsonObject jsonObject = GSON.fromJson("{\"stringKey\": \"stringValue\", \"nullKey\": null}", JsonObject.class);
+
+ assertThrows(IllegalJsonValueException.class, () -> helper.getIdsOfDocumentMatchingCriteria(jsonObject));
+ }
+
+ private void assertJsonPreparedKeyHasCorrectStructure(Document actual, String expectedPattern){
+ assertThat(actual.get("k").toString()).isEqualTo(Pattern.compile(String.format(":%s(?:(\\[[\\d]+\\]))?$", expectedPattern)).toString());
+
+ }
+}
diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/template/search/handler/PrimitiveValueCriteriaBuilderTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/template/search/handler/PrimitiveValueCriteriaBuilderTest.java
new file mode 100644
index 0000000..31bcf1c
--- /dev/null
+++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/template/search/handler/PrimitiveValueCriteriaBuilderTest.java
@@ -0,0 +1,75 @@
+/*-
+ * ============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.pnfsimulator.template.search.handler;
+
+import com.google.gson.JsonPrimitive;
+import org.junit.jupiter.api.Test;
+import org.springframework.data.mongodb.core.query.Criteria;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+
+class PrimitiveValueCriteriaBuilderTest {
+
+ private PrimitiveValueCriteriaBuilder builder = new PrimitiveValueCriteriaBuilder();
+
+ @Test
+ void testShouldAddRegexLikeCriteriaForStringType(){
+ Criteria criteria = builder.applyValueCriteriaBasedOnPrimitiveType(Criteria.where("k").is("10").and("v"), new JsonPrimitive("sample"));
+
+ assertThat(criteria.getCriteriaObject().toJson()).isEqualTo("{ \"k\" : \"10\", \"v\" : { \"$regex\" : \"^\\\\Qsample\\\\E$\", \"$options\" : \"iu\" } }");
+ }
+
+ @Test
+ void testShouldAddRegexLikeAndEscapeStringWithMetaChars(){
+ Criteria criteria = builder.applyValueCriteriaBasedOnPrimitiveType(Criteria.where("k").is("10").and("v"), new JsonPrimitive("[1,2,3,4,5]"));
+
+ assertThat(criteria.getCriteriaObject().toJson()).isEqualTo("{ \"k\" : \"10\", \"v\" : { \"$regex\" : \"^\\\\Q[1,2,3,4,5]\\\\E$\", \"$options\" : \"iu\" } }");
+ }
+
+ @Test
+ void testShouldAddRegexLikeCriteriaForIntType(){
+ Criteria criteria = builder.applyValueCriteriaBasedOnPrimitiveType(Criteria.where("k").is("10").and("v"), new JsonPrimitive(1));
+
+ assertThat(criteria.getCriteriaObject().toJson()).isEqualTo("{ \"k\" : \"10\", \"v\" : 1.0 }");
+ }
+
+ @Test
+ void testShouldAddRegexLikeCriteriaForLongType(){
+ Criteria criteria = builder.applyValueCriteriaBasedOnPrimitiveType(Criteria.where("k").is("10").and("v"), new JsonPrimitive(Long.MAX_VALUE));
+
+ assertThat(criteria.getCriteriaObject().toJson()).isEqualTo("{ \"k\" : \"10\", \"v\" : 9.223372036854776E18 }");
+ }
+
+ @Test
+ void testShouldAddRegexLikeCriteriaForDoubleType(){
+ Criteria criteria = builder.applyValueCriteriaBasedOnPrimitiveType(Criteria.where("k").is("10").and("v"), new JsonPrimitive(2.5));
+
+ assertThat(criteria.getCriteriaObject().toJson()).isEqualTo("{ \"k\" : \"10\", \"v\" : 2.5 }");
+ }
+
+ @Test
+ void testShouldAddRegexLikeCriteriaForBooleanType(){
+ Criteria criteria = builder.applyValueCriteriaBasedOnPrimitiveType(Criteria.where("k").is("10").and("v"), new JsonPrimitive(true));
+
+ assertThat(criteria.getCriteriaObject().toJson()).isEqualTo("{ \"k\" : \"10\", \"v\" : \"true\" }");
+ }
+
+}
diff --git a/pnfsimulator/src/test/resources/application.properties b/pnfsimulator/src/test/resources/application.properties
new file mode 100644
index 0000000..6815680
--- /dev/null
+++ b/pnfsimulator/src/test/resources/application.properties
@@ -0,0 +1,2 @@
+templates.dir=src/test/resources/org/onap/pnfsimulator/simulator
+ssl.clientCertificateEnabled=false
diff --git a/pnfsimulator/src/test/resources/client.p12 b/pnfsimulator/src/test/resources/client.p12
new file mode 100644
index 0000000..0bbec38
--- /dev/null
+++ b/pnfsimulator/src/test/resources/client.p12
Binary files differ
diff --git a/pnfsimulator/src/test/resources/logback-test.xml b/pnfsimulator/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..ad4f0c8
--- /dev/null
+++ b/pnfsimulator/src/test/resources/logback-test.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============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=========================================================
+ -->
+
+<Configuration complete="true" compact="true">
+
+ <Property name="outputFilename" value="pnfsimulator_output"/>
+ <Property name="log-path" value="${java.io.tmpdir}"/>
+ <property name="maxFileSize" value="50MB"/>
+ <property name="maxHistory" value="30"/>
+ <property name="totalSizeCap" value="10GB"/>
+
+ <appender name="Console" target="SYSTEM_OUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <Pattern>%nopexception%logger
+ |%date{yyyy-MM-dd'T'HH:mm:ss.SSSXXX,UTC}
+ |%level
+ |%replace(%replace(%message){'\t','\\\\t'}){'\n','\\\\n'}
+ |%replace(%replace(%mdc){'\t','\\\\t'}){'\n','\\\\n'}
+ |%replace(%replace(%rootException){'\t','\\\\t'}){'\n','\\\\n'}
+ |%replace(%replace(%marker){'\t','\\\\t'}){'\n','\\\\n'}
+ |%thread
+ |%n</Pattern>
+ </encoder>
+ </appender>
+
+ <appender name="ROLLING-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <encoder>
+ <pattern>%nopexception%logger
+ |%date{yyyy-MM-dd'T'HH:mm:ss.SSSXXX,UTC}
+ |%level
+ |%replace(%replace(%message){'\t','\\\\t'}){'\n','\\\\n'}
+ |%replace(%replace(%mdc){'\t','\\\\t'}){'\n','\\\\n'}
+ |%replace(%replace(%rootException){'\t','\\\\t'}){'\n','\\\\n'}
+ |%replace(%replace(%marker){'\t','\\\\t'}){'\n','\\\\n'}
+ |%thread
+ |%n</pattern>
+ </encoder>
+ <File>${log-path}/${outputFilename}.log</File>
+ <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <FileNamePattern>${log-path}/${outputFilename}.%d{yyyy-MM-dd}.%i.log.zip</FileNamePattern>
+ <MaxFileSize>${maxFileSize}</MaxFileSize>
+ <MaxHistory>${maxHistory}</MaxHistory>
+ <TotalSizeCap>${totalSizeCap}</TotalSizeCap>
+ </rollingPolicy>
+ </appender>
+
+ <root level="info">
+ <appender-ref ref="Console" />
+ <appender-ref ref="ROLLING-FILE" />
+ </root>
+</Configuration>
diff --git a/pnfsimulator/src/test/resources/org/onap/pnfsimulator/simulator/filesystem/test1.json b/pnfsimulator/src/test/resources/org/onap/pnfsimulator/simulator/filesystem/test1.json
new file mode 100644
index 0000000..89e4a76
--- /dev/null
+++ b/pnfsimulator/src/test/resources/org/onap/pnfsimulator/simulator/filesystem/test1.json
@@ -0,0 +1,12 @@
+{
+ "field1": "value1",
+ "field2": 2,
+ "nested": {
+ "key1": [
+ 1,
+ 2,
+ 3
+ ],
+ "key2": "sampleValue2"
+ }
+}
diff --git a/pnfsimulator/src/test/resources/org/onap/pnfsimulator/simulator/invalidJsonStructureEvent.json b/pnfsimulator/src/test/resources/org/onap/pnfsimulator/simulator/invalidJsonStructureEvent.json
new file mode 100644
index 0000000..4d6ef7d
--- /dev/null
+++ b/pnfsimulator/src/test/resources/org/onap/pnfsimulator/simulator/invalidJsonStructureEvent.json
@@ -0,0 +1 @@
+{"sampleKey1": [{"sampleKey2": "1"}, {"sampleKey2": "2"}]
diff --git a/pnfsimulator/src/test/resources/org/onap/pnfsimulator/simulator/validExampleMeasurementEvent.json b/pnfsimulator/src/test/resources/org/onap/pnfsimulator/simulator/validExampleMeasurementEvent.json
new file mode 100644
index 0000000..989d6ea
--- /dev/null
+++ b/pnfsimulator/src/test/resources/org/onap/pnfsimulator/simulator/validExampleMeasurementEvent.json
@@ -0,0 +1,89 @@
+{
+ "event": {
+ "commonEventHeader": {
+ "domain": "measurementsForVfScaling",
+ "eventName": "vFirewallBroadcastPackets",
+ "eventId": "4cfc-91cf-31a46",
+ "nfType": "mrfx",
+ "priority": "Normal",
+ "reportingEntityName": "myVNF",
+ "sequence": 1,
+ "sourceName": "ClosedLoopVNF",
+ "startEpochMicrosec": 1531616794,
+ "lastEpochMicrosec": 1531719042,
+ "version": 2.0
+ },
+ "measurementsForVfScalingFields": {
+ "measurementsForVfSclaingFieldsVersion": 2.0,
+ "measurementsForVfScalingVersion": 2.0,
+ "measurementInterval": 180,
+ "concurrentSessions": 2,
+ "cpuUsageArray": [
+ {
+ "cpuIdentifier": "INTEL_CORE_I7_1",
+ "percentUsage": 50
+ },
+ {
+ "cpuIdentifier": "INTEL_CORE_I7_2",
+ "percentUsage": 70
+ }
+ ],
+ "memoryUsageArray": [
+ {
+ "vmIdentifier": "vmIdentifier",
+ "memoryFree": 50,
+ "memoryUsed": 10
+ }
+ ],
+ "vNicUsageArray": [
+ {
+ "receivedTotalPacketsDelta": 30
+ }
+ ],
+ "numberOfMediaPortsInUse": 100,
+ "additionalMeasurements": [
+ {
+ "name": "licenseUsage",
+ "arrayOfFields": [
+ {
+ "name": "G711AudioPort",
+ "value": "1"
+ },
+ {
+ "name": "G729AudioPort",
+ "value": "1"
+ },
+ {
+ "name": "G722AudioPort",
+ "value": "1"
+ },
+ {
+ "name": "AMRAudioPort",
+ "value": "4"
+ },
+ {
+ "name": "AMRWBAudioPort",
+ "value": "5"
+ },
+ {
+ "name": "OpusAudioPort",
+ "value": "6"
+ },
+ {
+ "name": "H263VideoPort",
+ "value": "7"
+ },
+ {
+ "name": "H264NonHCVideoPort",
+ "value": "8"
+ },
+ {
+ "name": "H264HCVideoPort",
+ "value": "9"
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/pnfsimulator/src/test/resources/trustStore b/pnfsimulator/src/test/resources/trustStore
new file mode 100644
index 0000000..e90b710
--- /dev/null
+++ b/pnfsimulator/src/test/resources/trustStore
Binary files differ
diff --git a/pnfsimulator/store/client.p12 b/pnfsimulator/store/client.p12
new file mode 100644
index 0000000..0bbec38
--- /dev/null
+++ b/pnfsimulator/store/client.p12
Binary files differ
diff --git a/pnfsimulator/store/trustStore b/pnfsimulator/store/trustStore
new file mode 100644
index 0000000..e90b710
--- /dev/null
+++ b/pnfsimulator/store/trustStore
Binary files differ
diff --git a/pnfsimulator/templates/measurement.json b/pnfsimulator/templates/measurement.json
new file mode 100644
index 0000000..a29b063
--- /dev/null
+++ b/pnfsimulator/templates/measurement.json
@@ -0,0 +1,44 @@
+{
+ "event": {
+ "commonEventHeader": {
+ "domain": "measurementsForVfScaling",
+ "eventName": "vFirewallBroadcastPackets",
+ "eventId": "eventID123121312323",
+ "nfType": "mrfx",
+ "priority": "Normal",
+ "reportingEntityName": "vnf",
+ "sequence": 1,
+ "sourceName": "sample-vnf-#RandomInteger(1,10)",
+ "startEpochMicrosec": "#TimestampPrimitive",
+ "lastEpochMicrosec": "#TimestampPrimitive",
+ "version": 1.0
+ },
+ "measurementsForVfScalingFields": {
+ "measurementsForVfScalingFieldsVersion": 2.0,
+ "measurementsForVfScalingVersion": 2.0,
+ "measurementInterval": 180,
+ "concurrentSessions": 2,
+ "requestRate": "#RandomPrimitiveInteger(50,100)",
+ "meanRequestLatency": "#RandomPrimitiveInteger(1,1000)",
+ "cpuUsageArray": [
+ {
+ "cpuIdentifier": "INTEL_CORE_I7_1",
+ "percentUsage": "#RandomPrimitiveInteger(1,100)"
+ },
+ {
+ "cpuIdentifier": "INTEL_CORE_I7_2",
+ "percentUsage": "#RandomPrimitiveInteger(1,100)"
+ }
+ ],
+ "memoryUsageArray": [
+ {
+ "vmIdentifier": "vmIdentifier",
+ "memoryFree": 50,
+ "memoryUsed": 10
+ }
+ ],
+ "numberOfMediaPortsInUse": 100,
+ "additionalMeasurements": []
+ }
+ }
+}
diff --git a/pnfsimulator/templates/notification.json b/pnfsimulator/templates/notification.json
new file mode 100644
index 0000000..74888fb
--- /dev/null
+++ b/pnfsimulator/templates/notification.json
@@ -0,0 +1,42 @@
+{
+ "event": {
+ "commonEventHeader": {
+ "domain": "notification",
+ "eventName": "vFirewallBroadcastPackets",
+ "eventId": "4cfc-91cf-31a46",
+ "priority": "Normal",
+ "reportingEntityName": "myVNF",
+ "sequence": 1,
+ "sourceName": "ClosedLoopVNF",
+ "startEpochMicrosec": 1531616794,
+ "lastEpochMicrosec": 1531719042,
+ "vesEventListenerVersion": "7.0.1",
+ "version": "4.0.1"
+ },
+ "notificationFields": {
+ "changeIdentifier": "PM_MEAS_FILES",
+ "changeType": "FileReady",
+ "arrayOfNamedHashMap": [
+ {
+ "name": "A20161221.1031-1041.bin.gz",
+ "hashMap": {
+ "fileformatType": "org.3GPP.32.435#measCollec",
+ "fileFormatVersion": "V10",
+ "location": "ftpes://192.169.0.1:22/ftp/rop/A20161224.1030-1045.bin.gz",
+ "compression": "gzip"
+ }
+ },
+ {
+ "name": "A20161222.1042-1102.bin.gz",
+ "hashMap": {
+ "fileFormatType": "org.3GPP.32.435#measCollec",
+ "fileFormatVersion": "V10",
+ "location": "ftpes://192.168.0.102:22/ftp/rop/A20161224.1045-1100.bin.gz",
+ "compression": "gzip"
+ }
+ }
+ ],
+ "notificationFieldsVersion": "2.0"
+ }
+ }
+}
diff --git a/pnfsimulator/templates/registration.json b/pnfsimulator/templates/registration.json
new file mode 100644
index 0000000..5c4dea5
--- /dev/null
+++ b/pnfsimulator/templates/registration.json
@@ -0,0 +1,33 @@
+{
+ "event": {
+ "commonEventHeader": {
+ "eventId": "registration_39239592",
+ "eventType": "pnfRegistration",
+ "reportingEntityName": "NOK6061ZW3",
+ "domain": "pnfRegistration",
+ "nfcNamingCode": "oam",
+ "sequence": 0,
+ "sourceId": "val13",
+ "internalHeaderFields": {},
+ "priority": "Normal",
+ "sourceName": "NOK6061ZW3",
+ "eventName": "pnfRegistration_Nokia_5gDu",
+ "version": "4.0.1",
+ "nfNamingCode": "gNB",
+ "startEpochMicrosec": 1539239592379,
+ "vesEventListenerVersion": "7.0.1",
+ "lastEpochMicrosec": 1539239592379
+ },
+ "pnfRegistrationFields": {
+ "pnfRegistrationFieldsVersion": "2.0",
+ "serialNumber": "6061ZW3",
+ "vendorName": "Nokia",
+ "oamV4IpAddress": "val3",
+ "oamV6IpAddress": "val4",
+ "unitFamily": "BBU",
+ "modelNumber": "val6",
+ "softwareVersion": "val7",
+ "unitType": "val8"
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..0bd2f23
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============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=========================================================
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>pom</packaging>
+
+ <parent>
+ <groupId>org.onap.oparent</groupId>
+ <artifactId>oparent</artifactId>
+ <version>2.0.0</version>
+ </parent>
+
+ <groupId>org.onap.simulator</groupId>
+ <artifactId>simulator-parent</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
+ <modules>
+ <module>pnfsimulator</module>
+ <module>netconfsimulator</module>
+ <module>deployment</module>
+ </modules>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ <docker-maven-plugin.version>0.31.0</docker-maven-plugin.version>
+ <skipDockerPush>true</skipDockerPush>
+ </properties>
+
+ <profiles>
+ <profile>
+ <id>tests</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <modules>
+ <module>pnfsimulator/integration</module>
+ </modules>
+ </profile>
+ </profiles>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>2.17</version>
+ <configuration>
+ <suppressionsLocation>checkstyle-suppressions.xml</suppressionsLocation>
+ <suppressionsFileExpression>checkstyle.suppressions.file</suppressionsFileExpression>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>io.fabric8</groupId>
+ <artifactId>docker-maven-plugin</artifactId>
+ <version>0.31.0</version>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/simulator-cli/.gitignore b/simulator-cli/.gitignore
new file mode 100644
index 0000000..96a29d6
--- /dev/null
+++ b/simulator-cli/.gitignore
@@ -0,0 +1,7 @@
+**/*.iml
+**/.idea
+**/target
+**/__pycache__
+build/**
+dist/**
+pnf_simulator_cli.egg-info/** \ No newline at end of file
diff --git a/simulator-cli/README.md b/simulator-cli/README.md
new file mode 100644
index 0000000..5dc6f76
--- /dev/null
+++ b/simulator-cli/README.md
@@ -0,0 +1,304 @@
+## PNF/NETCONF SIMULATOR CLI
+
+### Overview
+Anytime you want to see a basic usage of a tool, you can run fully descriptive help using command:
+```
+./{tool_name}.py -h # --help argument is also acceptable
+```
+
+#### PNF Simulator CLI
+PNF Simulator CLI provides command line interface to remotely interact with running PNF Simulator.
+
+Using the PNF Simulator CLI user is able to trigger events, retrieve simulator's configuration and change default VES url stored
+inside simulator.
+
+#### Netconf Simulator CLI
+Dedicated tool to help with management of the Netconf Server is also available.
+
+Using the Netconf Simulator CLI user is able to retrieve simulator's cm history stored inside simulator as well as open the live session to actively listen for new configuration changes.
+
+### Requirements and installation
+Requirements
+* Python > 3.5
+
+Installation:
+* Go to directory containing setup.py and invoke `python setup.py install`
+* Go to cli directory
+* Add executable privilege to pnf_simulator.py and netconf_simulator.py (for example `chmod +x <path_to_pnf_simulator.py>`)
+
+### Pnf simulator
+#### Usage
+* [send](#send-action)
+* [configure](#configure-action)
+* [get-config](#get-config-action)
+* [template](#template-action)
+* [filter](#filter-templates-action)
+
+
+#### Help
+Invoke `pnf_simulator.py [send|configure|get-config] -h` to display help.
+
+##### Send Action
+Send action allows user to trigger sending events from Simulator to VES Collector.
+
+*sending repeating events backed by template persisted in db
+`usage: pnf_simulator.py send template [-h] --address ADDRESS --name NAME
+ [--patch PATCH] [--repeats REPEATS]
+ [--interval INTERVAL]
+ [--ves-server-url VES_SERVER_URL] [--verbose]
+`
+
+Parameters
+` --address ADDRESS` `IP address of simulator`
+` --name NAME` `Name of template file which should be used as a base for event.
+ Cannot be used simultaneously with parameter: event.`
+` --patch PATCH` `Json which should be merged into template to override parameters.
+ Acceptable format: valid json wrapped using single quotes (example:'{"abc":1}').
+ Cannot be used simultaneously with parameter: event.`
+` --repeats REPEATS` `Number of events to be send`
+` --interval INTERVAL` `Interval between two consecutive events (in seconds)`
+` --ves-server-url VES_SERVER_URL` `Well-formed URL which will override current VES endpoint stored in simulator's DB`
+` --verbose` `Displays additional logs`
+
+
+*sending event only once by passing path to file with complete event
+`usage: pnf_simulator.py send event [-h] --address ADDRESS --filepath FILEPATH
+ [--ves-server-url VES_SERVER_URL] [--verbose]
+`
+Parameters
+` --address ADDRESS` `IP address of simulator`
+` --filepath FILEPATH` `Path to file with full, legitimate event that is to be send directly to VES only once.
+ This event is not associated with template and will not be persisted in db.
+ Cannot be used simultaneously with parameters: template and patch.`
+` --ves-server-url VES_SERVER_URL` `Well-formed URL which will override current VES endpoint stored in simulator's DB`
+` --verbose` `Displays additional logs`
+
+example content of file with complete event:
+```
+{
+ "commonEventHeader": {
+ "eventId": "#Timestamp",
+ "sourceName": "#Increment",
+ "version": 3.0
+ }
+}
+```
+
+##### Configure Action
+Configure action allows user to change Simulator's configuration (VES Server URL)
+`usage: pnf_simulator.py configure [-h] --address ADDRESS --ves-server-url
+ VES_SERVER_URL [--verbose]
+`
+
+Parameters
+
+` --address ADDRESS` `IP address of simulator`
+` --ves-server-url VES_SERVER_URL` `Well-formed URL which should be set as a default VES Server URL in simulator`
+` --verbose` `Displays additional logs`
+
+##### Get Config Action
+Get Config action allows user to retrieve actual Simulator's configuration
+`usage: pnf_simulator.py get-config [-h] --address ADDRESS [--verbose] `
+
+Parameters
+
+`--address ADDRESS` `IP address of simulator`
+`--verbose` `Displays additional logs`
+
+##### Template Action
+Template action allows user to:
+* retrieve a single template by name
+* list all available templates.
+* upload template to PNF Simulator (can overwrite existing template)
+
+`usage: pnf_simulator.py template [-h]
+ (--list | --get-content NAME | --upload FILENAME)
+ [--override] --address ADDRESS [--verbose]`
+
+Parameters
+
+`--get-content NAME` `Gets the template by name`
+`--list` `List all templates`
+`--upload FILENAME [--override]` `Uploads the template given as FILENAME file. Optionally overrides any exisitng templates with matching filename`
+`--address ADDRESS` `IP address of simulator`
+`--verbose` `Displays additional logs`
+
+#### Filter Templates Action
+Filter template action allows to search through templates in order to find names of those that satisfy given criteria.
+Criteria are passed in JSON format, as key-values pairs. Relation between pairs with criteria is AND (all conditions must be satisfied by template to have it returned).
+No searching for null values is supported.
+Search expression must be valid JSON, thus no duplicate keys are allowed - user could specify the same parameter multiple times, but only last occurrence will be applied to query.
+
+
+`usage: pnf_simulator.py filter [-h]
+ --criteria CRITERIA --address ADDRESS [--verbose]`
+
+Parameters
+`--criteria CRITERIA` `Json with criteria as key-value pairs, where values can be one of following data types: string, integer, double, boolean.
+ Acceptable format: valid json wrapped using single quotes (example:'{"searchedInt":1}').
+ Cannot be used simultaneously with parameter: event.`
+`--address ADDRESS` `IP address of simulator`
+`--verbose` `Displays additional logs`
+
+
+### Netconf simulator
+#### Usage
+* [load-model](#load-model-action)
+* [delete-model](#delete-model-action)
+* [get-config](#get-config-action)
+* [edit-config](#edit-config-action)
+* [tailf](#tailf-action)
+* [less](#less-action)
+* [cm-history](#cm-history-action)
+
+#### Help
+Invoke `netconf_simulator.py [tailf|less|cm-history] -h` to display help.
+
+
+#### Load-model action
+Loads to netconf server new YANG model that corresponds with schema passed as yang-model parameter,
+assigns name specified in module-name and initializes model with startup configuration passed in config file.
+`usage: netconf_simulator.py load-module [-h] --address ADDRESS ---module-name MODULE_NAME --yang-model YANG_MODEL_FILEPATH --config <XML_CONFIG_FILEPATH> [--verbose]`
+
+example YANG schema (file content for YANG_MODEL)
+```
+Response status: 200
+module pnf-simulator {
+ namespace "http://nokia.com/pnf-simulator";
+ prefix config;
+ container config {
+ config true;
+ leaf itemValue1 {type uint32;}
+ leaf itemValue2 {type uint32;}
+ leaf itemValue3 {type uint32;}
+ leaf-list allow-user {
+ type string;
+ ordered-by user;
+ description "A sample list of user names.";
+ }
+ }
+}
+```
+
+example startup configuration (file content of XML_CONFIG)
+```
+<config xmlns="http://nokia.com/pnf-simulator">
+ <itemValue1>100</itemValue1>
+ <itemValue2>200</itemValue2>
+ <itemValue3>300</itemValue3>
+</config>
+```
+
+
+example output (without verbose flag):
+```
+Response status: 200
+Successfully started
+```
+
+#### Delete-model action
+Deletes a YANG model loaded in the netconf server.
+
+`usage: netconf_simulator.py delete-model [-h] --address ADDRESS --model-name
+ MODEL_NAME [--verbose]`
+
+Example output (without verbose flag):
+```
+Response status: 200
+Successfully deleted
+```
+
+#### Get-config Action
+Returns active running configurations.
+By default it returns all running configurations. To retrieve one specific configuration (represented by _/'module_name':'container'_ ) user needs to pass module-name and container.
+Example:
+`
+netconf_simulator.py get-config --address localhost --module-name pnf-simulator --container config
+`
+
+
+`usage: netconf_simulator.py get-config [-h] --address ADDRESS [--verbose] [--module-name MODULE-NAME] [--container CONTAINER]`
+
+example output (without verbose flag):
+```
+Response status: 200
+<config xmlns="http://nokia.com/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>2781</itemValue1>
+ <itemValue2>3782</itemValue2>
+ <itemValue3>3333</itemValue3>
+</config>
+```
+
+#### Edit-config Action
+Modifies existing configuration (e.g. change parameter values, modify or remove parameter from model).
+To edit configuration, netconf compliant XML file should be prepared and used as one of edit-config parameters.
+`usage: netconf_simulator.py edit-config [-h] --address ADDRESS --config <XML_CONFIG_FILEPATH> [--verbose]`
+
+example - parameter values modification
+file content:
+```
+<config xmlns="http://nokia.com/pnf-simulator">
+ <itemValue1>1</itemValue1>
+ <itemValue2>2</itemValue2>
+ <itemValue3>3</itemValue3>
+</config>
+```
+
+example output (without verbose flag):
+```
+Response status: 202
+<config xmlns="http://nokia.com/pnf-simulator" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <itemValue1>1</itemValue1>
+ <itemValue2>2</itemValue2>
+ <itemValue3>3</itemValue3>
+</config>
+```
+
+##### Less Action
+Less action allows user to watch historical configuration changes.
+Size of the configuration changes list is limited to the 100 last cm events by default, but can be incresed/decresead using a 'limit' attribute.
+`usage: netconf_simulator.py less [-h] --address ADDRESS [--limit LIMIT] [--verbose]`
+
+Output from the command can be easily piped into other tools like native less, more, etc. e.g.:
+`netconf_simulator.py less --address 127.0.0.1 | less`
+
+Last known configuration is last printed to the output, so order of the printed configuration events complies with time when the configuration was stored inside the simulator.
+
+Parameters:
+
+`--address ADDRESS` - `IP address of simulator`
+
+`--limit LIMIT` - ` Number of configurations to print at output`
+
+`--verbose` - ` Displays additional logs`
+
+Single message is represented as a pair of timestamp in epoch format and suitable configuration entry.
+
+##### Tailf Action
+Tailf action allows user to actively listen for new uploaded configuration changes.
+Size of the historical configuration changes list is limited to the 10 last cm events.
+`usage: netconf_simulator.py tailf [-h] --address ADDRESS [--verbose]`
+
+The listener can be easily terminated at anytime using `CTRL+C` shortcut.
+
+Parameters:
+
+`--address ADDRESS` - `IP address of simulator`
+
+`--verbose` - ` Displays additional logs`
+
+Single message is represented as a pair of timestamp in epoch format and suitable configuration entry.
+
+##### Cm-history Action
+Cm-history action allows user to view list of all uploaded configuration changes.
+`usage: netconf_simulator.py cm-history [-h] --address ADDRESS [--verbose]`
+
+Last known configuration is last printed to the output, so order of the printed configuration events complies with time when the configuration was stored inside the simulator.
+
+Parameters:
+
+`--address ADDRESS` - `IP address of simulator`
+
+`--verbose` - ` Displays additional logs`
+
+Single message is represented as a pair of timestamp in epoch format and suitable configuration entry.
diff --git a/simulator-cli/cli/__init__.py b/simulator-cli/cli/__init__.py
new file mode 100644
index 0000000..aa8b4f9
--- /dev/null
+++ b/simulator-cli/cli/__init__.py
@@ -0,0 +1,19 @@
+###
+# ============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=========================================================
+###
diff --git a/simulator-cli/cli/client/__init__.py b/simulator-cli/cli/client/__init__.py
new file mode 100644
index 0000000..aa8b4f9
--- /dev/null
+++ b/simulator-cli/cli/client/__init__.py
@@ -0,0 +1,19 @@
+###
+# ============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=========================================================
+###
diff --git a/simulator-cli/cli/client/tailf_client.py b/simulator-cli/cli/client/tailf_client.py
new file mode 100644
index 0000000..d1cb60d
--- /dev/null
+++ b/simulator-cli/cli/client/tailf_client.py
@@ -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=========================================================
+###
+
+import logging
+
+import websockets
+import asyncio
+import signal
+import sys
+
+
+class TailfClient(object):
+
+ def __init__(self, url: str, verbose: bool = False) -> None:
+ self._url = url
+ self._is_running = False
+ self._connection = None
+ self.logger = logging.getLogger()
+ self.logger.setLevel(logging.DEBUG if verbose else logging.INFO)
+ signal.signal(signal.SIGINT, self._handle_keyboard_interrupt)
+
+ def tailf_messages(self):
+ self._is_running = True
+ self.logger.debug("Attempting to connect to websocket server on %s", self._url)
+ asyncio.get_event_loop().run_until_complete(
+ self._tailf_messages()
+ )
+
+ async def _tailf_messages(self):
+ try:
+ async with websockets.connect(self._url) as connection:
+ self.logger.debug("Connection with %s established", self._url)
+ self._connection = connection
+ while self._is_running:
+ print(await self._connection.recv(), "\n")
+ except ConnectionRefusedError:
+ self.logger.error("Cannot establish connection with %s", self._url)
+
+ def _handle_keyboard_interrupt(self, sig, frame):
+ self.logger.warning("CTR-C pressed, interrupting.")
+ self._is_running = False
+ sys.exit(0)
diff --git a/simulator-cli/cli/data/logging.ini b/simulator-cli/cli/data/logging.ini
new file mode 100644
index 0000000..8b2b402
--- /dev/null
+++ b/simulator-cli/cli/data/logging.ini
@@ -0,0 +1,20 @@
+[loggers]
+keys=root
+
+[handlers]
+keys=consoleHandler
+
+[formatters]
+keys=simpleFormatter
+
+[logger_root]
+level=DEBUG
+handlers=consoleHandler
+
+[handler_consoleHandler]
+class=StreamHandler
+formatter=simpleFormatter
+args=(sys.stdout,)
+
+[formatter_simpleFormatter]
+format=%(message)s
diff --git a/simulator-cli/cli/netconf_simulator.py b/simulator-cli/cli/netconf_simulator.py
new file mode 100755
index 0000000..a3b3bf1
--- /dev/null
+++ b/simulator-cli/cli/netconf_simulator.py
@@ -0,0 +1,278 @@
+#!/usr/bin/env python3
+###
+# ============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=========================================================
+###
+
+import argparse
+import logging
+import logging.config
+import requests
+import os
+import sys
+from requests import Response
+
+from cli.client.tailf_client import TailfClient
+
+TAILF_FUNC_ENDPOINT = "ws://{}:9000/netconf"
+LESS_FUNC_ENDPOINT = "/store/less"
+CM_HISTORY_ENDPOINT = "/store/cm-history"
+GET_CONFIG_ENDPOINT = "/netconf/get"
+MODEL_ENDPOINT = "/netconf/model/{}"
+EDIT_CONFIG_ENDPOINT = "/netconf/edit-config"
+logging.basicConfig()
+
+DEFAULT_EXTERNAL_SIM_PORT = 8080
+DEFAULT_INTERNAL_SIM_PORT = 9000
+
+
+class NetconfSimulatorClient(object):
+ def __init__(self, ip: str, protocol: str = 'http', port: int = DEFAULT_EXTERNAL_SIM_PORT, verbose: bool = False) -> None:
+ self._ip = ip
+ self._protocol = protocol
+ self._port = port
+ self._configure_logger(verbose)
+ self._verbose=verbose
+
+ def tailf_like_func(self) -> None:
+ url = TAILF_FUNC_ENDPOINT.format(self._ip)
+ client = TailfClient(url, self._verbose)
+ client.tailf_messages()
+
+ def get_cm_history(self) -> None:
+ self.logger.info("Attempting to retrieve all netconf configuration changes")
+ simulator_address = "{}://{}:{}{}".format(self._protocol, self._ip, self._port, CM_HISTORY_ENDPOINT)
+ self.logger.debug("Simulator address: %s", simulator_address)
+ try:
+ response = requests.get(simulator_address)
+ self._log_json_response(response)
+ except requests.ConnectionError:
+ self.logger.error("Failed to establish connection with {}".format(simulator_address))
+
+ def less_like_func(self, limit: int) -> None:
+ self.logger.info("Attempting to run less on CM change")
+ simulator_address = "{}://{}:{}{}".format(self._protocol, self._ip, self._port, LESS_FUNC_ENDPOINT)
+ parameters = {"offset": limit} if limit else None
+ self.logger.debug("Simulator address: %s", simulator_address)
+ try:
+ response = requests.get(url = simulator_address, params = parameters)
+ self._log_json_response(response)
+ except requests.ConnectionError:
+ self.logger.error("Failed to establish connection with {}".format(simulator_address))
+
+ def get_config(self, module_name: str=None, container:str=None)-> None:
+ self.logger.info("Attempting to run get-config")
+ simulator_address = self._create_get_endpoint(module_name, container)
+ self.logger.debug("Simulator address: %s", simulator_address)
+ try:
+ response = requests.get(simulator_address)
+ self._log_string_response(response)
+ except requests.ConnectionError:
+ self.logger.error("Failed to establish connection with {}".format(simulator_address))
+
+ def load_yang_model(self, module_name: str, yang_model_path: str, config_path: str) -> None:
+ self.logger.info(
+ "Attempting to load new yang model with its initial configuration")
+ simulator_address = "{}://{}:{}{}".format(self._protocol, self._ip, self._port, MODEL_ENDPOINT.format(module_name))
+ files = {"yangModel": open(yang_model_path, "rb"),
+ "initialConfig": open(config_path, "rb")}
+ self.logger.debug("Simulator address: %s", simulator_address)
+
+ try:
+ response = requests.post(simulator_address, files=files)
+ self._log_string_response(response)
+ except requests.ConnectionError:
+ self.logger.error("Failed to establish connection with {}".format(simulator_address))
+
+ def delete_yang_model(self, model_name: str) -> None:
+ self.logger.info(
+ "Attempting to delete a yang model")
+ simulator_address = "{}://{}:{}{}".format(self._protocol, self._ip, self._port, MODEL_ENDPOINT.format(model_name))
+ self.logger.debug("Simulator address: %s", simulator_address)
+
+ try:
+ response = requests.delete(simulator_address)
+ self._log_string_response(response)
+ except requests.ConnectionError:
+ self.logger.error("Failed to establish connection with {}".format(simulator_address))
+
+ def edit_config(self, new_config_path: str):
+ self.logger.info("Attempting to apply new configuration")
+ simulator_address = "{}://{}:{}{}".format(self._protocol, self._ip, self._port, EDIT_CONFIG_ENDPOINT)
+ files = {"editConfigXml": open(new_config_path,"rb")}
+ self.logger.debug("Simulator address: %s", simulator_address)
+
+ try:
+ response = requests.post(simulator_address, files=files)
+ self._log_string_response(response)
+ except requests.ConnectionError:
+ self.logger.error("Failed to establish connection with {}".format(simulator_address))
+
+ def _log_json_response(self, response: Response) ->None:
+ self.logger.info("Response status: %d", response.status_code)
+ self.logger.info(" ----- HEAD -----")
+ for message in response.json():
+ self.logger.info("{}: {}".format(str(message['timestamp']), message['configuration']))
+ self.logger.info(" ----- END ------")
+ self.logger.debug(response.headers)
+
+ def _configure_logger(self, verbose):
+ logging_conf = os.path.join(sys.prefix, 'logging.ini')
+ if os.path.exists(logging_conf):
+ logging.config.fileConfig(logging_conf)
+ else:
+ print("Couldn't find logging.ini, using default logger config")
+ self.logger = logging.getLogger()
+ self.logger.setLevel(logging.DEBUG if verbose else logging.INFO)
+
+ def _log_string_response(self, response: Response)->None:
+ self.logger.info("Response status: %d", response.status_code)
+ self.logger.info(response.text)
+ self.logger.debug(response.headers)
+
+ def _create_get_endpoint(self, module_name: str, container: str):
+ endpoint = "{}://{}:{}{}".format(self._protocol, self._ip, self._port,
+ GET_CONFIG_ENDPOINT)
+ if module_name and container:
+ endpoint = endpoint + "/{}/{}".format(module_name, container)
+ elif (not module_name and container) or (module_name and not container):
+ raise AttributeError(
+ "Both module_name and container must be present or absent")
+ return endpoint
+
+def create_argument_parser():
+ parser = argparse.ArgumentParser(description="Netconf Simulator Command Line Interface. ")
+ subparsers = parser.add_subparsers(title="Available actions")
+ tailf_parser = subparsers.add_parser("tailf",
+ description="Method which allows user to view N last lines of configuration changes")
+
+ __configure_tailf_like_parser(tailf_parser)
+ less_parser = subparsers.add_parser("less", description="Method which allows user to traverse configuration changes")
+ __configure_less_like_parser(less_parser)
+ cm_history_parser = subparsers.add_parser("cm-history",
+ description="Method which allows user to view all configuration changes")
+ __configure_cm_history_parser(cm_history_parser)
+
+ load_model_parser = subparsers.add_parser("load-model")
+ __configure_load_model_parser(load_model_parser)
+
+ delete_model_parser = subparsers.add_parser("delete-model")
+ __configure_delete_model_parser(delete_model_parser)
+
+ get_config_parser = subparsers.add_parser("get-config")
+ __configure_get_config_parser(get_config_parser)
+ edit_config_parser = subparsers.add_parser("edit-config")
+ __configure_edit_config_parser(edit_config_parser)
+ return parser
+
+
+def run_tailf(args):
+ client = NetconfSimulatorClient(args.address, verbose=args.verbose)
+ client.tailf_like_func()
+
+
+def run_get_cm_history(args):
+ client = NetconfSimulatorClient(args.address, verbose=args.verbose, port=DEFAULT_INTERNAL_SIM_PORT)
+ client.get_cm_history()
+
+
+def run_less(args):
+ client = NetconfSimulatorClient(args.address, verbose=args.verbose, port=DEFAULT_INTERNAL_SIM_PORT)
+ client.less_like_func(args.limit)
+
+
+def run_load_model(args):
+ client = NetconfSimulatorClient(args.address, verbose=args.verbose,
+ port=DEFAULT_INTERNAL_SIM_PORT)
+ client.load_yang_model(args.module_name, args.yang_model, args.config)
+
+
+def run_delete_model(args):
+ client = NetconfSimulatorClient(args.address, verbose=args.verbose,
+ port=DEFAULT_INTERNAL_SIM_PORT)
+ client.delete_yang_model(args.model_name)
+
+
+def run_get_config(args):
+ client = NetconfSimulatorClient(args.address, verbose=args.verbose, port=DEFAULT_INTERNAL_SIM_PORT)
+ client.get_config(args.module_name, args.container)
+
+
+def run_edit_config(args):
+ client = NetconfSimulatorClient(args.address, verbose=args.verbose, port=DEFAULT_INTERNAL_SIM_PORT)
+ client.edit_config(args.config)
+
+
+def __configure_tailf_like_parser(tailf_func_parser):
+ tailf_func_parser.add_argument("--address", required=True, help="IP address of simulator")
+ tailf_func_parser.add_argument("--verbose", action='store_true',
+ help="Displays additional logs")
+ tailf_func_parser.set_defaults(func=run_tailf)
+
+
+def __configure_less_like_parser(less_func_parser):
+ less_func_parser.add_argument("--address", required=True, help="IP address of simulator")
+ less_func_parser.add_argument("--limit", help="Limit of configurations to retrieve")
+ less_func_parser.add_argument("--verbose", action='store_true', help="Displays additional logs")
+ less_func_parser.set_defaults(func=run_less)
+
+
+def __configure_cm_history_parser(cm_history_parser):
+ cm_history_parser.add_argument("--address", required=True, help="IP address of simulator")
+ cm_history_parser.add_argument("--verbose", action='store_true', help="Displays additional logs")
+ cm_history_parser.set_defaults(func=run_get_cm_history)
+
+
+def __configure_load_model_parser(load_model_parser):
+ load_model_parser.add_argument("--address", required=True, help="IP address of simulator")
+ load_model_parser.add_argument("--module-name", required=True, help="Module name corresponding to yang-model")
+ load_model_parser.add_argument("--verbose", action='store_true', help="Displays additional logs")
+ load_model_parser.add_argument("--yang-model", required=True, help="Path to file with yang model")
+ load_model_parser.add_argument("--config", required=True, help="Path to file with initial xml config")
+ load_model_parser.set_defaults(func=run_load_model)
+
+
+def __configure_delete_model_parser(delete_model_parser):
+ delete_model_parser.add_argument("--address", required=True, help="IP address of simulator")
+ delete_model_parser.add_argument("--model-name", required=True, help="YANG model name to delete")
+ delete_model_parser.add_argument("--verbose", action='store_true', help="Displays additional logs")
+ delete_model_parser.set_defaults(func=run_delete_model)
+
+
+def __configure_get_config_parser(get_config_parser):
+ get_config_parser.add_argument("--address", required=True, help="IP address of simulator")
+ get_config_parser.add_argument("--verbose", action='store_true',help="Displays additional logs")
+ get_config_parser.add_argument("--module-name", help="Module name corresponding to yang-model", default=None)
+ get_config_parser.add_argument("--container", help="Container name corresponding to module name", default=None)
+ get_config_parser.set_defaults(func=run_get_config)
+
+
+def __configure_edit_config_parser(edit_config_parser):
+ edit_config_parser.add_argument("--address", required=True, help="IP address of simulator")
+ edit_config_parser.add_argument("--verbose", action='store_true', help="Displays additional logs")
+ edit_config_parser.add_argument("--config", required=True, help="Path to file with xml config to apply")
+ edit_config_parser.set_defaults(func=run_edit_config)
+
+
+if __name__ == "__main__":
+ argument_parser = create_argument_parser()
+ result = argument_parser.parse_args()
+ if hasattr(result, 'func'):
+ result.func(result)
+ else:
+ argument_parser.parse_args(['-h'])
diff --git a/simulator-cli/cli/pnf_simulator.py b/simulator-cli/cli/pnf_simulator.py
new file mode 100755
index 0000000..9176fd8
--- /dev/null
+++ b/simulator-cli/cli/pnf_simulator.py
@@ -0,0 +1,374 @@
+#!/usr/bin/env python3
+###
+# ============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=========================================================
+###
+import argparse
+import http.client
+import json
+import logging
+import ntpath
+from typing import Dict
+
+SEND_PERIODIC_EVENT_ENDPOINT = "/simulator/start"
+SEND_ONE_TIME_EVENT_ENDPOINT = "/simulator/event"
+CONFIG_ENDPOINT = "/simulator/config"
+LIST_TEMPLATES_ENDPOINT = "/template/list"
+GET_TEMPLATE_BY_NAME_ENDPOINT = "/template/get"
+UPLOAD_TEMPLATE_NOFORCE = "/template/upload"
+UPLOAD_TEMPLATE_FORCE = "/template/upload?override=true"
+FILTER_TEMPLATES_ENDPOINT = "/template/search"
+
+logging.basicConfig()
+
+
+class Messages(object):
+ OVERRIDE_VALID_ONLY_WITH_UPLOAD = "--override is valid only with --upload parameter"
+
+
+class SimulatorParams(object):
+ def __init__(self, repeats: int = 1, interval: int = 1, ves_server_url: str = None) -> None:
+ self.repeats_count = repeats
+ self.repeats_interval = interval
+ self.ves_server_url = ves_server_url
+
+ def to_json(self) -> Dict:
+ to_return = {"repeatCount": self.repeats_count,
+ "repeatInterval": self.repeats_interval}
+ if self.ves_server_url:
+ to_return["vesServerUrl"] = self.ves_server_url
+ return to_return
+
+ def __repr__(self) -> str:
+ return str(self.to_json())
+
+
+class PersistedEventRequest(object):
+ def __init__(self, simulator_params: SimulatorParams, template: str, patch: Dict = None) -> None:
+ self.params = simulator_params
+ self.template = template
+ self.patch = patch or {}
+
+ def to_json(self) -> Dict:
+ return {"simulatorParams": self.params, "templateName": self.template,
+ "patch": self.patch}
+
+ def __repr__(self) -> str:
+ return str(self.to_json())
+
+
+class FullEventRequest(object):
+ def __init__(self, event_body: Dict, ves_server_url: str = None) -> None:
+ self.event_body = event_body
+ self.ves_server_url = ves_server_url or ""
+
+ def to_json(self) -> Dict:
+ return {"vesServerUrl": self.ves_server_url, "event": self.event_body}
+
+ def __repr__(self) -> str:
+ return str(self.to_json())
+
+
+class TemplateUploadRequest(object):
+ def __init__(self, template_name: str, template_body: Dict) -> None:
+ self.template_name = template_name
+ self.template_body = template_body
+
+ def to_json(self) -> Dict:
+ return {"name": self.template_name, "template": self.template_body}
+
+ def __repr__(self) -> str:
+ return str(self.to_json())
+
+
+class SimulatorClient(object):
+ def __init__(self, ip: str, port: int = 5000, verbose: bool = False) -> None:
+ self._ip = ip
+ self._port = port
+ self.logger = logging.getLogger()
+ self.logger.setLevel(logging.DEBUG if verbose else logging.INFO)
+
+ def send_event(self, request: PersistedEventRequest) -> None:
+ connection = http.client.HTTPConnection(self._ip, self._port)
+ self.logger.info("Attempting to send event")
+ self.logger.debug("Simulator address: ip %s, port %s, endpoint %s", self._ip, self._port, SEND_PERIODIC_EVENT_ENDPOINT)
+ self.logger.debug("REQUEST %s", request)
+
+ connection.request("POST", SEND_PERIODIC_EVENT_ENDPOINT, body=json.dumps(request, cls=RequestSerializer),
+ headers={"Content-Type": "application/json"})
+
+ response = connection.getresponse()
+
+ self._log_response(response)
+ connection.close()
+
+ def send_one_time_event(self, request: FullEventRequest) -> None:
+ connection = http.client.HTTPConnection(self._ip, self._port)
+ self.logger.info("Attempting to send one time event")
+ self.logger.debug("Simulator address: ip %s, port %s, endpoint %s", self._ip, self._port, SEND_ONE_TIME_EVENT_ENDPOINT)
+ self.logger.debug("REQUEST %s", request.to_json())
+
+ connection.request("POST", SEND_ONE_TIME_EVENT_ENDPOINT, body=json.dumps(request.to_json()),
+ headers={"Content-Type": "application/json"})
+
+ response = connection.getresponse()
+
+ self._log_response(response)
+ connection.close()
+
+ def get_configuration(self) -> None:
+ connection = http.client.HTTPConnection(self._ip, self._port)
+ self.logger.info("Attempting to retrieve Simulator configuration")
+ self.logger.debug("Simulator address: ip %s, port %s, endpoint %s", self._ip, self._port, CONFIG_ENDPOINT)
+ connection.request("GET", CONFIG_ENDPOINT)
+ response = connection.getresponse()
+
+ self._log_response(response)
+ connection.close()
+
+ def edit_configuration(self, ves_server_url: str) -> None:
+ connection = http.client.HTTPConnection(self._ip, self._port)
+ self.logger.info("Attempting to update Simulator configuration")
+ self.logger.debug("Simulator address: ip %s, port %s, endpoint %s", self._ip, self._port, CONFIG_ENDPOINT)
+ request = {"vesServerUrl": ves_server_url}
+ self.logger.debug("REQUEST %s", request)
+ connection.request("PUT", CONFIG_ENDPOINT, body=json.dumps(request),
+ headers={"Content-Type": "application/json"})
+
+ response = connection.getresponse()
+
+ self._log_response(response)
+ connection.close()
+
+ def _log_response(self, response: http.client.HTTPResponse):
+ self.logger.info("Response status: %s ", response.status)
+ self.logger.info(response.read().decode())
+ self.logger.debug(response.headers)
+
+ def list_templates(self):
+ connection = http.client.HTTPConnection(self._ip, self._port)
+ self.logger.info("Attempting to retrieve all templates")
+ self.logger.debug("Simulator address: ip %s, port %s, endpoint %s", self._ip, self._port, LIST_TEMPLATES_ENDPOINT)
+ connection.request("GET", LIST_TEMPLATES_ENDPOINT)
+ response = connection.getresponse()
+
+ self._log_response(response)
+ connection.close()
+
+ def get_template_by_name(self, name):
+ connection = http.client.HTTPConnection(self._ip, self._port)
+ endpoint = GET_TEMPLATE_BY_NAME_ENDPOINT + "/" + name
+ self.logger.info("Attempting to retrieve template by name: '%s'", name)
+ self.logger.debug("Simulator address: ip %s, port %s, endpoint %s", self._ip, self._port, endpoint)
+ connection.request("GET", endpoint)
+ response = connection.getresponse()
+
+ self._log_response(response)
+ connection.close()
+
+ def upload_template(self, template_request, force):
+ connection = http.client.HTTPConnection(self._ip, self._port)
+ endpoint = UPLOAD_TEMPLATE_FORCE if force else UPLOAD_TEMPLATE_NOFORCE
+ self.logger.info("Attempting to upload template: '%s'", template_request)
+ self.logger.debug("Simulator address: ip %s, port %s, endpoint %s", self._ip, self._port, endpoint)
+ connection.request("POST", endpoint,
+ body=json.dumps(template_request.to_json()),
+ headers={"Content-Type": "application/json"})
+ response = connection.getresponse()
+
+ self._log_response(response)
+ connection.close()
+
+ def search_for_templates(self, filter_criteria: str):
+ connection = http.client.HTTPConnection(self._ip, self._port)
+ self.logger.debug("Simulator address: ip %s, port %s, endpoint %s", self._ip, self._port, FILTER_TEMPLATES_ENDPOINT)
+ filter_request = {"searchExpr": json.loads(filter_criteria)}
+ self.logger.debug("Filter criteria: %s", str(filter_criteria))
+ connection.request("POST", FILTER_TEMPLATES_ENDPOINT,
+ body=json.dumps(filter_request),
+ headers={"Content-Type": "application/json"})
+ response = connection.getresponse()
+
+ self._log_response(response)
+ connection.close()
+
+
+class RequestSerializer(json.JSONEncoder):
+ def default(self, o):
+ return o.to_json() if (isinstance(o, SimulatorParams) or isinstance(o, PersistedEventRequest)) else o
+
+
+
+def create_argument_parser():
+ parser = argparse.ArgumentParser(description="PNF Simulator Command Line Interface. ")
+ subparsers = parser.add_subparsers(title="Available actions")
+ send_parser = subparsers.add_parser("send",
+ description="Method which allows user to trigger simulator to start sending "
+ "events. Available options: [template, event]")
+
+ send_subparsers = send_parser.add_subparsers()
+ one_time_send_event_parser = send_subparsers.add_parser("event", description="Option for direct, one-time event sending to VES. This option does not require having corresponging template.")
+ __configure_one_time_send_parser(one_time_send_event_parser)
+ persisted_send_event_parser = send_subparsers.add_parser("template")
+ __configure_persisted_send_parser(persisted_send_event_parser)
+
+ configure_parser = subparsers.add_parser("configure", description="Method which allows user to set new default "
+ "value for VES Endpoint")
+ __configure_config_parser(configure_parser)
+
+ get_config_parser = subparsers.add_parser("get-config",
+ description="Method which allows user to view simulator configuration")
+ __configure_get_config_parser(get_config_parser)
+
+ template_config_parser = subparsers.add_parser("template", description="Template management operations")
+ __configure_template_parser(template_config_parser)
+
+ template_filter_parser = subparsers.add_parser("filter", description="Method for searching through templates to find those satisfying given criteria")
+ __configure_template_filter_parser(template_filter_parser)
+
+ return parser
+
+
+def _perform_send_action(args):
+ if (not args.interval and args.repeats) or (args.interval and not args.repeats):
+ raise Exception("Either both repeats and interval must be present or missing")
+
+ client = SimulatorClient(args.address, verbose=args.verbose)
+ client.send_event(_create_scheduled_event_request(args))
+
+
+def _perform_one_time_send_action(args):
+ client = SimulatorClient(args.address, verbose=args.verbose)
+ client.send_one_time_event(_create_one_time_event_request(args.filepath, args.ves_server_url))
+
+
+def get_configuration(args):
+ client = SimulatorClient(args.address, verbose=args.verbose)
+ client.get_configuration()
+
+
+def edit_configuration(args):
+ client = SimulatorClient(args.address, verbose=args.verbose)
+ client.edit_configuration(args.ves_server_url)
+
+
+def perform_template_action(args):
+ client = SimulatorClient(args.address, verbose=args.verbose)
+ if args.list:
+ client.list_templates()
+ elif args.get_content:
+ client.get_template_by_name(args.get_content)
+ elif args.upload:
+ client.upload_template(_create_upload_template_request(args.upload), args.override)
+ elif args.force:
+ raise Exception(Messages.OVERRIDE_VALID_ONLY_WITH_UPLOAD)
+
+
+def list_all_templates(args):
+ client = SimulatorClient(args.address, verbose=args.verbose)
+ client.list_templates()
+
+
+def filter_templates(args):
+ client = SimulatorClient(args.address, verbose=args.verbose)
+ client.search_for_templates(args.criteria)
+
+
+def _create_upload_template_request(template_filename):
+ with open(template_filename) as json_template:
+ template_body = json.load(json_template)
+ return TemplateUploadRequest(path_leaf(template_filename), template_body)
+
+
+def _create_scheduled_event_request(args):
+ simulator_params = SimulatorParams(args.repeats, args.interval, args.ves_server_url)
+ return PersistedEventRequest(simulator_params, args.name, json.loads(args.patch) if args.patch else {})
+
+
+def _create_one_time_event_request(event_filename, ves_server_url):
+ with open(event_filename) as json_event:
+ event_body = json.load(json_event)
+ return FullEventRequest(event_body, ves_server_url)
+
+
+def __configure_persisted_send_parser(send_parser):
+ send_parser.add_argument("--address", required=True, help="IP address of simulator")
+ send_parser.add_argument("--name", required=True, help="Name of template file which should be used as a base for event")
+ send_parser.add_argument("--patch", help="Json which should be merged into template to override parameters")
+ send_parser.add_argument("--repeats", help="Number of events to be send", type=int)
+ send_parser.add_argument("--interval", help="Interval between two consecutive events (in seconds)", type=int)
+ send_parser.add_argument("--ves_server_url",
+ help="Well-formed URL which will override current VES endpoint stored in simulator's DB")
+ send_parser.add_argument("--verbose", action='store_true', help="Displays additional logs")
+ send_parser.set_defaults(func=_perform_send_action)
+
+
+def __configure_one_time_send_parser(send_parser):
+ send_parser.add_argument("--address", required=True, help="IP address of simulator")
+ send_parser.add_argument("--filepath", required=True, help="Name of file with complete event for direct sending.")
+ send_parser.add_argument("--ves_server_url",
+ help="Well-formed URL which will override current VES endpoint stored in simulator's DB")
+ send_parser.add_argument("--verbose", action='store_true', help="Displays additional logs")
+ send_parser.set_defaults(func=_perform_one_time_send_action)
+
+
+def __configure_config_parser(config_parser):
+ config_parser.add_argument("--address", required=True, help="IP address of simulator")
+ config_parser.add_argument("--ves-server-url", required=True,
+ help="Well-formed URL which should be set as a default VES Server URL in simulator")
+ config_parser.add_argument("--verbose", action='store_true', help="Displays additional logs")
+ config_parser.set_defaults(func=edit_configuration)
+
+
+def __configure_get_config_parser(get_config_parser):
+ get_config_parser.add_argument("--address", required=True, help="IP address of simulator")
+ get_config_parser.add_argument("--verbose", action='store_true', help="Displays additional logs")
+ get_config_parser.set_defaults(func=get_configuration)
+
+
+def __configure_template_parser(template_config_parser):
+ group = template_config_parser.add_mutually_exclusive_group(required=True)
+ group.add_argument("--list", action='store_true', help="List all templates")
+ group.add_argument("--get-content", help="Gets the template by name")
+ group.add_argument("--upload", help="Uploads the template given in parameter file.")
+
+ template_config_parser.add_argument("--override", action='store_true', help="Overwrites the template in case it exists.")
+ template_config_parser.add_argument("--address", required=True, help="IP address of simulator")
+ template_config_parser.add_argument("--verbose", action='store_true', help="Displays additional logs")
+ template_config_parser.set_defaults(func=perform_template_action)
+
+
+def __configure_template_filter_parser(template_filter_parser):
+ template_filter_parser.add_argument("--criteria", required=True, help="Json string with key-value search criteria")
+ template_filter_parser.add_argument("--address", required=True, help="IP address of simulator")
+ template_filter_parser.add_argument("--verbose", action='store_true', help="Displays additional logs")
+ template_filter_parser.set_defaults(func=filter_templates)
+
+
+def path_leaf(path):
+ head, tail = ntpath.split(path)
+ return tail or ntpath.basename(head)
+
+
+if __name__ == "__main__":
+ argument_parser = create_argument_parser()
+ result = argument_parser.parse_args()
+ if hasattr(result, 'func'):
+ result.func(result)
+ else:
+ argument_parser.parse_args(['-h'])
diff --git a/simulator-cli/requirements.txt b/simulator-cli/requirements.txt
new file mode 100644
index 0000000..4f962de
--- /dev/null
+++ b/simulator-cli/requirements.txt
@@ -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=========================================================
+###
+requests==2.20.1
+websockets==7.0
+asynctest
+mock
diff --git a/simulator-cli/setup.py b/simulator-cli/setup.py
new file mode 100644
index 0000000..26578a0
--- /dev/null
+++ b/simulator-cli/setup.py
@@ -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=========================================================
+###
+
+import setuptools
+
+setuptools.setup(
+ name="pnf_simulator_cli",
+ version="5.0.0",
+ description="Command line interface which allows to communicate with PNF SIMULATOR",
+ packages=setuptools.find_packages(),
+ data_files=['cli/data/logging.ini'],
+ classifiers=["Programming Language :: Python :: 3"],
+ install_requires=[
+ 'requests==2.20.1',
+ 'websockets==7.0'
+ ]
+)
diff --git a/simulator-cli/tests/__init__.py b/simulator-cli/tests/__init__.py
new file mode 100644
index 0000000..aa8b4f9
--- /dev/null
+++ b/simulator-cli/tests/__init__.py
@@ -0,0 +1,19 @@
+###
+# ============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=========================================================
+###
diff --git a/simulator-cli/tests/resources/notification.json b/simulator-cli/tests/resources/notification.json
new file mode 100644
index 0000000..ca9641e
--- /dev/null
+++ b/simulator-cli/tests/resources/notification.json
@@ -0,0 +1,17 @@
+{
+ "commonEventHeader": {
+ "domain": "notification",
+ "eventName": "#RandomString(20)",
+ "version": "4.0.1"
+ },
+ "notificationFields": {
+ "arrayOfNamedHashMap": [
+ {
+ "name": "A20161221.1031-1041.bin.gz",
+ "hashMap": {
+ "fileformatType": "org.3GPP.32.435#measCollec"
+ }
+ }
+ ]
+ }
+}
diff --git a/simulator-cli/tests/test_netconf_simulator.py b/simulator-cli/tests/test_netconf_simulator.py
new file mode 100644
index 0000000..2b78a1b
--- /dev/null
+++ b/simulator-cli/tests/test_netconf_simulator.py
@@ -0,0 +1,165 @@
+###
+# ============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=========================================================
+###
+import logging
+import unittest
+import os
+from mock import patch
+
+from cli.netconf_simulator import create_argument_parser, NetconfSimulatorClient
+
+
+class TestArgumentParser(unittest.TestCase):
+
+ def test_should_properly_parse_edit_config_with_all_params(self):
+ parser = create_argument_parser()
+ args = parser.parse_args(
+ ['edit-config', '--address', '127.0.0.1', '--config', 'sample_path',
+ "--verbose"]
+ )
+
+ self.assertEqual(args.address, '127.0.0.1')
+ self.assertEqual(args.config, 'sample_path')
+ self.assertTrue(args.verbose)
+
+ def test_should_properly_parse_load_yang_model(self):
+ parser = create_argument_parser()
+
+ args = parser.parse_args(
+ ['load-model', '--address', '127.0.0.1', '--module-name',
+ 'sample_name', '--yang-model', 'sample_model', '--config',
+ 'sample_config',
+ "--verbose"]
+ )
+
+ self.assertEqual(args.address, '127.0.0.1')
+ self.assertEqual(args.config, 'sample_config')
+ self.assertEqual(args.yang_model, 'sample_model')
+ self.assertEqual(args.module_name, 'sample_name')
+ self.assertTrue(args.verbose)
+
+ def test_should_properly_parse_delete_yang_model(self):
+ parser = create_argument_parser()
+
+ args = parser.parse_args(
+ ['delete-model', '--address', '127.0.0.1', '--model-name',
+ 'sample_name', "--verbose"]
+ )
+
+ self.assertEqual(args.address, '127.0.0.1')
+ self.assertEqual(args.model_name, 'sample_name')
+ self.assertTrue(args.verbose)
+
+ def test_should_properly_parse_get_config(self):
+ parser = create_argument_parser()
+ args = parser.parse_args(
+ ['get-config', '--address', '127.0.0.1', '--verbose']
+ )
+
+ self.assertEqual(args.address, '127.0.0.1')
+ self.assertTrue(args.verbose)
+
+
+class TestNetconfSimulatorClient(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ with open("example", "w+") as f:
+ f.write("sampleContent")
+
+ @classmethod
+ def tearDownClass(cls):
+ os.remove("example")
+
+ @patch('cli.netconf_simulator.requests')
+ @patch('cli.netconf_simulator.NetconfSimulatorClient._configure_logger')
+ def test_should_properly_get_config(self, logger, requests):
+ client = NetconfSimulatorClient('localhost')
+ client.logger = logging.getLogger()
+
+ client.get_config()
+
+ requests.get.assert_called_with('http://localhost:8080/netconf/get')
+
+ @patch('cli.netconf_simulator.requests')
+ @patch('cli.netconf_simulator.NetconfSimulatorClient._configure_logger')
+ def test_should_properly_get_config_for_given_module(self, logger, requests):
+ client = NetconfSimulatorClient('localhost')
+ client.logger = logging.getLogger()
+
+ client.get_config("module", "container")
+
+ requests.get.assert_called_with('http://localhost:8080/netconf/get/module/container')
+
+ @patch('cli.netconf_simulator.NetconfSimulatorClient._configure_logger')
+ def test_should_raise_exception_when_module_is_present_and_container_is_absent(self, logger):
+ client = NetconfSimulatorClient('localhost')
+ client.logger = logging.getLogger()
+
+ with self.assertRaises(AttributeError) as context: # pylint: disable=W0612
+ client.get_config(module_name="test")
+
+ @patch('cli.netconf_simulator.NetconfSimulatorClient._configure_logger')
+ def test_should_raise_exception_when_module_is_absent_and_container_is_present(self, logger):
+ client = NetconfSimulatorClient('localhost')
+ client.logger = logging.getLogger()
+
+ with self.assertRaises(AttributeError) as context: # pylint: disable=W0612
+ client.get_config(container="test")
+
+ @patch('cli.netconf_simulator.requests')
+ @patch('cli.netconf_simulator.NetconfSimulatorClient._configure_logger')
+ def test_should_properly_load_yang_model(self, logger, requests):
+ client = NetconfSimulatorClient('localhost')
+ client.logger = logging.getLogger()
+
+ client.load_yang_model('sample_module_name', 'example', 'example')
+
+ requests.post.assert_called()
+
+ @patch('cli.netconf_simulator.requests')
+ @patch('cli.netconf_simulator.NetconfSimulatorClient._configure_logger')
+ def test_should_properly_delete_yang_model(self, logger, requests):
+ client = NetconfSimulatorClient('localhost')
+ client.logger = logging.getLogger()
+
+ client.delete_yang_model('sample_model_name')
+
+ requests.delete.assert_called()
+
+ @patch('cli.netconf_simulator.requests')
+ @patch('cli.netconf_simulator.NetconfSimulatorClient._configure_logger')
+ def test_should_properly_edit_config(self, logger, requests):
+ client = NetconfSimulatorClient('localhost')
+ client.logger = logging.getLogger()
+
+ client.edit_config('example')
+
+ requests.post.assert_called()
+
+ @patch('cli.netconf_simulator.requests')
+ @patch('cli.netconf_simulator.NetconfSimulatorClient._configure_logger')
+ def test_should_properly_run_less_like_mode(self, logger, requests):
+ client = NetconfSimulatorClient('localhost')
+ client.logger = logging.getLogger()
+
+ client.less_like_func(100)
+
+ requests.get.assert_called_with(
+ params={"offset": 100}, url="http://localhost:8080/store/less")
diff --git a/simulator-cli/tests/test_pnf_simulator.py b/simulator-cli/tests/test_pnf_simulator.py
new file mode 100644
index 0000000..50f2200
--- /dev/null
+++ b/simulator-cli/tests/test_pnf_simulator.py
@@ -0,0 +1,270 @@
+###
+# ============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=========================================================
+###
+import json
+import os
+import unittest
+from http.client import HTTPResponse, HTTPConnection
+from unittest import mock
+from unittest.mock import patch, Mock
+
+from cli.pnf_simulator import SimulatorClient, FullEventRequest, Messages
+from cli.pnf_simulator import create_argument_parser, SimulatorParams, PersistedEventRequest
+
+
+class TestArgumentParser(unittest.TestCase):
+
+ def test_should_properly_parse_send_template_action_with_all_params(self):
+ parser = create_argument_parser()
+
+ result = parser.parse_args(
+ ['send', 'template', '--address', '127.0.0.1', "--name", 'sample_template', '--patch', '"{}"', '--repeats', '2',
+ "--interval", '5', '--verbose', '--ves_server_url', 'sample_url'])
+
+ self.assertEqual(result.address, '127.0.0.1')
+ self.assertEqual(result.name, "sample_template")
+ self.assertEqual(result.patch, "\"{}\"")
+ self.assertEqual(result.repeats, 2)
+ self.assertEqual(result.interval, 5)
+ self.assertEqual(result.ves_server_url, 'sample_url')
+ self.assertTrue(result.verbose)
+
+ def test_should_properly_parse_send_event_action_with_all_params(self):
+ parser = create_argument_parser()
+
+ result = parser.parse_args(
+ ['send', 'event', '--address', '127.0.0.1', "--filepath", 'sample_filepath.json', '--verbose', '--ves_server_url', 'sample_url'])
+
+ self.assertEqual(result.address, '127.0.0.1')
+ self.assertEqual(result.filepath, "sample_filepath.json")
+ self.assertEqual(result.ves_server_url, 'sample_url')
+ self.assertTrue(result.verbose)
+
+ def test_should_properly_parse_configure_action_with_all_params(self):
+ parser = create_argument_parser()
+ result = parser.parse_args(
+ ['configure', '--address', '127.0.0.1', "--verbose", '--ves-server-url', 'sample_url']
+ )
+
+ self.assertEqual(result.address, '127.0.0.1')
+ self.assertTrue(result.verbose)
+ self.assertEqual(result.ves_server_url, 'sample_url')
+
+ def test_should_properly_parse_get_config_action_with_all_params(self):
+ parser = create_argument_parser()
+ result = parser.parse_args(
+ ['get-config', '--address', '127.0.0.1', '--verbose']
+ )
+
+ self.assertEqual(result.address, '127.0.0.1')
+ self.assertTrue(result.verbose)
+
+ def test_should_not_parse_arguments_when_mandatory_params_are_missing_for_template(self):
+ parser = create_argument_parser()
+
+ with self.assertRaises(SystemExit) as context:
+ parser.parse_args(['send', 'template'])
+ self.assertTrue('the following arguments are required: --address, --name' in context.exception)
+
+ def test_should_not_parse_arguments_when_mandatory_params_are_missing_for_event(self):
+ parser = create_argument_parser()
+
+ with self.assertRaises(SystemExit) as context:
+ parser.parse_args(['send', 'event'])
+ self.assertTrue('the following arguments are required: --address, --filepath' in context.exception)
+
+ def test_should_not_parse_arguments_when_mandatory_template_params_are_missing(self):
+ parser = create_argument_parser()
+
+ with self.assertRaises(SystemExit) as context:
+ parser.parse_args(['template'])
+ self.assertTrue('one of the arguments --list --get-content is required' in context.exception)
+
+ def test_should_not_parse_template_action_with_all_params(self):
+ parser = create_argument_parser()
+ with self.assertRaises(SystemExit) as context:
+ parser.parse_args(
+ ['template', '--address', '127.0.0.1', "--list", '--get-content', 'sample']
+ )
+ self.assertTrue('argument --get-content: not allowed with argument --list' in context.exception)
+
+ def test_should_properly_parse_template_action_with_list_param(self):
+ parser = create_argument_parser()
+ result = parser.parse_args(
+ ['template', '--address', '127.0.0.1', "--list"]
+ )
+
+ self.assertTrue(result.list)
+ self.assertEqual(result.address, '127.0.0.1')
+ self.assertFalse(result.verbose)
+
+ def test_should_properly_parse_template_action_with_get_content_param(self):
+ parser = create_argument_parser()
+ result = parser.parse_args(
+ ['template', '--address', '127.0.0.1', "--get-content", "sample"]
+ )
+
+ self.assertTrue(result.get_content)
+ self.assertEqual(result.address, '127.0.0.1')
+ self.assertFalse(result.verbose)
+
+ def test_should_not_parse_template_action_with_empty_get_content_param(self):
+ parser = create_argument_parser()
+ with self.assertRaises(SystemExit) as context:
+ parser.parse_args(
+ ['template', '--address', '127.0.0.1', "--list", '--get-content']
+ )
+ self.assertTrue('argument --get-content: expected one argument' in context.exception)
+
+ def test_should_not_parse_template_action_when_only_override_is_given(self):
+ parser = create_argument_parser()
+ with self.assertRaises(SystemExit) as context:
+ parser.parse_args(
+ ['template', '--address', '127.0.0.1', "--override"]
+ )
+ self.assertTrue(Messages.OVERRIDE_VALID_ONLY_WITH_UPLOAD in context.exception)
+
+ def test_should_parse_template_action_with_upload(self):
+ parser = create_argument_parser()
+ result = parser.parse_args(
+ ['template', '--address', '127.0.0.1', "--upload", "resources/notification.json"]
+ )
+
+ self.assertFalse(result.override)
+ self.assertEqual(result.upload, 'resources/notification.json')
+
+ def test_should_parse_template_action_with_upload_and_override(self):
+ parser = create_argument_parser()
+ result = parser.parse_args(
+ ['template', '--address', '127.0.0.1', "--upload", "resources/notification.json", "--override"]
+ )
+
+ self.assertTrue(result.override)
+ self.assertEqual(result.upload, 'resources/notification.json')
+
+
+ def test_should_properly_parse_filter_templates_action_with_all_params(self):
+ parser = create_argument_parser()
+
+ result = parser.parse_args(
+ ['filter', '--address', '127.0.0.1', '--criteria', '"{}"', '--verbose'])
+
+ self.assertEqual(result.address, '127.0.0.1')
+ self.assertEqual(result.criteria, "\"{}\"")
+ self.assertTrue(result.verbose)
+
+class TestSimulatorClient(unittest.TestCase):
+
+ @patch('cli.pnf_simulator.http.client.HTTPConnection')
+ def test_should_properly_send_event(self, http_connection):
+ request = self._create_request()
+ mocked_connection = Mock(HTTPConnection)
+ http_connection.return_value = mocked_connection
+ mocked_response = Mock(HTTPResponse)
+ mocked_connection.getresponse.return_value = mocked_response
+ mocked_response.status = '200'
+ mocked_response.headers = {}
+
+ client = SimulatorClient('localhost')
+ client.send_event(request)
+
+ mocked_connection.close.assert_called_with()
+ mocked_connection.request.assert_called_with('POST', '/simulator/start',
+ body=mock.ANY,
+ headers={'Content-Type': 'application/json'})
+
+ @patch('cli.pnf_simulator.http.client.HTTPConnection')
+ def test_should_properly_send_one_time_event(self, http_connection):
+ event_abs_filepath = os.path.join(os.path.dirname(os.path.abspath(__file__)),"resources/notification.json")
+ request = self._create_one_time_request(event_abs_filepath)
+ mocked_connection = Mock(HTTPConnection)
+ http_connection.return_value = mocked_connection
+ mocked_response = Mock(HTTPResponse)
+ mocked_connection.getresponse.return_value = mocked_response
+ mocked_response.status = '202'
+ mocked_response.headers = {}
+
+ client = SimulatorClient('localhost')
+ client.send_one_time_event(request)
+
+ mocked_connection.close.assert_called_with()
+ mocked_connection.request.assert_called_with('POST', '/simulator/event',
+ body=mock.ANY,
+ headers={'Content-Type': 'application/json'})
+
+ @patch('cli.pnf_simulator.http.client.HTTPConnection')
+ def test_should_properly_update_configuration(self, http_connection):
+ mocked_connection = Mock(HTTPConnection)
+ http_connection.return_value = mocked_connection
+ mocked_response = Mock(HTTPResponse)
+ mocked_connection.getresponse.return_value = mocked_response
+ mocked_response.status = '200'
+ mocked_response.headers = {}
+
+ client = SimulatorClient('localhost')
+ client.edit_configuration("sample_url")
+
+ mocked_connection.close.assert_called_with()
+ mocked_connection.request.assert_called_with('PUT', '/simulator/config',
+ body=json.dumps({"vesServerUrl": "sample_url"}),
+ headers={'Content-Type': 'application/json'})
+
+ @patch('cli.pnf_simulator.http.client.HTTPConnection')
+ def test_should_properly_retrieve_configuration(self, http_connection):
+ mocked_connection = Mock(HTTPConnection)
+ http_connection.return_value = mocked_connection
+ mocked_response = Mock(HTTPResponse)
+ mocked_connection.getresponse.return_value = mocked_response
+ mocked_response.status = '200'
+ mocked_response.headers = {}
+
+ client = SimulatorClient('localhost')
+ client.get_configuration()
+ mocked_connection.close.assert_called_with()
+ mocked_connection.request.assert_called_with('GET', '/simulator/config')
+
+
+ @patch('cli.pnf_simulator.http.client.HTTPConnection')
+ def test_should_properly_trigger_filter_template_action(self, http_connection):
+ request = '{"sampleSearchString": "sampleSearchValue"}'
+ mocked_connection = Mock(HTTPConnection)
+ http_connection.return_value = mocked_connection
+ mocked_response = Mock(HTTPResponse)
+ mocked_connection.getresponse.return_value = mocked_response
+ mocked_response.status = '200'
+ mocked_response.headers = {}
+
+ client = SimulatorClient('localhost')
+ client.search_for_templates(request)
+
+ mocked_connection.close.assert_called_with()
+ mocked_connection.request.assert_called_with('POST', '/template/search',
+ body=json.dumps({"searchExpr": {"sampleSearchString": "sampleSearchValue"}}),
+ headers={'Content-Type': 'application/json'})
+
+
+ @classmethod
+ def _create_request(cls):
+ return PersistedEventRequest(SimulatorParams(), 'sample_template')
+
+ @classmethod
+ def _create_one_time_request(cls, event_filepath):
+ with open(event_filepath) as json_event:
+ event_body = json.load(json_event)
+ return FullEventRequest(event_body, 'sample_url')
diff --git a/simulator-cli/tests/test_tailf_client.py b/simulator-cli/tests/test_tailf_client.py
new file mode 100644
index 0000000..da8bd62
--- /dev/null
+++ b/simulator-cli/tests/test_tailf_client.py
@@ -0,0 +1,47 @@
+###
+# ============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=========================================================
+###
+import unittest
+import asynctest
+
+from cli.client.tailf_client import TailfClient
+
+
+class TestTailfClient(unittest.TestCase):
+
+ def __init__(self, methodName='runTest'):
+ super().__init__(methodName)
+ self._client = TailfClient('ws://localhost:9999')
+
+ @asynctest.mock.patch('cli.client.tailf_client.websockets')
+ def test_should_connect_to_server_and_receive_message(self, websockets_mock):
+ recv_mock = asynctest.CoroutineMock(side_effect=self.interrupt)
+ aenter_mock = asynctest.MagicMock()
+ connection_mock = asynctest.MagicMock()
+ websockets_mock.connect.return_value = aenter_mock
+ aenter_mock.__aenter__.return_value = connection_mock
+ connection_mock.recv = recv_mock
+
+ self._client.tailf_messages()
+
+ recv_mock.assert_awaited_once()
+
+ def interrupt(self):
+ self._client._is_running = False
+ return 'test'