Merge "Storage of PM Data in influx"
authorPatrik Buhr <patrik.buhr@est.tech>
Thu, 9 Mar 2023 12:30:39 +0000 (12:30 +0000)
committerGerrit Code Review <gerrit@o-ran-sc.org>
Thu, 9 Mar 2023 12:30:39 +0000 (12:30 +0000)
128 files changed:
datafilecollector/.gitignore [new file with mode: 0644]
datafilecollector/Dockerfile [new file with mode: 0755]
datafilecollector/LICENSE.txt [new file with mode: 0644]
datafilecollector/README.md [new file with mode: 0644]
datafilecollector/config/README [new file with mode: 0644]
datafilecollector/config/application.yaml [new file with mode: 0644]
datafilecollector/config/ftps_keystore.p12 [new file with mode: 0755]
datafilecollector/config/ftps_keystore.pass [new file with mode: 0755]
datafilecollector/config/keystore.jks [new file with mode: 0644]
datafilecollector/config/truststore.jks [new file with mode: 0644]
datafilecollector/config/truststore.pass [new file with mode: 0755]
datafilecollector/onap-java-formatter.xml [new file with mode: 0644]
datafilecollector/pom.xml [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/MainApp.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/commons/FileCollectClient.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/commons/FileServerData.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/commons/Scheme.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/commons/SecurityUtil.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/configuration/AppConfig.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/configuration/CertificateConfig.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/configuration/SftpConfig.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/configuration/SwaggerConfig.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/controllers/StatusController.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/datastore/DataStore.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/datastore/FileStore.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/datastore/S3ObjectStore.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/exceptions/DatafileTaskException.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/exceptions/EnvironmentLoaderException.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/exceptions/NonRetryableDatafileTaskException.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/ftp/FtpesClient.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClient.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClientSettings.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/http/DfcHttpClient.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/http/DfcHttpsClient.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/http/HttpAsyncClientBuilderWrapper.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/http/HttpsClientConnectionManagerUtil.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/model/Counters.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/model/FileData.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/model/FilePublishInformation.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/model/FileReadyMessage.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/service/HttpUtils.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/tasks/CollectAndReportFiles.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/tasks/FileCollector.java [new file with mode: 0644]
datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/tasks/KafkaTopicListener.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/MockDatafile.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/controllers/StatusControllerTest.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/ftp/FtpesClientTest.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClientSettingsTest.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClientTest.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/http/DfcHttpClientTest.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/http/DfcHttpsClientTest.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/http/HttpClientResponseHelper.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/http/HttpsClientConnectionManagerUtilTest.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/scheme/SchemeTest.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/service/HttpUtilsTest.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/FileCollectorTest.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/utils/JsonMessage.java [new file with mode: 0644]
datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/utils/LoggingUtils.java [new file with mode: 0644]
datafilecollector/src/test/resources/cert.jks [new file with mode: 0755]
datafilecollector/src/test/resources/dfc.jks [new file with mode: 0644]
datafilecollector/src/test/resources/dfc.jks.pass [new file with mode: 0644]
datafilecollector/src/test/resources/ftp.jks.pass [new file with mode: 0644]
datafilecollector/src/test/resources/jks.pass [new file with mode: 0755]
datafilecollector/src/test/resources/keystore.p12 [new file with mode: 0644]
datafilecollector/src/test/resources/keystore.pass [new file with mode: 0644]
datafilecollector/src/test/resources/trust.jks [new file with mode: 0755]
datafilecollector/src/test/resources/trust.pass [new file with mode: 0755]
pmproducer/Dockerfile [new file with mode: 0644]
pmproducer/README.md [new file with mode: 0644]
pmproducer/config/README [new file with mode: 0644]
pmproducer/config/application.yaml [new file with mode: 0644]
pmproducer/config/application_configuration.json [new file with mode: 0644]
pmproducer/config/gen/CA-cert.pem [new file with mode: 0644]
pmproducer/config/gen/CA-cert.srl [new file with mode: 0644]
pmproducer/config/gen/CA-key.pem [new file with mode: 0644]
pmproducer/config/gen/ca_signed-cert.pem [new file with mode: 0644]
pmproducer/config/gen/keystore.jks [new file with mode: 0644]
pmproducer/config/gen/request.csr [new file with mode: 0644]
pmproducer/config/gen/truststore.jks [new file with mode: 0644]
pmproducer/config/keystore.jks [new file with mode: 0644]
pmproducer/config/truststore.jks [new file with mode: 0644]
pmproducer/eclipse-formatter.xml [new file with mode: 0644]
pmproducer/pom.xml [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/Application.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/BeanFactory.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/SwaggerConfig.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClient.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClientFactory.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/clients/SecurityContext.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/configuration/ApplicationConfig.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/configuration/WebClientConfig.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/controllers/ErrorResponse.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/controllers/ProducerCallbacksController.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/controllers/VoidResponse.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/datastore/DataStore.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/datastore/FileStore.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/datastore/S3ObjectStore.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/exceptions/ServiceException.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/filter/FilterFactory.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/filter/FilteredData.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/filter/PmReport.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/filter/PmReportFilter.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/r1/ConsumerJobInfo.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerInfoTypeInfo.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerJobInfo.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerRegistrationInfo.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/repository/InfoType.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/repository/InfoTypes.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/repository/Job.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/repository/Jobs.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/repository/MultiMap.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/tasks/JobDataDistributor.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/tasks/NewFileEvent.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/tasks/ProducerRegstrationTask.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/tasks/TopicListener.java [new file with mode: 0644]
pmproducer/src/main/java/org/oran/pmproducer/tasks/TopicListeners.java [new file with mode: 0644]
pmproducer/src/main/resources/typeSchemaPmData.json [new file with mode: 0644]
pmproducer/src/test/java/org/oran/pmproducer/ApplicationTest.java [new file with mode: 0644]
pmproducer/src/test/java/org/oran/pmproducer/IcsSimulatorController.java [new file with mode: 0644]
pmproducer/src/test/java/org/oran/pmproducer/IntegrationWithIcs.java [new file with mode: 0644]
pmproducer/src/test/java/org/oran/pmproducer/IntegrationWithKafka.java [new file with mode: 0644]
pmproducer/src/test/java/org/oran/pmproducer/filter/PmReportFilterTest.java [new file with mode: 0644]
pmproducer/src/test/resources/A20000626.2315+0200-2330+0200_HTTPS-6-73.json [new file with mode: 0644]
pmproducer/src/test/resources/A20000626.2315+0200-2330+0200_HTTPS-6-73.json.gz [new file with mode: 0644]
pmproducer/src/test/resources/A20220418.1900-1915_seliitdus00487.xml [new file with mode: 0644]
pmproducer/src/test/resources/pm_report.json [new file with mode: 0644]
pmproducer/src/test/resources/pm_report.json.gz [new file with mode: 0644]
pmproducer/src/test/resources/test_application_configuration.json [new file with mode: 0644]

diff --git a/datafilecollector/.gitignore b/datafilecollector/.gitignore
new file mode 100644 (file)
index 0000000..9ec364a
--- /dev/null
@@ -0,0 +1,54 @@
+# Compiled class file
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+# Intellij IDE
+.idea
+*.iml
+
+# Eclipse IDE
+.project
+.classpath
+.settings
+bin
+
+# Maven
+target
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+.mvn/wrapper/maven-wrapper.jar
+
+# CheckStyle files
+.checkstyle
+
+opt/
+
+# Visual Studio Code
+.factorypath
diff --git a/datafilecollector/Dockerfile b/datafilecollector/Dockerfile
new file mode 100755 (executable)
index 0000000..cee8d75
--- /dev/null
@@ -0,0 +1,51 @@
+#
+# ============LICENSE_START=======================================================
+#  Copyright (C) 2023 Nordix Foundation.
+#  Copyright (C) 2020 Nokia.
+#  Copyright (C) 2021 Samsung Electronics.
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#
+FROM openjdk:17-jdk-slim
+
+EXPOSE 8100 8433
+
+ARG user=datafile
+ARG group=datafile
+
+USER root
+WORKDIR /opt/app/datafile
+
+ADD /config/application.yaml /opt/app/datafile/config/
+ADD /config/ftps_keystore.pass /opt/app/datafile/config/
+ADD /config/ftps_keystore.p12 /opt/app/datafile/config/
+ADD /config/keystore.jks /opt/app/datafile/config/
+ADD /config/truststore.jks /opt/app/datafile/config/
+ADD /config/truststore.pass /opt/app/datafile/config/
+
+
+
+
+RUN mkdir -p /var/log/ONAP /opt/app/datafile/etc/cert/ && \
+    addgroup $group && adduser --system --disabled-password --no-create-home --ingroup $group $user && \
+    chown -R $user:$group /var/log/ONAP /opt/app/datafile/config && \
+    chmod -R u+rw /opt/app/datafile/config/
+
+
+USER $user
+
+COPY --chown=$user:$group /target/datafile-app-server.jar /opt/app/datafile/
+ENTRYPOINT ["java", "-jar", "/opt/app/datafile/datafile-app-server.jar"]
diff --git a/datafilecollector/LICENSE.txt b/datafilecollector/LICENSE.txt
new file mode 100644 (file)
index 0000000..581378e
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+* ============LICENSE_START==========================================
+* Copyright (C) 2018-2023 Nordix Foundation. All rights reserved.
+* Copyright (c) 2018 NOKIA Intellectual Property. All rights reserved.
+* ===================================================================
+*
+* Unless otherwise specified, all software contained herein is licensed
+* under the Apache License, Version 2.0 (the "License");
+* you may not use this software 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.
+*
+*
+*
+* Unless otherwise specified, all documentation contained herein is licensed
+* under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+* you may not use this documentation except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*             https://creativecommons.org/licenses/by/4.0/
+*
+* Unless required by applicable law or agreed to in writing, documentation
+* 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/datafilecollector/README.md b/datafilecollector/README.md
new file mode 100644 (file)
index 0000000..785a46d
--- /dev/null
@@ -0,0 +1,37 @@
+# DFC (DataFile Collector)
+
+Datafile Collector is responsible for collecting PM counter files from traffical functions.
+The files are stored in a persistent volume or in an S3 object store.
+
+The origin is from ONAP. This variant uses Kafka and S3 object store and does not use the Dmaap.
+
+## Introduction
+
+DFC is delivered as one **Docker container** which hosts application server and can be started by `docker-compose`.
+
+## Compiling DFC
+
+Whole project (top level of DFC directory) and each module (sub module directory) can be compiled using
+`mvn clean install` command.
+
+## Build image
+```
+mvn install docker:build
+```
+
+## Main API Endpoints
+
+Running with dev-mode of DFC
+
+- **Heartbeat**: http://<container_address>:8100/**heartbeat** or https://<container_address>:8443/**heartbeat**
+
+- **Start DFC**: http://<container_address>:8100/**start** or https://<container_address>:8433/**start**
+
+- **Stop DFC**: http://<container_address>:8100/**stopDatafile** or https://<container_address>:8433/**stopDatafile**
+
+
+
+## License
+
+Copyright (C) 2018-2019 NOKIA Intellectual Property, 2018-2023 Nordix Foundation. All rights reserved.
+[License](http://www.apache.org/licenses/LICENSE-2.0)
diff --git a/datafilecollector/config/README b/datafilecollector/config/README
new file mode 100644 (file)
index 0000000..cfde02e
--- /dev/null
@@ -0,0 +1,43 @@
+The keystore.jks and truststore.jks files are created by using the following commands (note that this is an example):
+
+1) Create a CA certificate and a private key:
+
+openssl genrsa -des3 -out CA-key.pem 2048
+openssl req -new -key CA-key.pem -x509 -days 3600 -out CA-cert.pem
+
+2) Create a keystore with a private key entry that is signed by the CA:
+
+Note: your name must be "localhost"
+
+keytool -genkeypair -alias policy_agent -keyalg RSA -keysize 2048 -keystore keystore.jks -validity 3650 -storepass policy_agent
+keytool -certreq -alias policy_agent -file request.csr -keystore keystore.jks -ext san=dns:your.domain.com -storepass policy_agent
+openssl x509 -req -days 3650 -in request.csr -CA CA-cert.pem -CAkey CA-key.pem -CAcreateserial -out ca_signed-cert.pem
+keytool -importcert -alias ca_cert -file CA-cert.pem -keystore keystore.jks -trustcacerts -storepass policy_agent
+keytool -importcert -alias policy_agent -file ca_signed-cert.pem -keystore keystore.jks -trustcacerts -storepass policy_agent
+
+
+3) Create a trust store containing the CA cert (to trust all certs signed by the CA):
+
+keytool -genkeypair -alias not_used -keyalg RSA -keysize 2048 -keystore truststore.jks -validity 3650 -storepass policy_agent
+keytool -importcert -alias ca_cert -file CA-cert.pem -keystore truststore.jks -trustcacerts -storepass policy_agent
+
+
+4) Command for listing of the contents of jks files, examples:
+keytool -list -v -keystore keystore.jks -storepass policy_agent
+keytool -list -v -keystore truststore.jks -storepass policy_agent
+
+## License
+
+Copyright (C) 2022 Nordix Foundation. 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.
+
diff --git a/datafilecollector/config/application.yaml b/datafilecollector/config/application.yaml
new file mode 100644 (file)
index 0000000..71f3172
--- /dev/null
@@ -0,0 +1,56 @@
+spring:
+  profiles:
+    active: prod
+management:
+  endpoints:
+    web:
+      exposure:
+        include: "loggers,logfile,health,info,metrics"
+server:
+  port: 8433
+  ssl:
+      key-store-type: JKS
+      key-store-password: policy_agent
+      key-store: config/keystore.jks
+      key-password: policy_agent
+      key-alias: policy_agent
+logging:
+  level:
+    ROOT: WARN
+    org.onap: WARN
+    org.springframework: WARN
+    org.springframework.data: WARN
+    org.springframework.web.reactive.function.client.ExchangeFunctions: WARN
+    org.onap.dcaegen2.collectors.datafile: INFO
+
+  file:
+    name: /var/log/ONAP/application.log
+app:
+  filepath: config/datafile_endpoints_test.json
+  collected-files-path: "/tmp/onap_datafile/"
+   # KAFKA boostrap servers. This is only needed if there are Information Types that uses a kafkaInputTopic
+  # several redundant boostrap servers can be specified, separated by a comma ','.
+  kafka:
+    bootstrap-servers: localhost:9092
+    # output topic
+    collected-file-topic: collected-file
+    client-id: datafile-1
+    # input topic
+    file-ready-event-topic: file-ready
+  sftp:
+    known-hosts-file-path:
+    strict-host-key-checking: false
+  ssl:
+     key-store-password-file: /opt/app/datafile/config/ftps_keystore.pass
+     key-store: /opt/app/datafile/config/ftps_keystore.p12
+     trust-store-password-file: /opt/app/datafile/config/truststore.pass
+     trust-store: /opt/app/datafile/config/truststore.jks
+  s3:
+    endpointOverride:
+    accessKeyId:
+    secretAccessKey:
+    bucket:
+    locksBucket:
+springdoc:
+  show-actuator: true
+  swagger-ui.disable-swagger-default-url: true
\ No newline at end of file
diff --git a/datafilecollector/config/ftps_keystore.p12 b/datafilecollector/config/ftps_keystore.p12
new file mode 100755 (executable)
index 0000000..b847707
Binary files /dev/null and b/datafilecollector/config/ftps_keystore.p12 differ
diff --git a/datafilecollector/config/ftps_keystore.pass b/datafilecollector/config/ftps_keystore.pass
new file mode 100755 (executable)
index 0000000..1e7befc
--- /dev/null
@@ -0,0 +1 @@
+HVpAf0kHGl4P#fdpblJLka6b
\ No newline at end of file
diff --git a/datafilecollector/config/keystore.jks b/datafilecollector/config/keystore.jks
new file mode 100644 (file)
index 0000000..563c67b
Binary files /dev/null and b/datafilecollector/config/keystore.jks differ
diff --git a/datafilecollector/config/truststore.jks b/datafilecollector/config/truststore.jks
new file mode 100644 (file)
index 0000000..50a0f9e
Binary files /dev/null and b/datafilecollector/config/truststore.jks differ
diff --git a/datafilecollector/config/truststore.pass b/datafilecollector/config/truststore.pass
new file mode 100755 (executable)
index 0000000..b915b0f
--- /dev/null
@@ -0,0 +1 @@
+policy_agent
\ No newline at end of file
diff --git a/datafilecollector/onap-java-formatter.xml b/datafilecollector/onap-java-formatter.xml
new file mode 100644 (file)
index 0000000..ccd07d9
--- /dev/null
@@ -0,0 +1,296 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<profiles version="13">
+<profile kind="CodeFormatterProfile" name="onap-java-formatter" version="12">
+<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
+<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
+<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>
+<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="48"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
+<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
+<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
+</profile>
+</profiles>
+
diff --git a/datafilecollector/pom.xml b/datafilecollector/pom.xml
new file mode 100644 (file)
index 0000000..c122253
--- /dev/null
@@ -0,0 +1,334 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ ============LICENSE_START=======================================================
+  ~ Copyright (C) 2023 Nordix Foundation
+  ~ ================================================================================
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~ ============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.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>3.0.4</version>
+    </parent>
+    <groupId>org.o-ran-sc.nonrtric.plt.ranpm</groupId>
+    <artifactId>datafile-app-server</artifactId>
+    <packaging>jar</packaging>
+    <properties>
+        <java.version>17</java.version>
+        <maven.build.timestamp.format>yyyyMMdd'T'HHmmss</maven.build.timestamp.format>
+        <docker-client.version>8.7.1</docker-client.version>
+        <springfox.version>3.0.0</springfox.version>
+        <gson.version>2.9.0</gson.version>
+        <docker-maven-plugin>0.30.0</docker-maven-plugin>
+        <jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.9.1</version>
+        </dependency>
+        <dependency>
+            <groupId>io.projectreactor.kafka</groupId>
+            <artifactId>reactor-kafka</artifactId>
+            <version>1.3.9</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpasyncclient</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.spotify</groupId>
+            <artifactId>docker-client</artifactId>
+            <version>${docker-client.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webflux</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-autoconfigure</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents.core5</groupId>
+            <artifactId>httpcore5</artifactId>
+        </dependency>
+        <!-- Actuator dependencies -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>software.amazon.awssdk</groupId>
+            <artifactId>s3</artifactId>
+            <version>2.17.292</version>
+        </dependency>
+        <!--TESTS
+        DEPENDENCIES -->
+        <dependency>
+            <groupId>io.projectreactor</groupId>
+            <artifactId>reactor-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.vintage</groupId>
+            <artifactId>junit-vintage-engine</artifactId>
+            <version>5.7.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!--REQUIRED
+        TO GENERATE DOCUMENTATION -->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-spring-web</artifactId>
+            <version>${springfox.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-spi</artifactId>
+            <version>${springfox.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-core</artifactId>
+            <version>${springfox.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-ui</artifactId>
+            <version>1.6.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>${springfox.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>${springfox.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-net</groupId>
+            <artifactId>commons-net</artifactId>
+            <version>3.6</version>
+        </dependency>
+        <dependency>
+            <groupId>com.jcraft</groupId>
+            <artifactId>jsch</artifactId>
+            <version>0.1.55</version>
+        </dependency>
+        <dependency>
+            <groupId>io.projectreactor.netty</groupId>
+            <artifactId>reactor-netty</artifactId>
+            <version>1.0.22</version>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <finalName>${project.artifactId}</finalName>
+                    <mainClass>org.onap.dcaegen2.collectors.datafile.MainApp</mainClass>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>build-info</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>io.fabric8</groupId>
+                <artifactId>docker-maven-plugin</artifactId>
+                <version>${docker-maven-plugin}</version>
+                <inherited>false</inherited>
+                <executions>
+                    <execution>
+                        <id>generate-image</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>build</goal>
+                        </goals>
+                        <configuration>
+                            <pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>
+                            <images>
+                                <image>
+                                    <name>
+                                        o-ran-sc/nonrtric-plt-ranpm-datafilecollector:${project.version}</name>
+                                    <build>
+                                        <cleanup>try</cleanup>
+                                        <contextDir>${basedir}</contextDir>
+                                        <dockerFile>Dockerfile</dockerFile>
+                                        <args>
+                                            <JAR>${project.build.finalName}.jar</JAR>
+                                        </args>
+                                        <tags>
+                                            <tag>${project.version}</tag>
+                                        </tags>
+                                    </build>
+                                </image>
+                            </images>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>push-image</id>
+                        <goals>
+                            <goal>build</goal>
+                            <goal>push</goal>
+                        </goals>
+                        <configuration>
+                            <pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>
+                            <pushRegistry>${env.CONTAINER_PUSH_REGISTRY}</pushRegistry>
+                            <images>
+                                <image>
+                                    <name>
+                                        o-ran-sc/nonrtric-plt-ranpm-datafilecollector:${project.version}</name>
+                                    <build>
+                                        <contextDir>${basedir}</contextDir>
+                                        <dockerFile>Dockerfile</dockerFile>
+                                        <args>
+                                            <JAR>${project.build.finalName}.jar</JAR>
+                                        </args>
+                                        <tags>
+                                            <tag>${project.version}</tag>
+                                            <tag>latest</tag>
+                                        </tags>
+                                    </build>
+                                </image>
+                            </images>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>pl.project13.maven</groupId>
+                <artifactId>git-commit-id-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>get-the-git-infos</id>
+                        <goals>
+                            <goal>revision</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <verbose>true</verbose>
+                    <dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
+                    <dateFormat>MM-dd-yyyy '@' HH:mm:ss Z</dateFormat>
+                    <generateGitPropertiesFile>true</generateGitPropertiesFile>
+                    <generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties
+                    </generateGitPropertiesFilename>
+                    <failOnNoGitDirectory>true</failOnNoGitDirectory>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>com.diffplug.spotless</groupId>
+                <artifactId>spotless-maven-plugin</artifactId>
+                <version>1.18.0</version>
+                <configuration>
+                    <java>
+                        <removeUnusedImports />
+                        <importOrder>
+                            <order>com,java,javax,org</order>
+                        </importOrder>
+                    </java>
+                </configuration>
+                <!-- https://github.com/diffplug/spotless/tree/master/plugin-maven use
+                            mvn spotless:apply to rewrite source files use mvn spotless:check to validate
+                            source files -->
+            </plugin>
+            <plugin>
+                <groupId>net.revelc.code.formatter</groupId>
+                <artifactId>formatter-maven-plugin</artifactId>
+                <version>2.8.1</version>
+                <configuration>
+                    <configFile>${project.basedir}/onap-java-formatter.xml</configFile>
+                </configuration>
+                <!-- https://code.revelc.net/formatter-maven-plugin/ use mvn formatter:format
+                          to rewrite source files use mvn formatter:validate to validate source files -->
+            </plugin>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <version>${jacoco-maven-plugin.version}</version>
+                <executions>
+                    <execution>
+                        <id>default-prepare-agent</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>default-report</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/MainApp.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/MainApp.java
new file mode 100644 (file)
index 0000000..851db32
--- /dev/null
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018 NOKIA Intellectual Property. All rights reserved.
+ * Copyright (C) 2018-2021 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
+
+/**
+ * The main app of DFC.
+ *
+ * @author <a href="mailto:przemyslaw.wasala@nokia.com">PrzemysÅ‚aw WÄ…sala</a> on 3/23/18
+ * @author <a href="mailto:henrik.b.andersson@est.tech">Henrik Andersson</a>
+ */
+@SpringBootApplication()
+@EnableScheduling
+public class MainApp {
+
+    public static void main(String[] args) {
+        SpringApplication.run(MainApp.class, args);
+    }
+
+    @Bean
+    TaskScheduler taskScheduler() {
+        return new ConcurrentTaskScheduler();
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/commons/FileCollectClient.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/commons/FileCollectClient.java
new file mode 100644 (file)
index 0000000..517e382
--- /dev/null
@@ -0,0 +1,32 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018-2019 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.commons;
+
+import java.nio.file.Path;
+
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+
+/**
+ * A closeable file client.
+ *
+ * @author <a href="mailto:henrik.b.andersson@est.tech">Henrik Andersson</a>
+ */
+public interface FileCollectClient extends AutoCloseable {
+    public void collectFile(String remoteFile, Path localFile) throws DatafileTaskException;
+
+    public void open() throws DatafileTaskException;
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/commons/FileServerData.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/commons/FileServerData.java
new file mode 100644 (file)
index 0000000..5896fe6
--- /dev/null
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018-2019 Nordix Foundation. All rights reserved.
+ * Modifications copyright (C) 2021 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.dcaegen2.collectors.datafile.commons;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import lombok.Builder;
+import lombok.ToString;
+
+import org.apache.hc.core5.http.NameValuePair;
+
+/**
+ * Data about the file server to collect a file from.
+ * In case of http protocol it also contains data required to recreate target
+ * uri
+ */
+@Builder
+@ToString
+public class FileServerData {
+
+    public String serverAddress;
+    public String userId;
+
+    @ToString.Exclude
+    public String password;
+
+    @Builder.Default
+    @ToString.Exclude
+    public List<NameValuePair> queryParameters = new ArrayList<>();
+
+    @Builder.Default
+    public String uriRawFragment = "";
+
+    public Integer port;
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/commons/Scheme.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/commons/Scheme.java
new file mode 100644 (file)
index 0000000..613fa39
--- /dev/null
@@ -0,0 +1,70 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 Nordix Foundation. All rights reserved.
+ * Copyright (C) 2020-2021 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.dcaegen2.collectors.datafile.commons;
+
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+
+/**
+ * Enum specifying the schemes that DFC support for downloading files.
+ *
+ * @author <a href="mailto:henrik.b.andersson@est.tech">Henrik Andersson</a>
+ *
+ */
+public enum Scheme {
+    FTPES, SFTP, HTTP, HTTPS;
+
+    public static final String DFC_DOES_NOT_SUPPORT_PROTOCOL_ERROR_MSG = "DFC does not support protocol ";
+    public static final String SUPPORTED_PROTOCOLS_ERROR_MESSAGE =
+        ". Supported protocols are FTPeS, sFTP, HTTP and HTTPS";
+
+    /**
+     * Get a <code>Scheme</code> from a string.
+     *
+     * @param schemeString the string to convert to <code>Scheme</code>.
+     * @return The corresponding <code>Scheme</code>
+     * @throws DatafileTaskException if the value of the string doesn't match any defined scheme.
+     */
+    public static Scheme getSchemeFromString(String schemeString) throws DatafileTaskException {
+        Scheme result;
+        if ("FTPES".equalsIgnoreCase(schemeString)) {
+            result = Scheme.FTPES;
+        } else if ("SFTP".equalsIgnoreCase(schemeString)) {
+            result = Scheme.SFTP;
+        } else if ("HTTP".equalsIgnoreCase(schemeString)) {
+            result = Scheme.HTTP;
+        } else if ("HTTPS".equalsIgnoreCase(schemeString)) {
+            result = Scheme.HTTPS;
+        } else {
+            throw new DatafileTaskException(
+                DFC_DOES_NOT_SUPPORT_PROTOCOL_ERROR_MSG + schemeString + SUPPORTED_PROTOCOLS_ERROR_MESSAGE);
+        }
+        return result;
+    }
+
+    /**
+     * Check if <code>Scheme</code> is FTP type or HTTP type.
+     *
+     * @param scheme the <code>Scheme</code> which has to be checked.
+     * @return true if <code>Scheme</code> is FTP type or false if it is HTTP type
+     */
+    public static boolean isFtpScheme(Scheme scheme) {
+        return scheme == SFTP || scheme == FTPES;
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/commons/SecurityUtil.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/commons/SecurityUtil.java
new file mode 100644 (file)
index 0000000..9d6b7f9
--- /dev/null
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2021 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.dcaegen2.collectors.datafile.commons;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class containing functions used for certificates configuration
+ *
+ * @author <a href="mailto:krzysztof.gajewski@nokia.com">Krzysztof Gajewski</a>
+ */
+public final class SecurityUtil {
+    private SecurityUtil() {
+    }
+
+    private static final Logger logger = LoggerFactory.getLogger(SecurityUtil.class);
+
+    public static String getKeystorePasswordFromFile(String passwordPath) {
+        return getPasswordFromFile(passwordPath, "Keystore");
+    }
+
+    public static String getTruststorePasswordFromFile(String passwordPath) {
+        return getPasswordFromFile(passwordPath, "Truststore");
+    }
+
+    public static String getPasswordFromFile(String passwordPath, String element) {
+        try {
+            return new String(Files.readAllBytes(Paths.get(passwordPath)));
+        } catch (IOException e) {
+            logger.error("{} password file at path: {} cannot be opened ", element, passwordPath);
+        }
+        return "";
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/configuration/AppConfig.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/configuration/AppConfig.java
new file mode 100644 (file)
index 0000000..f8be04d
--- /dev/null
@@ -0,0 +1,126 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018, 2020-2022 Nokia. All rights reserved.
+ * Copyright (C) 2018-2019 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.configuration;
+
+import java.util.Properties;
+
+import lombok.Getter;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * Holds all configuration for the DFC.
+ *
+ * @author <a href="mailto:przemyslaw.wasala@nokia.com">PrzemysÅ‚aw WÄ…sala</a> on
+ *         3/23/18
+ * @author <a href="mailto:henrik.b.andersson@est.tech">Henrik Andersson</a>
+ */
+
+@Component
+@EnableConfigurationProperties
+public class AppConfig {
+
+    @Value("#{systemEnvironment}")
+    Properties systemEnvironment;
+
+    @Value("${app.filepath}")
+    String filepath;
+
+    @Value("${app.kafka.bootstrap-servers:}")
+    private String kafkaBootStrapServers;
+
+    @Value("${app.kafka.collected-file-topic:}")
+    public String collectedFileTopic;
+
+    @Value("${app.kafka.file-ready-event-topic:}")
+    public String fileReadyEventTopic;
+
+    @Value("${app.kafka.client-id:undefined}")
+    public String kafkaClientId;
+
+    @Value("${app.collected-files-path:}")
+    public String collectedFilesPath;
+
+    @Value("${app.sftp.strict-host-key-checking:false}")
+    public boolean strictHostKeyChecking;
+
+    @Value("${app.sftp.known-hosts-file-path:}")
+    public String knownHostsFilePath;
+
+    @Value("${app.ssl.key-store-password-file}")
+    private String clientKeyStorePassword = "";
+
+    @Value("${app.ssl.key-store:}")
+    private String clientKeyStore = "";
+
+    @Value("${app.ssl.trust-store:}")
+    private String clientTrustStore = "";
+
+    @Value("${app.ssl.trust-store-password-file:}")
+    private String clientTrustStorePassword;
+
+    @Getter
+    @Value("${app.s3.endpointOverride:}")
+    private String s3EndpointOverride;
+
+    @Getter
+    @Value("${app.s3.accessKeyId:}")
+    private String s3AccessKeyId;
+
+    @Getter
+    @Value("${app.s3.secretAccessKey:}")
+    private String s3SecretAccessKey;
+
+    @Getter
+    @Value("${app.s3.bucket:}")
+    private String s3Bucket;
+
+    @Value("${app.s3.locksBucket:}")
+    private String s3LocksBucket;
+
+    public String getS3LocksBucket() {
+        return s3LocksBucket.isEmpty() ? s3Bucket : s3LocksBucket;
+    }
+
+    public boolean isS3Enabled() {
+        return !s3EndpointOverride.isEmpty() && !s3Bucket.isEmpty();
+    }
+
+    public String getKafkaBootStrapServers() {
+        return kafkaBootStrapServers;
+    }
+
+    public synchronized CertificateConfig getCertificateConfiguration() {
+        return CertificateConfig.builder() //
+            .trustedCa(this.clientTrustStore) //
+            .trustedCaPasswordPath(this.clientTrustStorePassword) //
+            .keyCert(this.clientKeyStore) //
+            .keyPasswordPath(this.clientKeyStorePassword) //
+            .build();
+    }
+
+    public synchronized SftpConfig getSftpConfiguration() {
+        return SftpConfig.builder() //
+            .knownHostsFilePath(this.knownHostsFilePath) //
+            .strictHostKeyChecking(this.strictHostKeyChecking) //
+            .build();
+    }
+
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/configuration/CertificateConfig.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/configuration/CertificateConfig.java
new file mode 100644 (file)
index 0000000..938d322
--- /dev/null
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2018-2022 Nokia. All rights reserved.
+ * Copyright (C) 2019 Nordix Foundation. 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.configuration;
+
+import lombok.Builder;
+
+@Builder
+public class CertificateConfig {
+
+    public String keyCert;
+
+    public String keyPasswordPath;
+
+    public String trustedCa;
+
+    public String trustedCaPasswordPath;
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/configuration/SftpConfig.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/configuration/SftpConfig.java
new file mode 100644 (file)
index 0000000..182a59e
--- /dev/null
@@ -0,0 +1,31 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 NOKIA Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.configuration;
+
+import lombok.Builder;
+
+@Builder
+public class SftpConfig {
+
+    public boolean strictHostKeyChecking;
+
+    public String knownHostsFilePath;
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/configuration/SwaggerConfig.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/configuration/SwaggerConfig.java
new file mode 100644 (file)
index 0000000..b7dc521
--- /dev/null
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018 NOKIA Intellectual Property. All rights reserved.
+ * Copyright (C) 2018-2021 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.configuration;
+
+import io.swagger.v3.oas.annotations.OpenAPIDefinition;
+import io.swagger.v3.oas.annotations.info.Info;
+import io.swagger.v3.oas.annotations.info.License;
+
+@OpenAPIDefinition(
+    info = @Info(
+        title = SwaggerConfig.API_TITLE,
+        version = SwaggerConfig.VERSION,
+        description = SwaggerConfig.DESCRIPTION,
+        license = @License(
+            name = "Copyright (C) 2020 Nordix Foundation. Licensed under the Apache License.",
+            url = "http://www.apache.org/licenses/LICENSE-2.0")))
+public class SwaggerConfig {
+
+    public static final String VERSION = "1.0";
+    public static final String API_TITLE = "DATAFILE App Server";
+    static final String DESCRIPTION = "<p>This page lists all the rest apis for DATAFILE app server.</p>";
+
+    private SwaggerConfig() {
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/controllers/StatusController.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/controllers/StatusController.java
new file mode 100644 (file)
index 0000000..d1f615b
--- /dev/null
@@ -0,0 +1,99 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018 NOKIA Intellectual Property. All rights reserved.
+ * Copyright (C) 2018-2021 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.controllers;
+
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+import org.onap.dcaegen2.collectors.datafile.model.Counters;
+import org.onap.dcaegen2.collectors.datafile.tasks.CollectAndReportFiles;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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.RequestHeader;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
+
+/**
+ * REST Controller to check the heart beat and status of the DFC.
+ */
+@RestController
+@Tag(name = "StatusController")
+public class StatusController {
+
+    private static final Logger logger = LoggerFactory.getLogger(StatusController.class);
+
+    private final CollectAndReportFiles collectAndReportFiles;
+
+    public StatusController(CollectAndReportFiles task) {
+        this.collectAndReportFiles = task;
+    }
+
+    /**
+     * Checks the heart beat of DFC.
+     *
+     * @return the heart beat status of DFC.
+     */
+    @GetMapping("/heartbeat")
+    @Operation(summary = "Returns liveness of DATAFILE service")
+    @ApiResponses(
+        value = { //
+            @ApiResponse(code = 200, message = "DATAFILE service is living"),
+            @ApiResponse(code = 401, message = "You are not authorized to view the resource"),
+            @ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
+            @ApiResponse(code = 404, message = "The resource you were trying to reach is not found")})
+    public Mono<ResponseEntity<String>> heartbeat(@RequestHeader HttpHeaders headers) {
+        logger.info("ENTRY {}", "Heartbeat request");
+
+        String statusString = "I'm living!";
+
+        Mono<ResponseEntity<String>> response = Mono.just(new ResponseEntity<>(statusString, HttpStatus.OK));
+        logger.info("EXIT {}", "Heartbeat request");
+        return response;
+    }
+
+    /**
+     * Returns diagnostics and statistics information. It is intended for testing
+     * and trouble
+     * shooting.
+     *
+     * @return information.
+     */
+    @GetMapping("/status")
+    @Operation(summary = "Returns status and statistics of DATAFILE service")
+    @ApiResponses(
+        value = { //
+            @ApiResponse(code = 200, message = "DATAFILE service is living"),
+            @ApiResponse(code = 401, message = "You are not authorized to view the resource"),
+            @ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
+            @ApiResponse(code = 404, message = "The resource you were trying to reach is not found")})
+    public Mono<ResponseEntity<String>> status(@RequestHeader HttpHeaders headers) {
+
+        logger.info("ENTRY {}", "Status request");
+
+        Counters counters = collectAndReportFiles.getCounters();
+        Mono<ResponseEntity<String>> response = Mono.just(new ResponseEntity<>(counters.toString(), HttpStatus.OK));
+        logger.info("EXIT {}", "Status request");
+        return response;
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/datastore/DataStore.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/datastore/DataStore.java
new file mode 100644 (file)
index 0000000..af0512e
--- /dev/null
@@ -0,0 +1,57 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2021 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.datastore;
+
+import java.nio.file.Path;
+
+import org.onap.dcaegen2.collectors.datafile.configuration.AppConfig;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public interface DataStore {
+    public enum Bucket {
+        FILES, LOCKS
+    }
+
+    public Flux<String> listObjects(Bucket bucket, String prefix);
+
+    public Mono<byte[]> readObject(Bucket bucket, String name);
+
+    public Mono<Boolean> createLock(String name);
+
+    public Mono<Boolean> deleteLock(String name);
+
+    public Mono<Boolean> deleteObject(Bucket bucket, String name);
+
+    public Mono<String> copyFileTo(Path from, String to);
+
+    public Mono<String> create(DataStore.Bucket bucket);
+
+    public Mono<String> deleteBucket(Bucket bucket);
+
+    public Mono<Boolean> fileExists(Bucket bucket, String key);
+
+    public static DataStore create(AppConfig config) {
+        return config.isS3Enabled() ? new S3ObjectStore(config) : new FileStore(config);
+    }
+
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/datastore/FileStore.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/datastore/FileStore.java
new file mode 100644 (file)
index 0000000..7f497be
--- /dev/null
@@ -0,0 +1,160 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2021 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.datastore;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.onap.dcaegen2.collectors.datafile.configuration.AppConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.FileSystemUtils;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class FileStore implements DataStore {
+    private static final Logger logger = LoggerFactory.getLogger(java.lang.invoke.MethodHandles.lookup().lookupClass());
+
+    AppConfig applicationConfig;
+
+    public FileStore(AppConfig applicationConfig) {
+        this.applicationConfig = applicationConfig;
+    }
+
+    @Override
+    public Flux<String> listObjects(Bucket bucket, String prefix) {
+        Path root = Path.of(applicationConfig.collectedFilesPath, prefix);
+        if (!root.toFile().exists()) {
+            root = root.getParent();
+        }
+
+        logger.debug("Listing files in: {}", root);
+
+        List<String> result = new ArrayList<>();
+        try (Stream<Path> stream = Files.walk(root, Integer.MAX_VALUE)) {
+
+            stream.forEach(path -> filterListFiles(path, prefix, result));
+
+            return Flux.fromIterable(result);
+        } catch (Exception e) {
+            return Flux.error(e);
+        }
+    }
+
+    private void filterListFiles(Path path, String prefix, List<String> result) {
+        if (path.toFile().isFile() && externalName(path).startsWith(prefix)) {
+            result.add(externalName(path));
+        } else {
+            logger.debug("Ignoring file {} that does not start with: {}", path, prefix);
+        }
+    }
+
+    private String externalName(Path path) {
+        String fullName = path.toString();
+        String externalName = fullName.substring(applicationConfig.collectedFilesPath.length());
+        if (externalName.startsWith("/")) {
+            externalName = externalName.substring(1);
+        }
+        return externalName;
+    }
+
+    @Override
+    public Mono<byte[]> readObject(Bucket bucket, String fileName) {
+        try {
+            byte[] contents = Files.readAllBytes(path(fileName));
+            return Mono.just(contents);
+        } catch (Exception e) {
+            return Mono.error(e);
+        }
+    }
+
+    @Override
+    public Mono<Boolean> createLock(String name) {
+        File file = path(name).toFile();
+        try {
+            Files.createDirectories(path(name).getParent());
+            boolean res = file.createNewFile();
+            return Mono.just(res);
+        } catch (Exception e) {
+            logger.warn("Could not create lock file: {}, reason: {}", file.getPath(), e.getMessage());
+            return Mono.just(!file.exists());
+        }
+    }
+
+    @Override
+    public Mono<String> copyFileTo(Path from, String to) {
+        try {
+            Path toPath = path(to);
+            Files.createDirectories(toPath);
+            Files.copy(from, path(to), StandardCopyOption.REPLACE_EXISTING);
+            return Mono.just(to);
+        } catch (Exception e) {
+            return Mono.error(e);
+        }
+    }
+
+    @Override
+    public Mono<Boolean> deleteLock(String name) {
+        return deleteObject(Bucket.LOCKS, name);
+    }
+
+    @Override
+    public Mono<Boolean> deleteObject(Bucket bucket, String name) {
+        try {
+            Files.delete(path(name));
+            return Mono.just(true);
+        } catch (Exception e) {
+            return Mono.just(false);
+        }
+    }
+
+    @Override
+    public Mono<String> create(Bucket bucket) {
+        return Mono.just("OK");
+    }
+
+    private Path path(String name) {
+        return Path.of(applicationConfig.collectedFilesPath, name);
+    }
+
+    public Mono<Boolean> fileExists(Bucket bucket, String key) {
+        return Mono.just(path(key).toFile().exists());
+    }
+
+    @Override
+    public Mono<String> deleteBucket(Bucket bucket) {
+        try {
+            FileSystemUtils.deleteRecursively(Path.of(applicationConfig.collectedFilesPath));
+        } catch (IOException e) {
+            logger.debug("Could not delete directory: {}, reason; {}", applicationConfig.collectedFilesPath,
+                e.getMessage());
+        }
+        return Mono.just("OK");
+    }
+
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/datastore/S3ObjectStore.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/datastore/S3ObjectStore.java
new file mode 100644 (file)
index 0000000..f93bbaf
--- /dev/null
@@ -0,0 +1,313 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2021 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.datastore;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.CompletableFuture;
+
+import org.onap.dcaegen2.collectors.datafile.configuration.AppConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.core.BytesWrapper;
+import software.amazon.awssdk.core.ResponseBytes;
+import software.amazon.awssdk.core.async.AsyncRequestBody;
+import software.amazon.awssdk.core.async.AsyncResponseTransformer;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.s3.S3AsyncClient;
+import software.amazon.awssdk.services.s3.S3AsyncClientBuilder;
+import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
+import software.amazon.awssdk.services.s3.model.CreateBucketResponse;
+import software.amazon.awssdk.services.s3.model.Delete;
+import software.amazon.awssdk.services.s3.model.DeleteBucketRequest;
+import software.amazon.awssdk.services.s3.model.DeleteBucketResponse;
+import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
+import software.amazon.awssdk.services.s3.model.DeleteObjectResponse;
+import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest;
+import software.amazon.awssdk.services.s3.model.DeleteObjectsResponse;
+import software.amazon.awssdk.services.s3.model.GetObjectRequest;
+import software.amazon.awssdk.services.s3.model.GetObjectResponse;
+import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
+import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
+import software.amazon.awssdk.services.s3.model.ListObjectsRequest;
+import software.amazon.awssdk.services.s3.model.ListObjectsResponse;
+import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
+import software.amazon.awssdk.services.s3.model.PutObjectRequest;
+import software.amazon.awssdk.services.s3.model.PutObjectResponse;
+import software.amazon.awssdk.services.s3.model.S3Object;
+
+public class S3ObjectStore implements DataStore {
+    private static final Logger logger = LoggerFactory.getLogger(S3ObjectStore.class);
+    private final AppConfig applicationConfig;
+
+    private static S3AsyncClient s3AsynchClient;
+
+    public S3ObjectStore(AppConfig applicationConfig) {
+        this.applicationConfig = applicationConfig;
+
+        getS3AsynchClient(applicationConfig);
+    }
+
+    private static synchronized S3AsyncClient getS3AsynchClient(AppConfig applicationConfig) {
+        if (applicationConfig.isS3Enabled() && s3AsynchClient == null) {
+            s3AsynchClient = getS3AsyncClientBuilder(applicationConfig).build();
+        }
+        return s3AsynchClient;
+    }
+
+    private static S3AsyncClientBuilder getS3AsyncClientBuilder(AppConfig applicationConfig) {
+        URI uri = URI.create(applicationConfig.getS3EndpointOverride());
+        return S3AsyncClient.builder() //
+            .region(Region.US_EAST_1) //
+            .endpointOverride(uri) //
+            .credentialsProvider(StaticCredentialsProvider.create( //
+                AwsBasicCredentials.create(applicationConfig.getS3AccessKeyId(), //
+                    applicationConfig.getS3SecretAccessKey())));
+    }
+
+    @Override
+    public Flux<String> listObjects(Bucket bucket, String prefix) {
+        return listObjectsInBucket(bucket(bucket), prefix).map(S3Object::key);
+    }
+
+    @Override
+    public Mono<Boolean> createLock(String name) {
+        return getHeadObject(bucket(Bucket.LOCKS), name).flatMap(head -> createLock(name, head)) //
+            .onErrorResume(t -> createLock(name, null));
+    }
+
+    private Mono<Boolean> createLock(String name, HeadObjectResponse head) {
+        if (head == null) {
+
+            return this.putObject(Bucket.LOCKS, name, "") //
+                .flatMap(resp -> Mono.just(true)) //
+                .doOnError(t -> logger.warn("Failed to create lock {}, reason: {}", name, t.getMessage())) //
+                .onErrorResume(t -> Mono.just(false));
+        } else {
+            return Mono.just(false);
+        }
+    }
+
+    @Override
+    public Mono<Boolean> deleteLock(String name) {
+        return deleteObject(Bucket.LOCKS, name);
+    }
+
+    @Override
+    public Mono<Boolean> deleteObject(Bucket bucket, String name) {
+
+        DeleteObjectRequest request = DeleteObjectRequest.builder() //
+            .bucket(bucket(bucket)) //
+            .key(name) //
+            .build();
+
+        CompletableFuture<DeleteObjectResponse> future = s3AsynchClient.deleteObject(request);
+
+        return Mono.fromFuture(future).map(resp -> true);
+    }
+
+    @Override
+    public Mono<byte[]> readObject(Bucket bucket, String fileName) {
+        return getDataFromS3Object(bucket(bucket), fileName);
+    }
+
+    public Mono<String> putObject(Bucket bucket, String fileName, String bodyString) {
+        PutObjectRequest request = PutObjectRequest.builder() //
+            .bucket(bucket(bucket)) //
+            .key(fileName) //
+            .build();
+
+        AsyncRequestBody body = AsyncRequestBody.fromString(bodyString);
+
+        CompletableFuture<PutObjectResponse> future = s3AsynchClient.putObject(request, body);
+
+        return Mono.fromFuture(future) //
+            .map(putObjectResponse -> fileName) //
+            .doOnError(t -> logger.error("Failed to store file in S3 {}", t.getMessage()));
+    }
+
+    @Override
+    public Mono<String> copyFileTo(Path fromFile, String toFile) {
+        return copyFileToS3Bucket(bucket(Bucket.FILES), fromFile, toFile);
+    }
+
+    public Mono<Boolean> fileExists(Bucket bucket, String key) {
+        return this.getHeadObject(bucket(bucket), key).map(obj -> true) //
+            .onErrorResume(t -> Mono.just(false));
+    }
+
+    @Override
+    public Mono<String> create(Bucket bucket) {
+        return createS3Bucket(bucket(bucket));
+    }
+
+    private Mono<String> createS3Bucket(String s3Bucket) {
+
+        CreateBucketRequest request = CreateBucketRequest.builder() //
+            .bucket(s3Bucket) //
+            .build();
+
+        CompletableFuture<CreateBucketResponse> future = s3AsynchClient.createBucket(request);
+
+        return Mono.fromFuture(future) //
+            .map(f -> s3Bucket) //
+            .doOnError(t -> logger.trace("Could not create S3 bucket: {}", t.getMessage()))
+            .onErrorResume(t -> Mono.just(s3Bucket));
+    }
+
+    @Override
+    public Mono<String> deleteBucket(Bucket bucket) {
+        return deleteAllFiles(bucket) //
+            .collectList() //
+            .flatMap(list -> deleteBucketFromS3Storage(bucket)) //
+            .map(resp -> "OK")
+            .doOnError(t -> logger.warn("Could not delete bucket: {}, reason: {}", bucket(bucket), t.getMessage()))
+            .onErrorResume(t -> Mono.just("NOK"));
+    }
+
+    private Flux<DeleteObjectsResponse> deleteAllFiles(Bucket bucket) {
+        return listObjectsInBucket(bucket(bucket), "") //
+            .buffer(500) //
+            .flatMap(list -> deleteObjectsFromS3Storage(bucket, list)) //
+            .doOnError(t -> logger.info("Deleted all files {}", t.getMessage())) //
+            .onErrorStop() //
+            .onErrorResume(t -> Flux.empty()); //
+
+    }
+
+    private Mono<DeleteObjectsResponse> deleteObjectsFromS3Storage(Bucket bucket, Collection<S3Object> objects) {
+        Collection<ObjectIdentifier> oids = new ArrayList<>();
+        for (S3Object o : objects) {
+            ObjectIdentifier oid = ObjectIdentifier.builder() //
+                .key(o.key()) //
+                .build();
+            oids.add(oid);
+        }
+
+        Delete delete = Delete.builder() //
+            .objects(oids) //
+            .build();
+
+        DeleteObjectsRequest request = DeleteObjectsRequest.builder() //
+            .bucket(bucket(bucket)) //
+            .delete(delete) //
+            .build();
+
+        CompletableFuture<DeleteObjectsResponse> future = s3AsynchClient.deleteObjects(request);
+
+        return Mono.fromFuture(future);
+    }
+
+    private Mono<ListObjectsResponse> listObjectsRequest(String bucket, String prefix,
+        ListObjectsResponse prevResponse) {
+        ListObjectsRequest.Builder builder = ListObjectsRequest.builder() //
+            .bucket(bucket) //
+            .maxKeys(1000) //
+            .prefix(prefix);
+
+        if (prevResponse != null) {
+            if (Boolean.TRUE.equals(prevResponse.isTruncated())) {
+                builder.marker(prevResponse.nextMarker());
+            } else {
+                return Mono.empty();
+            }
+        }
+
+        ListObjectsRequest listObjectsRequest = builder.build();
+        CompletableFuture<ListObjectsResponse> future = s3AsynchClient.listObjects(listObjectsRequest);
+        return Mono.fromFuture(future);
+    }
+
+    private Flux<S3Object> listObjectsInBucket(String bucket, String prefix) {
+
+        return listObjectsRequest(bucket, prefix, null) //
+            .expand(response -> listObjectsRequest(bucket, prefix, response)) //
+            .map(ListObjectsResponse::contents) //
+            .doOnNext(f -> logger.debug("Found objects in {}: {}", bucket, f.size())) //
+            .doOnError(t -> logger.warn("Error fromlist objects: {}", t.getMessage())) //
+            .flatMap(Flux::fromIterable) //
+            .doOnNext(obj -> logger.debug("Found object: {}", obj.key()));
+    }
+
+    private Mono<DeleteBucketResponse> deleteBucketFromS3Storage(Bucket bucket) {
+        DeleteBucketRequest request = DeleteBucketRequest.builder() //
+            .bucket(bucket(bucket)) //
+            .build();
+
+        CompletableFuture<DeleteBucketResponse> future = s3AsynchClient.deleteBucket(request);
+
+        return Mono.fromFuture(future);
+    }
+
+    private String bucket(Bucket bucket) {
+        return bucket == Bucket.FILES ? applicationConfig.getS3Bucket() : applicationConfig.getS3LocksBucket();
+    }
+
+    private Mono<String> copyFileToS3Bucket(String s3Bucket, Path fileName, String s3Key) {
+
+        PutObjectRequest request = PutObjectRequest.builder() //
+            .bucket(s3Bucket) //
+            .key(s3Key) //
+            .build();
+
+        AsyncRequestBody body = AsyncRequestBody.fromFile(fileName);
+
+        CompletableFuture<PutObjectResponse> future = s3AsynchClient.putObject(request, body);
+
+        return Mono.fromFuture(future) //
+            .map(f -> s3Key) //
+            .doOnError(t -> logger.error("Failed to store file in S3 {}", t.getMessage()));
+
+    }
+
+    private Mono<HeadObjectResponse> getHeadObject(String bucket, String key) {
+        HeadObjectRequest request = HeadObjectRequest.builder().bucket(bucket).key(key).build();
+
+        CompletableFuture<HeadObjectResponse> future = s3AsynchClient.headObject(request);
+        return Mono.fromFuture(future);
+    }
+
+    private Mono<byte[]> getDataFromS3Object(String bucket, String key) {
+
+        GetObjectRequest request = GetObjectRequest.builder() //
+            .bucket(bucket) //
+            .key(key) //
+            .build();
+
+        CompletableFuture<ResponseBytes<GetObjectResponse>> future =
+            s3AsynchClient.getObject(request, AsyncResponseTransformer.toBytes());
+
+        return Mono.fromFuture(future) //
+            .map(BytesWrapper::asByteArray) //
+            .doOnError(
+                t -> logger.error("Failed to get file from S3, key:{}, bucket: {}, {}", key, bucket, t.getMessage())) //
+            .doOnNext(n -> logger.debug("Read file from S3: {} {}", bucket, key)) //
+            .onErrorResume(t -> Mono.empty());
+    }
+
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/exceptions/DatafileTaskException.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/exceptions/DatafileTaskException.java
new file mode 100644 (file)
index 0000000..6aa7615
--- /dev/null
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018-2019 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.exceptions;
+
+public class DatafileTaskException extends Exception {
+
+    private static final long serialVersionUID = 1L;
+
+    public DatafileTaskException(String message) {
+        super(message);
+    }
+
+    public DatafileTaskException(String message, Exception originalException) {
+        super(message, originalException);
+    }
+
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/exceptions/EnvironmentLoaderException.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/exceptions/EnvironmentLoaderException.java
new file mode 100644 (file)
index 0000000..d49a051
--- /dev/null
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2018 NOKIA Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.exceptions;
+
+/**
+ * Exception thrown when there is a problem with the Consul environment.
+ *
+ * @author <a href="mailto:przemyslaw.wasala@nokia.com">PrzemysÅ‚aw WÄ…sala</a> on 9/19/18
+ */
+public class EnvironmentLoaderException extends Exception {
+
+    private static final long serialVersionUID = 1L;
+
+    public EnvironmentLoaderException(String message) {
+        super(message);
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/exceptions/NonRetryableDatafileTaskException.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/exceptions/NonRetryableDatafileTaskException.java
new file mode 100644 (file)
index 0000000..5c2a0d2
--- /dev/null
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2019 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.exceptions;
+
+public class NonRetryableDatafileTaskException extends DatafileTaskException {
+
+    private static final long serialVersionUID = 1L;
+
+    public NonRetryableDatafileTaskException(String message) {
+        super(message);
+    }
+
+    public NonRetryableDatafileTaskException(String message, Exception originalException) {
+        super(message, originalException);
+    }
+
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/ftp/FtpesClient.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/ftp/FtpesClient.java
new file mode 100644 (file)
index 0000000..aef5033
--- /dev/null
@@ -0,0 +1,230 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018-2019 Nordix Foundation. All rights reserved.
+ * Copyright (C) 2020-2021 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.dcaegen2.collectors.datafile.ftp;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.commons.net.ftp.FTP;
+import org.apache.commons.net.ftp.FTPReply;
+import org.apache.commons.net.ftp.FTPSClient;
+import org.onap.dcaegen2.collectors.datafile.commons.FileCollectClient;
+import org.onap.dcaegen2.collectors.datafile.commons.FileServerData;
+import org.onap.dcaegen2.collectors.datafile.commons.SecurityUtil;
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.exceptions.NonRetryableDatafileTaskException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.FileSystemResource;
+
+/**
+ * Gets file from PNF with FTPS protocol.
+ *
+ * @author <a href="mailto:martin.c.yan@est.tech">Martin Yan</a>
+ */
+public class FtpesClient implements FileCollectClient {
+    private static final Logger logger = LoggerFactory.getLogger(FtpesClient.class);
+
+    private static final int DEFAULT_PORT = 21;
+
+    FTPSClient realFtpsClient = new FTPSClient();
+    private final FileServerData fileServerData;
+    private static TrustManager theTrustManager = null;
+    private static KeyManager theKeyManager = null;
+
+    private final Path keyCertPath;
+    private final String keyCertPasswordPath;
+    private final Path trustedCaPath;
+    private final String trustedCaPasswordPath;
+
+    /**
+     * Constructor.
+     *
+     * @param fileServerData info needed to connect to the PNF.
+     * @param keyCertPath path to DFC's key cert.
+     * @param keyCertPasswordPath path of file containing password for DFC's key
+     *        cert.
+     * @param trustedCaPath path to the PNF's trusted keystore.
+     * @param trustedCaPasswordPath path of file containing password for the PNF's
+     *        trusted keystore.
+     */
+    public FtpesClient(FileServerData fileServerData, Path keyCertPath, String keyCertPasswordPath, Path trustedCaPath,
+        String trustedCaPasswordPath) {
+        this.fileServerData = fileServerData;
+        this.keyCertPath = keyCertPath;
+        this.keyCertPasswordPath = keyCertPasswordPath;
+        this.trustedCaPath = trustedCaPath;
+        this.trustedCaPasswordPath = trustedCaPasswordPath;
+    }
+
+    @Override
+    public void open() throws DatafileTaskException {
+        try {
+            realFtpsClient.setNeedClientAuth(trustedCaPath != null);
+            realFtpsClient.setKeyManager(getKeyManager(keyCertPath, keyCertPasswordPath));
+            realFtpsClient.setTrustManager(getTrustManager(trustedCaPath, trustedCaPasswordPath));
+            setUpConnection();
+        } catch (DatafileTaskException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new DatafileTaskException("Could not open connection: " + e, e);
+        }
+    }
+
+    @Override
+    public void close() {
+        logger.trace("starting to closeDownConnection");
+        if (realFtpsClient.isConnected()) {
+            try {
+                boolean logOut = realFtpsClient.logout();
+                logger.trace("logOut: {}", logOut);
+            } catch (Exception e) {
+                logger.trace("Unable to logout connection.", e);
+            }
+            try {
+                realFtpsClient.disconnect();
+                logger.trace("disconnected!");
+            } catch (Exception e) {
+                logger.trace("Unable to disconnect connection.", e);
+            }
+        }
+    }
+
+    @Override
+    public void collectFile(String remoteFileName, Path localFileName) throws DatafileTaskException {
+        logger.trace("collectFile called");
+
+        try (OutputStream output = createOutputStream(localFileName)) {
+            logger.trace("begin to retrieve from xNF.");
+            if (!realFtpsClient.retrieveFile(remoteFileName, output)) {
+                throw new NonRetryableDatafileTaskException(
+                    "Could not retrieve file. No retry attempts will be done, file :" + remoteFileName);
+            }
+        } catch (IOException e) {
+            throw new DatafileTaskException("Could not fetch file: " + e, e);
+        }
+        logger.trace("collectFile fetched: {}", localFileName);
+    }
+
+    private static int getPort(Integer port) {
+        return port != null ? port : DEFAULT_PORT;
+    }
+
+    private void setUpConnection() throws DatafileTaskException, IOException {
+
+        realFtpsClient.connect(fileServerData.serverAddress, getPort(fileServerData.port));
+        logger.trace("after ftp connect");
+
+        if (!realFtpsClient.login(fileServerData.userId, fileServerData.password)) {
+            throw new DatafileTaskException("Unable to log in to xNF. " + fileServerData.serverAddress);
+        }
+
+        if (FTPReply.isPositiveCompletion(realFtpsClient.getReplyCode())) {
+            realFtpsClient.enterLocalPassiveMode();
+            realFtpsClient.setFileType(FTP.BINARY_FILE_TYPE);
+            // Set protection buffer size
+            realFtpsClient.execPBSZ(0);
+            // Set data channel protection to private
+            realFtpsClient.execPROT("P");
+            realFtpsClient.setBufferSize(1024 * 1024);
+        } else {
+            throw new DatafileTaskException("Unable to connect to xNF. " + fileServerData.serverAddress
+                + " xNF reply code: " + realFtpsClient.getReplyCode());
+        }
+
+        logger.trace("setUpConnection successfully!");
+    }
+
+    private TrustManager createTrustManager(Path trustedCaPath, String trustedCaPassword)
+        throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
+        logger.trace("Creating trust manager from file: {}", trustedCaPath);
+        try (InputStream fis = createInputStream(trustedCaPath)) {
+            KeyStore keyStore = KeyStore.getInstance("JKS");
+            keyStore.load(fis, trustedCaPassword.toCharArray());
+            TrustManagerFactory factory = TrustManagerFactory.getInstance("SunX509");
+            factory.init(keyStore);
+            return factory.getTrustManagers()[0];
+        }
+    }
+
+    protected InputStream createInputStream(Path localFileName) throws IOException {
+        FileSystemResource realResource = new FileSystemResource(localFileName);
+        return realResource.getInputStream();
+    }
+
+    protected OutputStream createOutputStream(Path localFileName) throws IOException, DatafileTaskException {
+        File localFile = localFileName.toFile();
+        if (!localFile.createNewFile()) {
+            logger.debug("Local file {} already created", localFileName);
+            throw new NonRetryableDatafileTaskException("Local file already created: " + localFileName);
+        }
+        OutputStream output = new FileOutputStream(localFile);
+        logger.trace("File {} opened xNF", localFileName);
+        return output;
+    }
+
+    protected TrustManager getTrustManager(Path trustedCaPath, String trustedCaPasswordPath)
+        throws KeyStoreException, NoSuchAlgorithmException, IOException, CertificateException {
+        synchronized (FtpesClient.class) {
+            if (theTrustManager == null && trustedCaPath != null) {
+                String trustedCaPassword = SecurityUtil.getTruststorePasswordFromFile(trustedCaPasswordPath);
+                theTrustManager = createTrustManager(trustedCaPath, trustedCaPassword);
+            }
+            return theTrustManager;
+        }
+    }
+
+    protected KeyManager getKeyManager(Path keyCertPath, String keyCertPasswordPath)
+        throws IOException, GeneralSecurityException {
+
+        synchronized (FtpesClient.class) {
+            if (theKeyManager == null) {
+                String keyCertPassword = SecurityUtil.getKeystorePasswordFromFile(keyCertPasswordPath);
+                theKeyManager = createKeyManager(keyCertPath, keyCertPassword);
+            }
+            return theKeyManager;
+        }
+    }
+
+    private KeyManager createKeyManager(Path keyCertPath, String keyCertPassword) throws IOException, KeyStoreException,
+        NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
+        logger.trace("Creating key manager from file: {}", keyCertPath);
+        try (InputStream fis = createInputStream(keyCertPath)) {
+            KeyStore keyStore = KeyStore.getInstance("JKS");
+            keyStore.load(fis, keyCertPassword.toCharArray());
+            KeyManagerFactory factory = KeyManagerFactory.getInstance("SunX509");
+            factory.init(keyStore, keyCertPassword.toCharArray());
+            return factory.getKeyManagers()[0];
+        }
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClient.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClient.java
new file mode 100644 (file)
index 0000000..0c6db35
--- /dev/null
@@ -0,0 +1,151 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018-2019 Nordix Foundation, 2020 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.dcaegen2.collectors.datafile.ftp;
+
+import com.jcraft.jsch.Channel;
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpException;
+
+import java.nio.file.Path;
+
+import org.onap.dcaegen2.collectors.datafile.commons.FileCollectClient;
+import org.onap.dcaegen2.collectors.datafile.commons.FileServerData;
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.exceptions.NonRetryableDatafileTaskException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Gets file from xNF with SFTP protocol.
+ *
+ * @author <a href="mailto:martin.c.yan@est.tech">Martin Yan</a>
+ */
+public class SftpClient implements FileCollectClient {
+
+    private static final Logger logger = LoggerFactory.getLogger(SftpClient.class);
+
+    private static final int SFTP_DEFAULT_PORT = 22;
+    private static final String STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";
+
+    private final FileServerData fileServerData;
+    protected Session session = null;
+    protected ChannelSftp sftpChannel = null;
+    private final SftpClientSettings settings;
+
+    public SftpClient(FileServerData fileServerData, SftpClientSettings sftpConfig) {
+        this.fileServerData = fileServerData;
+        this.settings = sftpConfig;
+    }
+
+    @Override
+    public void collectFile(String remoteFile, Path localFile) throws DatafileTaskException {
+        logger.trace("collectFile {}", localFile);
+
+        try {
+            sftpChannel.get(remoteFile, localFile.toString());
+            logger.trace("File {} Download successful from xNF", localFile.getFileName());
+        } catch (SftpException e) {
+            boolean retry = e.id != ChannelSftp.SSH_FX_NO_SUCH_FILE && e.id != ChannelSftp.SSH_FX_PERMISSION_DENIED
+                && e.id != ChannelSftp.SSH_FX_OP_UNSUPPORTED;
+            if (retry) {
+                throw new DatafileTaskException("Unable to get file from xNF. Data: " + fileServerData, e);
+            } else {
+                throw new NonRetryableDatafileTaskException(
+                    "Unable to get file from xNF. No retry attempts will be done. Data: " + fileServerData, e);
+            }
+        }
+
+        logger.trace("collectFile OK");
+    }
+
+    @Override
+    public void close() {
+        logger.trace("closing sftp session");
+        if (sftpChannel != null) {
+            sftpChannel.exit();
+            sftpChannel = null;
+        }
+        if (session != null) {
+            session.disconnect();
+            session = null;
+        }
+    }
+
+    @Override
+    public void open() throws DatafileTaskException {
+        try {
+            if (session == null) {
+                session = setUpSession(fileServerData);
+                sftpChannel = getChannel(session);
+            }
+        } catch (JSchException e) {
+            boolean retry = !e.getMessage().contains("Auth fail");
+            if (retry) {
+                throw new DatafileTaskException("Could not open Sftp client. " + e);
+            } else {
+                throw new NonRetryableDatafileTaskException(
+                    "Could not open Sftp client, no retry attempts will be done. " + e);
+            }
+        }
+    }
+
+    JSch createJsch() {
+        return new JSch();
+    }
+
+    private int getPort(Integer port) {
+        return port != null ? port : SFTP_DEFAULT_PORT;
+    }
+
+    private Session setUpSession(FileServerData fileServerData) throws JSchException {
+        boolean useStrictHostChecking = this.settings.shouldUseStrictHostChecking();
+        JSch jsch = createJschClient(useStrictHostChecking);
+        return createJshSession(jsch, fileServerData, useStrictHostChecking);
+    }
+
+    private JSch createJschClient(boolean useStrictHostChecking) throws JSchException {
+        JSch jsch = createJsch();
+        if (useStrictHostChecking) {
+            jsch.setKnownHosts(this.settings.getKnownHostsFilePath());
+        }
+        return jsch;
+    }
+
+    private Session createJshSession(JSch jsch, FileServerData fileServerData, boolean useStrictHostKeyChecking)
+        throws JSchException {
+        Session newSession =
+            jsch.getSession(fileServerData.userId, fileServerData.serverAddress, getPort(fileServerData.port));
+        newSession.setConfig(STRICT_HOST_KEY_CHECKING, toYesNo(useStrictHostKeyChecking));
+        newSession.setPassword(fileServerData.password);
+        newSession.connect();
+        return newSession;
+    }
+
+    private String toYesNo(boolean useStrictHostKeyChecking) {
+        return useStrictHostKeyChecking ? "yes" : "no";
+    }
+
+    private ChannelSftp getChannel(Session session) throws JSchException {
+        Channel channel = session.openChannel("sftp");
+        channel.connect();
+        return (ChannelSftp) channel;
+    }
+
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClientSettings.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClientSettings.java
new file mode 100644 (file)
index 0000000..23e254b
--- /dev/null
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2020 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.dcaegen2.collectors.datafile.ftp;
+
+import java.io.File;
+
+import org.onap.dcaegen2.collectors.datafile.configuration.SftpConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SftpClientSettings {
+
+    private static final Logger logger = LoggerFactory.getLogger(SftpClientSettings.class);
+
+    private final SftpConfig sftpConfig;
+
+    public SftpClientSettings(SftpConfig sftpConfig) {
+        this.sftpConfig = sftpConfig;
+    }
+
+    public boolean shouldUseStrictHostChecking() {
+        boolean strictHostKeyChecking = false;
+        if (this.sftpConfig.strictHostKeyChecking) {
+            File file = new File(getKnownHostsFilePath());
+            strictHostKeyChecking = file.isFile();
+            logUsageOfStrictHostCheckingFlag(strictHostKeyChecking, file.getAbsolutePath());
+        } else {
+            logger.info("StrictHostKeyChecking will be disabled.");
+        }
+        return strictHostKeyChecking;
+    }
+
+    public String getKnownHostsFilePath() {
+        return this.sftpConfig.knownHostsFilePath;
+    }
+
+    private void logUsageOfStrictHostCheckingFlag(boolean strictHostKeyChecking, String filePath) {
+        if (strictHostKeyChecking) {
+            logger.info("StrictHostKeyChecking will be enabled with KNOWN_HOSTS_FILE_PATH [{}].", filePath);
+        } else {
+            logger.warn(
+                "StrictHostKeyChecking is enabled but environment variable KNOWN_HOSTS_FILE_PATH is not set or points to not existing file [{}]  -->  falling back to StrictHostKeyChecking='no'.",
+                filePath);
+        }
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/http/DfcHttpClient.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/http/DfcHttpClient.java
new file mode 100644 (file)
index 0000000..eab0082
--- /dev/null
@@ -0,0 +1,179 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2020-2021 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.dcaegen2.collectors.datafile.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+import org.onap.dcaegen2.collectors.datafile.commons.FileCollectClient;
+import org.onap.dcaegen2.collectors.datafile.commons.FileServerData;
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.exceptions.NonRetryableDatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.service.HttpUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import reactor.core.Disposable;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.netty.http.client.HttpClient;
+import reactor.netty.http.client.HttpClientResponse;
+import reactor.netty.resources.ConnectionProvider;
+
+/**
+ * Gets file from PNF with HTTP protocol.
+ *
+ * @author <a href="mailto:krzysztof.gajewski@nokia.com">Krzysztof Gajewski</a>
+ */
+public class DfcHttpClient implements FileCollectClient {
+
+    // Be aware to be less than ScheduledTasks.NUMBER_OF_WORKER_THREADS
+    private static final int MAX_NUMBER_OF_CONNECTIONS = 200;
+    private static final Logger logger = LoggerFactory.getLogger(DfcHttpClient.class);
+    private static final ConnectionProvider pool = ConnectionProvider.create("default", MAX_NUMBER_OF_CONNECTIONS);
+
+    private final FileServerData fileServerData;
+    private Disposable disposableClient;
+
+    protected HttpClient client;
+
+    public DfcHttpClient(FileServerData fileServerData) {
+        this.fileServerData = fileServerData;
+    }
+
+    @Override
+    public void open() throws DatafileTaskException {
+        logger.trace("Setting httpClient for file download.");
+
+        String authorizationContent = getAuthorizationContent();
+        this.client =
+            HttpClient.create(pool).keepAlive(true).headers(h -> h.add("Authorization", authorizationContent));
+
+        logger.trace("httpClient, auth header was set.");
+    }
+
+    protected String getAuthorizationContent() throws DatafileTaskException {
+        String jwtToken = HttpUtils.getJWTToken(fileServerData);
+        if (!jwtToken.isEmpty()) {
+            return HttpUtils.jwtAuthContent(jwtToken);
+        }
+        if (!HttpUtils.isBasicAuthDataFilled(fileServerData)) {
+            throw new DatafileTaskException("Not sufficient basic auth data for file.");
+        }
+        return HttpUtils.basicAuthContent(this.fileServerData.userId, this.fileServerData.password);
+    }
+
+    @Override
+    public void collectFile(String remoteFile, Path localFile) throws DatafileTaskException {
+        logger.trace("Prepare to collectFile {}", localFile);
+        CountDownLatch latch = new CountDownLatch(1);
+        final AtomicReference<Exception> errorMessage = new AtomicReference<>();
+
+        Consumer<Throwable> onError = processFailedConnectionWithServer(latch, errorMessage);
+        Consumer<InputStream> onSuccess = processDataFromServer(localFile, latch, errorMessage);
+
+        Flux<InputStream> responseContent = getServerResponse(remoteFile);
+        disposableClient = responseContent.subscribe(onSuccess, onError);
+
+        try {
+            latch.await();
+        } catch (InterruptedException e) {
+            throw new DatafileTaskException("Interrupted exception after datafile download - ", e);
+        }
+
+        if (isDownloadFailed(errorMessage)) {
+            if (errorMessage.get() instanceof NonRetryableDatafileTaskException) {
+                throw (NonRetryableDatafileTaskException) errorMessage.get();
+            }
+            throw (DatafileTaskException) errorMessage.get();
+        }
+
+        logger.trace("HTTP collectFile OK");
+    }
+
+    protected boolean isDownloadFailed(AtomicReference<Exception> errorMessage) {
+        return (errorMessage.get() != null);
+    }
+
+    protected Consumer<Throwable> processFailedConnectionWithServer(CountDownLatch latch,
+        AtomicReference<Exception> errorMessages) {
+        return (Throwable response) -> {
+            Exception e = new Exception("Error in connection has occurred during file download", response);
+            errorMessages.set(new DatafileTaskException(response.getMessage(), e));
+            if (response instanceof NonRetryableDatafileTaskException) {
+                errorMessages.set(new NonRetryableDatafileTaskException(response.getMessage(), e));
+            }
+            latch.countDown();
+        };
+    }
+
+    protected Consumer<InputStream> processDataFromServer(Path localFile, CountDownLatch latch,
+        AtomicReference<Exception> errorMessages) {
+        return (InputStream response) -> {
+            logger.trace("Starting to process response.");
+            try {
+                long numBytes = Files.copy(response, localFile);
+                logger.trace("Transmission was successful - {} bytes downloaded.", numBytes);
+                logger.trace("CollectFile fetched: {}", localFile);
+                response.close();
+            } catch (IOException e) {
+                errorMessages.set(new DatafileTaskException("Error fetching file with", e));
+            } finally {
+                latch.countDown();
+            }
+        };
+    }
+
+    protected Flux<InputStream> getServerResponse(String remoteFile) {
+        return client.get().uri(HttpUtils.prepareHttpUri(fileServerData, remoteFile))
+            .response((responseReceiver, byteBufFlux) -> {
+                logger.trace("HTTP response status - {}", responseReceiver.status());
+                if (isResponseOk(responseReceiver)) {
+                    return byteBufFlux.aggregate().asInputStream();
+                }
+                if (isErrorInConnection(responseReceiver)) {
+                    return Mono.error(new NonRetryableDatafileTaskException(
+                        HttpUtils.nonRetryableResponse(getResponseCode(responseReceiver))));
+                }
+                return Mono
+                    .error(new DatafileTaskException(HttpUtils.retryableResponse(getResponseCode(responseReceiver))));
+            });
+    }
+
+    protected boolean isResponseOk(HttpClientResponse httpClientResponse) {
+        return getResponseCode(httpClientResponse) == 200;
+    }
+
+    private int getResponseCode(HttpClientResponse responseReceiver) {
+        return responseReceiver.status().code();
+    }
+
+    protected boolean isErrorInConnection(HttpClientResponse httpClientResponse) {
+        return getResponseCode(httpClientResponse) >= 400;
+    }
+
+    @Override
+    public void close() {
+        logger.trace("Starting http client disposal.");
+        disposableClient.dispose();
+        logger.trace("Http client disposed.");
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/http/DfcHttpsClient.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/http/DfcHttpsClient.java
new file mode 100644 (file)
index 0000000..5c652cb
--- /dev/null
@@ -0,0 +1,182 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2021 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.dcaegen2.collectors.datafile.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.UnknownHostException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.config.SocketConfig;
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.conn.HttpHostConnectException;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.util.EntityUtils;
+import org.onap.dcaegen2.collectors.datafile.commons.FileCollectClient;
+import org.onap.dcaegen2.collectors.datafile.commons.FileServerData;
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.exceptions.NonRetryableDatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.service.HttpUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Gets file from PNF with HTTPS protocol.
+ *
+ * @author <a href="mailto:krzysztof.gajewski@nokia.com">Krzysztof Gajewski</a>
+ */
+public class DfcHttpsClient implements FileCollectClient {
+
+    protected CloseableHttpClient httpsClient;
+
+    private static final Logger logger = LoggerFactory.getLogger(DfcHttpsClient.class);
+    private static final int FIFTEEN_SECONDS = 15 * 1000;
+
+    private final FileServerData fileServerData;
+    private final PoolingHttpClientConnectionManager connectionManager;
+
+    public DfcHttpsClient(FileServerData fileServerData, PoolingHttpClientConnectionManager connectionManager) {
+        this.fileServerData = fileServerData;
+        this.connectionManager = connectionManager;
+    }
+
+    @Override
+    public void open() {
+        logger.trace("Setting httpsClient for file download.");
+        SocketConfig socketConfig = SocketConfig.custom().setSoKeepAlive(true).build();
+
+        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(FIFTEEN_SECONDS).build();
+
+        httpsClient = HttpClients.custom().setConnectionManager(connectionManager).setDefaultSocketConfig(socketConfig)
+            .setDefaultRequestConfig(requestConfig).build();
+
+        logger.trace("httpsClient prepared for connection.");
+    }
+
+    @Override
+    public void collectFile(String remoteFile, Path localFile) throws DatafileTaskException {
+        logger.trace("Prepare to collectFile {}", localFile);
+        HttpGet httpGet = new HttpGet(HttpUtils.prepareHttpsUri(fileServerData, remoteFile));
+
+        String authorizationContent = getAuthorizationContent();
+        if (!authorizationContent.isEmpty()) {
+            httpGet.addHeader("Authorization", authorizationContent);
+        }
+        try {
+            HttpResponse httpResponse = makeCall(httpGet);
+            processResponse(httpResponse, localFile);
+        } catch (IOException e) {
+            logger.error("marker", e);
+            throw new DatafileTaskException("Error downloading file from server. ", e);
+        }
+        logger.trace("HTTPS collectFile OK");
+    }
+
+    private String getAuthorizationContent() throws DatafileTaskException {
+        String jwtToken = HttpUtils.getJWTToken(fileServerData);
+        if (shouldUseBasicAuth(jwtToken)) {
+            return HttpUtils.basicAuthContent(this.fileServerData.userId, this.fileServerData.password);
+        }
+        return HttpUtils.jwtAuthContent(jwtToken);
+    }
+
+    private boolean shouldUseBasicAuth(String jwtToken) throws DatafileTaskException {
+        return basicAuthValidNotPresentOrThrow() && jwtToken.isEmpty();
+    }
+
+    protected boolean basicAuthValidNotPresentOrThrow() throws DatafileTaskException {
+        if (isAuthDataEmpty()) {
+            return false;
+        }
+        if (HttpUtils.isBasicAuthDataFilled(fileServerData)) {
+            return true;
+        }
+        throw new DatafileTaskException("Not sufficient basic auth data for file.");
+    }
+
+    private boolean isAuthDataEmpty() {
+        return this.fileServerData.userId.isEmpty() && this.fileServerData.password.isEmpty();
+    }
+
+    protected HttpResponse makeCall(HttpGet httpGet) throws IOException, DatafileTaskException {
+        try {
+            HttpResponse httpResponse = executeHttpClient(httpGet);
+            if (isResponseOk(httpResponse)) {
+                return httpResponse;
+            }
+
+            EntityUtils.consume(httpResponse.getEntity());
+            if (isErrorInConnection(httpResponse)) {
+                logger.warn("Failed to download file, reason: {}, code: {}",
+                    httpResponse.getStatusLine().getReasonPhrase(), httpResponse.getStatusLine());
+                throw new NonRetryableDatafileTaskException(HttpUtils.retryableResponse(getResponseCode(httpResponse)));
+            }
+            throw new DatafileTaskException(HttpUtils.nonRetryableResponse(getResponseCode(httpResponse)));
+        } catch (ConnectTimeoutException | UnknownHostException | HttpHostConnectException | SSLHandshakeException
+            | SSLPeerUnverifiedException e) {
+            logger.warn("Unable to get file from xNF: {}", e.getMessage());
+            throw new NonRetryableDatafileTaskException("Unable to get file from xNF. No retry attempts will be done.",
+                e);
+        }
+    }
+
+    protected CloseableHttpResponse executeHttpClient(HttpGet httpGet) throws IOException {
+        return httpsClient.execute(httpGet);
+    }
+
+    protected boolean isResponseOk(HttpResponse httpResponse) {
+        return getResponseCode(httpResponse) == 200;
+    }
+
+    private int getResponseCode(HttpResponse httpResponse) {
+        return httpResponse.getStatusLine().getStatusCode();
+    }
+
+    protected boolean isErrorInConnection(HttpResponse httpResponse) {
+        return getResponseCode(httpResponse) >= 400;
+    }
+
+    protected void processResponse(HttpResponse response, Path localFile) throws IOException {
+        logger.trace("Starting to process response.");
+        HttpEntity entity = response.getEntity();
+        InputStream stream = entity.getContent();
+        long numBytes = writeFile(localFile, stream);
+        stream.close();
+        EntityUtils.consume(entity);
+        logger.trace("Transmission was successful - {} bytes downloaded.", numBytes);
+    }
+
+    protected long writeFile(Path localFile, InputStream stream) throws IOException {
+        return Files.copy(stream, localFile, StandardCopyOption.REPLACE_EXISTING);
+    }
+
+    @Override
+    public void close() {
+        logger.trace("Https client has ended downloading process.");
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/http/HttpAsyncClientBuilderWrapper.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/http/HttpAsyncClientBuilderWrapper.java
new file mode 100644 (file)
index 0000000..b003727
--- /dev/null
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.http;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.client.RedirectStrategy;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+
+public class HttpAsyncClientBuilderWrapper {
+    HttpAsyncClientBuilder builder = HttpAsyncClients.custom();
+
+    public HttpAsyncClientBuilderWrapper setRedirectStrategy(RedirectStrategy redirectStrategy) {
+        builder.setRedirectStrategy(redirectStrategy);
+        return this;
+    }
+
+    public HttpAsyncClientBuilderWrapper setSslContext(SSLContext sslcontext) {
+        builder.setSSLContext(sslcontext);
+        return this;
+    }
+
+    public HttpAsyncClientBuilderWrapper setSslHostnameVerifier(HostnameVerifier hostnameVerifier) {
+        builder.setSSLHostnameVerifier(hostnameVerifier);
+        return this;
+    }
+
+    public HttpAsyncClientBuilderWrapper setDefaultRequestConfig(RequestConfig config) {
+        builder.setDefaultRequestConfig(config);
+        return this;
+    }
+
+    public CloseableHttpAsyncClient build() {
+        return builder.build();
+    }
+
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/http/HttpsClientConnectionManagerUtil.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/http/HttpsClientConnectionManagerUtil.java
new file mode 100644 (file)
index 0000000..7769e53
--- /dev/null
@@ -0,0 +1,138 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2021 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.dcaegen2.collectors.datafile.http;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Paths;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.ssl.DefaultHostnameVerifier;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.ssl.SSLContexts;
+import org.onap.dcaegen2.collectors.datafile.commons.SecurityUtil;
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.FileSystemResource;
+
+/**
+ * Utility class supplying connection manager for HTTPS protocol.
+ *
+ * @author <a href="mailto:krzysztof.gajewski@nokia.com">Krzysztof Gajewski</a>
+ */
+public class HttpsClientConnectionManagerUtil {
+
+    private HttpsClientConnectionManagerUtil() {
+    }
+
+    private static final Logger logger = LoggerFactory.getLogger(HttpsClientConnectionManagerUtil.class);
+    // Be aware to be less than ScheduledTasks.NUMBER_OF_WORKER_THREADS
+    private static final int MAX_NUMBER_OF_CONNECTIONS = 200;
+    private static PoolingHttpClientConnectionManager connectionManager;
+
+    public static PoolingHttpClientConnectionManager instance() throws DatafileTaskException {
+        if (connectionManager == null) {
+            throw new DatafileTaskException("ConnectionManager has to be set or update first");
+        }
+        return connectionManager;
+    }
+
+    public static void setupOrUpdate(String keyCertPath, String keyCertPasswordPath, String trustedCaPath,
+        String trustedCaPasswordPath, boolean useHostnameVerifier) throws DatafileTaskException {
+        synchronized (HttpsClientConnectionManagerUtil.class) {
+            if (connectionManager != null) {
+                connectionManager.close();
+                connectionManager = null;
+            }
+            setup(keyCertPath, keyCertPasswordPath, trustedCaPath, trustedCaPasswordPath, useHostnameVerifier);
+        }
+        logger.trace("HttpsConnectionManager setup or updated");
+    }
+
+    private static void setup(String keyCertPath, String keyCertPasswordPath, String trustedCaPath,
+        String trustedCaPasswordPath, boolean useHostnameVerifier) throws DatafileTaskException {
+        try {
+            SSLContextBuilder sslBuilder = SSLContexts.custom();
+            sslBuilder = supplyKeyInfo(keyCertPath, keyCertPasswordPath, sslBuilder);
+            if (!trustedCaPath.isEmpty()) {
+                sslBuilder = supplyTrustInfo(trustedCaPath, trustedCaPasswordPath, sslBuilder);
+            }
+
+            SSLContext sslContext = sslBuilder.build();
+
+            HostnameVerifier hostnameVerifier =
+                useHostnameVerifier ? new DefaultHostnameVerifier() : NoopHostnameVerifier.INSTANCE;
+
+            SSLConnectionSocketFactory sslConnectionSocketFactory =
+                new SSLConnectionSocketFactory(sslContext, new String[] {"TLSv1.2"}, null, hostnameVerifier);
+
+            Registry<ConnectionSocketFactory> socketFactoryRegistry =
+                RegistryBuilder.<ConnectionSocketFactory>create().register("https", sslConnectionSocketFactory).build();
+
+            connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
+            connectionManager.setMaxTotal(MAX_NUMBER_OF_CONNECTIONS);
+
+        } catch (Exception e) {
+            throw new DatafileTaskException("Unable to prepare HttpsConnectionManager  : ", e);
+        }
+    }
+
+    private static SSLContextBuilder supplyKeyInfo(String keyCertPath, String keyCertPasswordPath,
+        SSLContextBuilder sslBuilder) throws IOException, KeyStoreException, NoSuchAlgorithmException,
+        CertificateException, UnrecoverableKeyException {
+        String keyPass = SecurityUtil.getKeystorePasswordFromFile(keyCertPasswordPath);
+        KeyStore keyFile = createKeyStore(keyCertPath, keyPass);
+        return sslBuilder.loadKeyMaterial(keyFile, keyPass.toCharArray());
+    }
+
+    private static KeyStore createKeyStore(String path, String storePassword)
+        throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
+        logger.trace("Creating manager from file: {}", path);
+        try (InputStream fis = createInputStream(path)) {
+            KeyStore keyStore = KeyStore.getInstance("PKCS12");
+            keyStore.load(fis, storePassword.toCharArray());
+            return keyStore;
+        }
+    }
+
+    private static InputStream createInputStream(String localFileName) throws IOException {
+        FileSystemResource realResource = new FileSystemResource(Paths.get(localFileName));
+        return realResource.getInputStream();
+    }
+
+    private static SSLContextBuilder supplyTrustInfo(String trustedCaPath, String trustedCaPasswordPath,
+        SSLContextBuilder sslBuilder)
+        throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
+        String trustPass = SecurityUtil.getTruststorePasswordFromFile(trustedCaPasswordPath);
+        File trustStoreFile = new File(trustedCaPath);
+        return sslBuilder.loadTrustMaterial(trustStoreFile, trustPass.toCharArray());
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/model/Counters.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/model/Counters.java
new file mode 100644 (file)
index 0000000..2323d0e
--- /dev/null
@@ -0,0 +1,145 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.model;
+
+import java.time.Instant;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Various counters that can be shown via a REST API.
+ *
+ */
+public class Counters {
+
+    private long noOfCollectedFiles = 0;
+    private long noOfFailedFtpAttempts = 0;
+    private long noOfFailedHttpAttempts = 0;
+    private long noOfFailedFtp = 0;
+    private long noOfFailedHttp = 0;
+    private long noOfFailedPublishAttempts = 0;
+    private long totalPublishedFiles = 0;
+    private long noOfFailedPublish = 0;
+    private Instant lastPublishedTime = Instant.MIN;
+    private long totalReceivedEvents = 0;
+    private Instant lastEventTime = Instant.MIN;
+
+    public final AtomicInteger threadPoolQueueSize = new AtomicInteger();
+
+    public synchronized void incNoOfReceivedEvents() {
+        totalReceivedEvents++;
+        lastEventTime = Instant.now();
+    }
+
+    public synchronized void incNoOfCollectedFiles() {
+        noOfCollectedFiles++;
+    }
+
+    public synchronized void incNoOfFailedFtpAttempts() {
+        noOfFailedFtpAttempts++;
+    }
+
+    public synchronized void incNoOfFailedHttpAttempts() {
+        noOfFailedHttpAttempts++;
+    }
+
+    public synchronized void incNoOfFailedFtp() {
+        noOfFailedFtp++;
+    }
+
+    public synchronized void incNoOfFailedHttp() {
+        noOfFailedHttp++;
+    }
+
+    public synchronized void incNoOfFailedPublishAttempts() {
+        noOfFailedPublishAttempts++;
+    }
+
+    public synchronized void incTotalPublishedFiles() {
+        totalPublishedFiles++;
+        lastPublishedTime = Instant.now();
+    }
+
+    public synchronized void incNoOfFailedPublish() {
+        noOfFailedPublish++;
+    }
+
+    @Override
+    public synchronized String toString() {
+        StringBuilder str = new StringBuilder();
+        str.append(format("totalReceivedEvents", totalReceivedEvents));
+        str.append(format("lastEventTime", lastEventTime));
+        str.append("\n");
+        str.append(format("collectedFiles", noOfCollectedFiles));
+        str.append(format("failedFtpAttempts", noOfFailedFtpAttempts));
+        str.append(format("failedHttpAttempts", noOfFailedHttpAttempts));
+        str.append(format("failedFtp", noOfFailedFtp));
+        str.append(format("failedHttp", noOfFailedHttp));
+        str.append("\n");
+        str.append(format("totalPublishedFiles", totalPublishedFiles));
+        str.append(format("lastPublishedTime", lastPublishedTime));
+
+        str.append(format("failedPublishAttempts", noOfFailedPublishAttempts));
+        str.append(format("noOfFailedPublish", noOfFailedPublish));
+
+        return str.toString();
+    }
+
+    private static String format(String name, Object value) {
+        String header = name + ":";
+        return String.format("%-24s%-22s%n", header, value);
+    }
+
+    public long getNoOfCollectedFiles() {
+        return noOfCollectedFiles;
+    }
+
+    public long getNoOfFailedFtpAttempts() {
+        return noOfFailedFtpAttempts;
+    }
+
+    public long getNoOfFailedHttpAttempts() {
+        return noOfFailedHttpAttempts;
+    }
+
+    public long getNoOfFailedFtp() {
+        return noOfFailedFtp;
+    }
+
+    public long getNoOfFailedHttp() {
+        return noOfFailedHttp;
+    }
+
+    public long getNoOfFailedPublishAttempts() {
+        return noOfFailedPublishAttempts;
+    }
+
+    public long getTotalPublishedFiles() {
+        return totalPublishedFiles;
+    }
+
+    public long getNoOfFailedPublish() {
+        return noOfFailedPublish;
+    }
+
+    public long getTotalReceivedEvents() {
+        return totalReceivedEvents;
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/model/FileData.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/model/FileData.java
new file mode 100644 (file)
index 0000000..3de5817
--- /dev/null
@@ -0,0 +1,162 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ *  Copyright (C) 2021 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.model;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+import lombok.Builder;
+
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.net.URIBuilder;
+import org.onap.dcaegen2.collectors.datafile.commons.FileServerData;
+import org.onap.dcaegen2.collectors.datafile.commons.FileServerData.FileServerDataBuilder;
+import org.onap.dcaegen2.collectors.datafile.commons.Scheme;
+import org.onap.dcaegen2.collectors.datafile.configuration.AppConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Contains data, from the fileReady event, about the file to collect from the
+ * xNF.
+ *
+ */
+@Builder
+public class FileData {
+
+    private static final Logger logger = LoggerFactory.getLogger(FileData.class);
+
+    public FileReadyMessage.ArrayOfNamedHashMap fileInfo;
+
+    public FileReadyMessage.MessageMetaData messageMetaData;
+
+    public static Iterable<FileData> createFileData(FileReadyMessage msg) {
+        Collection<FileData> res = new ArrayList<>();
+        for (FileReadyMessage.ArrayOfNamedHashMap arr : msg.event.notificationFields.arrayOfNamedHashMap) {
+            FileData data = FileData.builder().fileInfo(arr).messageMetaData(msg.event.commonEventHeader).build();
+            res.add(data);
+        }
+        return res;
+    }
+
+    /**
+     * Get the name of the PNF, must be unique in the network.
+     *
+     * @return the name of the PNF, must be unique in the network
+     */
+    public String sourceName() {
+        return messageMetaData.sourceName;
+    }
+
+    public String name() {
+        return this.messageMetaData.sourceName + "/" + fileInfo.name;
+    }
+
+    /**
+     * Get the path to file to get from the PNF.
+     *
+     * @return the path to the file on the PNF.
+     */
+    public String remoteFilePath() {
+        return URI.create(fileInfo.hashMap.location).getPath();
+    }
+
+    public Scheme scheme() {
+        URI uri = URI.create(fileInfo.hashMap.location);
+        try {
+            return Scheme.getSchemeFromString(uri.getScheme());
+        } catch (Exception e) {
+            logger.warn("Could noit get scheme :{}", e.getMessage());
+            return Scheme.FTPES;
+        }
+    }
+
+    /**
+     * Get the path to the locally stored file.
+     *
+     * @return the path to the locally stored file.
+     */
+    public Path getLocalFilePath(AppConfig config) {
+        return Paths.get(config.collectedFilesPath, this.messageMetaData.sourceName, fileInfo.name);
+    }
+
+    /**
+     * Get the data about the file server where the file should be collected from.
+     * Query data included as it can contain JWT token
+     *
+     * @return the data about the file server where the file should be collected
+     *         from.
+     */
+    public FileServerData fileServerData() {
+        URI uri = URI.create(fileInfo.hashMap.location);
+        Optional<String[]> userInfo = getUserNameAndPasswordIfGiven(uri.getUserInfo());
+
+        FileServerDataBuilder builder = FileServerData.builder() //
+            .serverAddress(uri.getHost()) //
+            .userId(userInfo.isPresent() ? userInfo.get()[0] : "") //
+            .password(userInfo.isPresent() ? userInfo.get()[1] : "");
+        if (uri.getPort() > 0) {
+            builder.port(uri.getPort());
+        }
+        URIBuilder uriBuilder = new URIBuilder(uri);
+        List<NameValuePair> query = uriBuilder.getQueryParams();
+        if (query != null && !query.isEmpty()) {
+            builder.queryParameters(query);
+        }
+        String fragment = uri.getRawFragment();
+        if (fragment != null && fragment.length() > 0) {
+            builder.uriRawFragment(fragment);
+        }
+        return builder.build();
+    }
+
+    /**
+     * Extracts user name and password from the user info, if it they are given in
+     * the URI.
+     *
+     * @param userInfoString the user info string from the URI.
+     *
+     * @return An <code>Optional</code> containing a String array with the user name
+     *         and password if given, or an empty
+     *         <code>Optional</code> if not given.
+     */
+    private static Optional<String[]> getUserNameAndPasswordIfGiven(String userInfoString) {
+        if (userInfoString != null) {
+            String[] userAndPassword = userInfoString.split(":");
+            if (userAndPassword.length == 2) {
+                return Optional.of(userAndPassword);
+            } else if (userAndPassword.length == 1)// if just user
+            {
+                String[] tab = new String[2];
+                tab[0] = userAndPassword[0];
+                tab[1] = "";// add empty password
+                return Optional.of(tab);
+            }
+        }
+        return Optional.empty();
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/model/FilePublishInformation.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/model/FilePublishInformation.java
new file mode 100644 (file)
index 0000000..52e6413
--- /dev/null
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018 NOKIA Intellectual Property, 2018-2019 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.model;
+
+import lombok.Builder;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+
+@Builder
+@EqualsAndHashCode
+public class FilePublishInformation {
+
+    String productName;
+
+    String vendorName;
+
+    long lastEpochMicrosec;
+
+    @Getter
+    String sourceName;
+
+    long startEpochMicrosec;
+
+    String timeZoneOffset;
+
+    String compression;
+
+    String fileFormatType;
+
+    String fileFormatVersion;
+
+    @Getter
+    String name;
+
+    String changeIdentifier;
+
+    String objectStoreBucket;
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/model/FileReadyMessage.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/model/FileReadyMessage.java
new file mode 100644 (file)
index 0000000..9ee461e
--- /dev/null
@@ -0,0 +1,111 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.model;
+
+import java.util.List;
+
+import lombok.Builder;
+
+@Builder
+public class FileReadyMessage {
+
+    /**
+     * Meta data about a fileReady message.
+     */
+    @Builder
+    public static class MessageMetaData {
+
+        public String eventId;
+
+        public String priority;
+        public String version;
+        public String reportingEntityName;
+        public int sequence;
+        public String domain;
+
+        public String eventName;
+        public String vesEventListenerVersion;
+
+        public String sourceName;
+
+        public long lastEpochMicrosec;
+        public long startEpochMicrosec;
+
+        public String timeZoneOffset;
+
+        public String changeIdentifier;
+
+        /**
+         * Gets data from the event name. Defined as:
+         * {DomainAbbreviation}_{productName}-{vendorName}_{Description},
+         * example: Noti_RnNode-Ericsson_FileReady
+         *
+         */
+        public String productName() {
+            String[] eventArray = eventName.split("_|-");
+            if (eventArray.length >= 2) {
+                return eventArray[1];
+            } else {
+                return eventName;
+            }
+        }
+
+        public String vendorName() {
+            String[] eventArray = eventName.split("_|-");
+            if (eventArray.length >= 3) {
+                return eventArray[2];
+            } else {
+                return eventName;
+            }
+        }
+    }
+
+    @Builder
+    public static class FileInfo {
+        public String fileFormatType;
+        public String location;
+        public String fileFormatVersion;
+        public String compression;
+    }
+
+    @Builder
+    public static class ArrayOfNamedHashMap {
+        public String name;
+        public FileInfo hashMap;
+    }
+
+    @Builder
+    public static class NotificationFields {
+        public String notificationFieldsVersion;
+        public String changeType;
+        public String changeIdentifier;
+        public List<ArrayOfNamedHashMap> arrayOfNamedHashMap;
+    }
+
+    @Builder
+    public static class Event {
+        public MessageMetaData commonEventHeader;
+        public NotificationFields notificationFields;
+    }
+
+    public Event event;
+
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/service/HttpUtils.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/service/HttpUtils.java
new file mode 100644 (file)
index 0000000..208691f
--- /dev/null
@@ -0,0 +1,229 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018-2019 Nordix Foundation. All rights reserved.
+ * Modifications Copyright (C) 2020-2021 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.dcaegen2.collectors.datafile.service;
+
+import java.util.Base64;
+import java.util.List;
+
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.http.HttpStatus;
+import org.onap.dcaegen2.collectors.datafile.commons.FileServerData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class HttpUtils implements HttpStatus {
+
+    private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class);
+    public static final int HTTP_DEFAULT_PORT = 80;
+    public static final int HTTPS_DEFAULT_PORT = 443;
+    public static final String JWT_TOKEN_NAME = "access_token";
+    public static final String AUTH_JWT_WARN = "Both JWT token and Basic auth data present. Omitting basic auth info.";
+    public static final String AUTH_JWT_ERROR =
+        "More than one JWT token present in the queryParameters. Omitting JWT token.";
+
+    private HttpUtils() {
+    }
+
+    public static String nonRetryableResponse(int responseCode) {
+        return "Unexpected response code - " + responseCode;
+    }
+
+    public static String retryableResponse(int responseCode) {
+        return "Unexpected response code - " + responseCode + ". No retry attempts will be done.";
+    }
+
+    public static boolean isSuccessfulResponseCodeWithDataRouter(Integer statusCode) {
+        return statusCode >= 200 && statusCode < 300;
+    }
+
+    public static boolean isBasicAuthDataFilled(final FileServerData fileServerData) {
+        return !fileServerData.userId.isEmpty() && !fileServerData.password.isEmpty();
+    }
+
+    public static String basicAuthContent(String username, String password) {
+        return "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
+    }
+
+    public static String jwtAuthContent(String token) {
+        return "Bearer " + token;
+    }
+
+    /**
+     * Prepare uri to retrieve file from xNF using HTTP connection. If JWT token was
+     * included
+     * in the queryParameters, it is removed. Other entries are rewritten.
+     *
+     * @param fileServerData fileServerData including - server address, port,
+     *        queryParameters and uriRawFragment
+     * @param remoteFile file which has to be downloaded
+     * @return uri String representing the xNF HTTP location
+     */
+    public static String prepareHttpUri(FileServerData fileServerData, String remoteFile) {
+        return prepareUri("http", fileServerData, remoteFile, HTTP_DEFAULT_PORT);
+    }
+
+    /**
+     * Prepare uri to retrieve file from xNF using HTTPS connection. If JWT token
+     * was included
+     * in the queryParameters, it is removed. Other entries are rewritten.
+     *
+     * @param fileServerData fileServerData including - server address, port,
+     *        queryParameters and uriRawFragment
+     * @param remoteFile file which has to be downloaded
+     * @return uri String representing the xNF HTTPS location
+     */
+    public static String prepareHttpsUri(FileServerData fileServerData, String remoteFile) {
+        return prepareUri("https", fileServerData, remoteFile, HTTPS_DEFAULT_PORT);
+    }
+
+    /**
+     * Prepare uri to retrieve file from xNF. If JWT token was included
+     * in the queryParameters, it is removed. Other entries are rewritten.
+     *
+     * @param scheme scheme which is used during the connection
+     * @param fileServerData fileServerData including - server address, port, query
+     *        and fragment
+     * @param remoteFile file which has to be downloaded
+     * @param defaultPort default port which exchange empty entry for given
+     *        connection type
+     * @return uri String representing the xNF location
+     */
+    public static String prepareUri(String scheme, FileServerData fileServerData, String remoteFile, int defaultPort) {
+        int port = fileServerData.port != null ? fileServerData.port : defaultPort;
+        String query = rewriteQueryWithoutToken(fileServerData.queryParameters);
+        String fragment = fileServerData.uriRawFragment;
+        if (!query.isEmpty()) {
+            query = "?" + query;
+        }
+        if (!fragment.isEmpty()) {
+            fragment = "#" + fragment;
+        }
+        return scheme + "://" + fileServerData.serverAddress + ":" + port + remoteFile + query + fragment;
+    }
+
+    /**
+     * Returns JWT token string (if single exist) from the queryParameters.
+     *
+     * @param fileServerData file server data which contain queryParameters where
+     *        JWT token may exist
+     * @return JWT token value if single token entry exist or empty string
+     *         elsewhere.
+     *         If JWT token key has no value, empty string will be returned.
+     */
+    public static String getJWTToken(FileServerData fileServerData) {
+
+        if (fileServerData.queryParameters.isEmpty()) {
+            return "";
+        }
+        boolean jwtTokenKeyPresent = HttpUtils.isQueryWithSingleJWT(fileServerData.queryParameters);
+        if (!jwtTokenKeyPresent) {
+            return "";
+        }
+        String token = HttpUtils.getJWTToken(fileServerData.queryParameters);
+        if (HttpUtils.isBasicAuthDataFilled(fileServerData)) {
+            logger.warn(HttpUtils.AUTH_JWT_WARN);
+        }
+        return token;
+    }
+
+    /**
+     * Checks if the queryParameters contains single JWT token entry. Valid
+     * queryParameters
+     * contains only one token entry.
+     *
+     * @param query queryParameters
+     * @return true if queryParameters contains single token
+     */
+    public static boolean isQueryWithSingleJWT(List<NameValuePair> query) {
+        if (query == null) {
+            return false;
+        }
+        int i = getJWTTokenCount(query);
+        if (i == 0) {
+            return false;
+        }
+        if (i > 1) {
+            logger.error(AUTH_JWT_ERROR);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns the number of JWT token entries. Valid queryParameters contains only
+     * one token entry.
+     *
+     * @param queryElements elements of the queryParameters
+     * @return true if queryParameters contains single JWT token entry
+     */
+    public static int getJWTTokenCount(List<NameValuePair> queryElements) {
+        int i = 0;
+        for (NameValuePair element : queryElements) {
+            if (element.getName().equals(JWT_TOKEN_NAME)) {
+                i++;
+            }
+        }
+        return i;
+    }
+
+    private static String getJWTToken(List<NameValuePair> query) {
+        for (NameValuePair element : query) {
+            if (!element.getName().equals(JWT_TOKEN_NAME)) {
+                continue;
+            }
+            if (element.getValue() != null) {
+                return element.getValue();
+            }
+            return "";
+        }
+        return "";
+    }
+
+    /**
+     * Rewrites HTTP queryParameters without JWT token
+     *
+     * @param query list of NameValuePair of elements sent in the queryParameters
+     * @return String representation of queryParameters elements which were provided
+     *         in the input
+     *         Empty string is possible when queryParameters is empty or contains
+     *         only access_token key.
+     */
+    public static String rewriteQueryWithoutToken(List<NameValuePair> query) {
+        if (query.isEmpty()) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        for (NameValuePair nvp : query) {
+            if (nvp.getName().equals(JWT_TOKEN_NAME)) {
+                continue;
+            }
+            sb.append(nvp.getName());
+            if (nvp.getValue() != null) {
+                sb.append("=");
+                sb.append(nvp.getValue());
+            }
+            sb.append("&");
+        }
+        if ((sb.length() > 0) && (sb.charAt(sb.length() - 1) == '&')) {
+            sb.deleteCharAt(sb.length() - 1);
+        }
+        return sb.toString();
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/tasks/CollectAndReportFiles.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/tasks/CollectAndReportFiles.java
new file mode 100644 (file)
index 0000000..c127948
--- /dev/null
@@ -0,0 +1,301 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018, 2020 NOKIA Intellectual Property, 2018-2019 Nordix Foundation. All rights reserved.
+ * ===============================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END========================================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.tasks;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.apache.kafka.common.header.Header;
+import org.apache.kafka.common.header.internals.RecordHeader;
+import org.apache.kafka.common.serialization.StringSerializer;
+import org.onap.dcaegen2.collectors.datafile.commons.Scheme;
+import org.onap.dcaegen2.collectors.datafile.configuration.AppConfig;
+import org.onap.dcaegen2.collectors.datafile.configuration.CertificateConfig;
+import org.onap.dcaegen2.collectors.datafile.datastore.DataStore;
+import org.onap.dcaegen2.collectors.datafile.datastore.DataStore.Bucket;
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.http.HttpsClientConnectionManagerUtil;
+import org.onap.dcaegen2.collectors.datafile.model.Counters;
+import org.onap.dcaegen2.collectors.datafile.model.FileData;
+import org.onap.dcaegen2.collectors.datafile.model.FilePublishInformation;
+import org.onap.dcaegen2.collectors.datafile.model.FileReadyMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.core.scheduler.Scheduler;
+import reactor.core.scheduler.Schedulers;
+import reactor.kafka.sender.KafkaSender;
+import reactor.kafka.sender.SenderOptions;
+import reactor.kafka.sender.SenderRecord;
+import reactor.kafka.sender.SenderResult;
+import reactor.util.retry.Retry;
+
+/**
+ * This implements the main flow of the data file collector. Fetch file ready
+ * events from the
+ * message router, fetch new files from the PNF publish these in the data
+ * router.
+ */
+@Component
+public class CollectAndReportFiles {
+
+    private static Gson gson = new GsonBuilder() //
+        .disableHtmlEscaping() //
+        .create(); //
+
+    private static final int NUMBER_OF_WORKER_THREADS = 200;
+    private static final long FILE_TRANSFER_MAX_RETRIES = 2;
+    private static final Duration FILE_TRANSFER_INITIAL_RETRY_TIMEOUT = Duration.ofSeconds(2);
+
+    private static final Logger logger = LoggerFactory.getLogger(CollectAndReportFiles.class);
+
+    private final AppConfig appConfig;
+
+    private Counters counters = new Counters();
+
+    private final KafkaSender<String, String> kafkaSender;
+
+    private final DataStore dataStore;
+
+    /**
+     * Constructor for task registration in Datafile Workflow.
+     *
+     * @param applicationConfiguration - application configuration
+     */
+    @Autowired
+    public CollectAndReportFiles(AppConfig applicationConfiguration) {
+        this.appConfig = applicationConfiguration;
+        this.kafkaSender = KafkaSender.create(kafkaSenderOptions());
+        initCerts();
+
+        this.dataStore = DataStore.create(applicationConfiguration);
+
+        start();
+    }
+
+    private void initCerts() {
+        try {
+            CertificateConfig certificateConfig = appConfig.getCertificateConfiguration();
+            HttpsClientConnectionManagerUtil.setupOrUpdate(certificateConfig.keyCert, certificateConfig.keyPasswordPath,
+                certificateConfig.trustedCa, certificateConfig.trustedCaPasswordPath, true);
+        } catch (DatafileTaskException e) {
+            logger.error("Could not setup HttpsClient certs, reason: {}", e.getMessage());
+        }
+    }
+
+    /**
+     * Main function for scheduling for the file collection Workflow.
+     */
+    public void start() {
+        start(0);
+    }
+
+    private void start(int delayMillis) {
+        try {
+            logger.trace("Starting");
+            if (appConfig.isS3Enabled()) {
+                this.dataStore.create(Bucket.FILES).subscribe();
+                this.dataStore.create(Bucket.LOCKS).subscribe();
+            }
+            Thread.sleep(delayMillis);
+            createMainTask().subscribe(null, s -> start(2000), null);
+        } catch (Exception e) {
+            logger.error("Unexpected exception: {}", e.toString(), e);
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    Flux<FilePublishInformation> createMainTask() {
+        Scheduler scheduler = Schedulers.newParallel("FileCollectorWorker", NUMBER_OF_WORKER_THREADS);
+        return fetchFromKafka() //
+            .doOnNext(fileReadyMessage -> counters.threadPoolQueueSize.incrementAndGet()) //
+            .doOnNext(fileReadyMessage -> counters.incNoOfReceivedEvents()) //
+            .parallel(NUMBER_OF_WORKER_THREADS) // Each FileReadyMessage in a separate thread
+            .runOn(scheduler) //
+            .doOnNext(fileReadyMessage -> counters.threadPoolQueueSize.decrementAndGet()) //
+            .flatMap(fileReadyMessage -> Flux.fromIterable(FileData.createFileData(fileReadyMessage)), true, 1) //
+            .flatMap(this::filterNotFetched, false, 1, 1) //
+            .flatMap(this::fetchFile, false, 1, 1) //
+            .flatMap(data -> reportFetchedFile(data, this.appConfig.collectedFileTopic), false, 1) //
+            .sequential() //
+            .doOnError(t -> logger.error("Received error: {}", t.toString())); //
+    }
+
+    private Mono<FileData> deleteLock(FileData info) {
+        return dataStore.deleteLock(lockName(info.name())).map(b -> info); //
+    }
+
+    private Mono<FilePublishInformation> moveFileToS3Bucket(FilePublishInformation info) {
+        if (this.appConfig.isS3Enabled()) {
+            return dataStore.copyFileTo(locaFilePath(info), info.getName())
+                .doOnError(t -> logger.warn("Failed to store file '{}' in S3 {}", info.getName(), t.getMessage())) //
+                .retryWhen(Retry.fixedDelay(10, Duration.ofMillis(1000))) //
+                .map(f -> info) //
+                .doOnError(t -> logger.error("Failed to store file '{}' in S3 after retries {}", info.getName(),
+                    t.getMessage())) //
+                .doOnNext(n -> logger.debug("Stored file in S3: {}", info.getName())) //
+                .doOnNext(sig -> deleteLocalFile(info));
+        } else {
+            return Mono.just(info);
+        }
+    }
+
+    private Mono<FileData> filterNotFetched(FileData fileData) {
+        Path localPath = fileData.getLocalFilePath(this.appConfig);
+
+        return dataStore.fileExists(Bucket.FILES, fileData.name()) //
+            .filter(exists -> !exists) //
+            .filter(exists -> !localPath.toFile().exists()) //
+            .map(f -> fileData); //
+
+    }
+
+    private String lockName(String fileName) {
+        return fileName + ".lck";
+    }
+
+    private Path locaFilePath(FilePublishInformation info) {
+        return Paths.get(this.appConfig.collectedFilesPath, info.getName());
+    }
+
+    private void deleteLocalFile(FilePublishInformation info) {
+        Path path = locaFilePath(info);
+        try {
+            Files.delete(path);
+        } catch (Exception e) {
+            logger.warn("Could not delete local file: {}, reason:{}", path, e.getMessage());
+        }
+    }
+
+    private Flux<FilePublishInformation> reportFetchedFile(FilePublishInformation fileData, String topic) {
+        String json = gson.toJson(fileData);
+        return sendDataToStream(topic, fileData.getSourceName(), json) //
+            .map(result -> fileData);
+    }
+
+    public Flux<SenderResult<Integer>> sendDataToStream(String topic, String sourceName, String value) {
+        return sendDataToKafkaStream(Flux.just(senderRecord(topic, sourceName, value)));
+    }
+
+    private SenderRecord<String, String, Integer> senderRecord(String topic, String sourceName, String value) {
+        int correlationMetadata = 2;
+        String key = null;
+        var producerRecord = new ProducerRecord<>(topic, null, null, key, value, kafkaHeaders(sourceName));
+        return SenderRecord.create(producerRecord, correlationMetadata);
+    }
+
+    private Iterable<Header> kafkaHeaders(String sourceName) {
+        ArrayList<Header> result = new ArrayList<>();
+        Header h = new RecordHeader("SourceName", sourceName.getBytes());
+        result.add(h);
+        return result;
+    }
+
+    private Flux<SenderResult<Integer>> sendDataToKafkaStream(Flux<SenderRecord<String, String, Integer>> dataToSend) {
+
+        return kafkaSender.send(dataToSend) //
+            .doOnError(e -> logger.error("Send to kafka failed", e));
+    }
+
+    private SenderOptions<String, String> kafkaSenderOptions() {
+        String bootstrapServers = this.appConfig.getKafkaBootStrapServers();
+
+        Map<String, Object> props = new HashMap<>();
+        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
+        props.put(ProducerConfig.ACKS_CONFIG, "all");
+        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
+        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
+        return SenderOptions.create(props);
+    }
+
+    public Counters getCounters() {
+        return this.counters;
+    }
+
+    protected FileCollector createFileCollector() {
+        return new FileCollector(appConfig, counters);
+    }
+
+    private Mono<FilePublishInformation> fetchFile(FileData fileData) {
+        return this.dataStore.createLock(lockName(fileData.name())).filter(granted -> granted) //
+            .map(granted -> createFileCollector()) //
+            .flatMap(collector -> collector.collectFile(fileData, FILE_TRANSFER_MAX_RETRIES,
+                FILE_TRANSFER_INITIAL_RETRY_TIMEOUT)) //
+            .flatMap(this::moveFileToS3Bucket) //
+            .doOnNext(b -> deleteLock(fileData).subscribe()) //
+            .doOnError(b -> deleteLock(fileData).subscribe()) //
+            .onErrorResume(exception -> handleFetchFileFailure(fileData, exception)); //
+    }
+
+    private Mono<FilePublishInformation> handleFetchFileFailure(FileData fileData, Throwable t) {
+        Path localFilePath = fileData.getLocalFilePath(this.appConfig);
+        logger.error("File fetching failed, path {}, reason: {}", fileData.remoteFilePath(), t.getMessage());
+        deleteFile(localFilePath);
+        if (Scheme.isFtpScheme(fileData.scheme())) {
+            counters.incNoOfFailedFtp();
+        } else {
+            counters.incNoOfFailedHttp();
+        }
+        return Mono.empty();
+    }
+
+    /**
+     * Fetch more messages from the message router. This is done in a
+     * polling/blocking fashion.
+     */
+    private Flux<FileReadyMessage> fetchFromKafka() {
+        KafkaTopicListener listener = new KafkaTopicListener(this.appConfig.getKafkaBootStrapServers(),
+            this.appConfig.kafkaClientId, this.appConfig.fileReadyEventTopic);
+        return listener.getFlux() //
+            .flatMap(this::parseReceivedFileReadyMessage, 1);
+
+    }
+
+    Mono<FileReadyMessage> parseReceivedFileReadyMessage(KafkaTopicListener.DataFromTopic data) {
+        try {
+            FileReadyMessage msg = gson.fromJson(data.value, FileReadyMessage.class);
+            logger.debug("Received: {}", msg);
+            return Mono.just(msg);
+        } catch (Exception e) {
+            logger.warn("Could not parse received: {}, reason: {}", data.value, e.getMessage());
+            return Mono.empty();
+        }
+    }
+
+    private static void deleteFile(Path localFile) {
+        logger.trace("Deleting file: {}", localFile);
+        try {
+            Files.delete(localFile);
+        } catch (Exception e) {
+            logger.trace("Could not delete file: {}, reason: {}", localFile, e.getMessage());
+        }
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/tasks/FileCollector.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/tasks/FileCollector.java
new file mode 100644 (file)
index 0000000..b6c07e5
--- /dev/null
@@ -0,0 +1,187 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018-2019 Nordix Foundation. All rights reserved.
+ * Copyright (C) 2020-2022 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.dcaegen2.collectors.datafile.tasks;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.Optional;
+
+import org.apache.commons.io.FileUtils;
+import org.onap.dcaegen2.collectors.datafile.commons.FileCollectClient;
+import org.onap.dcaegen2.collectors.datafile.commons.Scheme;
+import org.onap.dcaegen2.collectors.datafile.configuration.AppConfig;
+import org.onap.dcaegen2.collectors.datafile.configuration.CertificateConfig;
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.exceptions.NonRetryableDatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.ftp.FtpesClient;
+import org.onap.dcaegen2.collectors.datafile.ftp.SftpClient;
+import org.onap.dcaegen2.collectors.datafile.ftp.SftpClientSettings;
+import org.onap.dcaegen2.collectors.datafile.http.DfcHttpClient;
+import org.onap.dcaegen2.collectors.datafile.http.DfcHttpsClient;
+import org.onap.dcaegen2.collectors.datafile.http.HttpsClientConnectionManagerUtil;
+import org.onap.dcaegen2.collectors.datafile.model.Counters;
+import org.onap.dcaegen2.collectors.datafile.model.FileData;
+import org.onap.dcaegen2.collectors.datafile.model.FilePublishInformation;
+import org.onap.dcaegen2.collectors.datafile.model.FileReadyMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import reactor.core.publisher.Mono;
+import reactor.util.retry.Retry;
+
+/**
+ * Collects a file from a PNF.
+ *
+ * @author <a href="mailto:henrik.b.andersson@est.tech">Henrik Andersson</a>
+ */
+public class FileCollector {
+
+    private static final Logger logger = LoggerFactory.getLogger(FileCollector.class);
+    private final AppConfig appConfig;
+    private final Counters counters;
+
+    /**
+     * Constructor.
+     *
+     * @param appConfig application configuration
+     */
+    public FileCollector(AppConfig appConfig, Counters counters) {
+        this.appConfig = appConfig;
+        this.counters = counters;
+    }
+
+    /**
+     * Collects a file from the PNF and stores it in the local file system.
+     *
+     * @param fileData data about the file to collect.
+     * @param numRetries the number of retries if the publishing fails
+     * @param firstBackoff the time to delay the first retry
+     * @param contextMap context for logging.
+     * @return the data needed to publish the file.
+     */
+    public Mono<FilePublishInformation> collectFile(FileData fileData, long numRetries, Duration firstBackoff) {
+
+        logger.trace("Entering collectFile with {}", fileData);
+
+        return Mono.just(fileData) //
+            .cache() //
+            .flatMap(fd -> tryCollectFile(fileData)) //
+            .retryWhen(Retry.backoff(numRetries, firstBackoff)) //
+            .flatMap(FileCollector::checkCollectedFile);
+    }
+
+    private static Mono<FilePublishInformation> checkCollectedFile(Optional<FilePublishInformation> info) {
+        if (info.isPresent()) {
+            return Mono.just(info.get());
+        } else {
+            // If there is no info, the file is not retrievable
+            return Mono.error(new DatafileTaskException("Non retryable file transfer failure"));
+        }
+    }
+
+    private Mono<Optional<FilePublishInformation>> tryCollectFile(FileData fileData) {
+        logger.trace("starting to collectFile {}", fileData.fileInfo.name);
+
+        final String remoteFile = fileData.remoteFilePath();
+        final Path localFile = fileData.getLocalFilePath(this.appConfig);
+
+        try (FileCollectClient currentClient = createClient(fileData)) {
+            currentClient.open();
+            FileUtils.forceMkdirParent(localFile.toFile());
+            currentClient.collectFile(remoteFile, localFile);
+            counters.incNoOfCollectedFiles();
+            return Mono.just(Optional.of(createFilePublishInformation(fileData)));
+        } catch (NonRetryableDatafileTaskException nre) {
+            logger.warn("Failed to download file, not retryable: {} {}, reason: {}", fileData.sourceName(),
+                fileData.fileInfo.name, nre.getMessage());
+            incFailedAttemptsCounter(fileData);
+            return Mono.just(Optional.empty()); // Give up
+        } catch (DatafileTaskException e) {
+            logger.warn("Failed to download file: {} {}, reason: {}", fileData.sourceName(), fileData.fileInfo.name,
+                e.getMessage());
+            incFailedAttemptsCounter(fileData);
+            return Mono.error(e);
+        } catch (Exception throwable) {
+            logger.warn("Failed to close client: {} {}, reason: {}", fileData.sourceName(), fileData.fileInfo.name,
+                throwable.getMessage(), throwable);
+            return Mono.just(Optional.of(createFilePublishInformation(fileData)));
+        }
+    }
+
+    private void incFailedAttemptsCounter(FileData fileData) {
+        if (Scheme.isFtpScheme(fileData.scheme())) {
+            counters.incNoOfFailedFtpAttempts();
+        } else {
+            counters.incNoOfFailedHttpAttempts();
+        }
+    }
+
+    private FileCollectClient createClient(FileData fileData) throws DatafileTaskException {
+        switch (fileData.scheme()) {
+            case SFTP:
+                return createSftpClient(fileData);
+            case FTPES:
+                return createFtpesClient(fileData);
+            case HTTP:
+                return createHttpClient(fileData);
+            case HTTPS:
+                return createHttpsClient(fileData);
+            default:
+                throw new DatafileTaskException("Unhandled protocol: " + fileData.scheme());
+        }
+    }
+
+    public FilePublishInformation createFilePublishInformation(FileData fileData) {
+        FileReadyMessage.MessageMetaData metaData = fileData.messageMetaData;
+        return FilePublishInformation.builder() //
+            .productName(metaData.productName()) //
+            .vendorName(metaData.vendorName()) //
+            .lastEpochMicrosec(metaData.lastEpochMicrosec) //
+            .sourceName(metaData.sourceName) //
+            .startEpochMicrosec(metaData.startEpochMicrosec) //
+            .timeZoneOffset(metaData.timeZoneOffset) //
+            .name(metaData.sourceName + "/" + fileData.fileInfo.name) //
+            .compression(fileData.fileInfo.hashMap.compression) //
+            .fileFormatType(fileData.fileInfo.hashMap.fileFormatType) //
+            .fileFormatVersion(fileData.fileInfo.hashMap.fileFormatVersion) //
+            .changeIdentifier(fileData.messageMetaData.changeIdentifier) //
+            .objectStoreBucket(this.appConfig.isS3Enabled() ? this.appConfig.getS3Bucket() : null) //
+            .build();
+    }
+
+    protected SftpClient createSftpClient(FileData fileData) {
+        return new SftpClient(fileData.fileServerData(), new SftpClientSettings(appConfig.getSftpConfiguration()));
+    }
+
+    protected FtpesClient createFtpesClient(FileData fileData) throws DatafileTaskException {
+        CertificateConfig config = appConfig.getCertificateConfiguration();
+        Path trustedCa = config.trustedCa.isEmpty() ? null : Paths.get(config.trustedCa);
+
+        return new FtpesClient(fileData.fileServerData(), Paths.get(config.keyCert), config.keyPasswordPath, trustedCa,
+            config.trustedCaPasswordPath);
+    }
+
+    protected FileCollectClient createHttpClient(FileData fileData) {
+        return new DfcHttpClient(fileData.fileServerData());
+    }
+
+    protected FileCollectClient createHttpsClient(FileData fileData) throws DatafileTaskException {
+        return new DfcHttpsClient(fileData.fileServerData(), HttpsClientConnectionManagerUtil.instance());
+    }
+}
diff --git a/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/tasks/KafkaTopicListener.java b/datafilecollector/src/main/java/org/onap/dcaegen2/collectors/datafile/tasks/KafkaTopicListener.java
new file mode 100644 (file)
index 0000000..969e5fa
--- /dev/null
@@ -0,0 +1,106 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2021 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.tasks;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import lombok.ToString;
+
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.common.serialization.StringDeserializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import reactor.core.publisher.Flux;
+import reactor.kafka.receiver.KafkaReceiver;
+import reactor.kafka.receiver.ReceiverOptions;
+
+/**
+ * The class streams incoming requests from a Kafka topic and sends them further
+ * to a multi cast sink, which several other streams can connect to.
+ */
+@SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
+public class KafkaTopicListener {
+
+    @ToString
+    public static class DataFromTopic {
+        public final String key;
+        public final String value;
+
+        public DataFromTopic(String key, String value) {
+            this.key = key;
+            this.value = value;
+        }
+    }
+
+    private static final Logger logger = LoggerFactory.getLogger(KafkaTopicListener.class);
+
+    private final String inputTopic;
+    private final String kafkaBoostrapServers;
+    private final String kafkaClientId;
+    private Flux<DataFromTopic> dataFromTopic;
+
+    public KafkaTopicListener(String kafkaBoostrapServers, String clientId, String topic) {
+        this.kafkaClientId = clientId;
+        this.kafkaBoostrapServers = kafkaBoostrapServers;
+        this.inputTopic = topic;
+    }
+
+    public Flux<DataFromTopic> getFlux() {
+        if (this.dataFromTopic == null) {
+            this.dataFromTopic = startReceiveFromTopic();
+        }
+        return this.dataFromTopic;
+    }
+
+    private Flux<DataFromTopic> startReceiveFromTopic() {
+        logger.debug("Listening to kafka topic: {}, client id: {}", this.inputTopic, this.kafkaClientId);
+        return KafkaReceiver.create(kafkaInputProperties()) //
+            .receive() //
+            .doOnNext(input -> logger.debug("Received from kafka topic: {} :{}", this.inputTopic, input.value())) //
+            .doOnError(t -> logger.error("KafkaTopicReceiver error: {}", t.getMessage())) //
+            .doFinally(sig -> logger.error("KafkaTopicReceiver stopped, reason: {}", sig)) //
+            .doFinally(sig -> this.dataFromTopic = null) //
+            .filter(t -> !t.value().isEmpty() || !t.key().isEmpty()) //
+            .map(input -> new DataFromTopic(input.key(), input.value())) //
+            .publish() //
+            .autoConnect();
+    }
+
+    private ReceiverOptions<String, String> kafkaInputProperties() {
+        Map<String, Object> consumerProps = new HashMap<>();
+        if (this.kafkaBoostrapServers.isEmpty()) {
+            logger.error("No kafka boostrap server is setup");
+        }
+        consumerProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, this.kafkaBoostrapServers);
+        consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "osc-dmaap-adapter-" + inputTopic);
+        consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
+        consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
+        consumerProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
+        consumerProps.put(ConsumerConfig.CLIENT_ID_CONFIG, this.kafkaClientId);
+
+        return ReceiverOptions.<String, String>create(consumerProps)
+            .subscription(Collections.singleton(this.inputTopic));
+    }
+
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/MockDatafile.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/MockDatafile.java
new file mode 100644 (file)
index 0000000..16a36c0
--- /dev/null
@@ -0,0 +1,341 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.onap.dcaegen2.collectors.datafile.configuration.AppConfig;
+import org.onap.dcaegen2.collectors.datafile.datastore.DataStore;
+import org.onap.dcaegen2.collectors.datafile.datastore.DataStore.Bucket;
+import org.onap.dcaegen2.collectors.datafile.model.Counters;
+import org.onap.dcaegen2.collectors.datafile.model.FileData;
+import org.onap.dcaegen2.collectors.datafile.model.FilePublishInformation;
+import org.onap.dcaegen2.collectors.datafile.model.FileReadyMessage;
+import org.onap.dcaegen2.collectors.datafile.model.FileReadyMessage.MessageMetaData;
+import org.onap.dcaegen2.collectors.datafile.tasks.CollectAndReportFiles;
+import org.onap.dcaegen2.collectors.datafile.tasks.FileCollector;
+import org.onap.dcaegen2.collectors.datafile.tasks.KafkaTopicListener;
+import org.onap.dcaegen2.collectors.datafile.tasks.KafkaTopicListener.DataFromTopic;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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.boot.test.web.server.LocalServerPort;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.test.context.TestPropertySource;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
+@TestPropertySource(properties = { //
+        "app.ssl.key-store-password-file=./config/ftps_keystore.pass", //
+        "app.ssl.key-store=./config/ftps_keystore.p12", //
+        "app.ssl.trust-store-password-file=./config/truststore.pass", //
+        "app.ssl.trust-store=", // No trust validation
+        "app.collected-files-path=/tmp/osc_datafile/", //
+        "logging.file.name=/tmp/datafile.log", //
+        "spring.main.allow-bean-definition-overriding=true", //
+        "app.s3.endpointOverride=http://localhost:9000", //
+        "app.s3.accessKeyId=minio", //
+        "app.s3.secretAccessKey=miniostorage", //
+        "app.s3.bucket=ropfiles", //
+        "app.s3.locksBucket=locks" })
+@SuppressWarnings("squid:S3577") // Not containing any tests since it is a mock.
+class MockDatafile {
+
+    private static final int LAST_EPOCH_MICROSEC = 151983;
+    private static final String SOURCE_NAME = "5GRAN_DU";
+    private static final int START_EPOCH_MICROSEC = 15198378;
+    private static final String TIME_ZONE_OFFSET = "UTC+05:00";
+    private static final String PM_FILE_NAME = "PM_FILE_NAME";
+
+    // This can be any downloadable file on the net
+    private static final String FTPES_LOCATION = "ftpes:// onap:pano@ftp-ftpes-6:2021/A20000626.2315+0200-2330+0200_GNODEB-15-4.xml.gz";
+    private static final String LOCATION = "https://launchpad.net/ubuntu/+source/perf-tools-unstable/1.0+git7ffb3fd-1ubuntu1/+build/13630748/+files/perf-tools-unstable_1.0+git7ffb3fd-1ubuntu1_all.deb";
+    private static final String GZIP_COMPRESSION = "gzip";
+    private static final String FILE_FORMAT_TYPE = "org.3GPP.32.435#measCollec";
+    private static final String FILE_FORMAT_VERSION = "V10";
+    private static final String CHANGE_IDENTIFIER = "PM_MEAS_FILES";
+    private static final String CHANGE_TYPE = "FileReady";
+
+    private static final Logger logger = LoggerFactory.getLogger(MockDatafile.class);
+    private static Gson gson = new GsonBuilder() //
+            .disableHtmlEscaping() //
+            .create(); //
+
+    @LocalServerPort
+    private int port;
+
+    @Autowired
+    AppConfig appConfig;
+
+    @Autowired
+    CollectAndReportFiles scheduledTask;
+
+    private static KafkaReceiver kafkaReceiver;
+
+    private static class KafkaReceiver {
+        public final String topic;
+        private DataFromTopic receivedKafkaOutput;
+        private final Logger logger = LoggerFactory.getLogger(MockDatafile.class);
+
+        int count = 0;
+
+        public KafkaReceiver(AppConfig applicationConfig, String outputTopic) {
+            this.topic = outputTopic;
+
+            // Create a listener to the output topic. The KafkaTopicListener happens to be
+            // suitable for that,
+
+            KafkaTopicListener topicListener = new KafkaTopicListener(applicationConfig.getKafkaBootStrapServers(),
+                    "MockDatafile", outputTopic);
+
+            topicListener.getFlux() //
+                    .doOnNext(this::set) //
+                    .doFinally(sig -> logger.info("Finally " + sig)) //
+                    .subscribe();
+        }
+
+        private void set(DataFromTopic receivedKafkaOutput) {
+            this.receivedKafkaOutput = receivedKafkaOutput;
+            this.count++;
+            logger.debug("*** received {}, {}", topic, receivedKafkaOutput);
+        }
+
+        public synchronized String lastKey() {
+            return this.receivedKafkaOutput.key;
+        }
+
+        public synchronized String lastValue() {
+            return this.receivedKafkaOutput.value;
+        }
+
+        public void reset() {
+            count = 0;
+            this.receivedKafkaOutput = new DataFromTopic("", "");
+        }
+    }
+
+    static class FileCollectorMock extends FileCollector {
+        final AppConfig appConfig;
+
+        public FileCollectorMock(AppConfig appConfig) {
+            super(appConfig, new Counters());
+            this.appConfig = appConfig;
+        }
+
+        @Override // (override fetchFile to disable the actual file fetching)
+        public Mono<FilePublishInformation> collectFile(FileData fileData, long numRetries, Duration firstBackoff) {
+            FileCollector fc = new FileCollector(this.appConfig, new Counters());
+            FilePublishInformation i = fc.createFilePublishInformation(fileData);
+
+            try {
+                File from = new File("config/application.yaml");
+                File to = new File(this.appConfig.collectedFilesPath + "/" + fileData.name());
+                FileUtils.forceMkdirParent(to);
+                com.google.common.io.Files.copy(from, to);
+            } catch (Exception e) {
+                logger.error("Could not copy file {}", e.getMessage());
+            }
+            return Mono.just(i);
+        }
+    }
+
+    static class CollectAndReportFilesMock extends CollectAndReportFiles {
+        final AppConfig appConfig;
+
+        public CollectAndReportFilesMock(AppConfig appConfig) {
+            super(appConfig);
+            this.appConfig = appConfig;
+        }
+
+        @Override // (override fetchFile to disable the actual file fetching)
+        protected FileCollector createFileCollector() {
+            return new FileCollectorMock(appConfig);
+        }
+    }
+
+    @TestConfiguration
+    static class TestBeanFactory {
+
+        @Bean
+        CollectAndReportFiles collectAndReportFiles(@Autowired AppConfig appConfig) {
+            return new CollectAndReportFilesMock(appConfig);
+        }
+    }
+
+    @BeforeEach
+    void init() {
+        if (kafkaReceiver == null) {
+            kafkaReceiver = new KafkaReceiver(this.appConfig, this.appConfig.collectedFileTopic);
+        }
+        kafkaReceiver.reset();
+        deleteAllFiles();
+    }
+
+    @AfterEach
+    void afterEach() {
+        DataStore store = DataStore.create(this.appConfig);
+        store.deleteBucket(Bucket.FILES).block();
+        store.deleteBucket(Bucket.LOCKS).block();
+        deleteAllFiles();
+
+    }
+
+    private void deleteAllFiles() {
+
+        try {
+            FileUtils.deleteDirectory(new File(this.appConfig.collectedFilesPath));
+        } catch (IOException e) {
+        }
+    }
+
+    @Test
+    void clear() {
+
+    }
+
+    @Test
+    void testKafka() throws InterruptedException {
+        waitForKafkaListener();
+
+        this.scheduledTask.sendDataToStream(this.appConfig.fileReadyEventTopic, "key", "junk").blockLast();
+
+        String fileReadyMessage = gson.toJson(fileReadyMessage());
+        this.scheduledTask.sendDataToStream(this.appConfig.fileReadyEventTopic, "key", fileReadyMessage).blockLast();
+
+        await().untilAsserted(() -> assertThat(kafkaReceiver.count).isEqualTo(1));
+        String rec = kafkaReceiver.lastValue();
+
+        assertThat(rec).contains("Ericsson");
+
+        FilePublishInformation recObj = gson.fromJson(rec, FilePublishInformation.class);
+
+        assertThat(recObj.getName()).isEqualTo(SOURCE_NAME + "/" + PM_FILE_NAME);
+    }
+
+    @Test
+    void testS3Concurrency() throws Exception {
+        waitForKafkaListener();
+
+        final int NO_OF_OBJECTS = 10;
+
+        Instant startTime = Instant.now();
+
+        Flux.range(1, NO_OF_OBJECTS) //
+                .map(i -> gson.toJson(fileReadyMessage("testS3Concurrency_" + i))) //
+                .flatMap(fileReadyMessage -> scheduledTask.sendDataToStream(appConfig.fileReadyEventTopic, "key",
+                        fileReadyMessage)) //
+                .blockLast(); //
+
+        while (kafkaReceiver.count < NO_OF_OBJECTS) {
+            logger.info("sleeping {}", kafkaReceiver.count);
+            Thread.sleep(1000 * 1);
+        }
+
+        String rec = kafkaReceiver.lastValue();
+        assertThat(rec).contains("Ericsson");
+
+        final long durationSeconds = Instant.now().getEpochSecond() - startTime.getEpochSecond();
+        logger.info("*** Duration :" + durationSeconds + ", objects/second: " + NO_OF_OBJECTS / durationSeconds);
+    }
+
+    @SuppressWarnings("squid:S2925") // "Thread.sleep" should not be used in tests.
+    private static void waitForKafkaListener() throws InterruptedException {
+        Thread.sleep(4000);
+    }
+
+    @Test
+    @SuppressWarnings("squid:S2699")
+    void runMock() throws Exception {
+        logger.warn("**************** Keeping server alive! " + this.port);
+        synchronized (this) {
+            this.wait();
+        }
+    }
+
+    FileReadyMessage.Event event(String fileName) {
+        MessageMetaData messageMetaData = MessageMetaData.builder() //
+                .lastEpochMicrosec(LAST_EPOCH_MICROSEC) //
+                .sourceName(SOURCE_NAME) //
+                .startEpochMicrosec(START_EPOCH_MICROSEC) //
+                .timeZoneOffset(TIME_ZONE_OFFSET) //
+                .changeIdentifier(CHANGE_IDENTIFIER) //
+                .eventName("Noti_RnNode-Ericsson_FileReady").build();
+
+        FileReadyMessage.FileInfo fileInfo = FileReadyMessage.FileInfo //
+                .builder() //
+                .fileFormatType(FILE_FORMAT_TYPE) //
+                .location(LOCATION) //
+                .fileFormatVersion(FILE_FORMAT_VERSION) //
+                .compression(GZIP_COMPRESSION) //
+                .build();
+
+        FileReadyMessage.ArrayOfNamedHashMap arrayOfNamedHashMap = FileReadyMessage.ArrayOfNamedHashMap //
+                .builder() //
+                .name(fileName) //
+                .hashMap(fileInfo) //
+                .build();
+
+        List<FileReadyMessage.ArrayOfNamedHashMap> arrayOfNamedHashMapList = new ArrayList<>();
+        arrayOfNamedHashMapList.add(arrayOfNamedHashMap);
+
+        FileReadyMessage.NotificationFields notificationFields = FileReadyMessage.NotificationFields //
+                .builder().notificationFieldsVersion("notificationFieldsVersion") //
+                .changeType(CHANGE_TYPE).changeIdentifier(CHANGE_IDENTIFIER) //
+                .arrayOfNamedHashMap(arrayOfNamedHashMapList) //
+                .build();
+
+        return FileReadyMessage.Event.builder() //
+                .commonEventHeader(messageMetaData) //
+                .notificationFields(notificationFields).build();
+    }
+
+    private FileReadyMessage fileReadyMessage(String fileName) {
+        FileReadyMessage message = FileReadyMessage.builder() //
+                .event(event(fileName)) //
+                .build();
+        return message;
+    }
+
+    private FileReadyMessage fileReadyMessage() {
+        return fileReadyMessage(PM_FILE_NAME);
+    }
+
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/controllers/StatusControllerTest.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/controllers/StatusControllerTest.java
new file mode 100644 (file)
index 0000000..8bc6330
--- /dev/null
@@ -0,0 +1,73 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ *  Copyright (C) 2020 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.controllers;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.doReturn;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.onap.dcaegen2.collectors.datafile.model.Counters;
+import org.onap.dcaegen2.collectors.datafile.tasks.CollectAndReportFiles;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.ResponseEntity;
+import reactor.core.publisher.Mono;
+
+@ExtendWith(MockitoExtension.class)
+public class StatusControllerTest {
+    @Mock
+    CollectAndReportFiles scheduledTasksMock;
+
+    StatusController controllerUnderTest;
+
+    @BeforeEach
+    public void setup() {
+        controllerUnderTest = new StatusController(scheduledTasksMock);
+    }
+
+    @Test
+    public void heartbeat_success() {
+        HttpHeaders httpHeaders = new HttpHeaders();
+
+        Mono<ResponseEntity<String>> result = controllerUnderTest.heartbeat(httpHeaders);
+
+        String body = result.block().getBody();
+        assertTrue(body.startsWith("I'm living!"));
+    }
+
+    @Test
+    public void status() {
+        Counters counters = new Counters();
+        doReturn(counters).when(scheduledTasksMock).getCounters();
+
+        HttpHeaders httpHeaders = new HttpHeaders();
+
+        Mono<ResponseEntity<String>> result = controllerUnderTest.status(httpHeaders);
+
+        String body = result.block().getBody();
+        System.out.println(body);
+    }
+
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/ftp/FtpesClientTest.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/ftp/FtpesClientTest.java
new file mode 100644 (file)
index 0000000..3423826
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018-2019 Nordix Foundation. All rights reserved.
+ * Copyright (C) 2020 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.dcaegen2.collectors.datafile.ftp;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.TrustManager;
+
+import org.apache.commons.net.ftp.FTP;
+import org.apache.commons.net.ftp.FTPSClient;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentMatchers;
+import org.onap.dcaegen2.collectors.datafile.commons.FileServerData;
+import org.springframework.http.HttpStatus;
+
+public class FtpesClientTest {
+
+    private static final String REMOTE_FILE_PATH = "/dir/sample.txt";
+    private static final Path LOCAL_FILE_PATH = Paths.get("target/sample.txt");
+    private static final String XNF_ADDRESS = "127.0.0.1";
+    private static final int PORT = 8021;
+    private static final String FTP_KEY_PATH = "ftpKeyPath";
+    private static final String FTP_KEY_PASSWORD = "ftpKeyPassword";
+    private static final Path TRUSTED_CA_PATH = Paths.get("trustedCaPath");
+    private static final String TRUSTED_CA_PASSWORD = "trustedCaPassword";
+
+    private static final String USERNAME = "bob";
+    private static final String PASSWORD = "123";
+
+    private FTPSClient ftpsClientMock = mock(FTPSClient.class);
+    private KeyManager keyManagerMock = mock(KeyManager.class);
+    private TrustManager trustManagerMock = mock(TrustManager.class);
+    private InputStream inputStreamMock = mock(InputStream.class);
+    private OutputStream outputStreamMock = mock(OutputStream.class);
+
+    FtpesClient clientUnderTestSpy;
+
+    private FileServerData createFileServerData() {
+        return FileServerData.builder() //
+            .serverAddress(XNF_ADDRESS) //
+            .userId(USERNAME).password(PASSWORD) //
+            .port(PORT) //
+            .build();
+    }
+
+    @BeforeEach
+    protected void setUp() throws Exception {
+        clientUnderTestSpy = spy(new FtpesClient(createFileServerData(), Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD,
+            TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD));
+        clientUnderTestSpy.realFtpsClient = ftpsClientMock;
+    }
+
+    private void verifyFtpsClientMock_openOk() throws Exception {
+        doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
+
+        when(ftpsClientMock.retrieveFile(ArgumentMatchers.eq(REMOTE_FILE_PATH),
+            ArgumentMatchers.any(OutputStream.class))).thenReturn(true);
+        verify(ftpsClientMock).setNeedClientAuth(true);
+        verify(ftpsClientMock).setKeyManager(keyManagerMock);
+        verify(ftpsClientMock).setTrustManager(trustManagerMock);
+        verify(ftpsClientMock).connect(XNF_ADDRESS, PORT);
+        verify(ftpsClientMock).login(USERNAME, PASSWORD);
+        verify(ftpsClientMock).getReplyCode();
+        verify(ftpsClientMock, times(1)).enterLocalPassiveMode();
+        verify(ftpsClientMock).execPBSZ(0);
+        verify(ftpsClientMock).execPROT("P");
+        verify(ftpsClientMock).setFileType(FTP.BINARY_FILE_TYPE);
+        verify(ftpsClientMock).setBufferSize(1024 * 1024);
+    }
+
+    @Test
+    public void collectFile_allOk() throws Exception {
+
+        doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD);
+        doReturn(trustManagerMock).when(clientUnderTestSpy).getTrustManager(TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD);
+        doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
+        doReturn(true).when(ftpsClientMock).login(USERNAME, PASSWORD);
+        doReturn(HttpStatus.OK.value()).when(ftpsClientMock).getReplyCode();
+
+        clientUnderTestSpy.open();
+
+        doReturn(true).when(ftpsClientMock).retrieveFile(REMOTE_FILE_PATH, outputStreamMock);
+        clientUnderTestSpy.collectFile(REMOTE_FILE_PATH, LOCAL_FILE_PATH);
+
+        doReturn(true).when(ftpsClientMock).isConnected();
+        clientUnderTestSpy.close();
+
+        verifyFtpsClientMock_openOk();
+        verify(ftpsClientMock, times(1)).isConnected();
+        verify(ftpsClientMock, times(1)).logout();
+        verify(ftpsClientMock, times(1)).disconnect();
+        verify(ftpsClientMock, times(1)).retrieveFile(ArgumentMatchers.eq(REMOTE_FILE_PATH), any());
+        verifyNoMoreInteractions(ftpsClientMock);
+    }
+
+    @Test
+    public void collectFileFaultyOwnKey_shouldFail() throws Exception {
+
+        doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
+        assertThatThrownBy(() -> clientUnderTestSpy.open())
+            .hasMessageContaining("Could not open connection: java.io.FileNotFoundException:");
+
+        verify(ftpsClientMock).setNeedClientAuth(true);
+
+        doReturn(false).when(ftpsClientMock).isConnected();
+        clientUnderTestSpy.close();
+        verify(ftpsClientMock).isConnected();
+        verifyNoMoreInteractions(ftpsClientMock);
+    }
+
+    @Test
+    public void collectFileFaultTrustedCA_shouldFail_no_trustedCA_file() throws Exception {
+
+        doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD);
+        doThrow(new IOException("problem")).when(clientUnderTestSpy).createInputStream(TRUSTED_CA_PATH);
+
+        assertThatThrownBy(() -> clientUnderTestSpy.open())
+            .hasMessage("Could not open connection: java.io.IOException: problem");
+    }
+
+    @Test
+    public void collectFileFaultTrustedCA_shouldFail_empty_trustedCA_file() throws Exception {
+
+        doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD);
+        doReturn(inputStreamMock).when(clientUnderTestSpy).createInputStream(TRUSTED_CA_PATH);
+
+        assertThatThrownBy(() -> clientUnderTestSpy.open())
+            .hasMessage("Could not open connection: java.io.EOFException");
+    }
+
+    @Test
+    public void collectFileFaultyLogin_shouldFail() throws Exception {
+
+        doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD);
+        doReturn(trustManagerMock).when(clientUnderTestSpy).getTrustManager(TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD);
+        doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
+        doReturn(false).when(ftpsClientMock).login(USERNAME, PASSWORD);
+
+        assertThatThrownBy(() -> clientUnderTestSpy.open()).hasMessage("Unable to log in to xNF. 127.0.0.1");
+
+        verify(ftpsClientMock).setNeedClientAuth(true);
+        verify(ftpsClientMock).setKeyManager(keyManagerMock);
+        verify(ftpsClientMock).setTrustManager(trustManagerMock);
+        verify(ftpsClientMock).connect(XNF_ADDRESS, PORT);
+        verify(ftpsClientMock).login(USERNAME, PASSWORD);
+    }
+
+    @Test
+    public void collectFileBadRequestResponse_shouldFail() throws Exception {
+        doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD);
+        doReturn(trustManagerMock).when(clientUnderTestSpy).getTrustManager(TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD);
+        doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
+        doReturn(true).when(ftpsClientMock).login(USERNAME, PASSWORD);
+        doReturn(503).when(ftpsClientMock).getReplyCode();
+
+        assertThatThrownBy(() -> clientUnderTestSpy.open())
+            .hasMessage("Unable to connect to xNF. 127.0.0.1 xNF reply code: 503");
+
+        verify(ftpsClientMock).setNeedClientAuth(true);
+        verify(ftpsClientMock).setKeyManager(keyManagerMock);
+        verify(ftpsClientMock).setTrustManager(trustManagerMock);
+        verify(ftpsClientMock).connect(XNF_ADDRESS, PORT);
+        verify(ftpsClientMock).login(USERNAME, PASSWORD);
+        verify(ftpsClientMock, times(2)).getReplyCode();
+        verifyNoMoreInteractions(ftpsClientMock);
+    }
+
+    @Test
+    public void collectFile_shouldFail() throws Exception {
+        doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD);
+        doReturn(trustManagerMock).when(clientUnderTestSpy).getTrustManager(TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD);
+        doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
+        doReturn(true).when(ftpsClientMock).login(USERNAME, PASSWORD);
+        doReturn(HttpStatus.OK.value()).when(ftpsClientMock).getReplyCode();
+        clientUnderTestSpy.open();
+
+        doReturn(false).when(ftpsClientMock).retrieveFile(REMOTE_FILE_PATH, outputStreamMock);
+
+        assertThatThrownBy(() -> clientUnderTestSpy.collectFile(REMOTE_FILE_PATH, LOCAL_FILE_PATH))
+            .hasMessageContaining(REMOTE_FILE_PATH).hasMessageContaining("No retry");
+
+        verifyFtpsClientMock_openOk();
+        verify(ftpsClientMock, times(1)).retrieveFile(ArgumentMatchers.eq(REMOTE_FILE_PATH), any());
+        verifyNoMoreInteractions(ftpsClientMock);
+    }
+
+    @Test
+    public void collectFile_shouldFail_ioexception() throws Exception {
+        doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD);
+        doReturn(trustManagerMock).when(clientUnderTestSpy).getTrustManager(TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD);
+        doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
+        doReturn(true).when(ftpsClientMock).login(USERNAME, PASSWORD);
+        doReturn(HttpStatus.OK.value()).when(ftpsClientMock).getReplyCode();
+        clientUnderTestSpy.open();
+        when(ftpsClientMock.isConnected()).thenReturn(false);
+
+        doThrow(new IOException("problem")).when(ftpsClientMock).retrieveFile(REMOTE_FILE_PATH, outputStreamMock);
+
+        assertThatThrownBy(() -> clientUnderTestSpy.collectFile(REMOTE_FILE_PATH, LOCAL_FILE_PATH))
+            .hasMessage("Could not fetch file: java.io.IOException: problem");
+
+        verifyFtpsClientMock_openOk();
+        verify(ftpsClientMock, times(1)).retrieveFile(ArgumentMatchers.eq(REMOTE_FILE_PATH), any());
+        verifyNoMoreInteractions(ftpsClientMock);
+    }
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClientSettingsTest.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClientSettingsTest.java
new file mode 100644 (file)
index 0000000..5ee379b
--- /dev/null
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018-2019 Nordix Foundation. All rights reserved.
+ * Copyright (C) 2020 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.dcaegen2.collectors.datafile.ftp;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.nio.file.Path;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.onap.dcaegen2.collectors.datafile.configuration.SftpConfig;
+
+public class SftpClientSettingsTest {
+
+    @Test
+    public void shouldUseFtpStrictHostChecking(@TempDir Path tempDir) throws Exception {
+        File knowHostsFile = new File(tempDir.toFile(), "known_hosts");
+        knowHostsFile.createNewFile();
+
+        SftpConfig config = createSampleSftpConfigWithStrictHostChecking(knowHostsFile.getAbsolutePath());
+        SftpClientSettings sftpClient = new SftpClientSettings(config);
+
+        assertThat(sftpClient.shouldUseStrictHostChecking()).isTrue();
+    }
+
+    @Test
+    public void shouldNotUseFtpStrictHostChecking_whenFileDoesNotExist() {
+        SftpConfig config = createSampleSftpConfigWithStrictHostChecking("unknown_file");
+        SftpClientSettings sftpClient = new SftpClientSettings(config);
+
+        sftpClient.shouldUseStrictHostChecking();
+        assertThat(sftpClient.shouldUseStrictHostChecking()).isFalse();
+    }
+
+    @Test
+    public void shouldNotUseFtpStrictHostChecking_whenExplicitlySwitchedOff() {
+        SftpClientSettings sftpClient = new SftpClientSettings(createSampleSftpConfigNoStrictHostChecking());
+        sftpClient.shouldUseStrictHostChecking();
+        assertThat(sftpClient.shouldUseStrictHostChecking()).isFalse();
+    }
+
+    private SftpConfig createSampleSftpConfigNoStrictHostChecking() {
+        return SftpConfig.builder() //
+            .strictHostKeyChecking(false).knownHostsFilePath("N/A").build();
+    }
+
+    private SftpConfig createSampleSftpConfigWithStrictHostChecking(String pathToKnownHostsFile) {
+        return SftpConfig.builder() //
+            .strictHostKeyChecking(true).knownHostsFilePath(pathToKnownHostsFile).build();
+    }
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClientTest.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/ftp/SftpClientTest.java
new file mode 100644 (file)
index 0000000..596bec8
--- /dev/null
@@ -0,0 +1,237 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2020 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.dcaegen2.collectors.datafile.ftp;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpException;
+
+import java.nio.file.Paths;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.onap.dcaegen2.collectors.datafile.commons.FileServerData;
+import org.onap.dcaegen2.collectors.datafile.configuration.SftpConfig;
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.exceptions.NonRetryableDatafileTaskException;
+
+@ExtendWith(MockitoExtension.class)
+public class SftpClientTest {
+
+    private static final String HOST = "127.0.0.1";
+    private static final int SFTP_PORT = 1021;
+    private static final String USERNAME = "bob";
+    private static final String PASSWORD = "123";
+
+    @Mock
+    private JSch jschMock;
+
+    @Mock
+    private Session sessionMock;
+
+    @Mock
+    private ChannelSftp channelMock;
+
+    @Test
+    public void openWithPort_success() throws Exception {
+        FileServerData expectedFileServerData = FileServerData.builder() //
+            .serverAddress(HOST) //
+            .userId(USERNAME) //
+            .password(PASSWORD) //
+            .port(SFTP_PORT) //
+            .build();
+
+        SftpClient sftpClientSpy = spy(new SftpClient(expectedFileServerData, createSampleSftpClientSettings()));
+
+        doReturn(jschMock).when(sftpClientSpy).createJsch();
+        when(jschMock.getSession(anyString(), anyString(), anyInt())).thenReturn(sessionMock);
+        when(sessionMock.openChannel(anyString())).thenReturn(channelMock);
+
+        sftpClientSpy.open();
+
+        verify(jschMock).getSession(USERNAME, HOST, SFTP_PORT);
+        verify(sessionMock).setConfig("StrictHostKeyChecking", "no");
+        verify(sessionMock).setPassword(PASSWORD);
+        verify(sessionMock).connect();
+        verify(sessionMock).openChannel("sftp");
+        verifyNoMoreInteractions(sessionMock);
+
+        verify(channelMock).connect();
+        verifyNoMoreInteractions(channelMock);
+    }
+
+    @Test
+    public void openWithoutPort_success() throws Exception {
+        FileServerData expectedFileServerData = FileServerData.builder() //
+            .serverAddress(HOST) //
+            .userId(USERNAME) //
+            .password(PASSWORD) //
+            .port(null) //
+            .build();
+
+        SftpClient sftpClientSpy = spy(new SftpClient(expectedFileServerData, createSampleSftpClientSettings()));
+
+        doReturn(jschMock).when(sftpClientSpy).createJsch();
+        when(jschMock.getSession(anyString(), anyString(), anyInt())).thenReturn(sessionMock);
+        when(sessionMock.openChannel(anyString())).thenReturn(channelMock);
+
+        sftpClientSpy.open();
+
+        verify(jschMock).getSession(USERNAME, HOST, 22);
+    }
+
+    @Test
+    public void open_throwsExceptionWithRetry() throws Exception {
+        FileServerData expectedFileServerData = FileServerData.builder() //
+            .serverAddress(HOST) //
+            .userId(USERNAME) //
+            .password(PASSWORD) //
+            .port(SFTP_PORT) //
+            .build();
+
+        SftpClient sftpClientSpy = spy(new SftpClient(expectedFileServerData, createSampleSftpClientSettings()));
+
+        doReturn(jschMock).when(sftpClientSpy).createJsch();
+        when(jschMock.getSession(anyString(), anyString(), anyInt())).thenThrow(new JSchException("Failed"));
+
+        DatafileTaskException exception = assertThrows(DatafileTaskException.class, () -> sftpClientSpy.open());
+        assertEquals("Could not open Sftp client. com.jcraft.jsch.JSchException: Failed", exception.getMessage());
+    }
+
+    @Test
+    public void openAuthFail_throwsExceptionWithoutRetry() throws Exception {
+        FileServerData expectedFileServerData = FileServerData.builder() //
+            .serverAddress(HOST) //
+            .userId(USERNAME) //
+            .password(PASSWORD) //
+            .port(SFTP_PORT) //
+            .build();
+
+        SftpClient sftpClientSpy = spy(new SftpClient(expectedFileServerData, createSampleSftpClientSettings()));
+
+        doReturn(jschMock).when(sftpClientSpy).createJsch();
+        when(jschMock.getSession(anyString(), anyString(), anyInt())).thenThrow(new JSchException("Auth fail"));
+
+        NonRetryableDatafileTaskException exception =
+            assertThrows(NonRetryableDatafileTaskException.class, () -> sftpClientSpy.open());
+        assertEquals(
+            "Could not open Sftp client, no retry attempts will be done. com.jcraft.jsch.JSchException: Auth fail",
+            exception.getMessage());
+    }
+
+    @SuppressWarnings("resource")
+    @Test
+    public void collectFile_success() throws DatafileTaskException, SftpException {
+        FileServerData expectedFileServerData = FileServerData.builder() //
+            .serverAddress(HOST) //
+            .userId(USERNAME) //
+            .password(PASSWORD) //
+            .port(SFTP_PORT) //
+            .build();
+        SftpClient sftpClient = new SftpClient(expectedFileServerData, createSampleSftpClientSettings());
+
+        sftpClient.sftpChannel = channelMock;
+
+        sftpClient.collectFile("remote.xml", Paths.get("local.xml"));
+
+        verify(channelMock).get("remote.xml", "local.xml");
+        verifyNoMoreInteractions(channelMock);
+    }
+
+    @Test
+    public void collectFile_throwsExceptionWithRetry() throws SftpException {
+        FileServerData expectedFileServerData = FileServerData.builder() //
+            .serverAddress(HOST) //
+            .userId(USERNAME) //
+            .password(PASSWORD) //
+            .port(SFTP_PORT) //
+            .build();
+
+        try (SftpClient sftpClient = new SftpClient(expectedFileServerData, createSampleSftpClientSettings())) {
+            sftpClient.sftpChannel = channelMock;
+            doThrow(new SftpException(ChannelSftp.SSH_FX_BAD_MESSAGE, "Failed")).when(channelMock).get(anyString(),
+                anyString());
+
+            assertThatThrownBy(() -> sftpClient.collectFile("remoteFile", Paths.get("localFile")))
+                .isInstanceOf(DatafileTaskException.class).hasMessageStartingWith("Unable to get file from xNF. ")
+                .hasMessageContaining(HOST);
+        }
+    }
+
+    @Test
+    public void collectFileFileMissing_throwsExceptionWithoutRetry() throws SftpException {
+        FileServerData expectedFileServerData = FileServerData.builder() //
+            .serverAddress(HOST) //
+            .userId(USERNAME) //
+            .password(PASSWORD) //
+            .port(SFTP_PORT) //
+            .build();
+
+        try (SftpClient sftpClient = new SftpClient(expectedFileServerData, createSampleSftpClientSettings())) {
+            sftpClient.sftpChannel = channelMock;
+            doThrow(new SftpException(ChannelSftp.SSH_FX_NO_SUCH_FILE, "Failed")).when(channelMock).get(anyString(),
+                anyString());
+
+            assertThatThrownBy(() -> sftpClient.collectFile("remoteFile", Paths.get("localFile")))
+                .isInstanceOf(NonRetryableDatafileTaskException.class)
+                .hasMessageStartingWith("Unable to get file from xNF. No retry attempts will be done")
+                .hasMessageContaining("" + SFTP_PORT);
+        }
+    }
+
+    @Test
+    public void close_success() {
+        SftpClient sftpClient = new SftpClient(null, createSampleSftpClientSettings());
+
+        sftpClient.session = sessionMock;
+        sftpClient.sftpChannel = channelMock;
+
+        sftpClient.close();
+
+        verify(sessionMock).disconnect();
+        verifyNoMoreInteractions(sessionMock);
+
+        verify(channelMock).exit();;
+        verifyNoMoreInteractions(channelMock);
+    }
+
+    private SftpClientSettings createSampleSftpClientSettings() {
+        return new SftpClientSettings(createSampleSftpConfigNoStrictHostChecking());
+    }
+
+    private SftpConfig createSampleSftpConfigNoStrictHostChecking() {
+        return SftpConfig.builder() //
+            .strictHostKeyChecking(false).knownHostsFilePath("N/A").build();
+    }
+
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/http/DfcHttpClientTest.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/http/DfcHttpClientTest.java
new file mode 100644 (file)
index 0000000..8550644
--- /dev/null
@@ -0,0 +1,159 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2020-2021 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.dcaegen2.collectors.datafile.http;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+
+import org.apache.hc.core5.net.URIBuilder;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.onap.dcaegen2.collectors.datafile.commons.FileServerData;
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.service.HttpUtils;
+
+import reactor.core.publisher.Flux;
+import reactor.netty.http.client.HttpClientConfig;
+
+@ExtendWith(MockitoExtension.class)
+class DfcHttpClientTest {
+
+    private static final String USERNAME = "bob";
+    private static final String PASSWORD = "123";
+    private static final String XNF_ADDRESS = "127.0.0.1";
+    private static final int PORT = 80;
+    private static final String JWT_PASSWORD = "thisIsThePassword";
+    private static String ACCESS_TOKEN = "access_token";
+
+    @Mock
+    private Path pathMock;
+
+    DfcHttpClient dfcHttpClientSpy;
+
+    @BeforeEach
+    public void setup() {
+        dfcHttpClientSpy = spy(new DfcHttpClient(createFileServerData()));
+    }
+
+    @Test
+    void openConnection_successBasicAuthSetup() throws DatafileTaskException {
+        dfcHttpClientSpy.open();
+        HttpClientConfig config = dfcHttpClientSpy.client.configuration();
+        assertEquals(HttpUtils.basicAuthContent(USERNAME, PASSWORD), config.headers().get("Authorization"));
+    }
+
+    @Test
+    void openConnection_failedBasicAuthSetupThrowException() {
+        FileServerData serverData =
+            FileServerData.builder().serverAddress(XNF_ADDRESS).userId(USERNAME).password("").port(PORT).build();
+
+        DfcHttpClient dfcHttpClientSpy = spy(new DfcHttpClient(serverData));
+
+        assertThatThrownBy(() -> dfcHttpClientSpy.open())
+            .hasMessageContaining("Not sufficient basic auth data for file.");
+    }
+
+    @Test
+    void collectFile_AllOk() throws Exception {
+        String REMOTE_FILE = "any";
+        Flux<InputStream> fis = Flux.just(new ByteArrayInputStream("ReturnedString".getBytes()));
+
+        dfcHttpClientSpy.open();
+
+        when(dfcHttpClientSpy.getServerResponse(any())).thenReturn(fis);
+        doReturn(false).when(dfcHttpClientSpy).isDownloadFailed(any());
+
+        dfcHttpClientSpy.collectFile(REMOTE_FILE, pathMock);
+        dfcHttpClientSpy.close();
+
+        verify(dfcHttpClientSpy, times(1)).getServerResponse(REMOTE_FILE);
+        verify(dfcHttpClientSpy, times(1)).processDataFromServer(any(), any(), any());
+        verify(dfcHttpClientSpy, times(1)).isDownloadFailed(any());
+    }
+
+    @Test
+    void collectFile_AllOkWithJWTToken() throws Exception {
+        dfcHttpClientSpy = spy(new DfcHttpClient(fileServerDataWithJWTToken()));
+        String REMOTE_FILE = "any";
+        Flux<InputStream> fis = Flux.just(new ByteArrayInputStream("ReturnedString".getBytes()));
+
+        dfcHttpClientSpy.open();
+        HttpClientConfig config = dfcHttpClientSpy.client.configuration();
+        assertEquals(HttpUtils.jwtAuthContent(JWT_PASSWORD), config.headers().get("Authorization"));
+
+        when(dfcHttpClientSpy.getServerResponse(any())).thenReturn(fis);
+        doReturn(false).when(dfcHttpClientSpy).isDownloadFailed(any());
+
+        dfcHttpClientSpy.collectFile(REMOTE_FILE, pathMock);
+        dfcHttpClientSpy.close();
+
+        verify(dfcHttpClientSpy, times(1)).getServerResponse(ArgumentMatchers.eq(REMOTE_FILE));
+        verify(dfcHttpClientSpy, times(1)).processDataFromServer(any(), any(), any());
+        verify(dfcHttpClientSpy, times(1)).isDownloadFailed(any());
+    }
+
+    @Test
+    void collectFile_No200ResponseWriteToErrorMessage() throws DatafileTaskException {
+        String ERROR_RESPONSE = "This is unexpected message";
+        String REMOTE_FILE = "any";
+        Flux<Throwable> fis = Flux.error(new Throwable(ERROR_RESPONSE));
+
+        dfcHttpClientSpy.open();
+
+        doReturn(fis).when(dfcHttpClientSpy).getServerResponse(any());
+
+        assertThatThrownBy(() -> dfcHttpClientSpy.collectFile(REMOTE_FILE, pathMock))
+            .hasMessageContaining(ERROR_RESPONSE);
+        verify(dfcHttpClientSpy, times(1)).getServerResponse(REMOTE_FILE);
+        verify(dfcHttpClientSpy, times(1)).processFailedConnectionWithServer(any(), any());
+        dfcHttpClientSpy.close();
+    }
+
+    @Test
+    void isResponseOk_validateResponse() {
+        assertTrue(dfcHttpClientSpy.isResponseOk(HttpClientResponseHelper.NETTY_RESPONSE_OK));
+        assertFalse(dfcHttpClientSpy.isResponseOk(HttpClientResponseHelper.RESPONSE_ANY_NO_OK));
+    }
+
+    private FileServerData createFileServerData() {
+        return FileServerData.builder().serverAddress(XNF_ADDRESS).userId(USERNAME).password(PASSWORD).port(PORT)
+            .build();
+    }
+
+    private FileServerData fileServerDataWithJWTToken() throws URISyntaxException {
+        String query = "?" + ACCESS_TOKEN + "=" + JWT_PASSWORD;
+
+        return FileServerData.builder().serverAddress(XNF_ADDRESS).userId("").password("").port(PORT)
+            .queryParameters(new URIBuilder(query).getQueryParams()).build();
+    }
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/http/DfcHttpsClientTest.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/http/DfcHttpsClientTest.java
new file mode 100644 (file)
index 0000000..4295fe8
--- /dev/null
@@ -0,0 +1,178 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2021 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.dcaegen2.collectors.datafile.http;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+
+import org.apache.hc.core5.net.URIBuilder;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.onap.dcaegen2.collectors.datafile.commons.FileServerData;
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.exceptions.NonRetryableDatafileTaskException;
+
+@ExtendWith(MockitoExtension.class)
+class DfcHttpsClientTest {
+
+    private static final String USERNAME = "bob";
+    private static final String PASSWORD = "123";
+    private static final String XNF_ADDRESS = "127.0.0.1";
+    private static final int PORT = 443;
+    private static final String JWT_PASSWORD = "thisIsThePassword";
+    private static String ACCESS_TOKEN = "access_token";
+    private static String remoteFile = "remoteFile";
+
+    @Mock
+    private PoolingHttpClientConnectionManager connectionManager;
+    @Mock
+    private Path localFile;
+
+    DfcHttpsClient dfcHttpsClientSpy;
+
+    @BeforeEach
+    public void setup() {
+        dfcHttpsClientSpy = spy(new DfcHttpsClient(createFileServerData(), connectionManager));
+    }
+
+    @Test
+    void fileServerData_properLocationBasicAuth() throws Exception {
+        boolean result = dfcHttpsClientSpy.basicAuthValidNotPresentOrThrow();
+        assertEquals(true, result);
+    }
+
+    @Test
+    void fileServerData_properLocationNoBasicAuth() throws Exception {
+        dfcHttpsClientSpy = spy(new DfcHttpsClient(emptyUserInFileServerData(), connectionManager));
+
+        boolean result = dfcHttpsClientSpy.basicAuthValidNotPresentOrThrow();
+        assertEquals(false, result);
+    }
+
+    @Test
+    void fileServerData_improperAuthDataExceptionOccurred() throws Exception {
+        dfcHttpsClientSpy = spy(new DfcHttpsClient(invalidUserInFileServerData(), connectionManager));
+
+        assertThrows(DatafileTaskException.class, () -> dfcHttpsClientSpy.basicAuthValidNotPresentOrThrow());
+    }
+
+    @Test
+    void dfcHttpsClient_flow_successfulCallAndResponseProcessing() throws Exception {
+        doReturn(HttpClientResponseHelper.APACHE_RESPONSE_OK).when(dfcHttpsClientSpy)
+            .executeHttpClient(any(HttpGet.class));
+        doReturn((long) 3).when(dfcHttpsClientSpy).writeFile(eq(localFile), any(InputStream.class));
+
+        dfcHttpsClientSpy.open();
+        dfcHttpsClientSpy.collectFile(remoteFile, localFile);
+        dfcHttpsClientSpy.close();
+
+        verify(dfcHttpsClientSpy, times(1)).makeCall(any(HttpGet.class));
+        verify(dfcHttpsClientSpy, times(1)).executeHttpClient(any(HttpGet.class));
+        verify(dfcHttpsClientSpy, times(1)).processResponse(HttpClientResponseHelper.APACHE_RESPONSE_OK, localFile);
+        verify(dfcHttpsClientSpy, times(1)).writeFile(eq(localFile), any(InputStream.class));
+    }
+
+    @Test
+    void dfcHttpsClient_flow_successfulCallWithJWTAndResponseProcessing() throws Exception {
+        FileServerData serverData = jWTTokenInFileServerData();
+        dfcHttpsClientSpy = spy(new DfcHttpsClient(serverData, connectionManager));
+
+        doReturn(HttpClientResponseHelper.APACHE_RESPONSE_OK).when(dfcHttpsClientSpy)
+            .executeHttpClient(any(HttpGet.class));
+        doReturn((long) 3).when(dfcHttpsClientSpy).writeFile(eq(localFile), any(InputStream.class));
+
+        dfcHttpsClientSpy.open();
+        dfcHttpsClientSpy.collectFile(remoteFile, localFile);
+        dfcHttpsClientSpy.close();
+
+        verify(dfcHttpsClientSpy, times(1)).makeCall(any(HttpGet.class));
+        verify(dfcHttpsClientSpy, times(1)).executeHttpClient(any(HttpGet.class));
+        verify(dfcHttpsClientSpy, times(1)).processResponse(HttpClientResponseHelper.APACHE_RESPONSE_OK, localFile);
+        verify(dfcHttpsClientSpy, times(1)).writeFile(eq(localFile), any(InputStream.class));
+        String str = serverData.toString();
+        assertFalse(str.contains(JWT_PASSWORD));
+    }
+
+    @Test
+    void dfcHttpsClient_flow_failedCallUnexpectedResponseCode() throws Exception {
+        doReturn(HttpClientResponseHelper.APACHE_RESPONSE_OK).when(dfcHttpsClientSpy)
+            .executeHttpClient(any(HttpGet.class));
+        doReturn(false).when(dfcHttpsClientSpy).isResponseOk(any(HttpResponse.class));
+
+        dfcHttpsClientSpy.open();
+
+        assertThrows(DatafileTaskException.class, () -> dfcHttpsClientSpy.collectFile(remoteFile, localFile));
+    }
+
+    @Test
+    void dfcHttpsClient_flow_failedCallConnectionTimeout() throws Exception {
+        doThrow(ConnectTimeoutException.class).when(dfcHttpsClientSpy).executeHttpClient(any(HttpGet.class));
+
+        dfcHttpsClientSpy.open();
+
+        assertThrows(NonRetryableDatafileTaskException.class,
+            () -> dfcHttpsClientSpy.collectFile(remoteFile, localFile));
+    }
+
+    @Test
+    void dfcHttpsClient_flow_failedCallIOExceptionForExecuteHttpClient() throws Exception {
+        doThrow(IOException.class).when(dfcHttpsClientSpy).executeHttpClient(any(HttpGet.class));
+
+        dfcHttpsClientSpy.open();
+
+        assertThrows(DatafileTaskException.class, () -> dfcHttpsClientSpy.collectFile(remoteFile, localFile));
+    }
+
+    private FileServerData createFileServerData() {
+        return FileServerData.builder().serverAddress(XNF_ADDRESS).userId(USERNAME).password(PASSWORD).port(PORT)
+            .build();
+    }
+
+    private FileServerData emptyUserInFileServerData() {
+        return FileServerData.builder().serverAddress(XNF_ADDRESS).userId("").password("").port(PORT).build();
+    }
+
+    private FileServerData invalidUserInFileServerData() {
+        return FileServerData.builder().serverAddress(XNF_ADDRESS).userId(USERNAME).password("").port(PORT).build();
+    }
+
+    private FileServerData jWTTokenInFileServerData() throws URISyntaxException {
+        String query = "?" + ACCESS_TOKEN + "=" + JWT_PASSWORD;
+
+        return FileServerData.builder().serverAddress(XNF_ADDRESS).userId("").password("").port(PORT)
+            .queryParameters(new URIBuilder(query).getQueryParams()).build();
+    }
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/http/HttpClientResponseHelper.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/http/HttpClientResponseHelper.java
new file mode 100644 (file)
index 0000000..3df2cad
--- /dev/null
@@ -0,0 +1,419 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2020-2021 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.dcaegen2.collectors.datafile.http;
+
+import io.netty.handler.codec.http.HttpHeaders;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpVersion;
+import io.netty.handler.codec.http.cookie.Cookie;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpEntity;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.params.HttpParams;
+
+import reactor.core.publisher.Mono;
+import reactor.netty.http.client.HttpClientResponse;
+import reactor.util.context.Context;
+import reactor.util.context.ContextView;
+
+public class HttpClientResponseHelper {
+
+    public static final HttpClientResponse NETTY_RESPONSE_OK = new HttpClientResponse() {
+
+        @Override
+        public Map<CharSequence, Set<Cookie>> cookies() {
+            return null;
+        }
+
+        @Override
+        public boolean isKeepAlive() {
+            return false;
+        }
+
+        @Override
+        public boolean isWebsocket() {
+            return false;
+        }
+
+        @Override
+        public HttpMethod method() {
+            return null;
+        }
+
+        @Override
+        public String path() {
+            return null;
+        }
+
+        @Override
+        public String fullPath() {
+            return null;
+        }
+
+        @Override
+        public String requestId() {
+            return null;
+        }
+
+        @Override
+        public String uri() {
+            return null;
+        }
+
+        @Override
+        public HttpVersion version() {
+            return null;
+        }
+
+        @Override
+        public Context currentContext() {
+            return null;
+        }
+
+        @Override
+        public ContextView currentContextView() {
+            return null;
+        }
+
+        @Override
+        public String[] redirectedFrom() {
+            return new String[0];
+        }
+
+        @Override
+        public HttpHeaders requestHeaders() {
+            return null;
+        }
+
+        @Override
+        public String resourceUrl() {
+            return null;
+        }
+
+        @Override
+        public HttpHeaders responseHeaders() {
+            return null;
+        }
+
+        @Override
+        public HttpResponseStatus status() {
+            return HttpResponseStatus.OK;
+        }
+
+        @Override
+        public Mono<HttpHeaders> trailerHeaders() {
+            return null;
+        }
+    };
+
+    public static final HttpClientResponse RESPONSE_ANY_NO_OK = new HttpClientResponse() {
+
+        @Override
+        public Map<CharSequence, Set<Cookie>> cookies() {
+            return null;
+        }
+
+        @Override
+        public boolean isKeepAlive() {
+            return false;
+        }
+
+        @Override
+        public boolean isWebsocket() {
+            return false;
+        }
+
+        @Override
+        public HttpMethod method() {
+            return null;
+        }
+
+        @Override
+        public String fullPath() {
+            return null;
+        }
+
+        @Override
+        public String requestId() {
+            return null;
+        }
+
+        @Override
+        public String uri() {
+            return null;
+        }
+
+        @Override
+        public HttpVersion version() {
+            return null;
+        }
+
+        @Override
+        public Context currentContext() {
+            return null;
+        }
+
+        @Override
+        public ContextView currentContextView() {
+            return null;
+        }
+
+        @Override
+        public String[] redirectedFrom() {
+            return new String[0];
+        }
+
+        @Override
+        public HttpHeaders requestHeaders() {
+            return null;
+        }
+
+        @Override
+        public String resourceUrl() {
+            return null;
+        }
+
+        @Override
+        public HttpHeaders responseHeaders() {
+            return null;
+        }
+
+        @Override
+        public HttpResponseStatus status() {
+            return HttpResponseStatus.NOT_IMPLEMENTED;
+        }
+
+        @Override
+        public Mono<HttpHeaders> trailerHeaders() {
+            return null;
+        }
+    };
+
+    public static final CloseableHttpResponse APACHE_RESPONSE_OK = new CloseableHttpResponse() {
+        @Override
+        public void close() throws IOException {
+            getEntity().getContent().close();
+        }
+
+        @Override
+        public StatusLine getStatusLine() {
+            return new StatusLine() {
+                @Override
+                public ProtocolVersion getProtocolVersion() {
+                    return null;
+                }
+
+                @Override
+                public int getStatusCode() {
+                    return 200;
+                }
+
+                @Override
+                public String getReasonPhrase() {
+                    return null;
+                }
+            };
+        }
+
+        @Override
+        public void setStatusLine(StatusLine statusLine) {
+
+        }
+
+        @Override
+        public void setStatusLine(ProtocolVersion protocolVersion, int i) {
+
+        }
+
+        @Override
+        public void setStatusLine(ProtocolVersion protocolVersion, int i, String s) {
+
+        }
+
+        @Override
+        public void setStatusCode(int i) throws IllegalStateException {
+
+        }
+
+        @Override
+        public void setReasonPhrase(String s) throws IllegalStateException {
+
+        }
+
+        @Override
+        public HttpEntity getEntity() {
+            return new HttpEntity() {
+                @Override
+                public boolean isRepeatable() {
+                    return false;
+                }
+
+                @Override
+                public boolean isChunked() {
+                    return false;
+                }
+
+                @Override
+                public long getContentLength() {
+                    return 0;
+                }
+
+                @Override
+                public Header getContentType() {
+                    return null;
+                }
+
+                @Override
+                public Header getContentEncoding() {
+                    return null;
+                }
+
+                @Override
+                public InputStream getContent() throws IOException, UnsupportedOperationException {
+                    return new ByteArrayInputStream("abc".getBytes());
+                }
+
+                @Override
+                public void writeTo(OutputStream outputStream) throws IOException {
+
+                }
+
+                @Override
+                public boolean isStreaming() {
+                    return false;
+                }
+
+                @Override
+                public void consumeContent() throws IOException {
+
+                }
+            };
+        }
+
+        @Override
+        public void setEntity(HttpEntity httpEntity) {
+
+        }
+
+        @Override
+        public Locale getLocale() {
+            return null;
+        }
+
+        @Override
+        public void setLocale(Locale locale) {
+
+        }
+
+        @Override
+        public ProtocolVersion getProtocolVersion() {
+            return null;
+        }
+
+        @Override
+        public boolean containsHeader(String s) {
+            return false;
+        }
+
+        @Override
+        public Header[] getHeaders(String s) {
+            return new Header[0];
+        }
+
+        @Override
+        public Header getFirstHeader(String s) {
+            return null;
+        }
+
+        @Override
+        public Header getLastHeader(String s) {
+            return null;
+        }
+
+        @Override
+        public Header[] getAllHeaders() {
+            return new Header[0];
+        }
+
+        @Override
+        public void addHeader(Header header) {
+
+        }
+
+        @Override
+        public void addHeader(String s, String s1) {
+
+        }
+
+        @Override
+        public void setHeader(Header header) {
+
+        }
+
+        @Override
+        public void setHeader(String s, String s1) {
+
+        }
+
+        @Override
+        public void setHeaders(Header[] headers) {
+
+        }
+
+        @Override
+        public void removeHeader(Header header) {
+
+        }
+
+        @Override
+        public void removeHeaders(String s) {
+
+        }
+
+        @Override
+        public HeaderIterator headerIterator() {
+            return null;
+        }
+
+        @Override
+        public HeaderIterator headerIterator(String s) {
+            return null;
+        }
+
+        @Override
+        public HttpParams getParams() {
+            return null;
+        }
+
+        @Override
+        public void setParams(HttpParams params) {
+        }
+
+    };
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/http/HttpsClientConnectionManagerUtilTest.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/http/HttpsClientConnectionManagerUtilTest.java
new file mode 100644 (file)
index 0000000..bb1a93f
--- /dev/null
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2021 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.dcaegen2.collectors.datafile.http;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+
+@ExtendWith(MockitoExtension.class)
+public class HttpsClientConnectionManagerUtilTest {
+
+    private static final String KEY_PATH = "src/test/resources/keystore.p12";
+    private static final String KEY_PASSWORD = "src/test/resources/keystore.pass";
+    private static final String KEY_IMPROPER_PASSWORD = "src/test/resources/dfc.jks.pass";
+    private static final String TRUSTED_CA_PATH = "src/test/resources/trust.jks";
+    private static final String TRUSTED_CA_PASSWORD = "src/test/resources/trust.pass";
+
+    @Test
+    public void emptyManager_shouldThrowException() {
+        assertThrows(DatafileTaskException.class, () -> HttpsClientConnectionManagerUtil.instance());
+    }
+
+    @Test
+    public void creatingManager_successfulCase() throws Exception {
+        HttpsClientConnectionManagerUtil.setupOrUpdate(KEY_PATH, KEY_PASSWORD, TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD, //
+            true);
+        assertNotNull(HttpsClientConnectionManagerUtil.instance());
+    }
+
+    @Test
+    public void creatingManager_improperSecretShouldThrowException() {
+        assertThrows(DatafileTaskException.class, () -> HttpsClientConnectionManagerUtil.setupOrUpdate(KEY_PATH, //
+            KEY_IMPROPER_PASSWORD, TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD, true));
+        assertThrows(DatafileTaskException.class, () -> HttpsClientConnectionManagerUtil.instance());
+    }
+
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/scheme/SchemeTest.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/scheme/SchemeTest.java
new file mode 100644 (file)
index 0000000..413cd13
--- /dev/null
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 Nordix Foundation. All rights reserved.
+ * Copyright (C) 2020 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.dcaegen2.collectors.datafile.scheme;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+import org.onap.dcaegen2.collectors.datafile.commons.Scheme;
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+
+public class SchemeTest {
+
+    @Test
+    public void shouldReturnSchemeForSupportedProtocol() throws DatafileTaskException {
+        assertEquals(Scheme.FTPES, Scheme.getSchemeFromString("FTPES"));
+        assertEquals(Scheme.SFTP, Scheme.getSchemeFromString("SFTP"));
+        assertEquals(Scheme.HTTP, Scheme.getSchemeFromString("HTTP"));
+        assertEquals(Scheme.HTTPS, Scheme.getSchemeFromString("HTTPS"));
+    }
+
+    @Test
+    public void shouldThrowExceptionForUnsupportedProtocol() {
+        assertThrows(DatafileTaskException.class, () -> Scheme.getSchemeFromString("FTPS"));
+    }
+
+    @Test
+    public void shouldThrowExceptionForInvalidProtocol() {
+        assertThrows(DatafileTaskException.class, () -> Scheme.getSchemeFromString("invalid"));
+    }
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/service/HttpUtilsTest.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/service/HttpUtilsTest.java
new file mode 100644 (file)
index 0000000..0ee9f72
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018-2019 Nordix Foundation. All rights reserved.
+ * Modifications Copyright (C) 2021 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.dcaegen2.collectors.datafile.service;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.net.URIBuilder;
+import org.junit.jupiter.api.Test;
+import org.onap.dcaegen2.collectors.datafile.commons.FileServerData;
+
+class HttpUtilsTest {
+
+    private static final String XNF_ADDRESS = "127.0.0.1";
+    private static final int PORT = 443;
+    private static final String JWT_PASSWORD = "thisIsThePassword";
+    private static final String ACCESS_TOKEN = "access_token";
+    private static final String ANOTHER_TOKEN = "another_token";
+    private static final String ANOTHER_DATA = "another_data";
+    private static final String FRAGMENT = "thisIsTheFragment";
+    private static final String USERNAME = "bob";
+    private static final String PASSWORD = "123";
+
+    @Test
+    void shouldReturnSuccessfulResponse() {
+        assertTrue(HttpUtils.isSuccessfulResponseCodeWithDataRouter(200));
+    }
+
+    @Test
+    void shouldReturnBadResponse() {
+        assertFalse(HttpUtils.isSuccessfulResponseCodeWithDataRouter(404));
+    }
+
+    @Test
+    void isSingleQueryWithJWT_validToken() throws URISyntaxException {
+        assertTrue(HttpUtils.isQueryWithSingleJWT(validTokenSingleQueryData()));
+        assertTrue(HttpUtils.isQueryWithSingleJWT(validTokenDoubleQueryData()));
+    }
+
+    @Test
+    void isSingleQueryWithJWT_invalidToken() throws URISyntaxException {
+        assertFalse(HttpUtils.isQueryWithSingleJWT(validQueryNoToken()));
+        assertFalse(HttpUtils.isQueryWithSingleJWT(queryDataDoubleToken()));
+        assertFalse(HttpUtils.isQueryWithSingleJWT(null));
+    }
+
+    @Test
+    void getJWTToken_jWTTokenPresent() throws URISyntaxException {
+        assertEquals(JWT_PASSWORD, HttpUtils.getJWTToken(fileServerDataWithJWTToken()));
+        assertEquals(JWT_PASSWORD, HttpUtils.getJWTToken(fileServerDataWithJWTTokenLongQueryAndFragment()));
+    }
+
+    @Test
+    void getJWTToken_JWTTokenNotPresent() throws URISyntaxException {
+        assertEquals("", HttpUtils.getJWTToken(fileServerDataQueryWithoutToken()));
+    }
+
+    @Test
+    void prepareUri_UriWithoutPort() {
+        FileServerData serverData =
+            FileServerData.builder().serverAddress(XNF_ADDRESS).userId(USERNAME).password(PASSWORD).build();
+        String REMOTE_FILE = "any";
+
+        String retrievedUri = HttpUtils.prepareUri("http", serverData, REMOTE_FILE, 80);
+        assertTrue(retrievedUri.startsWith("http://" + XNF_ADDRESS + ":80"));
+    }
+
+    @Test
+    void prepareUri_verifyUriWithTokenAndFragment() throws URISyntaxException {
+        String file = "/file";
+        String expected = "http://" + XNF_ADDRESS + ":" + PORT + file + "?" + ANOTHER_TOKEN + "=" + ANOTHER_DATA + "&"
+            + ANOTHER_TOKEN + "=" + ANOTHER_DATA + "&" + ANOTHER_TOKEN + "=" + ANOTHER_DATA + "#" + FRAGMENT;
+        assertEquals(expected,
+            HttpUtils.prepareUri("http", fileServerDataWithJWTTokenLongQueryAndFragment(), file, 443));
+    }
+
+    @Test
+    void prepareUri_verifyUriWithoutTokenAndWithoutFragment() throws URISyntaxException {
+        String file = "/file";
+        String expected = "http://" + XNF_ADDRESS + ":" + PORT + file;
+        assertEquals(expected, HttpUtils.prepareUri("http", fileServerDataNoTokenNoFragment(), file, 443));
+    }
+
+    private List<NameValuePair> validTokenSingleQueryData() throws URISyntaxException {
+        String query = "?" + ACCESS_TOKEN + "=" + JWT_PASSWORD;
+        return new URIBuilder(query).getQueryParams();
+    }
+
+    private List<NameValuePair> validTokenDoubleQueryData() throws URISyntaxException {
+        StringBuilder doubleQuery = new StringBuilder();
+        doubleQuery.append("?" + ANOTHER_TOKEN + "=" + ANOTHER_DATA + "&");
+        doubleQuery.append(ACCESS_TOKEN + "=" + JWT_PASSWORD);
+        return new URIBuilder(doubleQuery.toString()).getQueryParams();
+    }
+
+    private List<NameValuePair> validQueryNoToken() throws URISyntaxException {
+        String query = "?" + ANOTHER_TOKEN + "=" + JWT_PASSWORD;
+        return new URIBuilder(query).getQueryParams();
+    }
+
+    private List<NameValuePair> queryDataDoubleToken() throws URISyntaxException {
+        StringBuilder doubleToken = new StringBuilder();
+        doubleToken.append("?" + ACCESS_TOKEN + "=" + JWT_PASSWORD + "&");
+        doubleToken.append(ACCESS_TOKEN + "=" + JWT_PASSWORD + "&");
+        doubleToken.append(ANOTHER_TOKEN + "=" + ANOTHER_DATA);
+        return new URIBuilder(doubleToken.toString()).getQueryParams();
+    }
+
+    private FileServerData fileServerDataWithJWTToken() throws URISyntaxException {
+        String query = "?" + ACCESS_TOKEN + "=" + JWT_PASSWORD;
+
+        return FileServerData.builder().serverAddress(XNF_ADDRESS).userId("").password("").port(PORT)
+            .queryParameters(new URIBuilder(query).getQueryParams()).build();
+    }
+
+    private FileServerData fileServerDataWithJWTTokenLongQueryAndFragment() throws URISyntaxException {
+        StringBuilder query = new StringBuilder();
+        query.append("?" + ANOTHER_TOKEN + "=" + ANOTHER_DATA + "&");
+        query.append(ANOTHER_TOKEN + "=" + ANOTHER_DATA + "&");
+        query.append(ACCESS_TOKEN + "=" + JWT_PASSWORD + "&");
+        query.append(ANOTHER_TOKEN + "=" + ANOTHER_DATA);
+
+        return FileServerData.builder().serverAddress(XNF_ADDRESS).userId("").password("").port(PORT)
+            .queryParameters(new URIBuilder(query.toString()).getQueryParams()).uriRawFragment(FRAGMENT).build();
+    }
+
+    private FileServerData fileServerDataQueryWithoutToken() throws URISyntaxException {
+        StringBuilder query = new StringBuilder();
+        query.append("?" + ANOTHER_TOKEN + "=" + ANOTHER_DATA);
+
+        return FileServerData.builder().serverAddress(XNF_ADDRESS).userId("").password("").port(PORT)
+            .queryParameters(new URIBuilder(query.toString()).getQueryParams()).build();
+    }
+
+    private FileServerData fileServerDataNoTokenNoFragment() throws URISyntaxException {
+        return FileServerData.builder().serverAddress(XNF_ADDRESS).userId("").password("").port(PORT)
+            .queryParameters(new URIBuilder("").getQueryParams()).uriRawFragment("").build();
+    }
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/FileCollectorTest.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/tasks/FileCollectorTest.java
new file mode 100644 (file)
index 0000000..6d437ae
--- /dev/null
@@ -0,0 +1,368 @@
+/*-
+ * ============LICENSE_START======================================================================
+ * Copyright (C) 2018-2019 Nordix Foundation. All rights reserved.
+ * Copyright (C) 2020-2022 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.dcaegen2.collectors.datafile.tasks;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.onap.dcaegen2.collectors.datafile.configuration.AppConfig;
+import org.onap.dcaegen2.collectors.datafile.configuration.CertificateConfig;
+import org.onap.dcaegen2.collectors.datafile.exceptions.DatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.exceptions.NonRetryableDatafileTaskException;
+import org.onap.dcaegen2.collectors.datafile.ftp.FtpesClient;
+import org.onap.dcaegen2.collectors.datafile.ftp.SftpClient;
+import org.onap.dcaegen2.collectors.datafile.http.DfcHttpClient;
+import org.onap.dcaegen2.collectors.datafile.http.DfcHttpsClient;
+import org.onap.dcaegen2.collectors.datafile.model.Counters;
+import org.onap.dcaegen2.collectors.datafile.model.FileData;
+import org.onap.dcaegen2.collectors.datafile.model.FilePublishInformation;
+import org.onap.dcaegen2.collectors.datafile.model.FileReadyMessage;
+import reactor.test.StepVerifier;
+
+public class FileCollectorTest {
+
+    final static String DATAFILE_TMPDIR = "/tmp/onap_datafile/";
+    private static final String PRODUCT_NAME = "NrRadio";
+    private static final String VENDOR_NAME = "Ericsson";
+    private static final int LAST_EPOCH_MICROSEC = 87457457;
+    private static final String SOURCE_NAME = "oteNB5309";
+    private static final int START_EPOCH_MICROSEC = 874575764;
+    private static final String TIME_ZONE_OFFSET = "UTC+05:00";
+    private static final String FTPES_SCHEME = "ftpes://";
+    private static final String SFTP_SCHEME = "sftp://";
+    private static final String HTTP_SCHEME = "http://";
+    private static final String HTTPS_SCHEME = "https://";
+    private static final String SERVER_ADDRESS = "192.168.0.101";
+    private static final int PORT_22 = 22;
+    private static final String PM_FILE_NAME = "A20161224.1030-1045.bin.gz";
+    private static final Path LOCAL_FILE_LOCATION = Paths.get(DATAFILE_TMPDIR, SOURCE_NAME, PM_FILE_NAME);
+    private static final String REMOTE_FILE_LOCATION = "/ftp/rop/" + PM_FILE_NAME;
+    private static final String USER = "usr";
+    private static final String PWD = "pwd";
+    private static final String FTPES_LOCATION =
+        FTPES_SCHEME + USER + ":" + PWD + "@" + SERVER_ADDRESS + ":" + PORT_22 + REMOTE_FILE_LOCATION;
+
+    private static final String FTPES_LOCATION_NO_PORT =
+        FTPES_SCHEME + USER + ":" + PWD + "@" + SERVER_ADDRESS + REMOTE_FILE_LOCATION;
+    private static final String SFTP_LOCATION = SFTP_SCHEME + SERVER_ADDRESS + ":" + PORT_22 + REMOTE_FILE_LOCATION;
+    private static final String SFTP_LOCATION_NO_PORT = SFTP_SCHEME + SERVER_ADDRESS + REMOTE_FILE_LOCATION;
+
+    private static final String HTTP_LOCATION =
+        HTTP_SCHEME + USER + ":" + PWD + "@" + SERVER_ADDRESS + ":" + PORT_22 + REMOTE_FILE_LOCATION;
+    private static final String HTTP_LOCATION_NO_PORT =
+        HTTP_SCHEME + USER + ":" + PWD + "@" + SERVER_ADDRESS + REMOTE_FILE_LOCATION;
+    private static final String HTTPS_LOCATION =
+        HTTPS_SCHEME + USER + ":" + PWD + "@" + SERVER_ADDRESS + ":" + PORT_22 + REMOTE_FILE_LOCATION;
+    private static final String HTTPS_LOCATION_NO_PORT =
+        HTTPS_SCHEME + USER + ":" + PWD + "@" + SERVER_ADDRESS + REMOTE_FILE_LOCATION;
+
+    private static final String GZIP_COMPRESSION = "gzip";
+    private static final String MEAS_COLLECT_FILE_FORMAT_TYPE = "org.3GPP.32.435#measCollec";
+    private static final String FILE_FORMAT_VERSION = "V10";
+    private static final String CERTIFICATE_KEY_PASSWORD_PATH = "certificateKeyPassword";
+    private static final String TRUSTED_CA_PATH = "trustedCAPath";
+    private static final String TRUSTED_CA_PASSWORD_PATH = "trustedCAPassword";
+    private static final String CHANGE_IDENTIFIER = "PM_MEAS_FILES";
+    private static final String FILE_FORMAT_TYPE = "org.3GPP.32.435#measCollec";
+    private static final String CHANGE_TYPE = "FileReady";
+
+    private static AppConfig appConfigMock = mock(AppConfig.class);
+    private static CertificateConfig certificateConfigMock = mock(CertificateConfig.class);
+
+    private FtpesClient ftpesClientMock = mock(FtpesClient.class);
+
+    private SftpClient sftpClientMock = mock(SftpClient.class);
+
+    private DfcHttpClient dfcHttpClientMock = mock(DfcHttpClient.class);
+    private DfcHttpsClient dfcHttpsClientMock = mock(DfcHttpsClient.class);
+
+    private Counters counters;
+
+    FileReadyMessage.Event event(String location) {
+        FileReadyMessage.MessageMetaData messageMetaData = FileReadyMessage.MessageMetaData.builder() //
+            .lastEpochMicrosec(LAST_EPOCH_MICROSEC) //
+            .sourceName(SOURCE_NAME) //
+            .startEpochMicrosec(START_EPOCH_MICROSEC) //
+            .timeZoneOffset(TIME_ZONE_OFFSET) //
+            .changeIdentifier(CHANGE_IDENTIFIER) //
+            .eventName("Noti_NrRadio-Ericsson_FileReady").build();
+
+        FileReadyMessage.FileInfo fileInfo = FileReadyMessage.FileInfo //
+            .builder() //
+            .fileFormatType(FILE_FORMAT_TYPE) //
+            .location(location) //
+            .fileFormatVersion(FILE_FORMAT_VERSION) //
+            .compression(GZIP_COMPRESSION) //
+            .build();
+
+        FileReadyMessage.ArrayOfNamedHashMap arrayOfNamedHashMap = FileReadyMessage.ArrayOfNamedHashMap //
+            .builder().name(PM_FILE_NAME) //
+            .hashMap(fileInfo).build();
+
+        List<FileReadyMessage.ArrayOfNamedHashMap> arrayOfNamedHashMapList = new ArrayList<>();
+        arrayOfNamedHashMapList.add(arrayOfNamedHashMap);
+
+        FileReadyMessage.NotificationFields notificationFields = FileReadyMessage.NotificationFields //
+            .builder().notificationFieldsVersion("notificationFieldsVersion") //
+            .changeType(CHANGE_TYPE).changeIdentifier(CHANGE_IDENTIFIER) //
+            .arrayOfNamedHashMap(arrayOfNamedHashMapList) //
+            .build();
+
+        return FileReadyMessage.Event.builder() //
+            .commonEventHeader(messageMetaData) //
+            .notificationFields(notificationFields).build();
+    }
+
+    private FileReadyMessage fileReadyMessage(String location) {
+        FileReadyMessage message = FileReadyMessage.builder() //
+            .event(event(location)) //
+            .build();
+        return message;
+    }
+
+    private FileData createFileData(String location) {
+        return FileData.createFileData(fileReadyMessage(location)).iterator().next();
+    }
+
+    private FilePublishInformation createExpectedFilePublishInformation(String location) {
+        return FilePublishInformation.builder() //
+            .productName(PRODUCT_NAME) //
+            .vendorName(VENDOR_NAME) //
+            .lastEpochMicrosec(LAST_EPOCH_MICROSEC) //
+            .sourceName(SOURCE_NAME) //
+            .startEpochMicrosec(START_EPOCH_MICROSEC) //
+            .timeZoneOffset(TIME_ZONE_OFFSET) //
+            .name(SOURCE_NAME + "/" + PM_FILE_NAME) //
+            .compression(GZIP_COMPRESSION) //
+            .fileFormatType(MEAS_COLLECT_FILE_FORMAT_TYPE) //
+            .fileFormatVersion(FILE_FORMAT_VERSION) //
+            .changeIdentifier(CHANGE_IDENTIFIER) //
+            .build();
+    }
+
+    @BeforeAll
+    static void setUpConfiguration() {
+        when(appConfigMock.getCertificateConfiguration()).thenReturn(certificateConfigMock);
+        appConfigMock.collectedFilesPath = DATAFILE_TMPDIR;
+        certificateConfigMock.keyPasswordPath = CERTIFICATE_KEY_PASSWORD_PATH;
+        certificateConfigMock.trustedCa = TRUSTED_CA_PATH;
+        certificateConfigMock.trustedCaPasswordPath = TRUSTED_CA_PASSWORD_PATH;
+    }
+
+    @BeforeEach
+    void setUpTest() {
+        counters = new Counters();
+    }
+
+    @Test
+    public void whenFtpesFile_returnCorrectResponse() throws Exception {
+        FileCollector collectorUndetTest = spy(new FileCollector(appConfigMock, counters));
+        doReturn(ftpesClientMock).when(collectorUndetTest).createFtpesClient(any());
+
+        FileData fileData = createFileData(FTPES_LOCATION_NO_PORT);
+
+        FilePublishInformation expectedfilePublishInformation =
+            createExpectedFilePublishInformation(FTPES_LOCATION_NO_PORT);
+
+        StepVerifier.create(collectorUndetTest.collectFile(fileData, 3, Duration.ofSeconds(0)))
+            .expectNext(expectedfilePublishInformation) //
+            .verifyComplete();
+
+        verify(ftpesClientMock, times(1)).open();
+        verify(ftpesClientMock, times(1)).collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION);
+        verify(ftpesClientMock, times(1)).close();
+        verifyNoMoreInteractions(ftpesClientMock);
+
+        assertEquals(1, counters.getNoOfCollectedFiles(), "collectedFiles should have been 1");
+        assertEquals(0, counters.getNoOfFailedFtpAttempts(), "failedFtpAttempts should have been 0");
+        assertEquals(0, counters.getNoOfFailedHttpAttempts(), "failedHttpAttempts should have been 0");
+    }
+
+    @Test
+    public void whenSftpFile_returnCorrectResponse() throws Exception {
+        FileCollector collectorUndetTest = spy(new FileCollector(appConfigMock, counters));
+        doReturn(sftpClientMock).when(collectorUndetTest).createSftpClient(any());
+
+        FileData fileData = createFileData(SFTP_LOCATION_NO_PORT);
+        FilePublishInformation expectedfilePublishInformation =
+            createExpectedFilePublishInformation(SFTP_LOCATION_NO_PORT);
+
+        StepVerifier.create(collectorUndetTest.collectFile(fileData, 3, Duration.ofSeconds(0)))
+            .expectNext(expectedfilePublishInformation) //
+            .verifyComplete();
+
+        // The same again, but with port
+        fileData = createFileData(SFTP_LOCATION);
+        expectedfilePublishInformation = createExpectedFilePublishInformation(SFTP_LOCATION);
+
+        StepVerifier.create(collectorUndetTest.collectFile(fileData, 3, Duration.ofSeconds(0)))
+            .expectNext(expectedfilePublishInformation) //
+            .verifyComplete();
+
+        verify(sftpClientMock, times(2)).open();
+        verify(sftpClientMock, times(2)).collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION);
+        verify(sftpClientMock, times(2)).close();
+        verifyNoMoreInteractions(sftpClientMock);
+
+        assertEquals(2, counters.getNoOfCollectedFiles(), "collectedFiles should have been 2");
+    }
+
+    @Test
+    public void whenHttpFile_returnCorrectResponse() throws Exception {
+        FileCollector collectorUndetTest = spy(new FileCollector(appConfigMock, counters));
+        doReturn(dfcHttpClientMock).when(collectorUndetTest).createHttpClient(any());
+
+        FileData fileData = createFileData(HTTP_LOCATION_NO_PORT);
+
+        FilePublishInformation expectedfilePublishInformation =
+            createExpectedFilePublishInformation(HTTP_LOCATION_NO_PORT);
+
+        StepVerifier.create(collectorUndetTest.collectFile(fileData, 3, Duration.ofSeconds(0)))
+            .expectNext(expectedfilePublishInformation) //
+            .verifyComplete();
+
+        // The same again, but with port
+        fileData = createFileData(HTTP_LOCATION);
+        expectedfilePublishInformation = createExpectedFilePublishInformation(HTTP_LOCATION);
+
+        StepVerifier.create(collectorUndetTest.collectFile(fileData, 3, Duration.ofSeconds(0)))
+            .expectNext(expectedfilePublishInformation) //
+            .verifyComplete();
+
+        verify(dfcHttpClientMock, times(2)).open();
+        verify(dfcHttpClientMock, times(2)).collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION);
+        verify(dfcHttpClientMock, times(2)).close();
+        verifyNoMoreInteractions(dfcHttpClientMock);
+
+        assertEquals(2, counters.getNoOfCollectedFiles(), "collectedFiles should have been 1");
+        assertEquals(0, counters.getNoOfFailedFtpAttempts(), "failedFtpAttempts should have been 0");
+        assertEquals(0, counters.getNoOfFailedHttpAttempts(), "failedHttpAttempts should have been 0");
+    }
+
+    @Test
+    public void whenHttpsFile_returnCorrectResponse() throws Exception {
+        FileCollector collectorUndetTest = spy(new FileCollector(appConfigMock, counters));
+        doReturn(dfcHttpsClientMock).when(collectorUndetTest).createHttpsClient(any());
+
+        FileData fileData = createFileData(HTTPS_LOCATION_NO_PORT);
+
+        FilePublishInformation expectedfilePublishInformation =
+            createExpectedFilePublishInformation(HTTPS_LOCATION_NO_PORT);
+
+        StepVerifier.create(collectorUndetTest.collectFile(fileData, 3, Duration.ofSeconds(0)))
+            .expectNext(expectedfilePublishInformation) //
+            .verifyComplete();
+
+        // The same again, but with port
+        fileData = createFileData(HTTPS_LOCATION);
+        expectedfilePublishInformation = createExpectedFilePublishInformation(HTTPS_LOCATION);
+
+        StepVerifier.create(collectorUndetTest.collectFile(fileData, 3, Duration.ofSeconds(0)))
+            .expectNext(expectedfilePublishInformation) //
+            .verifyComplete();
+
+        verify(dfcHttpsClientMock, times(2)).open();
+        verify(dfcHttpsClientMock, times(2)).collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION);
+        verify(dfcHttpsClientMock, times(2)).close();
+        verifyNoMoreInteractions(dfcHttpsClientMock);
+
+        assertEquals(2, counters.getNoOfCollectedFiles(), "collectedFiles should have been 1");
+        assertEquals(0, counters.getNoOfFailedFtpAttempts(), "failedFtpAttempts should have been 0");
+        assertEquals(0, counters.getNoOfFailedHttpAttempts(), "failedHttpAttempts should have been 0");
+    }
+
+    @Test
+    public void whenFtpesFileAlwaysFail_retryAndFail() throws Exception {
+        FileCollector collectorUndetTest = spy(new FileCollector(appConfigMock, counters));
+        doReturn(ftpesClientMock).when(collectorUndetTest).createFtpesClient(any());
+
+        FileData fileData = createFileData(FTPES_LOCATION);
+        doThrow(new DatafileTaskException("Unable to collect file.")).when(ftpesClientMock)
+            .collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION);
+
+        StepVerifier.create(collectorUndetTest.collectFile(fileData, 3, Duration.ofSeconds(0)))
+            .expectErrorMessage("Retries exhausted: 3/3") //
+            .verify();
+
+        verify(ftpesClientMock, times(4)).collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION);
+
+        assertEquals(0, counters.getNoOfCollectedFiles(), "collectedFiles should have been 0");
+        assertEquals(4, counters.getNoOfFailedFtpAttempts(), "failedFtpAttempts should have been 4");
+        assertEquals(0, counters.getNoOfFailedHttpAttempts(), "failedHttpAttempts should have been 0");
+    }
+
+    @Test
+    public void whenFtpesFileAlwaysFail_failWithoutRetry() throws Exception {
+        FileCollector collectorUndetTest = spy(new FileCollector(appConfigMock, counters));
+        doReturn(ftpesClientMock).when(collectorUndetTest).createFtpesClient(any());
+
+        FileData fileData = createFileData(FTPES_LOCATION);
+        doThrow(new NonRetryableDatafileTaskException("Unable to collect file.")).when(ftpesClientMock)
+            .collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION);
+
+        StepVerifier.create(collectorUndetTest.collectFile(fileData, 3, Duration.ofSeconds(0)))
+            .expectErrorMessage("Non retryable file transfer failure") //
+            .verify();
+
+        verify(ftpesClientMock, times(1)).collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION);
+
+        assertEquals(0, counters.getNoOfCollectedFiles(), "collectedFiles should have been 0");
+        assertEquals(1, counters.getNoOfFailedFtpAttempts(), "failedFtpAttempts should have been 1");
+        assertEquals(0, counters.getNoOfFailedHttpAttempts(), "failedHttpAttempts should have been 0");
+    }
+
+    @Test
+    public void whenFtpesFileFailOnce_retryAndReturnCorrectResponse() throws Exception {
+        FileCollector collectorUndetTest = spy(new FileCollector(appConfigMock, counters));
+        doReturn(ftpesClientMock).when(collectorUndetTest).createFtpesClient(any());
+        doThrow(new DatafileTaskException("Unable to collect file.")).doNothing().when(ftpesClientMock)
+            .collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION);
+
+        FilePublishInformation expectedfilePublishInformation =
+            createExpectedFilePublishInformation(FTPES_LOCATION_NO_PORT);
+
+        FileData fileData = createFileData(FTPES_LOCATION_NO_PORT);
+
+        StepVerifier.create(collectorUndetTest.collectFile(fileData, 3, Duration.ofSeconds(0)))
+            .expectNext(expectedfilePublishInformation) //
+            .verifyComplete();
+
+        verify(ftpesClientMock, times(2)).collectFile(REMOTE_FILE_LOCATION, LOCAL_FILE_LOCATION);
+
+        assertEquals(1, counters.getNoOfCollectedFiles(), "collectedFiles should have been 1");
+        assertEquals(1, counters.getNoOfFailedFtpAttempts(), "failedFtpAttempts should have been 1");
+        assertEquals(0, counters.getNoOfFailedHttpAttempts(), "failedHttpAttempts should have been 0");
+    }
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/utils/JsonMessage.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/utils/JsonMessage.java
new file mode 100644 (file)
index 0000000..7c2706d
--- /dev/null
@@ -0,0 +1,262 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * 2018-2019 Nordix Foundation. 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.utils;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Utility class to produce correctly formatted fileReady event Json messages.
+ *
+ * @author <a href="mailto:henrik.b.andersson@est.tech">Henrik Andersson</a> on 7/25/18
+ *
+ */
+public class JsonMessage {
+    private String eventName;
+    private String changeIdentifier;
+    private String changeType;
+    private String notificationFieldsVersion;
+    private List<AdditionalField> arrayOfAdditionalFields;
+
+    public List<AdditionalField> getAdditionalFields() {
+        return arrayOfAdditionalFields;
+    }
+
+    @Override
+    public String toString() {
+        return "[" + getParsed() + "]";
+    }
+
+    /**
+     * Gets the message in parsed format.
+     *
+     * @return the massage in parsed format.
+     */
+    public String getParsed() {
+        StringBuffer additionalFieldsString = new StringBuffer();
+        if (arrayOfAdditionalFields.size() > 0) {
+            additionalFieldsString.append("\"arrayOfNamedHashMap\":[");
+            for (Iterator<AdditionalField> iterator = arrayOfAdditionalFields.iterator(); iterator.hasNext();) {
+                AdditionalField additionalField = iterator.next();
+                additionalFieldsString.append(additionalField.toString());
+                if (iterator.hasNext()) {
+                    additionalFieldsString.append(",");
+                }
+            }
+            additionalFieldsString.append("]");
+        }
+        return "{" //
+            + "\"event\":" //
+            + "{" //
+            + "\"commonEventHeader\":" //
+            + "{" //
+            + "\"domain\":\"notification\"," //
+            + "\"eventId\":\"<<SerialNumber>>-reg\"," //
+            + "\"eventName\":\"" + eventName + "\"," //
+            + "\"eventType\":\"fileReady\"," //
+            + "\"internalHeaderFields\":{}," //
+            + "\"lastEpochMicrosec\":1519837825682," //
+            + "\"nfNamingCode\":\"5GRAN\"," //
+            + "\"nfcNamingCode\":\"5DU\"," //
+            + "\"priority\":\"Normal\"," //
+            + "\"reportingEntityName\":\"5GRAN_DU\"," //
+            + "\"sequence\":0," //
+            + "\"sourceId\":\"<<SerialNumber>>\"," //
+            + "\"sourceName\":\"5GRAN_DU\"," //
+            + "\"timeZoneOffset\":\"UTC+05:00\"," //
+            + "\"startEpochMicrosec\":\"1519837825682\"," //
+            + "\"version\":3" //
+            + "}," //
+            + "\"notificationFields\":" //
+            + "{" //
+            + getAsStringIfParameterIsSet("changeIdentifier", changeIdentifier,
+                changeType != null || notificationFieldsVersion != null || arrayOfAdditionalFields.size() > 0)
+            + getAsStringIfParameterIsSet("changeType", changeType,
+                notificationFieldsVersion != null || arrayOfAdditionalFields.size() > 0)
+            + getAsStringIfParameterIsSet("notificationFieldsVersion", notificationFieldsVersion,
+                arrayOfAdditionalFields.size() > 0)
+            + additionalFieldsString.toString() //
+            + "}" //
+            + "}" //
+            + "}";
+    }
+
+    private JsonMessage(final JsonMessageBuilder builder) {
+        this.eventName = builder.eventName;
+        this.changeIdentifier = builder.changeIdentifier;
+        this.changeType = builder.changeType;
+        this.notificationFieldsVersion = builder.notificationFieldsVersion;
+        this.arrayOfAdditionalFields = builder.arrayOfAdditionalFields;
+    }
+
+    public static class AdditionalField {
+        private String name;
+        private String location;
+        private String compression;
+        private String fileFormatType;
+        private String fileFormatVersion;
+
+        @Override
+        public String toString() {
+            return "{" //
+                + getAsStringIfParameterIsSet("name", name, true) //
+                + "\"hashMap\":" //
+                + "{"
+                + getAsStringIfParameterIsSet("location", location,
+                    compression != null || fileFormatType != null || fileFormatVersion != null)
+                + getAsStringIfParameterIsSet("compression", compression,
+                    fileFormatType != null || fileFormatVersion != null)
+                + getAsStringIfParameterIsSet("fileFormatType", fileFormatType, fileFormatVersion != null)
+                + getAsStringIfParameterIsSet("fileFormatVersion", fileFormatVersion, false) //
+                + "}" //
+                + "}";
+        }
+
+        private AdditionalField(AdditionalFieldBuilder builder) {
+            this.name = builder.name;
+            this.location = builder.location;
+            this.compression = builder.compression;
+            this.fileFormatType = builder.fileFormatType;
+            this.fileFormatVersion = builder.fileFormatVersion;
+        }
+
+    }
+
+    public static class AdditionalFieldBuilder {
+        private String name;
+        private String location;
+        private String compression;
+        private String fileFormatType;
+        private String fileFormatVersion;
+
+        public AdditionalFieldBuilder name(String name) {
+            this.name = name;
+            return this;
+        }
+
+        public AdditionalFieldBuilder location(String location) {
+            this.location = location;
+            return this;
+        }
+
+        public AdditionalFieldBuilder compression(String compression) {
+            this.compression = compression;
+            return this;
+        }
+
+        public AdditionalFieldBuilder fileFormatType(String fileFormatType) {
+            this.fileFormatType = fileFormatType;
+            return this;
+        }
+
+        public AdditionalFieldBuilder fileFormatVersion(String fileFormatVersion) {
+            this.fileFormatVersion = fileFormatVersion;
+            return this;
+        }
+
+        public AdditionalField build() {
+            return new AdditionalField(this);
+        }
+    }
+
+    public static class JsonMessageBuilder {
+        private String eventName;
+        private String changeIdentifier;
+        private String changeType;
+        private String notificationFieldsVersion;
+        private List<AdditionalField> arrayOfAdditionalFields = new ArrayList<AdditionalField>();
+
+        public JsonMessageBuilder eventName(String eventName) {
+            this.eventName = eventName;
+            return this;
+        }
+
+        public JsonMessageBuilder changeIdentifier(String changeIdentifier) {
+            this.changeIdentifier = changeIdentifier;
+            return this;
+        }
+
+        public JsonMessageBuilder changeType(String changeType) {
+            this.changeType = changeType;
+            return this;
+        }
+
+        public JsonMessageBuilder notificationFieldsVersion(String notificationFieldsVersion) {
+            this.notificationFieldsVersion = notificationFieldsVersion;
+            return this;
+        }
+
+        public JsonMessageBuilder addAdditionalField(AdditionalField additionalField) {
+            this.arrayOfAdditionalFields.add(additionalField);
+            return this;
+        }
+
+        public JsonMessage build() {
+            return new JsonMessage(this);
+        }
+    }
+
+    private static String getAsStringIfParameterIsSet(String parameterName, String parameterValue,
+        boolean withSeparator) {
+        String result = "";
+        if (parameterValue != null) {
+            result = "\"" + parameterName + "\":\"" + parameterValue + "\"";
+
+            if (withSeparator) {
+                result = result + ",";
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Can be used to produce a correct test Json message. Tip! Check the formatting with
+     * <a href="https://jsonformatter.org/">Json formatter</a>
+     *
+     * @param args Not used
+     */
+    public static void main(String[] args) {
+        AdditionalField additionalField = new JsonMessage.AdditionalFieldBuilder() //
+            .name("A20161224.1030-1045.bin.gz") //
+            .location("ftpes://192.168.0.101:22/ftp/rop/A20161224.1030-1045.bin.gz") //
+            .compression("gzip") //
+            .fileFormatType("org.3GPP.32.435#measCollec") //
+            .fileFormatVersion("V10") //
+            .build();
+        AdditionalField secondAdditionalField = new JsonMessage.AdditionalFieldBuilder() //
+            .name("A20161224.1030-1045.bin.gz") //
+            .location("sftp://192.168.0.101:22/ftp/rop/A20161224.1030-1045.bin.gz") //
+            .compression("gzip") //
+            .fileFormatType("org.3GPP.32.435#measCollec") //
+            .fileFormatVersion("V10") //
+            .build();
+        JsonMessage message = new JsonMessage.JsonMessageBuilder() //
+            .eventName("Noti_NrRadio-Ericsson_FileReady") //
+            .changeIdentifier("PM_MEAS_FILES") //
+            .changeType("FileReady") //
+            .notificationFieldsVersion("2.0") //
+            .addAdditionalField(additionalField) //
+            .addAdditionalField(secondAdditionalField) //
+            .build();
+        System.out.println(message.toString());
+    }
+}
diff --git a/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/utils/LoggingUtils.java b/datafilecollector/src/test/java/org/onap/dcaegen2/collectors/datafile/utils/LoggingUtils.java
new file mode 100644 (file)
index 0000000..cfcb7bf
--- /dev/null
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dcaegen2.collectors.datafile.utils;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
+
+import org.slf4j.LoggerFactory;
+
+public class LoggingUtils {
+
+    /**
+     * Returns a ListAppender that contains all logging events. Call this method at the very beginning of the test
+     */
+    public static ListAppender<ILoggingEvent> getLogListAppender(Class<?> logClass) {
+        return getLogListAppender(logClass, false);
+    }
+
+    /**
+     * Returns a ListAppender that contains all logging events. Call this method at the very beginning of the test
+     *
+     * @param logClass class whose appender is wanted.
+     * @param allLevels true if all log levels should be activated.
+     */
+    public static ListAppender<ILoggingEvent> getLogListAppender(Class<?> logClass, boolean allLevels) {
+        Logger logger = (Logger) LoggerFactory.getLogger(logClass);
+        if (allLevels) {
+            logger.setLevel(Level.ALL);
+        }
+        ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
+        listAppender.start();
+        logger.addAppender(listAppender);
+
+        return listAppender;
+    }
+}
diff --git a/datafilecollector/src/test/resources/cert.jks b/datafilecollector/src/test/resources/cert.jks
new file mode 100755 (executable)
index 0000000..ff0e95c
Binary files /dev/null and b/datafilecollector/src/test/resources/cert.jks differ
diff --git a/datafilecollector/src/test/resources/dfc.jks b/datafilecollector/src/test/resources/dfc.jks
new file mode 100644 (file)
index 0000000..cdd1191
Binary files /dev/null and b/datafilecollector/src/test/resources/dfc.jks differ
diff --git a/datafilecollector/src/test/resources/dfc.jks.pass b/datafilecollector/src/test/resources/dfc.jks.pass
new file mode 100644 (file)
index 0000000..d97c5ea
--- /dev/null
@@ -0,0 +1 @@
+secret
diff --git a/datafilecollector/src/test/resources/ftp.jks.pass b/datafilecollector/src/test/resources/ftp.jks.pass
new file mode 100644 (file)
index 0000000..d97c5ea
--- /dev/null
@@ -0,0 +1 @@
+secret
diff --git a/datafilecollector/src/test/resources/jks.pass b/datafilecollector/src/test/resources/jks.pass
new file mode 100755 (executable)
index 0000000..b2c3df4
--- /dev/null
@@ -0,0 +1 @@
+hD:!w:CxF]lGvM6Mz9l^j[7U
\ No newline at end of file
diff --git a/datafilecollector/src/test/resources/keystore.p12 b/datafilecollector/src/test/resources/keystore.p12
new file mode 100644 (file)
index 0000000..b847707
Binary files /dev/null and b/datafilecollector/src/test/resources/keystore.p12 differ
diff --git a/datafilecollector/src/test/resources/keystore.pass b/datafilecollector/src/test/resources/keystore.pass
new file mode 100644 (file)
index 0000000..1e7befc
--- /dev/null
@@ -0,0 +1 @@
+HVpAf0kHGl4P#fdpblJLka6b
\ No newline at end of file
diff --git a/datafilecollector/src/test/resources/trust.jks b/datafilecollector/src/test/resources/trust.jks
new file mode 100755 (executable)
index 0000000..fc62ad2
Binary files /dev/null and b/datafilecollector/src/test/resources/trust.jks differ
diff --git a/datafilecollector/src/test/resources/trust.pass b/datafilecollector/src/test/resources/trust.pass
new file mode 100755 (executable)
index 0000000..047a411
--- /dev/null
@@ -0,0 +1 @@
+jeQ2l]iyB62D{WbSHL]dN*8R
\ No newline at end of file
diff --git a/pmproducer/Dockerfile b/pmproducer/Dockerfile
new file mode 100644 (file)
index 0000000..dd6d31c
--- /dev/null
@@ -0,0 +1,51 @@
+#
+# ============LICENSE_START=======================================================
+# O-RAN-SC
+# ================================================================================
+# Copyright (C) 2023 Nordix Foundation. 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+
+
+FROM openjdk:17-jdk-slim
+
+EXPOSE 8084 8435
+
+ARG JAR
+
+WORKDIR /opt/app/pm_producer-service
+RUN mkdir -p /var/log/pm_producer-service
+RUN mkdir -p /opt/app/pm_producer-service/etc/cert/
+RUN mkdir -p /var/pm_producer-service
+
+ADD /config/application.yaml /opt/app/pm_producer-service/config/application.yaml
+ADD /config/application_configuration.json /opt/app/pm_producer-service/data/application_configuration.json_example
+ADD /config/keystore.jks /opt/app/pm_producer-service/etc/cert/keystore.jks
+ADD /config/truststore.jks /opt/app/pm_producer-service/etc/cert/truststore.jks
+
+ARG user=nonrtric
+ARG group=nonrtric
+
+RUN groupadd $user && \
+    useradd -r -g $group $user
+RUN chown -R $user:$group /opt/app/pm_producer-service
+RUN chown -R $user:$group /var/log/pm_producer-service
+RUN chown -R $user:$group /var/pm_producer-service
+
+USER ${user}
+
+ADD target/${JAR} /opt/app/pm_producer-service/pm_producer.jar
+CMD ["java", "-jar", "/opt/app/pm_producer-service/pm_producer.jar"]
diff --git a/pmproducer/README.md b/pmproducer/README.md
new file mode 100644 (file)
index 0000000..8f40e79
--- /dev/null
@@ -0,0 +1,35 @@
+# O-RAN-SC Non-RealTime RIC PM Producer
+
+
+This product is a PM data information producer (as defined by the Information Coordinator Service (ICS)). Its main tasks is to distribute PM data to PM data consumers (using Kafka topics).
+
+This service will receive File Ready Events or PM Data from the Kafka streaming platform and will filter the information and distribute it further to the data consumers (information job owners).
+
+The component is a springboot service and is configured as any springboot service through the file `config/application.yaml`. The component log can be retrieved and logging can be controled by means of REST call. See the API documentation (api/api.yaml).
+
+The file `config/application_configuration.json` contains the configuration of job types that the producer will support.
+
+
+
+```sh
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "type": "object",
+  "properties": {
+
+     TBD
+
+  },
+  "additionalProperties": false
+}
+```
+
+
+## License
+
+Copyright (C) 2023 Nordix Foundation. Licensed under the Apache License, Version 2.0 (the "License") you may not use this file except in compliance with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+For more information about license please see the [LICENSE](LICENSE.txt) file for details.
diff --git a/pmproducer/config/README b/pmproducer/config/README
new file mode 100644 (file)
index 0000000..0ae19ca
--- /dev/null
@@ -0,0 +1,43 @@
+The keystore.jks and truststore.jks files are created by using the following commands (note that this is an example):
+
+1) Create a CA certificate and a private key:
+
+openssl genrsa -des3 -out CA-key.pem 2048
+openssl req -new -key CA-key.pem -x509 -days 3600 -out CA-cert.pem
+
+2) Create a keystore with a private key entry that is signed by the CA:
+
+Note: your name must be "localhost" for the unittest.
+
+keytool -genkeypair -alias policy_agent -keyalg RSA -keysize 2048 -keystore keystore.jks -validity 3650 -storepass policy_agent
+keytool -certreq -alias policy_agent -file request.csr -keystore keystore.jks -ext san=dns:your.domain.com -storepass policy_agent
+openssl x509 -req -days 365 -in request.csr -CA CA-cert.pem -CAkey CA-key.pem -CAcreateserial -out ca_signed-cert.pem
+keytool -importcert -alias ca_cert -file CA-cert.pem -keystore keystore.jks -trustcacerts -storepass policy_agent
+keytool -importcert -alias policy_agent -file ca_signed-cert.pem -keystore keystore.jks -trustcacerts -storepass policy_agent
+
+
+3) Create a trust store containing the CA cert (to trust all certs signed by the CA):
+
+keytool -genkeypair -alias not_used -keyalg RSA -keysize 2048 -keystore truststore.jks -validity 3650 -storepass policy_agent
+keytool -importcert -alias ca_cert -file CA-cert.pem -keystore truststore.jks -trustcacerts -storepass policy_agent
+
+
+4) Command for listing of the contents of jks files, examples:
+keytool -list -v -keystore keystore.jks -storepass policy_agent
+keytool -list -v -keystore truststore.jks -storepass policy_agent
+
+## License
+
+Copyright (C) 2023 Nordix Foundation. 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.
+
diff --git a/pmproducer/config/application.yaml b/pmproducer/config/application.yaml
new file mode 100644 (file)
index 0000000..b0fd88a
--- /dev/null
@@ -0,0 +1,99 @@
+#  ============LICENSE_START===============================================
+#  Copyright (C) 2023-2022 Nordix Foundation. 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=================================================
+#
+
+spring:
+  profiles:
+    active: prod
+  main:
+    allow-bean-definition-overriding: true
+  aop:
+    auto: false
+management:
+  endpoints:
+    web:
+      exposure:
+        # Enabling of springboot actuator features. See springboot documentation.
+        include: "loggers,logfile,health,info,metrics,threaddump,heapdump,shutdown"
+  endpoint:
+    shutdown:
+      enabled: true
+lifecycle:
+  timeout-per-shutdown-phase: "20s"
+springdoc:
+  show-actuator: true
+logging:
+  # Configuration of logging
+  level:
+    ROOT: WARN
+    org.apache.kafka: WARN
+    org.springframework: ERROR
+    org.springframework.data: ERROR
+    org.springframework.web.reactive.function.client.ExchangeFunctions: ERROR
+    org.oran.pmproducer: INFO
+  pattern:
+    console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level]  %logger{20} - %msg%n"
+    file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level]  %logger{20} - %msg%n"
+
+  file:
+    name: /var/log/pm_producer-service/application.log
+server:
+   # Configuration of the HTTP/REST server. The parameters are defined and handeled by the springboot framework.
+   # See springboot documentation.
+  port : 8435
+  http-port: 8084
+  ssl:
+    key-store-type: JKS
+    key-store-password: policy_agent
+    key-store: /opt/app/pm_producer-service/etc/cert/keystore.jks
+    key-password: policy_agent
+    key-alias: policy_agent
+  shutdown: "graceful"
+app:
+  webclient:
+    # Configuration of the trust store used for the HTTP client (outgoing requests)
+    # The file location and the password for the truststore is only relevant if trust-store-used == true
+    # Note that the same keystore as for the server is used.
+    trust-store-used: false
+    trust-store-password: policy_agent
+    trust-store: /opt/app/pm_producer-service/etc/cert/truststore.jks
+    # Configuration of usage of HTTP Proxy for the southbound accesses.
+    # The HTTP proxy (if configured) will only be used for accessing NearRT RIC:s
+    http.proxy-host:
+    http.proxy-port: 0
+  ics-base-url: https://localhost:8434
+  # Location of the component configuration file. The file will only be used if the Consul database is not used;
+  # configuration from the Consul will override the file.
+  configuration-filepath: /opt/app/pm_producer-service/data/application_configuration.json
+  dmaap-base-url: http://dradmin:dradmin@localhost:2222
+  # The url used to adress this component. This is used as a callback url sent to other components.
+  pm_producer-base-url: https://localhost:8435
+  # KAFKA boostrap servers. This is only needed if there are Information Types that uses a kafkaInputTopic
+  # several redundant boostrap servers can be specified, separated by a comma ','.
+  kafka:
+    bootstrap-servers: localhost:9092
+    # The maximum number of records returned in a single call to poll() (default 100)
+    max-poll-records: 500
+  # If the file name is empty, no authorization token is used
+  auth-token-file:
+  pm-files-path: /tmp
+  zip-output: false
+  s3:
+    endpointOverride: http://localhost:9000
+    accessKeyId: minio
+    secretAccessKey: miniostorage
+    locksBucket: ropfilelocks
+    bucket: ropfiles
diff --git a/pmproducer/config/application_configuration.json b/pmproducer/config/application_configuration.json
new file mode 100644 (file)
index 0000000..540ddce
--- /dev/null
@@ -0,0 +1,18 @@
+{
+   "types": [
+      {
+         "id": "PmDataOverKafka",
+         "kafkaInputTopic": "FileReadyEvent",
+         "inputJobType": "xml-file-data-to-filestore",
+         "inputJobDefinition": {
+            "kafkaOutputTopic": "FileReadyEvent",
+            "filestore-output-bucket": "pm-files-json",
+            "filterType": "pmdata",
+            "filter": {
+               "inputCompression": "xml.gz",
+               "outputCompression": "none"
+            }
+         }
+      }
+   ]
+}
\ No newline at end of file
diff --git a/pmproducer/config/gen/CA-cert.pem b/pmproducer/config/gen/CA-cert.pem
new file mode 100644 (file)
index 0000000..a203e77
--- /dev/null
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIUaLLdU6Iyw5Keg6XtNMV/km+Zc4kwDQYJKoZIhvcNAQEL
+BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X
+DTIyMDgxMDA5NTEzM1oXDTMyMDYxODA5NTEzM1owWTELMAkGA1UEBhMCQVUxEzAR
+BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5
+IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEA0C6EV4nKVC+RrLVIbJ/CwiXRIVNTkS2r8tKhQqAq1RlHSFU2wFOt
+bYaeFCYWFVTf+L8/a5/71zDCrBxn0SBpvzA5l4TNDEieZvOagTrQ+MFRkdm2N6BP
+hXD8eaL35SFO3uI//xvnxZbsYG/avAtos0kJiVYa8xcuiziyZnqyWCd9mEI/T4/O
+fOEJkTValFgXF5cXg6pp/rwk+UOs3Xf6LYVRVQrFdPn6sqLIB/wyKX8h6kZPKg1z
+pCmK8X7g4F1PjrbPKEeykrXEZND93+nJLkW78k22XDpr0HhZ9nJd0H5jfBDu8T+9
+2bHdmqcrVyC3b5uKONagp8BdZVamoab23wIDAQABo1MwUTAdBgNVHQ4EFgQUojxP
+gBLiUFzumNGV1I+/s0HsAgwwHwYDVR0jBBgwFoAUojxPgBLiUFzumNGV1I+/s0Hs
+AgwwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEABbPtAA5x7MCd
+pf6kWYgcLxTv0U2cmAsmMUTUR/w41TR1s2tnvwzLxsgLhY2QVCD7Jx1MKhZ02X/X
+2NoSvHa2UkqEV7KATN3e9+6TOoc8ZVzAjtU8v1BgiNv+QrqeZ3OcEgzRUsaeOh/f
+B+ilRCVzOXQ1ZALX6vGtx4QEl/PtTv0njsIdWfjZU61+LStb6Gjl2/kETRRh5k18
+9K64UGv7MAIWh+4HkaisN6VWZp9BudGMB+r06W7ioA/KmH/54unUEa/jR037eic9
+tF3BJesLcx9mD333eRPa7TXA8VRvEyu4VYJh/yh/gREQcKDLhwjUiWLtLN256M/p
+vAoEeTRR4w==
+-----END CERTIFICATE-----
diff --git a/pmproducer/config/gen/CA-cert.srl b/pmproducer/config/gen/CA-cert.srl
new file mode 100644 (file)
index 0000000..5ae7577
--- /dev/null
@@ -0,0 +1 @@
+0D7D3C2447B7B088388421B0E7A326F36CF5EB2B
diff --git a/pmproducer/config/gen/CA-key.pem b/pmproducer/config/gen/CA-key.pem
new file mode 100644 (file)
index 0000000..2b179b6
--- /dev/null
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,15952F62BAC0E2A7
+
+dC7XJiEJfNyGqnsaJ8m9vg+Y7JsmbexDioaI21LO4jo2nnxj7YRj7fKzeCQ1Iilo
+isQuBxC1PsRb3FfukvRWAfA0+RRDgV3RZBUP9N54SeA6IWUgDyo9NOjmEitvC2o6
+mWn2sWhF0eLaTlVa1xmy7f8yiVxzH1bLZIz8+xjlj2vaBAokAxnPEtsjzlFghosC
+7BvWDSQQZqQGi+mFdqiGYAycQCAqhRzmFW7xg7UcE/ivBO59sJJ0pEM4ieXkpQTJ
+VaUBZPTzKJ4l0lbs2T0VNOOPceQuln52rBRi9kn1iqt4m5GcYqEHBTIXplQfAGxV
+rmSR/gLqsDg89ms+cKQErENjmh8P39Fif6oySPdRE+PBp5IiooH9XLVvFmI68kqR
+qYE3UbQ2be7nHDG3pXpp0dVkUUEx0eUs1G/btZhMwQmXHu0nLqAAeP1s1DK/0JOL
+nPrtkJuMCm8no18EGiccMovTXUaFRhppQbdFhVr7b+B7gMYhxg4sCotiuFbYkOju
+9wYrTo5cFRP5ZLF0igBf95gQFYkpSpKtKaso2zcqR6dWcA7bYMkzMuPv4+9+pR2g
+uByYE3lma3L8JTyCyCorHL8/k8lsvDDopnCPT6dzbmjafWU71KG4JtM7jwwI2JyC
+vgok/gb2fAKo2fNLQxEXLXLLLaBzC3uBj6U8wZm0BphKKinQb3C8J9om0Nt5VG+6
+yXEVpSnCNXg8n1c7EtKKXfEhsD9e6Q7KkBgbnJXvI6VEV/fGi3M1znMZ9CPmcO4R
+YxsfF6Br8r7M8c6TsG4Qq9FD7LdqqEJm12fx+JK0iGkR1QsmYI9BKXOcVSIse1rl
+LeiQV5YDR6qf/dhUJKricUxO8uzIBoKWkB4QHXEnf7Rvv/+4yg1G6WJDwMi89cJi
+ye0oQoCFc/EVC0huXwSV9f5pivd9xX9ZQaGLrdv5kGhQ30NtEQ8THM5A1HiVHFR5
++C9YqBmX+3qKXdydG6QLA+9W5pCMUGzRahgiteXr7qecxakHXp3qTEt76yiEGDwX
+GTc0NY8QxBJrLs27uhK0cNcH2klMeaGZRPVJQg9H2xEKVCxdwWbftzkTGuIbU49j
+bnx1i7+F6//frrIgiLJGzJqx2oMeOKw6bOG1HqMlZNVz7F+xCSg82B3zYVl3uZTa
+vBBaEnNBnhN7OPjnkuNu6ef57fax1stCUGOQ3mN8BBYdnra4oJt51hukkfpCnyDg
++4rwr3QysY7uqyi4Ariaux9YW5JXb+v62GtKKyhnq60VSJ/Lb6n+GrU09Sf1gqPz
+Lqq2XyO4VOjyQ3WthDoSUIUug7cNexTzf756Faw+6oX/hKS28+qub/91FOIrlR8E
+Q4qdZfa60yjcurSaRRRHr6631jmWGf8cSkAnduAErbnzyfNu1JOVY0YdqCHUOnBc
+zUU2fmhWSNLzjbNsiUfnhffpLs0LLLWjOyNXlmibEDsU0zwbmV3g8X2rrK/eRhxU
+aQH8dmnCUjOGm6P9SX9IRPxlV/CS0LbsmPRYm8uyRZKXFTfLOuke+6m0dk7DvN1b
+Vk70/YKX/XpVAybY5ljSIGgx3aANBGtUkFsrGCtbdg739AxtcNA3VA==
+-----END RSA PRIVATE KEY-----
diff --git a/pmproducer/config/gen/ca_signed-cert.pem b/pmproducer/config/gen/ca_signed-cert.pem
new file mode 100644 (file)
index 0000000..b27492f
--- /dev/null
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDSTCCAjECFA19PCRHt7CIOIQhsOejJvNs9esrMA0GCSqGSIb3DQEBCwUAMFkx
+CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
+cm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMjA4
+MTAwOTUyMzBaFw0zMjA4MDcwOTUyMzBaMGkxCzAJBgNVBAYTAnNlMRAwDgYDVQQI
+EwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAw
+DgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCBXxnqapE7itAOZWL05mYYzKhf8Ip7l6oUza1A
+Of2xZyfM09ZkWDcX/ApJBYaC8fo0ZzYNRKaCv0p6R4zZ/GkL/wt8iMP3gN2AFa9a
+1sSYBceXX9qkYdNsGvPpWkLbvYkleetGlFvXtoRJb9A9uUbQCtxqOuyMhp8+zGFx
+kQYWDfJzfzlPnWTKXj/uPg/tQcgIXB42q6VUYziQ5P80nb6FrCef1kr+7kUYgjEO
+OkwKbX1NwzJ/IkcToIsQi4p1wDmwYZXc7u2qM9SzCfnPgdGsdxWdwHUJdIMlaef3
+QCEQtD4xIzqQEXR6pfN69/IrsE/clDqtFZU8z0xw89pkvuFlAgMBAAEwDQYJKoZI
+hvcNAQELBQADggEBALjZWEIjxaRVlOJdHlS1RDQYBLLnt7BHPkm9WSzPDO71fXzF
+7IpQrxcusLTH+hDncTGFS2rflqgFb1ws/KHL1ugv51PrJQdXdOzW5qCkBApYAg0X
+WALNSVA+iV2u/m2vX/1sHDcw9T7ZnwbQjc5eY/ndO2VhYSVPo0ZNll3P8jG/Yiar
+y54fTUEYXCv9vtMBSjcc2Yx2u0IOoB5jndMCHgcqhPOKx/X9D2S//k5c336d3MZB
+qraaMT3PcYmV0P/fOtWl5Y0DmRNM3paUYCfTnJm4TbBLewHGUOhUwB+aI2iWJB6v
+kz+JqVROY8uFR0PxzMDgdlFeWGaJqM238c/Gl3g=
+-----END CERTIFICATE-----
diff --git a/pmproducer/config/gen/keystore.jks b/pmproducer/config/gen/keystore.jks
new file mode 100644 (file)
index 0000000..2f3b8b8
Binary files /dev/null and b/pmproducer/config/gen/keystore.jks differ
diff --git a/pmproducer/config/gen/request.csr b/pmproducer/config/gen/request.csr
new file mode 100644 (file)
index 0000000..5fae9ee
--- /dev/null
@@ -0,0 +1,18 @@
+-----BEGIN NEW CERTIFICATE REQUEST-----
+MIIC+jCCAeICAQAwaTELMAkGA1UEBhMCc2UxEDAOBgNVBAgTB1Vua25vd24xEDAO\r
+BgNVBAcTB1Vua25vd24xEDAOBgNVBAoTB1Vua25vd24xEDAOBgNVBAsTB1Vua25v\r
+d24xEjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\r
+AQoCggEBAIFfGepqkTuK0A5lYvTmZhjMqF/winuXqhTNrUA5/bFnJ8zT1mRYNxf8\r
+CkkFhoLx+jRnNg1EpoK/SnpHjNn8aQv/C3yIw/eA3YAVr1rWxJgFx5df2qRh02wa\r
+8+laQtu9iSV560aUW9e2hElv0D25RtAK3Go67IyGnz7MYXGRBhYN8nN/OU+dZMpe\r
+P+4+D+1ByAhcHjarpVRjOJDk/zSdvoWsJ5/WSv7uRRiCMQ46TAptfU3DMn8iRxOg\r
+ixCLinXAObBhldzu7aoz1LMJ+c+B0ax3FZ3AdQl0gyVp5/dAIRC0PjEjOpARdHql\r
+83r38iuwT9yUOq0VlTzPTHDz2mS+4WUCAwEAAaBMMEoGCSqGSIb3DQEJDjE9MDsw\r
+HQYDVR0OBBYEFBywQ71a34HTNDcpvE6jkA8apu7/MBoGA1UdEQQTMBGCD3lvdXIu\r
+ZG9tYWluLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAba7ZJgcX7Fg1HtVA+qDssGog\r
+kpusk1i/ej4I3NZD31MBAFGMqhN7Xsm+Spf6ez94wAE0d8FLGEcyulHDZ2bjBO7D\r
+4DJ9N+Uq9YIHdNwGw0KrdZi1Pk+Ed9noleXc396mh8mNo6uu7V1/4ftssBpLzFfq\r
+6tCp/5X0FGcF2OA2Y/AJKs4HuSHpXvmEXZY+swd2fA5dQbOIA613Dy44orRnNeeO\r
+TTk/x19TQex0SbmO2TEZ0hkr1bL2bZRlKtrd6/Gm8vARPI+wnP4yB+/K7FKKX0LM\r
+isIiXYdcJsbUs7+DCrhnY0oXO4tgD8d8sPQrXQ3KWfHl8yelOizyEKqioLaPdQ==
+-----END NEW CERTIFICATE REQUEST-----
diff --git a/pmproducer/config/gen/truststore.jks b/pmproducer/config/gen/truststore.jks
new file mode 100644 (file)
index 0000000..e92b295
Binary files /dev/null and b/pmproducer/config/gen/truststore.jks differ
diff --git a/pmproducer/config/keystore.jks b/pmproducer/config/keystore.jks
new file mode 100644 (file)
index 0000000..563c67b
Binary files /dev/null and b/pmproducer/config/keystore.jks differ
diff --git a/pmproducer/config/truststore.jks b/pmproducer/config/truststore.jks
new file mode 100644 (file)
index 0000000..50a0f9e
Binary files /dev/null and b/pmproducer/config/truststore.jks differ
diff --git a/pmproducer/eclipse-formatter.xml b/pmproducer/eclipse-formatter.xml
new file mode 100644 (file)
index 0000000..b2e86eb
--- /dev/null
@@ -0,0 +1,362 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+ * ========================LICENSE_START=================================
+ * ONAP : ccsdk oran
+ * ======================================================================
+ * Copyright (C) 2019-2020 Nordix Foundation. 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===================================
+  -->
+<profiles version="13">
+    <profile kind="CodeFormatterProfile" name="ONAP_GoogleStyle" version="13">
+        <setting id="org.eclipse.jdt.core.compiler.source" value="1.8" />
+        <setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8" />
+        <setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8" />
+        <setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled" />
+        <setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error" />
+        <setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error" />
+        <setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field" value="1585" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field.count_dependent" value="1585|-1|1585" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable" value="1585" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable.count_dependent" value="1585|-1|1585" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method" value="1585" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method.count_dependent" value="1585|-1|1585" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package" value="1585" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package.count_dependent" value="1585|-1|1585" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter" value="1040" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter.count_dependent" value="1040|-1|1040" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type" value="1585" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type.count_dependent" value="1585|-1|1585" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression.count_dependent" value="16|5|80" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation.count_dependent" value="16|-1|16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant.count_dependent" value="16|-1|16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call.count_dependent" value="16|5|80" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation.count_dependent" value="16|5|80" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression.count_dependent" value="16|4|80" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression.count_dependent" value="16|-1|16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_cascading_method_invocation_with_arguments" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_cascading_method_invocation_with_arguments.count_dependent" value="16|-1|16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="48" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants.count_dependent" value="16|5|48" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer.count_dependent" value="16|5|80" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_field_declaration" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_for_statement" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_generic_type_arguments" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_generic_type_arguments.count_dependent" value="16|-1|16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_local_variable_declaration" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields.count_dependent" value="16|-1|16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_new_anonymous_class" value="20" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration.count_dependent" value="16|5|80" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration.count_dependent" value="16|5|80" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation.count_dependent" value="16|4|48" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration.count_dependent" value="16|4|49" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration.count_dependent" value="16|4|48" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration.count_dependent" value="16|4|48" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration.count_dependent" value="16|4|48" />
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16" />
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1" />
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1" />
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0" />
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0" />
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1" />
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="0" />
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1" />
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1" />
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="1" />
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1" />
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="2" />
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line" />
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line" />
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line" />
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line" />
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line" />
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line" />
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line" />
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line" />
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line" />
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line" />
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="120" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.comment_new_line_at_start_of_html_paragraph" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2" />
+        <setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2" />
+        <setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off" />
+        <setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on" />
+        <setting id="org.eclipse.jdt.core.formatter.force_if_else_statement_brace" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comment_prefix" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert" />
+        <setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120" />
+        <setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0" />
+        <setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="3" />
+        <setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space" />
+        <setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4" />
+        <setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.wrap_comment_inline_tags" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.wrap_non_simple_local_variable_annotation" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.wrap_non_simple_member_annotation" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.wrap_non_simple_package_annotation" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.wrap_non_simple_parameter_annotation" value="false" />
+        <setting id="org.eclipse.jdt.core.formatter.wrap_non_simple_type_annotation" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true" />
+        <setting id="org.eclipse.jdt.core.formatter.wrap_prefer_two_fragments" value="false" />
+    </profile>
+</profiles>
\ No newline at end of file
diff --git a/pmproducer/pom.xml b/pmproducer/pom.xml
new file mode 100644 (file)
index 0000000..0aa07e5
--- /dev/null
@@ -0,0 +1,375 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* ========================LICENSE_START=================================
+* O-RAN-SC
+* %%
+* Copyright (C) 2023 Nordix Foundation
+* %%
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+* ========================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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>3.0.4</version>
+        <relativePath />
+    </parent>
+    <groupId>org.o-ran-sc.nonrtric.plt</groupId>
+    <artifactId>pmproducer</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+    <licenses>
+        <license>
+            <name>The Apache Software License, Version 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+        </license>
+    </licenses>
+    <repositories>
+        <repository>
+            <id>onap-releases</id>
+            <name>onap-releases</name>
+            <url>https://nexus.onap.org/content/repositories/releases/</url>
+        </repository>
+    </repositories>
+    <properties>
+        <java.version>17</java.version>
+        <gson.version>2.9.0</gson.version>
+        <json.version>20211205</json.version>
+        <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
+        <formatter-maven-plugin.version>2.12.2</formatter-maven-plugin.version>
+        <spotless-maven-plugin.version>1.24.3</spotless-maven-plugin.version>
+        <swagger-codegen-maven-plugin.version>3.0.11</swagger-codegen-maven-plugin.version>
+        <docker-maven-plugin>0.30.0</docker-maven-plugin>
+        <sonar-maven-plugin.version>3.7.0.1746</sonar-maven-plugin.version>
+        <jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version>
+        <exec.skip>true</exec.skip>
+        <protobuf.version>4.0.0-rc-2</protobuf.version>
+        <protobuf-java-format.version>1.4</protobuf-java-format.version>
+        <springdoc.version>2.0.2</springdoc.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+            <version>${springdoc.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-ui</artifactId>
+            <version>${springdoc.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java</artifactId>
+            <version>${protobuf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.googlecode.protobuf-java-format</groupId>
+            <artifactId>protobuf-java-format</artifactId>
+            <version>${protobuf-java-format.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java-util</artifactId>
+            <version>3.21.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-thymeleaf</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-webflux</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webflux</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <!-- Actuator dependencies -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <!-- For development help -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <!-- TEST -->
+        <!-- https://mvnrepository.com/artifact/com.github.erosb/everit-json-schema -->
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-ui</artifactId>
+            <version>1.6.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.github.erosb</groupId>
+            <artifactId>everit-json-schema</artifactId>
+            <version>1.12.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.schibsted.spt.data</groupId>
+            <artifactId>jslt</artifactId>
+            <version>0.1.11</version>
+        </dependency>
+        <dependency>
+            <groupId>com.jayway.jsonpath</groupId>
+            <artifactId>json-path</artifactId>
+            <version>2.7.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.projectreactor</groupId>
+            <artifactId>reactor-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.projectreactor.kafka</groupId>
+            <artifactId>reactor-kafka</artifactId>
+            <version>1.3.13</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>31.0.1-jre</version>
+        </dependency>
+        <dependency>
+            <groupId>software.amazon.awssdk</groupId>
+            <artifactId>s3</artifactId>
+            <version>2.17.292</version>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>net.revelc.code.formatter</groupId>
+                <artifactId>formatter-maven-plugin</artifactId>
+                <version>${formatter-maven-plugin.version}</version>
+                <configuration>
+                    <configFile>${project.basedir}/eclipse-formatter.xml</configFile>
+                </configuration>
+                <!-- https://code.revelc.net/formatter-maven-plugin/ use mvn formatter:format
+                                       spotless:apply process-sources -->
+            </plugin>
+            <plugin>
+                <groupId>com.diffplug.spotless</groupId>
+                <artifactId>spotless-maven-plugin</artifactId>
+                <version>${spotless-maven-plugin.version}</version>
+                <configuration>
+                    <java>
+                        <removeUnusedImports />
+                        <importOrder>
+                            <order>com,java,javax,org</order>
+                        </importOrder>
+                    </java>
+                </configuration>
+                <!-- https://github.com/diffplug/spotless/tree/master/plugin-maven use
+                                       mvn spotless:apply to rewrite source files use mvn spotless:check to validate
+                                       source files -->
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>false</skipTests>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>add-source</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>${project.build.directory}/generated-sources/annotations/</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <version>${jacoco-maven-plugin.version}</version>
+                <executions>
+                    <execution>
+                        <id>default-prepare-agent</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>default-report</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>io.swagger.codegen.v3</groupId>
+                <artifactId>swagger-codegen-maven-plugin</artifactId>
+                <version>${swagger-codegen-maven-plugin.version}</version>
+                <executions>
+                    <execution>
+                        <phase>test</phase>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                        <configuration>
+                            <inputSpec>${project.basedir}/api/api.json</inputSpec>
+                            <language>openapi-yaml</language>
+                            <output>${project.basedir}/api</output>
+                            <configOptions>
+                                <outputFile>api.yaml</outputFile>
+                            </configOptions>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>io.fabric8</groupId>
+                <artifactId>docker-maven-plugin</artifactId>
+                <version>${docker-maven-plugin}</version>
+                <inherited>false</inherited>
+                <executions>
+                    <execution>
+                        <id>generate-nonrtric-plt-pmproducer-image</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>build</goal>
+                        </goals>
+                        <configuration>
+                            <pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>
+                            <images>
+                                <image>
+                                    <name>o-ran-sc/nonrtric-plt-pmproducer:${project.version}</name>
+                                    <build>
+                                        <cleanup>try</cleanup>
+                                        <contextDir>${basedir}</contextDir>
+                                        <dockerFile>Dockerfile</dockerFile>
+                                        <args>
+                                            <JAR>${project.build.finalName}.jar</JAR>
+                                        </args>
+                                        <tags>
+                                            <tag>${project.version}</tag>
+                                        </tags>
+                                    </build>
+                                </image>
+                            </images>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>push-nonrtric-plt-pmproducer-image</id>
+                        <goals>
+                            <goal>build</goal>
+                            <goal>push</goal>
+                        </goals>
+                        <configuration>
+                            <pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>
+                            <pushRegistry>${env.CONTAINER_PUSH_REGISTRY}</pushRegistry>
+                            <images>
+                                <image>
+                                    <name>o-ran-sc/nonrtric-plt-pmproducer:${project.version}</name>
+                                    <build>
+                                        <contextDir>${basedir}</contextDir>
+                                        <dockerFile>Dockerfile</dockerFile>
+                                        <args>
+                                            <JAR>${project.build.finalName}.jar</JAR>
+                                        </args>
+                                        <tags>
+                                            <tag>${project.version}</tag>
+                                            <tag>latest</tag>
+                                        </tags>
+                                    </build>
+                                </image>
+                            </images>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <!-- support sonar in multi-module project -->
+            <plugin>
+                <groupId>org.sonarsource.scanner.maven</groupId>
+                <artifactId>sonar-maven-plugin</artifactId>
+                <version>${sonar-maven-plugin.version}</version>
+            </plugin>
+        </plugins>
+    </build>
+    <issueManagement>
+        <system>JIRA</system>
+        <url>https://jira.o-ran-sc.org/</url>
+    </issueManagement>
+</project>
\ No newline at end of file
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/Application.java b/pmproducer/src/main/java/org/oran/pmproducer/Application.java
new file mode 100644 (file)
index 0000000..14cb353
--- /dev/null
@@ -0,0 +1,86 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer;
+
+import java.io.File;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+
+@SpringBootApplication
+@EnableConfigurationProperties
+@EnableScheduling
+public class Application {
+
+    private static final Logger logger = LoggerFactory.getLogger(Application.class);
+
+    @Value("${app.configuration-filepath}")
+    private String localConfigurationFilePath;
+
+    private long configFileLastModification = 0;
+    private static ConfigurableApplicationContext applicationContext;
+
+    public static void main(String[] args) {
+        applicationContext = SpringApplication.run(Application.class);
+        Runtime.getRuntime().addShutdownHook(new Thread() {
+            @Override
+            public void run() {
+                logger.warn("Shutting down, received signal SIGTERM");
+                SpringApplication.exit(applicationContext);
+                applicationContext = null;
+            }
+        });
+    }
+
+    @Scheduled(fixedRate = 10 * 1000)
+    public void checkConfigFileChanges() {
+        long timestamp = new File(localConfigurationFilePath).lastModified();
+        if (configFileLastModification != 0 && timestamp != configFileLastModification) {
+            logger.info("Restarting due to change in the file {}", localConfigurationFilePath);
+            restartApplication();
+        }
+        configFileLastModification = timestamp;
+    }
+
+    private static void restartApplication() {
+        if (applicationContext == null) {
+            logger.info("Cannot restart in unittest");
+            return;
+        }
+        ApplicationArguments args = applicationContext.getBean(ApplicationArguments.class);
+
+        Thread thread = new Thread(() -> {
+            applicationContext.close();
+            applicationContext = SpringApplication.run(Application.class, args.getSourceArgs());
+        });
+
+        thread.setDaemon(false);
+        thread.start();
+    }
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/BeanFactory.java b/pmproducer/src/main/java/org/oran/pmproducer/BeanFactory.java
new file mode 100644 (file)
index 0000000..1b099dc
--- /dev/null
@@ -0,0 +1,70 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer;
+
+import java.util.Collection;
+
+import org.apache.catalina.connector.Connector;
+import org.oran.pmproducer.configuration.ApplicationConfig;
+import org.oran.pmproducer.repository.InfoType;
+import org.oran.pmproducer.repository.InfoTypes;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
+import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class BeanFactory {
+
+    @Value("${server.http-port}")
+    private int httpPort = 0;
+
+    @Bean
+    public ApplicationConfig getApplicationConfig() {
+        return new ApplicationConfig();
+    }
+
+    @Bean
+    public InfoTypes types(@Autowired ApplicationConfig appConfig) {
+        Collection<InfoType> types = appConfig.getTypes();
+        return new InfoTypes(types);
+    }
+
+    @Bean
+    public ServletWebServerFactory servletContainer() {
+        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
+        if (httpPort > 0) {
+            tomcat.addAdditionalTomcatConnectors(getHttpConnector(httpPort));
+        }
+        return tomcat;
+    }
+
+    private static Connector getHttpConnector(int httpPort) {
+        Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
+        connector.setScheme("http");
+        connector.setPort(httpPort);
+        connector.setSecure(false);
+        return connector;
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/SwaggerConfig.java b/pmproducer/src/main/java/org/oran/pmproducer/SwaggerConfig.java
new file mode 100644 (file)
index 0000000..e131eed
--- /dev/null
@@ -0,0 +1,43 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer;
+
+import io.swagger.v3.oas.annotations.OpenAPIDefinition;
+import io.swagger.v3.oas.annotations.info.Info;
+import io.swagger.v3.oas.annotations.info.License;
+
+/**
+ * Swagger configuration class that uses swagger documentation type and scans
+ * all the controllers. To access the swagger gui go to
+ * http://ip:port/swagger-ui.html
+ */
+@OpenAPIDefinition( //
+        info = @Info(title = SwaggerConfig.API_TITLE, //
+                version = "1.0", //
+                description = SwaggerConfig.DESCRIPTION, //
+                license = @License(name = "Copyright (C) 2023 Nordix Foundation. Licensed under the Apache License.",
+                        url = "http://www.apache.org/licenses/LICENSE-2.0")))
+public class SwaggerConfig {
+    private SwaggerConfig() {}
+
+    static final String API_TITLE = "Generic Dmaap and Kafka Information Producer";
+    static final String DESCRIPTION = "Reads data from DMaaP and Kafka and posts it further to information consumers";
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClient.java b/pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClient.java
new file mode 100644 (file)
index 0000000..ac76436
--- /dev/null
@@ -0,0 +1,222 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.clients;
+
+import io.netty.channel.ChannelOption;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.timeout.ReadTimeoutHandler;
+import io.netty.handler.timeout.WriteTimeoutHandler;
+
+import java.lang.invoke.MethodHandles;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.oran.pmproducer.configuration.WebClientConfig.HttpProxyConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
+import org.springframework.lang.Nullable;
+import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
+import org.springframework.web.reactive.function.client.ExchangeStrategies;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
+
+import reactor.core.publisher.Mono;
+import reactor.netty.http.client.HttpClient;
+import reactor.netty.transport.ProxyProvider;
+
+/**
+ * Generic reactive REST client.
+ */
+public class AsyncRestClient {
+
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+    private WebClient webClient = null;
+    private final String baseUrl;
+    private static final AtomicInteger sequenceNumber = new AtomicInteger();
+    private final SslContext sslContext;
+    private final HttpProxyConfig httpProxyConfig;
+    private final SecurityContext securityContext;
+
+    public AsyncRestClient(String baseUrl, @Nullable SslContext sslContext, @Nullable HttpProxyConfig httpProxyConfig,
+            SecurityContext securityContext) {
+        this.baseUrl = baseUrl;
+        this.sslContext = sslContext;
+        this.httpProxyConfig = httpProxyConfig;
+        this.securityContext = securityContext;
+    }
+
+    @SuppressWarnings("java:S4449") // contentType, is not @Nullable
+    public Mono<ResponseEntity<String>> postForEntity(String uri, @Nullable String body,
+            @Nullable MediaType mediaType) {
+        Mono<String> bodyProducer = body != null ? Mono.just(body) : Mono.empty();
+
+        RequestHeadersSpec<?> request = getWebClient() //
+                .post() //
+                .uri(uri) //
+                .contentType(mediaType) //
+                .body(bodyProducer, String.class);
+        return retrieve(request);
+    }
+
+    public Mono<String> post(String uri, @Nullable String body, @Nullable MediaType mediaType) {
+        return postForEntity(uri, body, mediaType) //
+                .map(this::toBody);
+    }
+
+    public Mono<ResponseEntity<String>> putForEntity(String uri, String body) {
+        RequestHeadersSpec<?> request = getWebClient() //
+                .put() //
+                .uri(uri) //
+                .contentType(MediaType.APPLICATION_JSON) //
+                .bodyValue(body);
+        return retrieve(request);
+    }
+
+    public Mono<String> put(String uri, String body) {
+        return putForEntity(uri, body) //
+                .map(this::toBody);
+    }
+
+    public Mono<ResponseEntity<String>> getForEntity(String uri) {
+        RequestHeadersSpec<?> request = getWebClient().get().uri(uri);
+        return retrieve(request);
+    }
+
+    public Mono<String> get(String uri) {
+        return getForEntity(uri) //
+                .map(this::toBody);
+    }
+
+    public Mono<ResponseEntity<String>> deleteForEntity(String uri) {
+        RequestHeadersSpec<?> request = getWebClient().delete().uri(uri);
+        return retrieve(request);
+    }
+
+    public Mono<String> delete(String uri) {
+        return deleteForEntity(uri) //
+                .map(this::toBody);
+    }
+
+    public Mono<ResponseEntity<String>> postForEntity(String uri, @Nullable String body) {
+        Mono<String> bodyProducer = body != null ? Mono.just(body) : Mono.empty();
+
+        RequestHeadersSpec<?> request = getWebClient() //
+                .post() //
+                .uri(uri) //
+                .contentType(MediaType.APPLICATION_JSON) //
+                .body(bodyProducer, String.class);
+        return retrieve(request);
+    }
+
+    public Mono<String> post(String uri, @Nullable String body) {
+        return postForEntity(uri, body) //
+                .map(this::toBody);
+    }
+
+    private Mono<ResponseEntity<String>> retrieve(RequestHeadersSpec<?> request) {
+        if (securityContext.isConfigured()) {
+            request.headers(h -> h.setBearerAuth(securityContext.getBearerAuthToken()));
+        }
+        return request.retrieve() //
+                .toEntity(String.class) //
+                .doOnError(this::onError); //
+    }
+
+    private void onError(Throwable t) {
+        if (t instanceof WebClientResponseException) {
+            WebClientResponseException e = (WebClientResponseException) t;
+            logger.debug("Response error: {}", e.getResponseBodyAsString());
+        }
+    }
+
+    private static Object createTraceTag() {
+        return sequenceNumber.incrementAndGet();
+    }
+
+    private String toBody(ResponseEntity<String> entity) {
+        if (entity.getBody() == null) {
+            return "";
+        } else {
+            return entity.getBody();
+        }
+    }
+
+    private boolean isHttpProxyConfigured() {
+        return httpProxyConfig != null && httpProxyConfig.getHttpProxyPort() > 0
+                && !httpProxyConfig.getHttpProxyHost().isEmpty();
+    }
+
+    private HttpClient buildHttpClient() {
+        HttpClient httpClient = HttpClient.create() //
+                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000) //
+                .doOnConnected(connection -> {
+                    connection.addHandlerLast(new ReadTimeoutHandler(30));
+                    connection.addHandlerLast(new WriteTimeoutHandler(30));
+                });
+
+        if (this.sslContext != null) {
+            httpClient = httpClient.secure(ssl -> ssl.sslContext(sslContext));
+        }
+
+        if (isHttpProxyConfigured()) {
+            httpClient = httpClient.proxy(proxy -> proxy.type(ProxyProvider.Proxy.HTTP)
+                    .host(httpProxyConfig.getHttpProxyHost()).port(httpProxyConfig.getHttpProxyPort()));
+        }
+        return httpClient;
+    }
+
+    public WebClient buildWebClient(String baseUrl) {
+        Object traceTag = createTraceTag();
+
+        final HttpClient httpClient = buildHttpClient();
+        ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder() //
+                .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)) //
+                .build();
+
+        ExchangeFilterFunction reqLogger = ExchangeFilterFunction.ofRequestProcessor(req -> {
+            logger.debug("{} {} uri = '{}''", traceTag, req.method(), req.url());
+            return Mono.just(req);
+        });
+
+        ExchangeFilterFunction respLogger = ExchangeFilterFunction.ofResponseProcessor(resp -> {
+            logger.debug("{} resp: {}", traceTag, resp.statusCode());
+            return Mono.just(resp);
+        });
+
+        return WebClient.builder() //
+                .clientConnector(new ReactorClientHttpConnector(httpClient)) //
+                .baseUrl(baseUrl) //
+                .exchangeStrategies(exchangeStrategies) //
+                .filter(reqLogger) //
+                .filter(respLogger) //
+                .build();
+    }
+
+    private WebClient getWebClient() {
+        if (this.webClient == null) {
+            this.webClient = buildWebClient(baseUrl);
+        }
+        return this.webClient;
+    }
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClientFactory.java b/pmproducer/src/main/java/org/oran/pmproducer/clients/AsyncRestClientFactory.java
new file mode 100644 (file)
index 0000000..783da3a
--- /dev/null
@@ -0,0 +1,194 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.clients;
+
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.invoke.MethodHandles;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.net.ssl.KeyManagerFactory;
+
+import org.oran.pmproducer.configuration.WebClientConfig;
+import org.oran.pmproducer.configuration.WebClientConfig.HttpProxyConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.ResourceUtils;
+
+/**
+ * Factory for a generic reactive REST client.
+ */
+public class AsyncRestClientFactory {
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    private final SslContextFactory sslContextFactory;
+    private final HttpProxyConfig httpProxyConfig;
+    private final SecurityContext securityContext;
+
+    public AsyncRestClientFactory(WebClientConfig clientConfig, SecurityContext securityContext) {
+        if (clientConfig != null) {
+            this.sslContextFactory = new CachingSslContextFactory(clientConfig);
+            this.httpProxyConfig = clientConfig.getHttpProxyConfig();
+        } else {
+            logger.warn("No configuration for web client defined, HTTPS will not work");
+            this.sslContextFactory = null;
+            this.httpProxyConfig = null;
+        }
+        this.securityContext = securityContext;
+    }
+
+    public AsyncRestClient createRestClientNoHttpProxy(String baseUrl) {
+        return createRestClient(baseUrl, false);
+    }
+
+    public AsyncRestClient createRestClientUseHttpProxy(String baseUrl) {
+        return createRestClient(baseUrl, true);
+    }
+
+    private AsyncRestClient createRestClient(String baseUrl, boolean useHttpProxy) {
+        if (this.sslContextFactory != null) {
+            try {
+                return new AsyncRestClient(baseUrl, this.sslContextFactory.createSslContext(),
+                        useHttpProxy ? httpProxyConfig : null, this.securityContext);
+            } catch (Exception e) {
+                String exceptionString = e.toString();
+                logger.error("Could not init SSL context, reason: {}", exceptionString);
+            }
+        }
+        return new AsyncRestClient(baseUrl, null, httpProxyConfig, this.securityContext);
+    }
+
+    private class SslContextFactory {
+        private final WebClientConfig clientConfig;
+
+        public SslContextFactory(WebClientConfig clientConfig) {
+            this.clientConfig = clientConfig;
+        }
+
+        public SslContext createSslContext() throws UnrecoverableKeyException, NoSuchAlgorithmException,
+                CertificateException, KeyStoreException, IOException {
+            return this.createSslContext(createKeyManager());
+        }
+
+        private SslContext createSslContext(KeyManagerFactory keyManager)
+                throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
+            if (this.clientConfig.isTrustStoreUsed()) {
+                return createSslContextRejectingUntrustedPeers(this.clientConfig.getTrustStore(),
+                        this.clientConfig.getTrustStorePassword(), keyManager);
+            } else {
+                // Trust anyone
+                return SslContextBuilder.forClient() //
+                        .keyManager(keyManager) //
+                        .trustManager(InsecureTrustManagerFactory.INSTANCE) //
+                        .build();
+            }
+        }
+
+        private SslContext createSslContextRejectingUntrustedPeers(String trustStorePath, String trustStorePass,
+                KeyManagerFactory keyManager)
+                throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
+
+            final KeyStore trustStore = getTrustStore(trustStorePath, trustStorePass);
+            List<Certificate> certificateList = Collections.list(trustStore.aliases()).stream() //
+                    .filter(alias -> isCertificateEntry(trustStore, alias)) //
+                    .map(alias -> getCertificate(trustStore, alias)) //
+                    .collect(Collectors.toList());
+            final X509Certificate[] certificates = certificateList.toArray(new X509Certificate[certificateList.size()]);
+
+            return SslContextBuilder.forClient() //
+                    .keyManager(keyManager) //
+                    .trustManager(certificates) //
+                    .build();
+        }
+
+        private boolean isCertificateEntry(KeyStore trustStore, String alias) {
+            try {
+                return trustStore.isCertificateEntry(alias);
+            } catch (KeyStoreException e) {
+                logger.error("Error reading truststore {}", e.getMessage());
+                return false;
+            }
+        }
+
+        private Certificate getCertificate(KeyStore trustStore, String alias) {
+            try {
+                return trustStore.getCertificate(alias);
+            } catch (KeyStoreException e) {
+                logger.error("Error reading truststore {}", e.getMessage());
+                return null;
+            }
+        }
+
+        private KeyManagerFactory createKeyManager() throws NoSuchAlgorithmException, CertificateException, IOException,
+                UnrecoverableKeyException, KeyStoreException {
+            final KeyManagerFactory keyManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+            final KeyStore keyStore = KeyStore.getInstance(this.clientConfig.getKeyStoreType());
+            final String keyStoreFile = this.clientConfig.getKeyStore();
+            final String keyStorePassword = this.clientConfig.getKeyStorePassword();
+            final String keyPassword = this.clientConfig.getKeyPassword();
+            try (final InputStream inputStream = new FileInputStream(keyStoreFile)) {
+                keyStore.load(inputStream, keyStorePassword.toCharArray());
+            }
+            keyManager.init(keyStore, keyPassword.toCharArray());
+            return keyManager;
+        }
+
+        private synchronized KeyStore getTrustStore(String trustStorePath, String trustStorePass)
+                throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
+
+            KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
+            store.load(new FileInputStream(ResourceUtils.getFile(trustStorePath)), trustStorePass.toCharArray());
+            return store;
+        }
+    }
+
+    public class CachingSslContextFactory extends SslContextFactory {
+        private SslContext cachedContext = null;
+
+        public CachingSslContextFactory(WebClientConfig clientConfig) {
+            super(clientConfig);
+        }
+
+        @Override
+        public SslContext createSslContext() throws UnrecoverableKeyException, NoSuchAlgorithmException,
+                CertificateException, KeyStoreException, IOException {
+            if (this.cachedContext == null) {
+                this.cachedContext = super.createSslContext();
+            }
+            return this.cachedContext;
+
+        }
+    }
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/clients/SecurityContext.java b/pmproducer/src/main/java/org/oran/pmproducer/clients/SecurityContext.java
new file mode 100644 (file)
index 0000000..5b77447
--- /dev/null
@@ -0,0 +1,76 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.clients;
+
+import java.lang.invoke.MethodHandles;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import lombok.Setter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@EnableConfigurationProperties
+@ConfigurationProperties()
+@Component
+public class SecurityContext {
+
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    private long tokenTimestamp = 0;
+
+    private String authToken = "";
+
+    @Setter
+    private Path authTokenFilePath;
+
+    public SecurityContext(@Value("${app.auth-token-file:}") String authTokenFilename) {
+        if (!authTokenFilename.isEmpty()) {
+            this.authTokenFilePath = Path.of(authTokenFilename);
+        }
+    }
+
+    public boolean isConfigured() {
+        return authTokenFilePath != null;
+    }
+
+    public synchronized String getBearerAuthToken() {
+        if (!isConfigured()) {
+            return "";
+        }
+        try {
+            long lastModified = authTokenFilePath.toFile().lastModified();
+            if (tokenTimestamp == 0 || lastModified != this.tokenTimestamp) {
+                this.authToken = Files.readString(authTokenFilePath);
+                this.tokenTimestamp = lastModified;
+            }
+        } catch (Exception e) {
+            logger.warn("Could not read auth token file: {}, reason: {}", authTokenFilePath, e.getMessage());
+        }
+        return this.authToken;
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/configuration/ApplicationConfig.java b/pmproducer/src/main/java/org/oran/pmproducer/configuration/ApplicationConfig.java
new file mode 100644 (file)
index 0000000..2048b42
--- /dev/null
@@ -0,0 +1,177 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.configuration;
+
+import java.lang.invoke.MethodHandles;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Collections;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import org.oran.pmproducer.configuration.WebClientConfig.HttpProxyConfig;
+import org.oran.pmproducer.repository.InfoType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+
+@EnableConfigurationProperties
+@ToString
+public class ApplicationConfig {
+
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    @Getter
+    @Value("${app.configuration-filepath}")
+    private String localConfigurationFilePath;
+
+    @Value("${server.ssl.key-store-type}")
+    private String sslKeyStoreType = "";
+
+    @Value("${server.ssl.key-store-password}")
+    private String sslKeyStorePassword = "";
+
+    @Value("${server.ssl.key-store}")
+    private String sslKeyStore = "";
+
+    @Value("${server.ssl.key-password}")
+    private String sslKeyPassword = "";
+
+    @Value("${app.webclient.trust-store-used}")
+    private boolean sslTrustStoreUsed = false;
+
+    @Value("${app.webclient.trust-store-password}")
+    private String sslTrustStorePassword = "";
+
+    @Value("${app.webclient.trust-store}")
+    private String sslTrustStore = "";
+
+    @Value("${app.webclient.http.proxy-host:}")
+    private String httpProxyHost = "";
+
+    @Value("${app.webclient.http.proxy-port:0}")
+    private int httpProxyPort = 0;
+
+    @Getter
+    @Setter
+    @Value("${server.port}")
+    private int localServerHttpPort;
+
+    @Getter
+    @Value("${app.ics-base-url}")
+    private String icsBaseUrl;
+
+    @Getter
+    @Value("${app.pm_producer-base-url}")
+    private String selfUrl;
+
+    @Getter
+    @Value("${app.dmaap-base-url}")
+    private String dmaapBaseUrl;
+
+    @Getter
+    @Value("${app.kafka.bootstrap-servers:}")
+    private String kafkaBootStrapServers;
+
+    @Getter
+    @Value("${app.kafka.max-poll-records:300}")
+    private int kafkaMaxPollRecords;
+
+    @Getter
+    @Value("${app.pm-files-path:}")
+    private String pmFilesPath;
+
+    @Getter
+    @Value("${app.s3.endpointOverride:}")
+    private String s3EndpointOverride;
+
+    @Getter
+    @Value("${app.s3.accessKeyId:}")
+    private String s3AccessKeyId;
+
+    @Getter
+    @Value("${app.s3.secretAccessKey:}")
+    private String s3SecretAccessKey;
+
+    @Getter
+    @Value("${app.s3.locksBucket:}")
+    private String s3LocksBucket;
+
+    @Getter
+    @Value("${app.s3.bucket:}")
+    private String s3Bucket;
+
+    @Getter
+    @Setter
+    @Value("${app.zip-output:}")
+    private boolean zipOutput;
+
+    private WebClientConfig webClientConfig = null;
+
+    public WebClientConfig getWebClientConfig() {
+        if (this.webClientConfig == null) {
+            HttpProxyConfig httpProxyConfig = HttpProxyConfig.builder() //
+                    .httpProxyHost(this.httpProxyHost) //
+                    .httpProxyPort(this.httpProxyPort) //
+                    .build();
+
+            this.webClientConfig = WebClientConfig.builder() //
+                    .keyStoreType(this.sslKeyStoreType) //
+                    .keyStorePassword(this.sslKeyStorePassword) //
+                    .keyStore(this.sslKeyStore) //
+                    .keyPassword(this.sslKeyPassword) //
+                    .isTrustStoreUsed(this.sslTrustStoreUsed) //
+                    .trustStore(this.sslTrustStore) //
+                    .trustStorePassword(this.sslTrustStorePassword) //
+                    .httpProxyConfig(httpProxyConfig) //
+                    .build();
+        }
+        return this.webClientConfig;
+    }
+
+    public boolean isS3Enabled() {
+        return !(s3EndpointOverride.isBlank() || s3Bucket.isBlank());
+    }
+
+    // Adapter to parse the json format of the configuration file.
+    static class ConfigFile {
+        Collection<InfoType> types;
+    }
+
+    public Collection<InfoType> getTypes() {
+        com.google.gson.Gson gson = new com.google.gson.GsonBuilder().disableHtmlEscaping().create();
+        try {
+            String configJson = Files.readString(Path.of(getLocalConfigurationFilePath()), Charset.defaultCharset());
+            ConfigFile configData = gson.fromJson(configJson, ConfigFile.class);
+            return configData.types;
+        } catch (Exception e) {
+            logger.error("Could not load configuration file {}", getLocalConfigurationFilePath());
+            return Collections.emptyList();
+        }
+
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/configuration/WebClientConfig.java b/pmproducer/src/main/java/org/oran/pmproducer/configuration/WebClientConfig.java
new file mode 100644 (file)
index 0000000..bc6f395
--- /dev/null
@@ -0,0 +1,60 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.configuration;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.ToString;
+import reactor.netty.transport.ProxyProvider;
+
+@Builder
+@Getter
+@ToString
+public class WebClientConfig {
+    private String keyStoreType;
+
+    private String keyStorePassword;
+
+    private String keyStore;
+
+    @ToString.Exclude
+    private String keyPassword;
+
+    private boolean isTrustStoreUsed;
+
+    @ToString.Exclude
+    private String trustStorePassword;
+
+    private String trustStore;
+
+    @ToString
+    @Builder
+    @Getter
+    public static class HttpProxyConfig {
+        private String httpProxyHost;
+
+        private int httpProxyPort;
+
+        private ProxyProvider.Proxy httpProxyType;
+    }
+
+    private HttpProxyConfig httpProxyConfig;
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/controllers/ErrorResponse.java b/pmproducer/src/main/java/org/oran/pmproducer/controllers/ErrorResponse.java
new file mode 100644 (file)
index 0000000..8411181
--- /dev/null
@@ -0,0 +1,120 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.controllers;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.annotations.SerializedName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.lang.invoke.MethodHandles;
+
+import org.oran.pmproducer.exceptions.ServiceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import reactor.core.publisher.Mono;
+
+public class ErrorResponse {
+    private static Gson gson = new GsonBuilder() //
+            .disableHtmlEscaping() //
+            .create(); //
+
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    // Returned as body for all failed REST calls
+    @Schema(name = "error_information", description = "Problem as defined in https://tools.ietf.org/html/rfc7807")
+    public static class ErrorInfo {
+        @SerializedName("type")
+        private String type = "about:blank";
+
+        @SerializedName("title")
+        private String title = null;
+
+        @SerializedName("status")
+        private final Integer status;
+
+        @SerializedName("detail")
+        private String detail = null;
+
+        @SerializedName("instance")
+        private String instance = null;
+
+        public ErrorInfo(String detail, Integer status) {
+            this.detail = detail;
+            this.status = status;
+        }
+
+        @Schema(example = "503",
+                description = "The HTTP status code generated by the origin server for this occurrence of the problem. ")
+        public Integer getStatus() {
+            return status;
+        }
+
+        @Schema(example = "Policy type not found",
+                description = " A human-readable explanation specific to this occurrence of the problem.")
+        public String getDetail() {
+            return this.detail;
+        }
+
+    }
+
+    @Schema(name = "message", description = "message")
+    public final String message;
+
+    ErrorResponse(String message) {
+        this.message = message;
+    }
+
+    static Mono<ResponseEntity<Object>> createMono(String text, HttpStatus code) {
+        return Mono.just(create(text, code));
+    }
+
+    static Mono<ResponseEntity<Object>> createMono(Exception e, HttpStatus code) {
+        return createMono(e.toString(), code);
+    }
+
+    public static ResponseEntity<Object> create(String text, HttpStatus code) {
+        logger.debug("Error response: {}, {}", code, text);
+        ErrorInfo p = new ErrorInfo(text, code.value());
+        String json = gson.toJson(p);
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_PROBLEM_JSON);
+        return new ResponseEntity<>(json, headers, code);
+    }
+
+    public static ResponseEntity<Object> create(Throwable e, HttpStatus code) {
+        if (e instanceof RuntimeException) {
+            code = HttpStatus.INTERNAL_SERVER_ERROR;
+        } else if (e instanceof ServiceException) {
+            ServiceException se = (ServiceException) e;
+            if (se.getHttpStatus() != null) {
+                code = se.getHttpStatus();
+            }
+        }
+        return create(e.toString(), code);
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/controllers/ProducerCallbacksController.java b/pmproducer/src/main/java/org/oran/pmproducer/controllers/ProducerCallbacksController.java
new file mode 100644 (file)
index 0000000..c7bca34
--- /dev/null
@@ -0,0 +1,178 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.controllers;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.oran.pmproducer.exceptions.ServiceException;
+import org.oran.pmproducer.r1.ProducerJobInfo;
+import org.oran.pmproducer.repository.InfoTypes;
+import org.oran.pmproducer.repository.Job;
+import org.oran.pmproducer.repository.Jobs;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+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.RestController;
+
+@RestController("ConfigurationControllerV2")
+@Tag(name = ProducerCallbacksController.API_NAME)
+public class ProducerCallbacksController {
+    private static final Logger logger = LoggerFactory.getLogger(ProducerCallbacksController.class);
+
+    public static final String API_NAME = "Producer job control API";
+    public static final String API_DESCRIPTION = "";
+    public static final String JOB_URL = "/generic_dataproducer/info_job";
+    public static final String SUPERVISION_URL = "/generic_dataproducer/health_check";
+
+    public static final String STATISTICS_URL = "/statistics";
+
+    private static Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+    private final Jobs jobs;
+    private final InfoTypes types;
+
+    public ProducerCallbacksController(@Autowired Jobs jobs, @Autowired InfoTypes types) {
+        this.jobs = jobs;
+        this.types = types;
+    }
+
+    @PostMapping(path = JOB_URL, produces = MediaType.APPLICATION_JSON_VALUE)
+    @Operation(summary = "Callback for Information Job creation/modification",
+            description = "The call is invoked to activate or to modify a data subscription. The endpoint is provided by the Information Producer.")
+    @ApiResponses(value = { //
+            @ApiResponse(responseCode = "200", description = "OK", //
+                    content = @Content(schema = @Schema(implementation = VoidResponse.class))), //
+            @ApiResponse(responseCode = "404", description = "Information type is not found", //
+                    content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))), //
+            @ApiResponse(responseCode = "400", description = "Other error in the request", //
+                    content = @Content(schema = @Schema(implementation = ErrorResponse.ErrorInfo.class))) //
+    })
+    public ResponseEntity<Object> jobCreatedCallback( //
+            @RequestBody String body) {
+        try {
+            ProducerJobInfo request = gson.fromJson(body, ProducerJobInfo.class);
+            logger.debug("Job started callback id: {}, body: {}", request.id, body);
+            this.jobs.addJob(request.id, types.getType(request.typeId), request.owner, request.lastUpdated,
+                    toJobParameters(request.jobData));
+            return new ResponseEntity<>(HttpStatus.OK);
+        } catch (ServiceException e) {
+            logger.warn("jobCreatedCallback failed: {}", e.getMessage());
+            return ErrorResponse.create(e, e.getHttpStatus());
+        } catch (Exception e) {
+            logger.warn("jobCreatedCallback failed: {}", e.getMessage(), e);
+            return ErrorResponse.create(e, HttpStatus.BAD_REQUEST);
+        }
+    }
+
+    private Job.Parameters toJobParameters(Object jobData) {
+        String json = gson.toJson(jobData);
+        return gson.fromJson(json, Job.Parameters.class);
+    }
+
+    @GetMapping(path = JOB_URL, produces = MediaType.APPLICATION_JSON_VALUE)
+    @Operation(summary = "Get all jobs", description = "Returns all info jobs, can be used for trouble shooting")
+    @ApiResponse(responseCode = "200", //
+            description = "Information jobs", //
+            content = @Content(array = @ArraySchema(schema = @Schema(implementation = ProducerJobInfo.class)))) //
+    public ResponseEntity<Object> getJobs() {
+
+        Collection<ProducerJobInfo> producerJobs = new ArrayList<>();
+        for (Job j : this.jobs.getAll()) {
+            producerJobs
+                    .add(new ProducerJobInfo(null, j.getId(), j.getType().getId(), j.getOwner(), j.getLastUpdated()));
+        }
+        return new ResponseEntity<>(gson.toJson(producerJobs), HttpStatus.OK);
+    }
+
+    @DeleteMapping(path = JOB_URL + "/{infoJobId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    @Operation(summary = "Callback for Information Job deletion",
+            description = "The call is invoked to terminate a data subscription. The endpoint is provided by the Information Producer.")
+    @ApiResponses(value = { //
+            @ApiResponse(responseCode = "200", description = "OK", //
+                    content = @Content(schema = @Schema(implementation = VoidResponse.class))) //
+    })
+    public ResponseEntity<Object> jobDeletedCallback( //
+            @PathVariable("infoJobId") String infoJobId) {
+
+        logger.debug("Job deleted callback {}", infoJobId);
+        this.jobs.remove(infoJobId);
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @GetMapping(path = SUPERVISION_URL, produces = MediaType.APPLICATION_JSON_VALUE)
+    @Operation(summary = "Producer supervision",
+            description = "The endpoint is provided by the Information Producer and is used for supervision of the producer.")
+    @ApiResponses(value = { //
+            @ApiResponse(responseCode = "200", description = "The producer is OK", //
+                    content = @Content(schema = @Schema(implementation = String.class))) //
+    })
+    public ResponseEntity<Object> producerSupervision() {
+        logger.debug("Producer supervision");
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @Schema(name = "statistics_info", description = "Statistics information")
+    public class StatisticsCollection {
+
+        @Schema(description = "Statistics per job")
+        public final Collection<Job.Statistics> jobStatistics;
+
+        public StatisticsCollection(Collection<Job.Statistics> stats) {
+            this.jobStatistics = stats;
+        }
+    }
+
+    @GetMapping(path = STATISTICS_URL, produces = MediaType.APPLICATION_JSON_VALUE)
+    @Operation(summary = "Returns statistics", description = "")
+    @ApiResponses(value = { //
+            @ApiResponse(responseCode = "200", description = "OK", //
+                    content = @Content(schema = @Schema(implementation = StatisticsCollection.class))) //
+    })
+    public ResponseEntity<Object> getStatistics() {
+        List<Job.Statistics> res = new ArrayList<>();
+        for (Job job : this.jobs.getAll()) {
+            res.add(job.getStatistics());
+        }
+
+        return new ResponseEntity<>(gson.toJson(new StatisticsCollection(res)), HttpStatus.OK);
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/controllers/VoidResponse.java b/pmproducer/src/main/java/org/oran/pmproducer/controllers/VoidResponse.java
new file mode 100644 (file)
index 0000000..2a3e212
--- /dev/null
@@ -0,0 +1,28 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.controllers;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "void", description = "Void/empty")
+public class VoidResponse {
+    private VoidResponse() {}
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/datastore/DataStore.java b/pmproducer/src/main/java/org/oran/pmproducer/datastore/DataStore.java
new file mode 100644 (file)
index 0000000..dd1d8f3
--- /dev/null
@@ -0,0 +1,55 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.datastore;
+
+import java.nio.file.Path;
+
+import org.oran.pmproducer.configuration.ApplicationConfig;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public interface DataStore {
+    public enum Bucket {
+        FILES, LOCKS
+    }
+
+    public Flux<String> listObjects(Bucket bucket, String prefix);
+
+    public Mono<byte[]> readObject(Bucket bucket, String name);
+
+    public Mono<Boolean> createLock(String name);
+
+    public Mono<Boolean> deleteLock(String name);
+
+    public Mono<Boolean> deleteObject(Bucket bucket, String name);
+
+    public Mono<String> copyFileTo(Path from, String to);
+
+    public Mono<String> create(DataStore.Bucket bucket);
+
+    public Mono<String> deleteBucket(Bucket bucket);
+
+    public static DataStore create(ApplicationConfig config) {
+        return config.isS3Enabled() ? new S3ObjectStore(config) : new FileStore(config);
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/datastore/FileStore.java b/pmproducer/src/main/java/org/oran/pmproducer/datastore/FileStore.java
new file mode 100644 (file)
index 0000000..d2d2ef1
--- /dev/null
@@ -0,0 +1,157 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.datastore;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.oran.pmproducer.configuration.ApplicationConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.FileSystemUtils;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+class FileStore implements DataStore {
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    ApplicationConfig applicationConfig;
+
+    public FileStore(ApplicationConfig applicationConfig) {
+        this.applicationConfig = applicationConfig;
+    }
+
+    @Override
+    public Flux<String> listObjects(Bucket bucket, String prefix) {
+        Path root = Path.of(applicationConfig.getPmFilesPath(), prefix);
+        if (!root.toFile().exists()) {
+            root = root.getParent();
+        }
+
+        logger.debug("Listing files in: {}", root);
+
+        List<String> result = new ArrayList<>();
+        try (Stream<Path> stream = Files.walk(root, Integer.MAX_VALUE)) {
+
+            stream.forEach(path -> filterListFiles(path, prefix, result));
+
+            return Flux.fromIterable(result);
+        } catch (Exception e) {
+            return Flux.error(e);
+        }
+    }
+
+    private void filterListFiles(Path path, String prefix, List<String> result) {
+        if (path.toFile().isFile() && externalName(path).startsWith(prefix)) {
+            result.add(externalName(path));
+        } else {
+            logger.debug("Ignoring file {} that does not start with: {}", path, prefix);
+        }
+    }
+
+    private String externalName(Path path) {
+        String fullName = path.toString();
+        String externalName = fullName.substring(applicationConfig.getPmFilesPath().length());
+        if (externalName.startsWith("/")) {
+            externalName = externalName.substring(1);
+        }
+        return externalName;
+    }
+
+    @Override
+    public Mono<byte[]> readObject(Bucket bucket, String fileName) {
+        try {
+            byte[] contents = Files.readAllBytes(path(fileName));
+            return Mono.just(contents);
+        } catch (Exception e) {
+            return Mono.error(e);
+        }
+    }
+
+    @Override
+    public Mono<Boolean> createLock(String name) {
+        File file = path(name).toFile();
+        try {
+            Files.createDirectories(path(name).getParent());
+            boolean res = file.createNewFile();
+            return Mono.just(res);
+        } catch (Exception e) {
+            logger.warn("Could not create lock file: {}, reason: {}", file.getPath(), e.getMessage());
+            return Mono.just(!file.exists());
+        }
+    }
+
+    @Override
+    public Mono<String> copyFileTo(Path from, String to) {
+        try {
+            Path toPath = path(to);
+            Files.createDirectories(toPath);
+            Files.copy(from, path(to), StandardCopyOption.REPLACE_EXISTING);
+            return Mono.just(to);
+        } catch (Exception e) {
+            return Mono.error(e);
+        }
+    }
+
+    @Override
+    public Mono<Boolean> deleteLock(String name) {
+        return deleteObject(Bucket.LOCKS, name);
+    }
+
+    @Override
+    public Mono<Boolean> deleteObject(Bucket bucket, String name) {
+        try {
+            Files.delete(path(name));
+            return Mono.just(true);
+        } catch (Exception e) {
+            return Mono.just(false);
+        }
+    }
+
+    @Override
+    public Mono<String> create(Bucket bucket) {
+        return Mono.just("OK");
+    }
+
+    private Path path(String name) {
+        return Path.of(applicationConfig.getPmFilesPath(), name);
+    }
+
+    @Override
+    public Mono<String> deleteBucket(Bucket bucket) {
+        try {
+            FileSystemUtils.deleteRecursively(Path.of(applicationConfig.getPmFilesPath()));
+        } catch (IOException e) {
+            logger.debug("Could not delete directory: {}, reason; {}", applicationConfig.getPmFilesPath(),
+                    e.getMessage());
+        }
+        return Mono.just("OK");
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/datastore/S3ObjectStore.java b/pmproducer/src/main/java/org/oran/pmproducer/datastore/S3ObjectStore.java
new file mode 100644 (file)
index 0000000..be30969
--- /dev/null
@@ -0,0 +1,270 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.datastore;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.util.concurrent.CompletableFuture;
+
+import org.oran.pmproducer.configuration.ApplicationConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.core.BytesWrapper;
+import software.amazon.awssdk.core.ResponseBytes;
+import software.amazon.awssdk.core.async.AsyncRequestBody;
+import software.amazon.awssdk.core.async.AsyncResponseTransformer;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.s3.S3AsyncClient;
+import software.amazon.awssdk.services.s3.S3AsyncClientBuilder;
+import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
+import software.amazon.awssdk.services.s3.model.CreateBucketResponse;
+import software.amazon.awssdk.services.s3.model.DeleteBucketRequest;
+import software.amazon.awssdk.services.s3.model.DeleteBucketResponse;
+import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
+import software.amazon.awssdk.services.s3.model.DeleteObjectResponse;
+import software.amazon.awssdk.services.s3.model.GetObjectRequest;
+import software.amazon.awssdk.services.s3.model.GetObjectResponse;
+import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
+import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
+import software.amazon.awssdk.services.s3.model.ListObjectsRequest;
+import software.amazon.awssdk.services.s3.model.ListObjectsResponse;
+import software.amazon.awssdk.services.s3.model.PutObjectRequest;
+import software.amazon.awssdk.services.s3.model.PutObjectResponse;
+import software.amazon.awssdk.services.s3.model.S3Object;
+
+class S3ObjectStore implements DataStore {
+    private static final Logger logger = LoggerFactory.getLogger(S3ObjectStore.class);
+    private final ApplicationConfig applicationConfig;
+
+    private static S3AsyncClient s3AsynchClient;
+
+    public S3ObjectStore(ApplicationConfig applicationConfig) {
+        this.applicationConfig = applicationConfig;
+
+        getS3AsynchClient(applicationConfig);
+    }
+
+    private static synchronized S3AsyncClient getS3AsynchClient(ApplicationConfig applicationConfig) {
+        if (applicationConfig.isS3Enabled() && s3AsynchClient == null) {
+            s3AsynchClient = getS3AsyncClientBuilder(applicationConfig).build();
+        }
+        return s3AsynchClient;
+    }
+
+    private static S3AsyncClientBuilder getS3AsyncClientBuilder(ApplicationConfig applicationConfig) {
+        URI uri = URI.create(applicationConfig.getS3EndpointOverride());
+        return S3AsyncClient.builder() //
+                .region(Region.US_EAST_1) //
+                .endpointOverride(uri) //
+                .credentialsProvider(StaticCredentialsProvider.create( //
+                        AwsBasicCredentials.create(applicationConfig.getS3AccessKeyId(), //
+                                applicationConfig.getS3SecretAccessKey())));
+    }
+
+    @Override
+    public Flux<String> listObjects(Bucket bucket, String prefix) {
+        return listObjectsInBucket(bucket(bucket), prefix).map(S3Object::key);
+    }
+
+    @Override
+    public Mono<Boolean> createLock(String name) {
+        return getHeadObject(bucket(Bucket.LOCKS), name).flatMap(head -> createLock(name, head)) //
+                .onErrorResume(t -> createLock(name, null));
+    }
+
+    private Mono<Boolean> createLock(String name, HeadObjectResponse head) {
+        if (head == null) {
+
+            return this.putObject(Bucket.LOCKS, name, "") //
+                    .flatMap(resp -> Mono.just(true)) //
+                    .doOnError(t -> logger.warn("Failed to create lock {}, reason: {}", name, t.getMessage())) //
+                    .onErrorResume(t -> Mono.just(false));
+        } else {
+            return Mono.just(false);
+        }
+    }
+
+    @Override
+    public Mono<Boolean> deleteLock(String name) {
+        return deleteObject(Bucket.LOCKS, name);
+    }
+
+    @Override
+    public Mono<Boolean> deleteObject(Bucket bucket, String name) {
+
+        DeleteObjectRequest request = DeleteObjectRequest.builder() //
+                .bucket(bucket(bucket)) //
+                .key(name) //
+                .build();
+
+        CompletableFuture<DeleteObjectResponse> future = s3AsynchClient.deleteObject(request);
+
+        return Mono.fromFuture(future).map(resp -> true);
+    }
+
+    @Override
+    public Mono<byte[]> readObject(Bucket bucket, String fileName) {
+        return getDataFromS3Object(bucket(bucket), fileName);
+    }
+
+    public Mono<String> putObject(Bucket bucket, String fileName, String bodyString) {
+        PutObjectRequest request = PutObjectRequest.builder() //
+                .bucket(bucket(bucket)) //
+                .key(fileName) //
+                .build();
+
+        AsyncRequestBody body = AsyncRequestBody.fromString(bodyString);
+
+        CompletableFuture<PutObjectResponse> future = s3AsynchClient.putObject(request, body);
+
+        return Mono.fromFuture(future) //
+                .map(putObjectResponse -> fileName) //
+                .doOnError(t -> logger.error("Failed to store file in S3 {}", t.getMessage()));
+    }
+
+    @Override
+    public Mono<String> copyFileTo(Path fromFile, String toFile) {
+        return copyFileToS3Bucket(bucket(Bucket.FILES), fromFile, toFile);
+    }
+
+    @Override
+    public Mono<String> create(Bucket bucket) {
+        return createS3Bucket(bucket(bucket));
+    }
+
+    private Mono<String> createS3Bucket(String s3Bucket) {
+
+        CreateBucketRequest request = CreateBucketRequest.builder() //
+                .bucket(s3Bucket) //
+                .build();
+
+        CompletableFuture<CreateBucketResponse> future = s3AsynchClient.createBucket(request);
+
+        return Mono.fromFuture(future) //
+                .map(f -> s3Bucket) //
+                .doOnError(t -> logger.trace("Could not create S3 bucket: {}", t.getMessage()))
+                .onErrorResume(t -> Mono.just(s3Bucket));
+    }
+
+    @Override
+    public Mono<String> deleteBucket(Bucket bucket) {
+        return listObjects(bucket, "") //
+                .flatMap(key -> deleteObject(bucket, key)) //
+                .collectList() //
+                .flatMap(list -> deleteBucketFromS3Storage(bucket)) //
+                .map(resp -> "OK")
+                .doOnError(t -> logger.warn("Could not delete bucket: {}, reason: {}", bucket(bucket), t.getMessage()))
+                .onErrorResume(t -> Mono.just("NOK"));
+    }
+
+    private Mono<DeleteBucketResponse> deleteBucketFromS3Storage(Bucket bucket) {
+        DeleteBucketRequest request = DeleteBucketRequest.builder() //
+                .bucket(bucket(bucket)) //
+                .build();
+
+        CompletableFuture<DeleteBucketResponse> future = s3AsynchClient.deleteBucket(request);
+
+        return Mono.fromFuture(future);
+    }
+
+    private String bucket(Bucket bucket) {
+        return bucket == Bucket.FILES ? applicationConfig.getS3Bucket() : applicationConfig.getS3LocksBucket();
+    }
+
+    private Mono<String> copyFileToS3Bucket(String s3Bucket, Path fileName, String s3Key) {
+
+        PutObjectRequest request = PutObjectRequest.builder() //
+                .bucket(s3Bucket) //
+                .key(s3Key) //
+                .build();
+
+        AsyncRequestBody body = AsyncRequestBody.fromFile(fileName);
+
+        CompletableFuture<PutObjectResponse> future = s3AsynchClient.putObject(request, body);
+
+        return Mono.fromFuture(future) //
+                .map(f -> s3Key) //
+                .doOnError(t -> logger.error("Failed to store file in S3 {}", t.getMessage()));
+
+    }
+
+    private Mono<HeadObjectResponse> getHeadObject(String bucket, String key) {
+        HeadObjectRequest request = HeadObjectRequest.builder().bucket(bucket).key(key).build();
+
+        CompletableFuture<HeadObjectResponse> future = s3AsynchClient.headObject(request);
+        return Mono.fromFuture(future);
+    }
+
+    private Mono<ListObjectsResponse> listObjectsRequest(String bucket, String prefix,
+            ListObjectsResponse prevResponse) {
+        ListObjectsRequest.Builder builder = ListObjectsRequest.builder() //
+                .bucket(bucket) //
+                .maxKeys(1000) //
+                .prefix(prefix);
+
+        if (prevResponse != null) {
+            if (Boolean.TRUE.equals(prevResponse.isTruncated())) {
+                builder.marker(prevResponse.nextMarker());
+            } else {
+                return Mono.empty();
+            }
+        }
+
+        ListObjectsRequest listObjectsRequest = builder.build();
+        CompletableFuture<ListObjectsResponse> future = s3AsynchClient.listObjects(listObjectsRequest);
+        return Mono.fromFuture(future);
+    }
+
+    private Flux<S3Object> listObjectsInBucket(String bucket, String prefix) {
+
+        return listObjectsRequest(bucket, prefix, null) //
+                .expand(response -> listObjectsRequest(bucket, prefix, response)) //
+                .map(ListObjectsResponse::contents) //
+                .doOnNext(f -> logger.debug("Found objects in {}: {}", bucket, f.size())) //
+                .doOnError(t -> logger.warn("Error fromlist objects: {}", t.getMessage())) //
+                .flatMap(Flux::fromIterable) //
+                .doOnNext(obj -> logger.debug("Found object: {}", obj.key()));
+    }
+
+    private Mono<byte[]> getDataFromS3Object(String bucket, String key) {
+
+        GetObjectRequest request = GetObjectRequest.builder() //
+                .bucket(bucket) //
+                .key(key) //
+                .build();
+
+        CompletableFuture<ResponseBytes<GetObjectResponse>> future =
+                s3AsynchClient.getObject(request, AsyncResponseTransformer.toBytes());
+
+        return Mono.fromFuture(future) //
+                .map(BytesWrapper::asByteArray) //
+                .doOnError(t -> logger.error("Failed to get file from S3, key:{}, bucket: {}, {}", key, bucket,
+                        t.getMessage())) //
+                .doOnNext(n -> logger.debug("Read file from S3: {} {}", bucket, key)) //
+                .onErrorResume(t -> Mono.empty());
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/exceptions/ServiceException.java b/pmproducer/src/main/java/org/oran/pmproducer/exceptions/ServiceException.java
new file mode 100644 (file)
index 0000000..55e2a48
--- /dev/null
@@ -0,0 +1,39 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.exceptions;
+
+import lombok.Getter;
+
+import org.springframework.http.HttpStatus;
+
+public class ServiceException extends Exception {
+
+    private static final long serialVersionUID = 1L;
+
+    @Getter
+    private final HttpStatus httpStatus;
+
+    public ServiceException(String message, HttpStatus httpStatus) {
+        super(message);
+        this.httpStatus = httpStatus;
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/filter/FilterFactory.java b/pmproducer/src/main/java/org/oran/pmproducer/filter/FilterFactory.java
new file mode 100644 (file)
index 0000000..85133de
--- /dev/null
@@ -0,0 +1,54 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.filter;
+
+import com.google.gson.GsonBuilder;
+
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FilterFactory {
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+    private static com.google.gson.Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+
+    private FilterFactory() {}
+
+    public static PmReportFilter create(Object filter) {
+        return new PmReportFilter(createPmFilterData(filter));
+    }
+
+    public static PmReportFilter createAggregateFilter(Collection<PmReportFilter> filters) {
+        PmReportFilter.FilterData resultFilterData = filters.iterator().next().getFilterData();
+        for (PmReportFilter filter : filters) {
+            resultFilterData.addAll(filter.getFilterData());
+        }
+        return new PmReportFilter(resultFilterData);
+    }
+
+    private static PmReportFilter.FilterData createPmFilterData(Object filter) {
+        String str = gson.toJson(filter);
+        return gson.fromJson(str, PmReportFilter.FilterData.class);
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/filter/FilteredData.java b/pmproducer/src/main/java/org/oran/pmproducer/filter/FilteredData.java
new file mode 100644 (file)
index 0000000..a2348ca
--- /dev/null
@@ -0,0 +1,75 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.filter;
+
+import java.util.ArrayList;
+
+import lombok.Getter;
+import lombok.ToString;
+
+import org.apache.kafka.common.header.Header;
+import org.apache.kafka.common.header.internals.RecordHeader;
+import org.oran.pmproducer.tasks.TopicListener.DataFromTopic;
+
+@ToString
+public class FilteredData {
+    public final byte[] key;
+    public final byte[] value;
+    public final String infoTypeId;
+
+    @Getter
+    private final boolean isZipped;
+
+    private static final FilteredData emptyData = new FilteredData(null, null, null);
+
+    public boolean isEmpty() {
+        return (key == null || key.length == 0) && (value == null || value.length == 0);
+    }
+
+    public FilteredData(String type, byte[] key, byte[] value) {
+        this(type, key, value, false);
+    }
+
+    public FilteredData(String type, byte[] key, byte[] value, boolean isZipped) {
+        this.key = key;
+        this.value = value;
+        this.isZipped = isZipped;
+        this.infoTypeId = type;
+    }
+
+    public String getValueAString() {
+        return value == null ? "" : new String(this.value);
+    }
+
+    public static FilteredData empty() {
+        return emptyData;
+    }
+
+    public Iterable<Header> headers() {
+        ArrayList<Header> result = new ArrayList<>();
+        if (isZipped()) {
+            result.add(new RecordHeader(DataFromTopic.ZIPPED_PROPERTY, null));
+        }
+        result.add(new RecordHeader(DataFromTopic.TYPE_ID_PROPERTY, infoTypeId.getBytes()));
+        return result;
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/filter/PmReport.java b/pmproducer/src/main/java/org/oran/pmproducer/filter/PmReport.java
new file mode 100644 (file)
index 0000000..35d426e
--- /dev/null
@@ -0,0 +1,176 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.filter;
+
+import com.google.gson.annotations.Expose;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+@Builder(toBuilder = true)
+public class PmReport {
+
+    @Expose
+    public Event event;
+
+    public static class CommonEventHeader {
+        @Expose
+        private String domain;
+
+        @Expose
+        private String eventId;
+
+        @Expose
+        private String eventName;
+
+        @Expose
+        @Getter
+        private String sourceName;
+
+        @Expose
+        private String reportingEntityName;
+
+        @Expose
+        private long startEpochMicrosec;
+
+        @Expose
+        private long lastEpochMicrosec;
+
+        @Expose
+        private String timeZoneOffset;
+
+        /* Not reported elements */
+        int sequence;
+        String priority;
+        String version;
+        String vesEventListenerVersion;
+    }
+
+    public static class MeasInfoId {
+        @Expose
+        private String sMeasInfoId = "";
+    }
+
+    public static class MeasTypes {
+        public String getMeasType(int pValue) {
+            if (pValue > sMeasTypesList.size()) {
+                return "MeasTypeIndexOutOfBounds:" + pValue;
+            }
+            return sMeasTypesList.get(pValue - 1);
+        }
+
+        @Expose
+        protected ArrayList<String> sMeasTypesList = new ArrayList<>();
+    }
+
+    @Getter
+    @Builder(toBuilder = true)
+    public static class MeasResult {
+        @Expose
+        @Setter
+        private int p;
+
+        @Expose
+        private String sValue;
+    }
+
+    @Builder(toBuilder = true)
+    public static class MeasValuesList {
+        @Expose
+        @Getter
+        private String measObjInstId;
+
+        @Expose
+        private String suspectFlag;
+
+        @Expose
+        @Getter
+        private Collection<MeasResult> measResults;
+
+        public boolean isEmpty() {
+            return this.measResults.isEmpty();
+        }
+
+        static MeasValuesList emptyList = MeasValuesList.builder().measResults(new ArrayList<>()).build();
+
+        public static MeasValuesList empty() {
+            return emptyList;
+        }
+    }
+
+    @Getter
+    @Builder(toBuilder = true)
+    public static class MeasInfoList {
+        @Expose
+        private MeasInfoId measInfoId;
+
+        @Expose
+        private MeasTypes measTypes;
+
+        @Expose
+        private Collection<MeasValuesList> measValuesList;
+
+    }
+
+    @Builder(toBuilder = true)
+    @Getter
+    public static class MeasDataCollection {
+        @Expose
+        private int granularityPeriod;
+
+        @Expose
+        private String measuredEntityUserName;
+
+        @Expose
+        private String measuredEntityDn;
+
+        @Expose
+        private String measuredEntitySoftwareVersion;
+
+        @Expose
+        private Collection<MeasInfoList> measInfoList;
+    }
+
+    @Builder(toBuilder = true)
+    @Getter
+    public static class Perf3gppFields {
+        @Expose
+        private String perf3gppFieldsVersion;
+
+        @Expose
+        private MeasDataCollection measDataCollection;
+    }
+
+    @Getter
+    @Builder(toBuilder = true)
+    public static class Event {
+        @Expose
+        private CommonEventHeader commonEventHeader;
+
+        @Expose
+        private Perf3gppFields perf3gppFields;
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/filter/PmReportFilter.java b/pmproducer/src/main/java/org/oran/pmproducer/filter/PmReportFilter.java
new file mode 100644 (file)
index 0000000..26bdf58
--- /dev/null
@@ -0,0 +1,364 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.filter;
+
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import org.oran.pmproducer.filter.PmReport.Event;
+import org.oran.pmproducer.filter.PmReport.MeasDataCollection;
+import org.oran.pmproducer.filter.PmReport.MeasInfoList;
+import org.oran.pmproducer.filter.PmReport.MeasResult;
+import org.oran.pmproducer.filter.PmReport.MeasTypes;
+import org.oran.pmproducer.filter.PmReport.MeasValuesList;
+import org.oran.pmproducer.filter.PmReport.Perf3gppFields;
+import org.oran.pmproducer.tasks.TopicListener.DataFromTopic;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.thymeleaf.util.StringUtils;
+
+public class PmReportFilter {
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    private static com.google.gson.Gson gson = new com.google.gson.GsonBuilder() //
+            .disableHtmlEscaping() //
+            .excludeFieldsWithoutExposeAnnotation() //
+            .create();
+
+    // excludeFieldsWithoutExposeAnnotation is not needed when parsing and this is a
+    // bit quicker
+    private static com.google.gson.Gson gsonParse = new com.google.gson.GsonBuilder() //
+            .disableHtmlEscaping() //
+            .create();
+
+    @Getter
+    private final FilterData filterData;
+
+    @Getter
+    public static class FilterData {
+
+        public static class MeasTypeSpec {
+            static MeasTypeSpec empty = new MeasTypeSpec();
+
+            static MeasTypeSpec empty() {
+                return empty;
+            }
+
+            @Getter
+            String measuredObjClass;
+
+            @Getter
+            final Set<String> measTypes = new HashSet<>();
+
+            @Override
+            public boolean equals(Object obj) {
+                return measuredObjClass.equals(obj);
+            }
+
+            @Override
+            public int hashCode() {
+                return measuredObjClass.hashCode();
+            }
+        }
+
+        final Set<String> sourceNames = new HashSet<>();
+        final Set<String> measObjInstIds = new HashSet<>();
+        final Collection<MeasTypeSpec> measTypeSpecs = new ArrayList<>();
+        final Set<String> measuredEntityDns = new HashSet<>();
+
+        public void addMeasTypes(String measObjClass, String... measTypes) {
+            MeasTypeSpec spec = this.findMeasTypeSpec(measObjClass);
+            if (spec == null) {
+                spec = new MeasTypeSpec();
+                spec.measuredObjClass = measObjClass;
+                this.measTypeSpecs.add(spec);
+            }
+            for (String measType : measTypes) {
+                spec.measTypes.add(measType);
+            }
+        }
+
+        public void addMeasTypes(String measObjClass, Collection<String> measTypes) {
+            for (String measType : measTypes) {
+                addMeasTypes(measObjClass, measType);
+            }
+        }
+
+        @Setter
+        String pmRopStartTime;
+
+        @Setter
+        String pmRopEndTime;
+
+        public void addAll(FilterData other) {
+            addAll(other.sourceNames, sourceNames);
+            addAll(other.measObjInstIds, measObjInstIds);
+            addAll(other.measTypeSpecs);
+            addAll(other.measuredEntityDns, measuredEntityDns);
+        }
+
+        public MeasTypeSpec getMeasTypeSpec(String measuredObjClass) {
+            if (measTypeSpecs.isEmpty()) {
+                return MeasTypeSpec.empty();
+            }
+            return findMeasTypeSpec(measuredObjClass);
+        }
+
+        private MeasTypeSpec findMeasTypeSpec(String measuredObjClass) {
+            for (MeasTypeSpec t : this.measTypeSpecs) {
+                if (t.measuredObjClass.equals(measuredObjClass)) {
+                    return t;
+                }
+            }
+            return null;
+        }
+
+        private void addAll(Collection<MeasTypeSpec> measTypes) {
+            for (MeasTypeSpec s : measTypes) {
+                addMeasTypes(s.getMeasuredObjClass(), s.getMeasTypes());
+            }
+        }
+
+        private void addAll(Set<String> source, Set<String> dst) {
+            if (source.isEmpty()) {
+                dst.clear();
+            } else if (dst.isEmpty()) {
+                // Nothing, this means 'match all'
+            } else {
+                dst.addAll(source);
+            }
+        }
+    }
+
+    public static PmReport parse(String string) {
+        return gsonParse.fromJson(string, PmReport.class);
+    }
+
+    private static class MeasTypesIndexed extends PmReport.MeasTypes {
+
+        private Map<String, Integer> map = new HashMap<>();
+
+        public int addP(String measTypeName) {
+            Integer p = map.get(measTypeName);
+            if (p != null) {
+                return p;
+            } else {
+                sMeasTypesList.add(measTypeName);
+                this.map.put(measTypeName, sMeasTypesList.size());
+                return sMeasTypesList.size();
+            }
+        }
+    }
+
+    public PmReportFilter(FilterData filterData) {
+        this.filterData = filterData;
+    }
+
+    public FilteredData filter(DataFromTopic data) {
+        try {
+            PmReport report = getPmReport(data);
+
+            if (report.event == null || report.event.getPerf3gppFields() == null) {
+                logger.warn("Received PM report with no perf3gppFields, ignored. {}", data);
+                return FilteredData.empty();
+            }
+
+            PmReport reportFiltered = filter(report, this.filterData);
+            if (reportFiltered == null) {
+                return FilteredData.empty();
+            }
+            return new FilteredData(data.infoTypeId, data.key, gson.toJson(reportFiltered).getBytes());
+        } catch (Exception e) {
+            logger.warn("Could not parse PM data. {}, reason: {}", data, e.getMessage());
+            return FilteredData.empty();
+        }
+    }
+
+    @SuppressWarnings("java:S2445") // "data" is a method parameter, and should not be used for synchronization.
+    private PmReport getPmReport(DataFromTopic data) {
+        synchronized (data) {
+            if (data.getCachedPmReport() == null) {
+                data.setCachedPmReport(parse((data.valueAsString())));
+            }
+            return data.getCachedPmReport();
+        }
+    }
+
+    /**
+     * Updates the report based on the filter data.
+     *
+     * @param report
+     * @param filterData
+     * @return true if there is anything left in the report
+     */
+    private PmReport filter(PmReport report, FilterData filterData) {
+        if (!matchSourceNames(report, filterData.sourceNames)) {
+            return null;
+        }
+
+        Collection<MeasInfoList> filteredMeasObjs = createMeasObjInstIds(report, filterData);
+        if (filteredMeasObjs.isEmpty()) {
+            return null;
+        }
+        MeasDataCollection measDataCollection = report.event.getPerf3gppFields().getMeasDataCollection().toBuilder() //
+                .measInfoList(filteredMeasObjs) //
+                .build();
+
+        Perf3gppFields perf3gppFields =
+                report.event.getPerf3gppFields().toBuilder().measDataCollection(measDataCollection) //
+                        .build();
+        Event event = report.event.toBuilder() //
+                .perf3gppFields(perf3gppFields) //
+                .build();
+
+        return report.toBuilder() //
+                .event(event) //
+                .build();
+    }
+
+    private boolean isContainedInAny(String aString, Collection<String> collection) {
+        for (String s : collection) {
+            if (StringUtils.contains(aString, s) == Boolean.TRUE) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isMeasResultMatch(MeasResult measResult, MeasTypes measTypes,
+            FilterData.MeasTypeSpec measTypesSpec) {
+        String measType = measTypes.getMeasType(measResult.getP());
+        return measTypesSpec.measTypes.isEmpty() || measTypesSpec.measTypes.contains(measType);
+    }
+
+    private Collection<MeasResult> createMeasResults(Collection<MeasResult> oldMeasResults, MeasTypes measTypes,
+            FilterData.MeasTypeSpec measTypesSpec) {
+        Collection<MeasResult> newMeasResults = new ArrayList<>();
+
+        for (MeasResult measResult : oldMeasResults) {
+            if (isMeasResultMatch(measResult, measTypes, measTypesSpec)) {
+                newMeasResults.add(measResult.toBuilder().build());
+            }
+        }
+        return newMeasResults;
+    }
+
+    private boolean isMeasInstIdMatch(String measObjInstId, FilterData filter) {
+        return filter.measObjInstIds.isEmpty() || isContainedInAny(measObjInstId, filter.measObjInstIds);
+    }
+
+    private String managedObjectClass(String distinguishedName) {
+        int lastRdn = distinguishedName.lastIndexOf(",");
+        if (lastRdn == -1) {
+            return "";
+        }
+        int lastEqualChar = distinguishedName.indexOf("=", lastRdn);
+        if (lastEqualChar == -1) {
+            return "";
+        }
+        return distinguishedName.substring(lastRdn + 1, lastEqualChar);
+    }
+
+    private FilterData.MeasTypeSpec getMeasTypeSpec(String measObjInstId, FilterData filter) {
+        String measObjClass = managedObjectClass(measObjInstId);
+        return filter.getMeasTypeSpec(measObjClass);
+    }
+
+    private MeasValuesList createMeasValuesList(MeasValuesList oldMeasValues, MeasTypes measTypes, FilterData filter) {
+        FilterData.MeasTypeSpec measTypesSpec = getMeasTypeSpec(oldMeasValues.getMeasObjInstId(), filter);
+        if (measTypesSpec == null) {
+            return MeasValuesList.empty();
+        }
+
+        if (!isMeasInstIdMatch(oldMeasValues.getMeasObjInstId(), filter)) {
+            return MeasValuesList.empty();
+        }
+
+        Collection<MeasResult> newResults = createMeasResults(oldMeasValues.getMeasResults(), measTypes, measTypesSpec);
+        return oldMeasValues.toBuilder() //
+                .measResults(newResults) //
+                .build();
+    }
+
+    private MeasTypes createMeasTypes(Collection<MeasValuesList> newMeasValues, MeasTypes oldMMeasTypes) {
+        MeasTypesIndexed newMeasTypes = new MeasTypesIndexed();
+        for (MeasValuesList l : newMeasValues) {
+            for (MeasResult r : l.getMeasResults()) {
+                String measTypeName = oldMMeasTypes.getMeasType(r.getP());
+                int newP = newMeasTypes.addP(measTypeName);
+                r.setP(newP);
+            }
+        }
+        return newMeasTypes;
+    }
+
+    private MeasInfoList createMeasInfoList(MeasInfoList oldMeasInfoList, FilterData filter) {
+
+        Collection<MeasValuesList> measValueLists = new ArrayList<>();
+        for (MeasValuesList oldValues : oldMeasInfoList.getMeasValuesList()) {
+            MeasValuesList newMeasValues = createMeasValuesList(oldValues, oldMeasInfoList.getMeasTypes(), filter);
+            if (!newMeasValues.isEmpty()) {
+                measValueLists.add(newMeasValues);
+            }
+        }
+
+        MeasTypes newMeasTypes = createMeasTypes(measValueLists, oldMeasInfoList.getMeasTypes());
+
+        return oldMeasInfoList.toBuilder() //
+                .measTypes(newMeasTypes).measValuesList(measValueLists) //
+                .build();
+
+    }
+
+    private boolean matchMeasuredEntityDns(PmReport report, FilterData filter) {
+        return filter.measuredEntityDns.isEmpty()
+                || this.isContainedInAny(report.event.getPerf3gppFields().getMeasDataCollection().getMeasuredEntityDn(),
+                        filter.measuredEntityDns);
+    }
+
+    private Collection<MeasInfoList> createMeasObjInstIds(PmReport report, FilterData filter) {
+        Collection<MeasInfoList> newList = new ArrayList<>();
+        if (!matchMeasuredEntityDns(report, filter)) {
+            return newList;
+        }
+        for (MeasInfoList oldMeasInfoList : report.event.getPerf3gppFields().getMeasDataCollection()
+                .getMeasInfoList()) {
+            MeasInfoList l = createMeasInfoList(oldMeasInfoList, filter);
+            if (!l.getMeasValuesList().isEmpty()) {
+                newList.add(l);
+            }
+        }
+        return newList;
+    }
+
+    private boolean matchSourceNames(PmReport report, Collection<String> sourceNames) {
+        return sourceNames.isEmpty() || sourceNames.contains(report.event.getCommonEventHeader().getSourceName());
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/r1/ConsumerJobInfo.java b/pmproducer/src/main/java/org/oran/pmproducer/r1/ConsumerJobInfo.java
new file mode 100644 (file)
index 0000000..c89aaa5
--- /dev/null
@@ -0,0 +1,65 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.r1;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.gson.annotations.SerializedName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "consumer_job", description = "Information for an Information Job")
+public class ConsumerJobInfo {
+
+    @Schema(name = "info_type_id", description = "Information type Idenitifier of the subscription job")
+    @SerializedName("info_type_id")
+    @JsonProperty(value = "info_type_id", required = true)
+    public String infoTypeId = "";
+
+    @Schema(name = "job_owner", description = "Identity of the owner of the job")
+    @SerializedName("job_owner")
+    @JsonProperty(value = "job_owner", required = true)
+    public String owner = "";
+
+    @Schema(name = "job_definition", description = "Information type specific job data")
+    @SerializedName("job_definition")
+    @JsonProperty(value = "job_definition", required = true)
+    public Object jobDefinition;
+
+    @Schema(name = "job_result_uri", description = "The target URI of the subscribed information")
+    @SerializedName("job_result_uri")
+    @JsonProperty(value = "job_result_uri", required = true)
+    public String jobResultUri = "";
+
+    @Schema(name = "status_notification_uri",
+            description = "The target of Information subscription job status notifications")
+    @SerializedName("status_notification_uri")
+    @JsonProperty(value = "status_notification_uri", required = false)
+    public String statusNotificationUri = "";
+
+    public ConsumerJobInfo() {}
+
+    public ConsumerJobInfo(String infoTypeId, Object jobData, String owner, String statusNotificationUri) {
+        this.infoTypeId = infoTypeId;
+        this.jobDefinition = jobData;
+        this.owner = owner;
+        this.statusNotificationUri = statusNotificationUri;
+    }
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerInfoTypeInfo.java b/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerInfoTypeInfo.java
new file mode 100644 (file)
index 0000000..500081e
--- /dev/null
@@ -0,0 +1,49 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.r1;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.gson.annotations.SerializedName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "producer_info_type_info", description = "Information for an Information Type")
+public class ProducerInfoTypeInfo {
+
+    @Schema(name = "info_job_data_schema", description = "Json schema for the job data", required = true)
+    @SerializedName("info_job_data_schema")
+    @JsonProperty(value = "info_job_data_schema", required = true)
+    public Object jobDataSchema;
+
+    @Schema(name = "info_type_information", description = "Type specific information for the information type",
+            required = true)
+    @SerializedName("info_type_information")
+    @JsonProperty(value = "info_type_information", required = true)
+    public Object typeSpecificInformation;
+
+    public ProducerInfoTypeInfo(Object jobDataSchema, Object typeSpecificInformation) {
+        this.jobDataSchema = jobDataSchema;
+        this.typeSpecificInformation = typeSpecificInformation;
+    }
+
+    public ProducerInfoTypeInfo() {}
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerJobInfo.java b/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerJobInfo.java
new file mode 100644 (file)
index 0000000..f421e1b
--- /dev/null
@@ -0,0 +1,67 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.r1;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.gson.annotations.SerializedName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Schema(name = "producer_info_job_request",
+        description = "The body of the Information Producer callbacks for Information Job creation and deletion")
+public class ProducerJobInfo {
+
+    @Schema(name = "info_job_identity", description = "Identity of the Information Job", required = true)
+    @SerializedName("info_job_identity")
+    @JsonProperty("info_job_identity")
+    public String id = "";
+
+    @Schema(name = "info_type_identity", description = "Type identity for the job")
+    @SerializedName("info_type_identity")
+    @JsonProperty("info_type_identity")
+    public String typeId = "";
+
+    @Schema(name = "info_job_data", description = "Json for the job data")
+    @SerializedName("info_job_data")
+    @JsonProperty("info_job_data")
+    public Object jobData;
+
+    @Schema(name = "owner", description = "The owner of the job")
+    @SerializedName("owner")
+    @JsonProperty("owner")
+    public String owner = "";
+
+    @Schema(name = "last_updated", description = "The time when the job was last updated or created (ISO-8601)")
+    @SerializedName("last_updated")
+    @JsonProperty("last_updated")
+    public String lastUpdated = "";
+
+    public ProducerJobInfo(Object jobData, String id, String typeId, String owner, String lastUpdated) {
+        this.id = id;
+        this.jobData = jobData;
+        this.typeId = typeId;
+        this.owner = owner;
+        this.lastUpdated = lastUpdated;
+    }
+
+    public ProducerJobInfo() {}
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerRegistrationInfo.java b/pmproducer/src/main/java/org/oran/pmproducer/r1/ProducerRegistrationInfo.java
new file mode 100644 (file)
index 0000000..502abb6
--- /dev/null
@@ -0,0 +1,61 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.r1;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.gson.annotations.SerializedName;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.util.Collection;
+
+import lombok.Builder;
+
+@Builder
+@Schema(name = "producer_registration_info", description = "Information for an Information Producer")
+public class ProducerRegistrationInfo {
+
+    @Schema(name = "supported_info_types", description = "Supported Information Type IDs", required = true)
+    @SerializedName("supported_info_types")
+    @JsonProperty(value = "supported_info_types", required = true)
+    public Collection<String> supportedTypeIds;
+
+    @Schema(name = "info_job_callback_url", description = "callback for Information Job", required = true)
+    @SerializedName("info_job_callback_url")
+    @JsonProperty(value = "info_job_callback_url", required = true)
+    public String jobCallbackUrl;
+
+    @Schema(name = "info_producer_supervision_callback_url", description = "callback for producer supervision",
+            required = true)
+    @SerializedName("info_producer_supervision_callback_url")
+    @JsonProperty(value = "info_producer_supervision_callback_url", required = true)
+    public String producerSupervisionCallbackUrl;
+
+    public ProducerRegistrationInfo(Collection<String> types, String jobCallbackUrl,
+            String producerSupervisionCallbackUrl) {
+        this.supportedTypeIds = types;
+        this.jobCallbackUrl = jobCallbackUrl;
+        this.producerSupervisionCallbackUrl = producerSupervisionCallbackUrl;
+    }
+
+    public ProducerRegistrationInfo() {}
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/repository/InfoType.java b/pmproducer/src/main/java/org/oran/pmproducer/repository/InfoType.java
new file mode 100644 (file)
index 0000000..7caea9f
--- /dev/null
@@ -0,0 +1,57 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.repository;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.ToString;
+
+import org.oran.pmproducer.configuration.ApplicationConfig;
+
+@ToString
+@Builder
+public class InfoType {
+
+    @Getter
+    private String id;
+
+    @Getter
+    @Builder.Default
+    private boolean useHttpProxy = false;
+
+    @Getter
+    private String kafkaInputTopic;
+
+    @Getter
+    private String inputJobType;
+
+    @Getter
+    private Object inputJobDefinition;
+
+    public String getKafkaGroupId() {
+        return this.kafkaInputTopic == null ? null : "osc-pm_producer-" + getId();
+    }
+
+    public String getKafkaClientId(ApplicationConfig appConfig) {
+        return this.kafkaInputTopic == null ? null : getId() + "_" + appConfig.getSelfUrl();
+
+    }
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/repository/InfoTypes.java b/pmproducer/src/main/java/org/oran/pmproducer/repository/InfoTypes.java
new file mode 100644 (file)
index 0000000..cb14725
--- /dev/null
@@ -0,0 +1,80 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.repository;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import org.oran.pmproducer.exceptions.ServiceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+
+public class InfoTypes {
+    private static final Logger logger = LoggerFactory.getLogger(InfoTypes.class);
+
+    private Map<String, InfoType> allTypes = new HashMap<>();
+
+    public InfoTypes(Collection<InfoType> types) {
+        for (InfoType type : types) {
+            put(type);
+        }
+    }
+
+    public synchronized InfoType get(String id) {
+        return allTypes.get(id);
+    }
+
+    public synchronized InfoType getType(String id) throws ServiceException {
+        InfoType type = allTypes.get(id);
+        if (type == null) {
+            throw new ServiceException("Could not find type: " + id, HttpStatus.NOT_FOUND);
+        }
+        return type;
+    }
+
+    public static class ConfigFile {
+        Collection<InfoType> types;
+    }
+
+    private synchronized void put(InfoType type) {
+        logger.debug("Put type: {}", type.getId());
+        allTypes.put(type.getId(), type);
+    }
+
+    public synchronized Iterable<InfoType> getAll() {
+        return new Vector<>(allTypes.values());
+    }
+
+    public synchronized Collection<String> typeIds() {
+        return allTypes.keySet();
+    }
+
+    public synchronized int size() {
+        return allTypes.size();
+    }
+
+    public synchronized void clear() {
+        allTypes.clear();
+    }
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/repository/Job.java b/pmproducer/src/main/java/org/oran/pmproducer/repository/Job.java
new file mode 100644 (file)
index 0000000..e3c0e8a
--- /dev/null
@@ -0,0 +1,166 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.repository;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.lang.invoke.MethodHandles;
+
+import lombok.Builder;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+
+import org.oran.pmproducer.configuration.ApplicationConfig;
+import org.oran.pmproducer.filter.FilterFactory;
+import org.oran.pmproducer.filter.FilteredData;
+import org.oran.pmproducer.filter.PmReportFilter;
+import org.oran.pmproducer.tasks.TopicListener.DataFromTopic;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ToString
+public class Job {
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    @Builder
+    @Getter
+    @Schema(name = "job_statistics", description = "Statistics information for one job")
+    public static class Statistics {
+
+        @JsonProperty(value = "jobId", required = true)
+        String jobId;
+
+        @JsonProperty(value = "typeId", required = true)
+        String typeId;
+
+        @JsonProperty(value = "inputTopic", required = false)
+        String inputTopic;
+
+        @JsonProperty(value = "outputTopic", required = false)
+        String outputTopic;
+
+        @JsonProperty(value = "groupId", required = false)
+        String groupId;
+
+        @JsonProperty(value = "clientId", required = false)
+        String clientId;
+
+        @JsonProperty(value = "noOfReceivedObjects", required = true)
+        @Builder.Default
+        long noOfReceivedObjects = 0;
+
+        @JsonProperty(value = "noOfReceivedBytes", required = true)
+        @Builder.Default
+        long noOfReceivedBytes = 0;
+
+        @JsonProperty(value = "noOfSentObjects", required = true)
+        @Builder.Default
+        long noOfSentObjects = 0;
+
+        @JsonProperty(value = "noOfSentBytes", required = true)
+        @Builder.Default
+        long noOfSentBytes = 0;
+
+        public void received(byte[] bytes) {
+            noOfReceivedBytes += bytes.length;
+            noOfReceivedObjects += 1;
+
+        }
+
+        public void filtered(byte[] bytes) {
+            noOfSentBytes += bytes.length;
+            noOfSentObjects += 1;
+        }
+
+    }
+
+    @Builder
+    public static class Parameters {
+
+        @Getter
+        private PmReportFilter.FilterData filter;
+
+        @Builder
+        @EqualsAndHashCode
+        public static class KafkaDeliveryInfo {
+            @Getter
+            private String topic;
+
+            @Getter
+            private String bootStrapServers;
+        }
+
+        @Getter
+        private KafkaDeliveryInfo deliveryInfo;
+    }
+
+    @Getter
+    private final String id;
+
+    @Getter
+    private final InfoType type;
+
+    @Getter
+    private final String owner;
+
+    @Getter
+    private final Parameters parameters;
+
+    @Getter
+    private final String lastUpdated;
+
+    @Getter
+    private final PmReportFilter filter;
+
+    @Getter
+    private final Statistics statistics;
+
+    public Job(String id, InfoType type, String owner, String lastUpdated, Parameters parameters,
+            ApplicationConfig appConfig) {
+        this.id = id;
+        this.type = type;
+        this.owner = owner;
+        this.lastUpdated = lastUpdated;
+        this.parameters = parameters;
+        filter = parameters.filter == null ? null : FilterFactory.create(parameters.getFilter());
+
+        statistics = Statistics.builder() //
+                .groupId(type.getKafkaGroupId()) //
+                .inputTopic(type.getKafkaInputTopic()) //
+                .jobId(id) //
+                .outputTopic(parameters.getDeliveryInfo() == null ? "" : parameters.getDeliveryInfo().topic) //
+                .typeId(type.getId()) //
+                .clientId(type.getKafkaClientId(appConfig)) //
+                .build();
+
+    }
+
+    public FilteredData filter(DataFromTopic data) {
+        if (filter == null) {
+            logger.debug("No filter used");
+            return new FilteredData(data.infoTypeId, data.key, data.value);
+        }
+        return filter.filter(data);
+    }
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/repository/Jobs.java b/pmproducer/src/main/java/org/oran/pmproducer/repository/Jobs.java
new file mode 100644 (file)
index 0000000..5b257bf
--- /dev/null
@@ -0,0 +1,224 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import lombok.Getter;
+
+import org.oran.pmproducer.clients.AsyncRestClientFactory;
+import org.oran.pmproducer.clients.SecurityContext;
+import org.oran.pmproducer.configuration.ApplicationConfig;
+import org.oran.pmproducer.exceptions.ServiceException;
+import org.oran.pmproducer.filter.FilterFactory;
+import org.oran.pmproducer.filter.FilteredData;
+import org.oran.pmproducer.filter.PmReportFilter;
+import org.oran.pmproducer.repository.Job.Parameters;
+import org.oran.pmproducer.repository.Job.Parameters.KafkaDeliveryInfo;
+import org.oran.pmproducer.tasks.TopicListener.DataFromTopic;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+
+@Component
+public class Jobs {
+    public interface Observer {
+        void onJobbGroupAdded(JobGroup jobGroup);
+
+        void onJobGroupRemoved(JobGroup jobGroup);
+    }
+
+    public static class JobGroup {
+        @Getter
+        private final KafkaDeliveryInfo deliveryInfo;
+
+        private Map<String, Job> jobs = new HashMap<>();
+
+        @Getter
+        private PmReportFilter filter;
+
+        @Getter
+        private final InfoType type;
+
+        public JobGroup(InfoType type, KafkaDeliveryInfo deliveryInfo) {
+            this.deliveryInfo = deliveryInfo;
+            this.type = type;
+        }
+
+        public synchronized void add(Job job) {
+            this.jobs.put(job.getId(), job);
+            this.filter = createFilter();
+        }
+
+        public synchronized void remove(Job job) {
+            this.jobs.remove(job.getId());
+            if (!this.jobs.isEmpty()) {
+                this.filter = createFilter();
+            }
+        }
+
+        public boolean isEmpty() {
+            return jobs.isEmpty();
+        }
+
+        public FilteredData filter(DataFromTopic data) {
+            return filter.filter(data);
+        }
+
+        public Job getAJob() {
+            if (this.jobs.isEmpty()) {
+                return null;
+            }
+            return this.jobs.values().iterator().next();
+        }
+
+        private PmReportFilter createFilter() {
+            Collection<PmReportFilter> filterData = new ArrayList<>();
+            this.jobs.forEach((key, value) -> filterData.add(value.getFilter()));
+            return FilterFactory.createAggregateFilter(filterData);
+        }
+
+        public String getId() {
+            return deliveryInfo.getTopic();
+        }
+
+        public Iterable<Job> getJobs() {
+            return this.jobs.values();
+        }
+    }
+
+    private static final Logger logger = LoggerFactory.getLogger(Jobs.class);
+
+    private Map<String, Job> allJobs = new HashMap<>();
+    private MultiMap<Job> jobsByType = new MultiMap<>();
+    private Map<String, JobGroup> jobGroups = new HashMap<>(); // Key is Topic
+    private final AsyncRestClientFactory restclientFactory;
+    private final List<Observer> observers = new ArrayList<>();
+    private final ApplicationConfig appConfig;
+
+    public Jobs(@Autowired ApplicationConfig applicationConfig, @Autowired SecurityContext securityContext,
+            @Autowired ApplicationConfig appConfig) {
+        restclientFactory = new AsyncRestClientFactory(applicationConfig.getWebClientConfig(), securityContext);
+        this.appConfig = appConfig;
+    }
+
+    public synchronized Job getJob(String id) throws ServiceException {
+        Job job = allJobs.get(id);
+        if (job == null) {
+            throw new ServiceException("Could not find job: " + id, HttpStatus.NOT_FOUND);
+        }
+        return job;
+    }
+
+    public synchronized Job get(String id) {
+        return allJobs.get(id);
+    }
+
+    public void addJob(String id, InfoType type, String owner, String lastUpdated, Parameters parameters)
+            throws ServiceException {
+
+        Job job = new Job(id, type, owner, lastUpdated, parameters, this.appConfig);
+        this.put(job);
+    }
+
+    public void addObserver(Observer obs) {
+        synchronized (observers) {
+            this.observers.add(obs);
+        }
+    }
+
+    private String jobGroupId(Job job) {
+        return job.getParameters().getDeliveryInfo().getTopic();
+    }
+
+    private synchronized void put(Job job) {
+        logger.debug("Put job: {}", job.getId());
+        remove(job.getId());
+
+        allJobs.put(job.getId(), job);
+        jobsByType.put(job.getType().getId(), job.getId(), job);
+
+        String jobGroupId = jobGroupId(job);
+        if (!this.jobGroups.containsKey(jobGroupId)) {
+            final JobGroup group = new JobGroup(job.getType(), job.getParameters().getDeliveryInfo());
+            this.jobGroups.put(jobGroupId, group);
+            group.add(job);
+            this.observers.forEach(obs -> obs.onJobbGroupAdded(group));
+        } else {
+            JobGroup group = this.jobGroups.get(jobGroupId);
+            group.add(job);
+        }
+    }
+
+    public synchronized Iterable<Job> getAll() {
+        return new Vector<>(allJobs.values());
+    }
+
+    public synchronized Job remove(String id) {
+        Job job = allJobs.get(id);
+        if (job != null) {
+            remove(job);
+        }
+        return job;
+    }
+
+    public void remove(Job job) {
+        String groupId = jobGroupId(job);
+        JobGroup group = this.jobGroups.get(groupId);
+        synchronized (this) {
+            this.allJobs.remove(job.getId());
+            jobsByType.remove(job.getType().getId(), job.getId());
+            group.remove(job);
+            if (group.isEmpty()) {
+                this.jobGroups.remove(groupId);
+            }
+        }
+
+        if (group.isEmpty()) {
+            this.observers.forEach(obs -> obs.onJobGroupRemoved(group));
+        }
+    }
+
+    public synchronized int size() {
+        return allJobs.size();
+    }
+
+    public synchronized Collection<Job> getJobsForType(InfoType type) {
+        return jobsByType.get(type.getId());
+    }
+
+    public void clear() {
+
+        this.jobGroups.forEach((id, group) -> this.observers.forEach(obs -> obs.onJobGroupRemoved(group)));
+
+        synchronized (this) {
+            allJobs.clear();
+            jobsByType.clear();
+        }
+    }
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/repository/MultiMap.java b/pmproducer/src/main/java/org/oran/pmproducer/repository/MultiMap.java
new file mode 100644 (file)
index 0000000..a4e8511
--- /dev/null
@@ -0,0 +1,87 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019-2021 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+/**
+ * A map, where each key can be bound to may values (where each value has an own
+ * ID)
+ */
+public class MultiMap<T> {
+
+    private final Map<String, Map<String, T>> map = new HashMap<>();
+
+    public void put(String key, String id, T value) {
+        this.map.computeIfAbsent(key, k -> new HashMap<>()).put(id, value);
+    }
+
+    public T remove(String key, String id) {
+        Map<String, T> innerMap = this.map.get(key);
+        if (innerMap != null) {
+            T removedElement = innerMap.remove(id);
+            if (innerMap.isEmpty()) {
+                this.map.remove(key);
+            }
+            return removedElement;
+        }
+        return null;
+    }
+
+    public T get(String key1, String key2) {
+        Map<String, T> innerMap = this.map.get(key1);
+        if (innerMap == null) {
+            return null;
+        }
+        return innerMap.get(key2);
+    }
+
+    public Collection<T> get(String key) {
+        Map<String, T> innerMap = this.map.get(key);
+        if (innerMap == null) {
+            return Collections.emptyList();
+        }
+        return new Vector<>(innerMap.values());
+    }
+
+    public Set<String> keySet() {
+        return this.map.keySet();
+    }
+
+    public Collection<T> values() {
+        ArrayList<T> result = new ArrayList<>();
+        for (String key : keySet()) {
+            result.addAll(get(key));
+        }
+        return result;
+    }
+
+    public void clear() {
+        this.map.clear();
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/tasks/JobDataDistributor.java b/pmproducer/src/main/java/org/oran/pmproducer/tasks/JobDataDistributor.java
new file mode 100644 (file)
index 0000000..d9f6632
--- /dev/null
@@ -0,0 +1,331 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.tasks;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.GZIPOutputStream;
+
+import lombok.Getter;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.apache.kafka.common.serialization.ByteArraySerializer;
+import org.oran.pmproducer.configuration.ApplicationConfig;
+import org.oran.pmproducer.datastore.DataStore;
+import org.oran.pmproducer.filter.FilteredData;
+import org.oran.pmproducer.filter.PmReportFilter;
+import org.oran.pmproducer.repository.Job.Parameters.KafkaDeliveryInfo;
+import org.oran.pmproducer.repository.Jobs.JobGroup;
+import org.oran.pmproducer.tasks.TopicListener.DataFromTopic;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import reactor.core.Disposable;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.kafka.sender.KafkaSender;
+import reactor.kafka.sender.SenderOptions;
+import reactor.kafka.sender.SenderRecord;
+
+/**
+ * The class streams data from a multi cast sink and sends the data to the Job
+ * owner via REST calls.
+ */
+@SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
+public class JobDataDistributor {
+    private static final Logger logger = LoggerFactory.getLogger(JobDataDistributor.class);
+
+    @Getter
+    private final JobGroup jobGroup;
+    private Disposable subscription;
+    private final ErrorStats errorStats = new ErrorStats();
+
+    private final DataStore dataStore;
+    private static com.google.gson.Gson gson = new com.google.gson.GsonBuilder().disableHtmlEscaping().create();
+    private final ApplicationConfig applConfig;
+
+    private KafkaSender<byte[], byte[]> sender;
+
+    private class ErrorStats {
+        @Getter
+        private int consumerFaultCounter = 0;
+
+        public void handleOkFromConsumer() {
+            this.consumerFaultCounter = 0;
+        }
+
+        public void handleException(Throwable t) {
+            ++this.consumerFaultCounter;
+        }
+    }
+
+    public JobDataDistributor(JobGroup jobGroup, ApplicationConfig applConfig) {
+        this.applConfig = applConfig;
+        this.jobGroup = jobGroup;
+        this.dataStore = DataStore.create(applConfig);
+        this.dataStore.create(DataStore.Bucket.FILES).subscribe();
+        this.dataStore.create(DataStore.Bucket.LOCKS).subscribe();
+
+        SenderOptions<byte[], byte[]> senderOptions = senderOptions(applConfig, jobGroup.getDeliveryInfo());
+        this.sender = KafkaSender.create(senderOptions);
+    }
+
+    public void start(Flux<TopicListener.DataFromTopic> input) {
+        logger.debug("Starting distribution, to topic: {}", jobGroup.getId());
+        PmReportFilter filter = jobGroup.getFilter();
+        if (filter == null || filter.getFilterData().getPmRopEndTime() == null) {
+            this.subscription = filter(input, this.jobGroup) //
+                    .flatMap(this::sendToClient) //
+                    .onErrorResume(this::handleError) //
+                    .subscribe(this::handleSentOk, //
+                            this::handleExceptionInStream, //
+                            () -> logger.warn("JobDataDistributor stopped jobId: {}", jobGroup.getId()));
+        }
+
+        if (filter != null && filter.getFilterData().getPmRopStartTime() != null) {
+            this.dataStore.createLock(collectHistoricalDataLockName()) //
+                    .doOnNext(isLockGranted -> {
+                        if (isLockGranted.booleanValue()) {
+                            logger.debug("Checking historical PM ROP files, jobId: {}", this.jobGroup.getId());
+                        } else {
+                            logger.debug("Skipping check of historical PM ROP files, already done. jobId: {}",
+                                    this.jobGroup.getId());
+                        }
+                    }) //
+                    .filter(isLockGranted -> isLockGranted) //
+                    .flatMapMany(b -> Flux.fromIterable(filter.getFilterData().getSourceNames())) //
+                    .doOnNext(sourceName -> logger.debug("Checking source name: {}, jobId: {}", sourceName,
+                            this.jobGroup.getId())) //
+                    .flatMap(sourceName -> dataStore.listObjects(DataStore.Bucket.FILES, sourceName), 1) //
+                    .filter(this::isRopFile) //
+                    .filter(fileName -> filterStartTime(filter.getFilterData(), fileName)) //
+                    .filter(fileName -> filterEndTime(filter.getFilterData(), fileName)) //
+                    .map(this::createFakeEvent) //
+                    .flatMap(data -> TopicListener.getDataFromFileIfNewPmFileEvent(data, this.jobGroup.getType(),
+                            dataStore), 100)
+                    .map(jobGroup::filter) //
+                    .map(this::gzip) //
+                    .flatMap(this::sendToClient, 1) //
+                    .onErrorResume(this::handleCollectHistoricalDataError) //
+                    .doFinally(sig -> sendLastStoredRecord()) //
+                    .subscribe();
+        }
+    }
+
+    private static SenderOptions<byte[], byte[]> senderOptions(ApplicationConfig config,
+            KafkaDeliveryInfo deliveryInfo) {
+
+        String bootstrapServers = deliveryInfo.getBootStrapServers();
+        if (bootstrapServers == null || bootstrapServers.isEmpty()) {
+            bootstrapServers = config.getKafkaBootStrapServers();
+        }
+
+        Map<String, Object> props = new HashMap<>();
+        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
+        props.put(ProducerConfig.ACKS_CONFIG, "all");
+        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class);
+        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class);
+        return SenderOptions.create(props);
+    }
+
+    private void sendLastStoredRecord() {
+        String data = "{}";
+        FilteredData output = new FilteredData(this.jobGroup.getType().getId(), null, data.getBytes());
+
+        sendToClient(output).subscribe();
+    }
+
+    private FilteredData gzip(FilteredData data) {
+        if (this.applConfig.isZipOutput()) {
+            try {
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                GZIPOutputStream gzip = new GZIPOutputStream(out);
+                gzip.write(data.value);
+                gzip.flush();
+                gzip.close();
+                byte[] zipped = out.toByteArray();
+                return new FilteredData(data.infoTypeId, data.key, zipped, true);
+            } catch (IOException e) {
+                logger.error("Unexpected exception when zipping: {}", e.getMessage());
+                return data;
+            }
+        } else {
+            return data;
+        }
+    }
+
+    private Mono<String> handleCollectHistoricalDataError(Throwable t) {
+        logger.error("Exception: {} job: {}", t.getMessage(), jobGroup.getId());
+        return tryDeleteLockFile() //
+                .map(bool -> "OK");
+    }
+
+    private String collectHistoricalDataLockName() {
+        return "collectHistoricalDataLock" + this.jobGroup.getId();
+    }
+
+    private DataFromTopic createFakeEvent(String fileName) {
+        NewFileEvent ev = new NewFileEvent(fileName);
+        return new DataFromTopic(this.jobGroup.getType().getId(), null, null, gson.toJson(ev).getBytes());
+    }
+
+    private static String fileTimePartFromRopFileName(String fileName) {
+        // "O-DU-1122/A20000626.2315+0200-2330+0200_HTTPS-6-73.json"
+        return fileName.substring(fileName.lastIndexOf("/") + 2);
+    }
+
+    private static boolean filterStartTime(PmReportFilter.FilterData filter, String fileName) {
+        try {
+            OffsetDateTime fileStartTime = getStartTimeFromFileName(fileName);
+            OffsetDateTime startTime = OffsetDateTime.parse(filter.getPmRopStartTime());
+            boolean isMatch = fileStartTime.isAfter(startTime);
+            logger.debug("Checking file: {}, fileStartTime: {}, filterStartTime: {}, isAfter: {}", fileName,
+                    fileStartTime, startTime, isMatch);
+            return isMatch;
+        } catch (Exception e) {
+            logger.warn("Time parsing exception: {}", e.getMessage());
+            return false;
+        }
+    }
+
+    private boolean isRopFile(String fileName) {
+        return fileName.endsWith(".json") || fileName.endsWith(".json.gz");
+    }
+
+    private static boolean filterEndTime(PmReportFilter.FilterData filter, String fileName) {
+        if (filter.getPmRopEndTime() == null) {
+            return true;
+        }
+        try {
+            OffsetDateTime fileEndTime = getEndTimeFromFileName(fileName);
+            OffsetDateTime endTime = OffsetDateTime.parse(filter.getPmRopEndTime());
+            boolean isMatch = fileEndTime.isBefore(endTime);
+            logger.debug("Checking file: {}, fileEndTime: {}, endTime: {}, isBefore: {}", fileName, fileEndTime,
+                    endTime, isMatch);
+            return isMatch;
+
+        } catch (Exception e) {
+            logger.warn("Time parsing exception: {}", e.getMessage());
+            return false;
+        }
+    }
+
+    private static OffsetDateTime getStartTimeFromFileName(String fileName) {
+        String fileTimePart = fileTimePartFromRopFileName(fileName);
+        // A20000626.2315+0200-2330+0200_HTTPS-6-73.json
+        fileTimePart = fileTimePart.substring(0, 18);
+        return parseFileDate(fileTimePart);
+    }
+
+    private static OffsetDateTime getEndTimeFromFileName(String fileName) {
+        String fileTimePart = fileTimePartFromRopFileName(fileName);
+        // A20000626.2315+0200-2330+0200_HTTPS-6-73.json
+        fileTimePart = fileTimePart.substring(0, 9) + fileTimePart.substring(19, 28);
+        return parseFileDate(fileTimePart);
+    }
+
+    private static OffsetDateTime parseFileDate(String timeStr) {
+        DateTimeFormatter startTimeFormatter =
+                new DateTimeFormatterBuilder().appendPattern("yyyyMMdd.HHmmZ").toFormatter();
+        return OffsetDateTime.parse(timeStr, startTimeFormatter);
+    }
+
+    private void handleExceptionInStream(Throwable t) {
+        logger.warn("JobDataDistributor exception: {}, jobId: {}", t.getMessage(), jobGroup.getId());
+    }
+
+    public Mono<String> sendToClient(FilteredData data) {
+
+        SenderRecord<byte[], byte[], Integer> senderRecord = senderRecord(data, this.getJobGroup().getDeliveryInfo());
+
+        logger.trace("Sending data '{}' to Kafka topic: {}", StringUtils.truncate(data.getValueAString(), 10),
+                this.getJobGroup().getDeliveryInfo());
+
+        return this.sender.send(Mono.just(senderRecord)) //
+                .doOnNext(n -> logger.debug("Sent data to Kafka topic: {}", this.getJobGroup().getDeliveryInfo())) //
+                .doOnError(t -> logger.warn("Failed to send to Kafka, job: {}, reason: {}", this.getJobGroup().getId(),
+                        t.getMessage())) //
+                .onErrorResume(t -> Mono.empty()) //
+                .collectList() //
+                .map(x -> "ok");
+
+    }
+
+    public synchronized void stop() {
+        if (this.subscription != null) {
+            logger.debug("Stopped, job: {}", jobGroup.getId());
+            this.subscription.dispose();
+            this.subscription = null;
+        }
+        if (sender != null) {
+            sender.close();
+            sender = null;
+        }
+
+        tryDeleteLockFile().subscribe();
+    }
+
+    private Mono<Boolean> tryDeleteLockFile() {
+        return dataStore.deleteLock(collectHistoricalDataLockName()) //
+                .doOnNext(res -> logger.debug("Removed lockfile {} {}", collectHistoricalDataLockName(), res))
+                .onErrorResume(t -> Mono.just(false));
+    }
+
+    public synchronized boolean isRunning() {
+        return this.subscription != null;
+    }
+
+    private SenderRecord<byte[], byte[], Integer> senderRecord(FilteredData output, KafkaDeliveryInfo deliveryInfo) {
+        int correlationMetadata = 2;
+        var producerRecord =
+                new ProducerRecord<>(deliveryInfo.getTopic(), null, null, output.key, output.value, output.headers());
+        return SenderRecord.create(producerRecord, correlationMetadata);
+    }
+
+    private Flux<FilteredData> filter(Flux<DataFromTopic> inputFlux, JobGroup jobGroup) {
+        return inputFlux.doOnNext(data -> logger.trace("Received data, job {}", jobGroup.getId())) //
+                .doOnNext(data -> jobGroup.getJobs().forEach(job -> job.getStatistics().received(data.value))) //
+                .map(jobGroup::filter) //
+                .filter(f -> !f.isEmpty()) //
+                .map(this::gzip) //
+                .doOnNext(f -> jobGroup.getJobs().forEach(job -> job.getStatistics().filtered(f.value))) //
+                .doOnNext(data -> logger.trace("Filtered data, job {}", jobGroup.getId())) //
+        ; //
+    }
+
+    private Mono<String> handleError(Throwable t) {
+        logger.warn("exception: {} job: {}", t.getMessage(), jobGroup.getId());
+        this.errorStats.handleException(t);
+        return Mono.empty(); // Ignore
+    }
+
+    private void handleSentOk(String data) {
+        this.errorStats.handleOkFromConsumer();
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/tasks/NewFileEvent.java b/pmproducer/src/main/java/org/oran/pmproducer/tasks/NewFileEvent.java
new file mode 100644 (file)
index 0000000..b5b6f73
--- /dev/null
@@ -0,0 +1,32 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.tasks;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.ToString;
+
+@ToString
+@Builder
+public class NewFileEvent {
+    @Getter
+    private String filename;
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/tasks/ProducerRegstrationTask.java b/pmproducer/src/main/java/org/oran/pmproducer/tasks/ProducerRegstrationTask.java
new file mode 100644 (file)
index 0000000..4bd95a1
--- /dev/null
@@ -0,0 +1,221 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.tasks;
+
+import com.google.common.io.CharStreams;
+import com.google.gson.JsonParser;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+
+import lombok.Getter;
+
+import org.oran.pmproducer.clients.AsyncRestClient;
+import org.oran.pmproducer.clients.AsyncRestClientFactory;
+import org.oran.pmproducer.clients.SecurityContext;
+import org.oran.pmproducer.configuration.ApplicationConfig;
+import org.oran.pmproducer.controllers.ProducerCallbacksController;
+import org.oran.pmproducer.exceptions.ServiceException;
+import org.oran.pmproducer.r1.ConsumerJobInfo;
+import org.oran.pmproducer.r1.ProducerInfoTypeInfo;
+import org.oran.pmproducer.r1.ProducerRegistrationInfo;
+import org.oran.pmproducer.repository.InfoType;
+import org.oran.pmproducer.repository.InfoTypes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+/**
+ * Registers the types and this producer in Innformation Coordinator Service.
+ * This is done when needed.
+ */
+@Component
+@EnableScheduling
+@SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
+public class ProducerRegstrationTask {
+
+    private static final Logger logger = LoggerFactory.getLogger(ProducerRegstrationTask.class);
+    private final AsyncRestClient restClient;
+    private final ApplicationConfig applicationConfig;
+    private final InfoTypes types;
+    private static com.google.gson.Gson gson = new com.google.gson.GsonBuilder().disableHtmlEscaping().create();
+
+    @Getter
+    private boolean isRegisteredInIcs = false;
+    private static final int REGISTRATION_SUPERVISION_INTERVAL_MS = 1000 * 10;
+
+    public ProducerRegstrationTask(@Autowired ApplicationConfig applicationConfig, @Autowired InfoTypes types,
+            @Autowired SecurityContext securityContext) {
+        AsyncRestClientFactory restClientFactory =
+                new AsyncRestClientFactory(applicationConfig.getWebClientConfig(), securityContext);
+        this.restClient = restClientFactory.createRestClientNoHttpProxy("");
+        this.applicationConfig = applicationConfig;
+        this.types = types;
+    }
+
+    @Scheduled(fixedRate = REGISTRATION_SUPERVISION_INTERVAL_MS)
+    public void runSupervisionTask() {
+        supervisionTask().subscribe( //
+                null, //
+                this::handleRegistrationFailure, //
+                this::handleRegistrationCompleted);
+    }
+
+    public Mono<String> supervisionTask() {
+        return checkRegistration() //
+                .filter(isRegistrationOk -> !isRegistrationOk || !this.isRegisteredInIcs) //
+                .flatMap(isRegisterred -> registerTypesAndProducer());
+    }
+
+    private void handleRegistrationCompleted() {
+        isRegisteredInIcs = true;
+    }
+
+    private void handleRegistrationFailure(Throwable t) {
+        logger.warn("Registration of producer failed {}", t.getMessage());
+    }
+
+    private String producerRegistrationUrl() {
+        final String producerId = this.applicationConfig.getSelfUrl().replace("/", "_");
+        return applicationConfig.getIcsBaseUrl() + "/data-producer/v1/info-producers/" + producerId;
+    }
+
+    // Returns TRUE if registration is correct
+    private Mono<Boolean> checkRegistration() {
+        return restClient.get(producerRegistrationUrl()) //
+                .flatMap(this::isRegisterredInfoCorrect) //
+                .onErrorResume(t -> Mono.just(Boolean.FALSE));
+    }
+
+    private Mono<Boolean> isRegisterredInfoCorrect(String registerredInfoStr) {
+        ProducerRegistrationInfo registerredInfo = gson.fromJson(registerredInfoStr, ProducerRegistrationInfo.class);
+        if (isEqual(producerRegistrationInfo(), registerredInfo)) {
+            logger.trace("Already registered in ICS");
+            return Mono.just(Boolean.TRUE);
+        } else {
+            return Mono.just(Boolean.FALSE);
+        }
+    }
+
+    private String registerTypeUrl(InfoType type) {
+        return applicationConfig.getIcsBaseUrl() + "/data-producer/v1/info-types/" + type.getId();
+    }
+
+    private Mono<String> registerTypesAndProducer() {
+        final int CONCURRENCY = 1;
+
+        return Flux.fromIterable(this.types.getAll()) //
+                .doOnNext(type -> logger.info("Registering type {}", type.getId())) //
+                .flatMap(this::createInputDataJob, CONCURRENCY)
+                .flatMap(type -> restClient.put(registerTypeUrl(type), gson.toJson(typeRegistrationInfo(type))),
+                        CONCURRENCY) //
+                .collectList() //
+                .doOnNext(type -> logger.info("Registering producer")) //
+                .flatMap(resp -> restClient.put(producerRegistrationUrl(), gson.toJson(producerRegistrationInfo())));
+    }
+
+    private Mono<InfoType> createInputDataJob(InfoType type) {
+        if (type.getInputJobType() == null) {
+            return Mono.just(type);
+        }
+
+        ConsumerJobInfo info =
+                new ConsumerJobInfo(type.getInputJobType(), type.getInputJobDefinition(), "pmproducer", "");
+
+        final String JOB_ID = type.getId() + "_5b3f4db6-3d9e-11ed-b878-0242ac120002";
+        String body = gson.toJson(info);
+
+        return restClient.put(consumerJobUrl(JOB_ID), body)
+                .doOnError(t -> logger.error("Could not create job of type {}, reason: {}", type.getInputJobType(),
+                        t.getMessage()))
+                .onErrorResume(t -> Mono.just("")) //
+                .doOnNext(n -> logger.info("Created input job: {}, type: {}", JOB_ID, type.getInputJobType())) //
+                .map(x -> type);
+    }
+
+    private String consumerJobUrl(String jobId) {
+        return applicationConfig.getIcsBaseUrl() + "/data-consumer/v1/info-jobs/" + jobId;
+    }
+
+    private Object typeSpecifcInfoObject() {
+        return jsonObject("{}");
+    }
+
+    private ProducerInfoTypeInfo typeRegistrationInfo(InfoType type) {
+        try {
+            return new ProducerInfoTypeInfo(jsonSchemaObject(type), typeSpecifcInfoObject());
+        } catch (Exception e) {
+            logger.error("Fatal error {}", e.getMessage());
+            return null;
+        }
+    }
+
+    private Object jsonSchemaObject(InfoType type) throws IOException, ServiceException {
+        final String schemaFile = "/typeSchemaPmData.json";
+        return jsonObject(readSchemaFile(schemaFile));
+    }
+
+    private String readSchemaFile(String filePath) throws IOException, ServiceException {
+        InputStream in = getClass().getResourceAsStream(filePath);
+        logger.debug("Reading application schema file from: {} with: {}", filePath, in);
+        if (in == null) {
+            throw new ServiceException("Could not readfile: " + filePath, HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+        return CharStreams.toString(new InputStreamReader(in, StandardCharsets.UTF_8));
+    }
+
+    @SuppressWarnings("java:S2139") // Log exception
+    private Object jsonObject(String json) {
+        try {
+            return JsonParser.parseString(json).getAsJsonObject();
+        } catch (Exception e) {
+            logger.error("Bug, error in JSON: {} {}", json, e.getMessage());
+            throw new NullPointerException(e.getMessage());
+        }
+    }
+
+    private boolean isEqual(ProducerRegistrationInfo a, ProducerRegistrationInfo b) {
+        return a.jobCallbackUrl.equals(b.jobCallbackUrl) //
+                && a.producerSupervisionCallbackUrl.equals(b.producerSupervisionCallbackUrl) //
+                && a.supportedTypeIds.size() == b.supportedTypeIds.size();
+    }
+
+    private ProducerRegistrationInfo producerRegistrationInfo() {
+        return ProducerRegistrationInfo.builder() //
+                .jobCallbackUrl(baseUrl() + ProducerCallbacksController.JOB_URL) //
+                .producerSupervisionCallbackUrl(baseUrl() + ProducerCallbacksController.SUPERVISION_URL) //
+                .supportedTypeIds(this.types.typeIds()) //
+                .build();
+    }
+
+    private String baseUrl() {
+        return this.applicationConfig.getSelfUrl();
+    }
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/tasks/TopicListener.java b/pmproducer/src/main/java/org/oran/pmproducer/tasks/TopicListener.java
new file mode 100644 (file)
index 0000000..351fcc6
--- /dev/null
@@ -0,0 +1,215 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.tasks;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.apache.kafka.common.header.Header;
+import org.apache.kafka.common.serialization.ByteArrayDeserializer;
+import org.oran.pmproducer.configuration.ApplicationConfig;
+import org.oran.pmproducer.datastore.DataStore;
+import org.oran.pmproducer.filter.PmReport;
+import org.oran.pmproducer.repository.InfoType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.kafka.receiver.KafkaReceiver;
+import reactor.kafka.receiver.ReceiverOptions;
+
+/**
+ * The class streams incoming requests from a Kafka topic and sends them further
+ * to a multi cast sink, which several other streams can connect to.
+ */
+@SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
+public class TopicListener {
+
+    @ToString
+    public static class DataFromTopic {
+        public final byte[] key;
+        public final byte[] value;
+
+        public final String infoTypeId;
+
+        public final Iterable<Header> headers;
+
+        private static byte[] noBytes = new byte[0];
+
+        @Getter
+        @Setter
+        @ToString.Exclude
+        private PmReport cachedPmReport;
+
+        public DataFromTopic(String typeId, Iterable<Header> headers, byte[] key, byte[] value) {
+            this.key = key == null ? noBytes : key;
+            this.value = value == null ? noBytes : value;
+            this.infoTypeId = typeId;
+            this.headers = headers;
+        }
+
+        public String valueAsString() {
+            return new String(this.value);
+        }
+
+        public static final String ZIPPED_PROPERTY = "gzip";
+        public static final String TYPE_ID_PROPERTY = "type-id";
+
+        public boolean isZipped() {
+            if (headers == null) {
+                return false;
+            }
+            for (Header h : headers) {
+                if (h.key().equals(ZIPPED_PROPERTY)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public String getTypeIdFromHeaders() {
+            if (headers == null) {
+                return "";
+            }
+            for (Header h : headers) {
+                if (h.key().equals(TYPE_ID_PROPERTY)) {
+                    return new String(h.value());
+                }
+            }
+            return "";
+        }
+    }
+
+    private static final Logger logger = LoggerFactory.getLogger(TopicListener.class);
+    private final ApplicationConfig applicationConfig;
+    private final InfoType type;
+    private Flux<DataFromTopic> dataFromTopic;
+    private static com.google.gson.Gson gson = new com.google.gson.GsonBuilder().disableHtmlEscaping().create();
+    private final DataStore dataStore;
+
+    @Setter
+    private String kafkaGroupId;
+
+    public TopicListener(ApplicationConfig applConfig, InfoType type) {
+        this.applicationConfig = applConfig;
+        this.type = type;
+        this.dataStore = DataStore.create(applConfig);
+        this.kafkaGroupId = this.type.getKafkaGroupId();
+    }
+
+    public Flux<DataFromTopic> getFlux() {
+        if (this.dataFromTopic == null) {
+            this.dataFromTopic = start(this.type.getKafkaClientId(this.applicationConfig));
+        }
+        return this.dataFromTopic;
+    }
+
+    private Flux<DataFromTopic> start(String clientId) {
+        logger.debug("Listening to kafka topic: {} type :{}", this.type.getKafkaInputTopic(), type.getId());
+
+        return receiveFromKafka(clientId) //
+                .filter(t -> t.value().length > 0 || t.key().length > 0) //
+                .map(input -> new DataFromTopic(this.type.getId(), input.headers(), input.key(), input.value())) //
+                .flatMap(data -> getDataFromFileIfNewPmFileEvent(data, type, dataStore)) //
+                .publish() //
+                .autoConnect(1);
+    }
+
+    public Flux<ConsumerRecord<byte[], byte[]>> receiveFromKafka(String clientId) {
+        return KafkaReceiver.create(kafkaInputProperties(clientId)) //
+                .receiveAutoAck() //
+                .concatMap(consumerRecord -> consumerRecord) //
+                .doOnNext(input -> logger.trace("Received from kafka topic: {}", this.type.getKafkaInputTopic())) //
+                .doOnError(t -> logger.error("Received error: {}", t.getMessage())) //
+                .onErrorResume(t -> Mono.empty()) //
+                .doFinally(sig -> logger.error("TopicListener stopped, type: {}, reason: {}", this.type.getId(), sig));
+    }
+
+    private ReceiverOptions<byte[], byte[]> kafkaInputProperties(String clientId) {
+        Map<String, Object> consumerProps = new HashMap<>();
+        if (this.applicationConfig.getKafkaBootStrapServers().isEmpty()) {
+            logger.error("No kafka boostrap server is setup");
+        }
+
+        consumerProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
+        consumerProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, this.applicationConfig.getKafkaBootStrapServers());
+        consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, kafkaGroupId);
+        consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class);
+        consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class);
+        consumerProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
+
+        consumerProps.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId + "_" + kafkaGroupId);
+
+        return ReceiverOptions.<byte[], byte[]>create(consumerProps)
+                .subscription(Collections.singleton(this.type.getKafkaInputTopic()));
+    }
+
+    public static Mono<DataFromTopic> getDataFromFileIfNewPmFileEvent(DataFromTopic data, InfoType type,
+            DataStore fileStore) {
+        try {
+            if (data.value.length > 200) {
+                return Mono.just(data);
+            }
+
+            NewFileEvent ev = gson.fromJson(data.valueAsString(), NewFileEvent.class);
+
+            if (ev.getFilename() == null) {
+                logger.warn("Ignoring received message: {}", data);
+                return Mono.empty();
+            }
+            logger.trace("Reading PM measurements, type: {}, inputTopic: {}", type.getId(), type.getKafkaInputTopic());
+            return fileStore.readObject(DataStore.Bucket.FILES, ev.getFilename()) //
+                    .map(bytes -> unzip(bytes, ev.getFilename())) //
+                    .map(bytes -> new DataFromTopic(data.infoTypeId, data.headers, data.key, bytes));
+
+        } catch (Exception e) {
+            return Mono.just(data);
+        }
+    }
+
+    public static byte[] unzip(byte[] bytes) throws IOException {
+        try (final GZIPInputStream gzipInput = new GZIPInputStream(new ByteArrayInputStream(bytes))) {
+            return gzipInput.readAllBytes();
+        }
+    }
+
+    private static byte[] unzip(byte[] bytes, String fileName) {
+        try {
+            return fileName.endsWith(".gz") ? unzip(bytes) : bytes;
+        } catch (IOException e) {
+            logger.error("Error while decompression, file: {}, reason: {}", fileName, e.getMessage());
+            return new byte[0];
+        }
+
+    }
+
+}
diff --git a/pmproducer/src/main/java/org/oran/pmproducer/tasks/TopicListeners.java b/pmproducer/src/main/java/org/oran/pmproducer/tasks/TopicListeners.java
new file mode 100644 (file)
index 0000000..7b41a6f
--- /dev/null
@@ -0,0 +1,107 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.tasks;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import lombok.Getter;
+
+import org.oran.pmproducer.configuration.ApplicationConfig;
+import org.oran.pmproducer.repository.InfoType;
+import org.oran.pmproducer.repository.InfoTypes;
+import org.oran.pmproducer.repository.Jobs;
+import org.oran.pmproducer.repository.Jobs.JobGroup;
+import org.oran.pmproducer.repository.MultiMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.stereotype.Component;
+
+@SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
+@Component
+@EnableScheduling
+public class TopicListeners {
+    private static final Logger logger = LoggerFactory.getLogger(TopicListeners.class);
+
+    @Getter
+    private final Map<String, TopicListener> topicListeners = new HashMap<>(); // Key is typeId
+
+    @Getter
+    private final MultiMap<JobDataDistributor> dataDistributors = new MultiMap<>(); // Key is typeId, jobId
+
+    private final ApplicationConfig appConfig;
+
+    public TopicListeners(@Autowired ApplicationConfig appConfig, @Autowired InfoTypes types, @Autowired Jobs jobs) {
+        this.appConfig = appConfig;
+
+        for (InfoType type : types.getAll()) {
+            TopicListener topicConsumer = new TopicListener(appConfig, type);
+            topicListeners.put(type.getId(), topicConsumer);
+        }
+
+        jobs.addObserver(new Jobs.Observer() {
+            @Override
+            public void onJobbGroupAdded(JobGroup jobGroup) {
+                addJob(jobGroup);
+            }
+
+            @Override
+            public void onJobGroupRemoved(JobGroup jobGroup) {
+                removeDistributor(jobGroup);
+            }
+        });
+    }
+
+    public synchronized void addJob(JobGroup jobGroup) {
+        removeDistributor(jobGroup);
+        logger.debug("Job added {}", jobGroup.getId());
+        addDistributor(jobGroup, dataDistributors, topicListeners);
+    }
+
+    private JobDataDistributor createDistributor(JobGroup jobGroup) {
+        return new JobDataDistributor(jobGroup, appConfig);
+    }
+
+    private void addDistributor(JobGroup jobGroup, MultiMap<JobDataDistributor> distributors,
+            Map<String, TopicListener> topicListeners) {
+        TopicListener topicListener = topicListeners.get(jobGroup.getType().getId());
+        JobDataDistributor distributor = createDistributor(jobGroup);
+
+        distributor.start(topicListener.getFlux());
+
+        distributors.put(jobGroup.getType().getId(), jobGroup.getId(), distributor);
+    }
+
+    private synchronized void removeDistributor(JobGroup jobGroup) {
+        removeDistributor(jobGroup, dataDistributors);
+    }
+
+    private static void removeDistributor(JobGroup jobGroup, MultiMap<JobDataDistributor> distributors) {
+        JobDataDistributor distributor = distributors.remove(jobGroup.getType().getId(), jobGroup.getId());
+        if (distributor != null) {
+            logger.debug("Job removed {}", jobGroup.getId());
+            distributor.stop();
+        }
+    }
+
+}
diff --git a/pmproducer/src/main/resources/typeSchemaPmData.json b/pmproducer/src/main/resources/typeSchemaPmData.json
new file mode 100644 (file)
index 0000000..16b973d
--- /dev/null
@@ -0,0 +1,86 @@
+{
+   "$schema": "http://json-schema.org/draft-04/schema#",
+   "type": "object",
+   "additionalProperties": false,
+   "properties": {
+      "filter": {
+         "type": "object",
+         "additionalProperties": false,
+         "properties": {
+            "sourceNames": {
+               "type": "array",
+               "items": [
+                  {
+                     "type": "string"
+                  }
+               ]
+            },
+            "measObjInstIds": {
+               "type": "array",
+               "items": [
+                  {
+                     "type": "string"
+                  }
+               ]
+            },
+            "measTypeSpecs": {
+               "type": "array",
+               "items": [
+                  {
+                     "type": "object",
+                     "properties": {
+                        "measuredObjClass": {
+                           "type": "string"
+                        },
+                        "measTypes": {
+                           "type": "array",
+                           "items": [
+                              {
+                                 "type": "string"
+                              }
+                           ]
+                        }
+                     },
+                     "required": [
+                        "measuredObjClass"
+                     ]
+                  }
+               ]
+            },
+            "measuredEntityDns": {
+               "type": "array",
+               "items": [
+                  {
+                     "type": "string"
+                  }
+               ]
+            },
+            "pmRopStartTime": {
+               "type": "string"
+            },
+            "pmRopEndTime": {
+               "type": "string"
+            }
+         }
+      },    
+      "deliveryInfo": {
+         "type": "object",
+         "additionalProperties": false,
+         "properties": {
+            "topic": {
+               "type": "string"
+            },
+            "bootStrapServers": {
+               "type": "string"
+            }
+         },
+         "required": [
+            "topic"
+         ]
+      }
+   },
+   "required": [
+      "filter", "deliveryInfo"  
+   ]
+}
+
diff --git a/pmproducer/src/test/java/org/oran/pmproducer/ApplicationTest.java b/pmproducer/src/test/java/org/oran/pmproducer/ApplicationTest.java
new file mode 100644 (file)
index 0000000..49b8ea2
--- /dev/null
@@ -0,0 +1,499 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.gson.JsonParser;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.invoke.MethodHandles;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.time.OffsetDateTime;
+import java.util.List;
+
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.json.JSONObject;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.mockito.ArgumentCaptor;
+import org.oran.pmproducer.clients.AsyncRestClient;
+import org.oran.pmproducer.clients.AsyncRestClientFactory;
+import org.oran.pmproducer.clients.SecurityContext;
+import org.oran.pmproducer.configuration.ApplicationConfig;
+import org.oran.pmproducer.configuration.WebClientConfig;
+import org.oran.pmproducer.configuration.WebClientConfig.HttpProxyConfig;
+import org.oran.pmproducer.controllers.ProducerCallbacksController;
+import org.oran.pmproducer.datastore.DataStore;
+import org.oran.pmproducer.datastore.DataStore.Bucket;
+import org.oran.pmproducer.filter.FilteredData;
+import org.oran.pmproducer.filter.PmReport;
+import org.oran.pmproducer.filter.PmReportFilter;
+import org.oran.pmproducer.filter.PmReportFilter.FilterData;
+import org.oran.pmproducer.r1.ConsumerJobInfo;
+import org.oran.pmproducer.r1.ProducerJobInfo;
+import org.oran.pmproducer.repository.InfoType;
+import org.oran.pmproducer.repository.InfoTypes;
+import org.oran.pmproducer.repository.Job;
+import org.oran.pmproducer.repository.Job.Parameters;
+import org.oran.pmproducer.repository.Job.Parameters.KafkaDeliveryInfo;
+import org.oran.pmproducer.repository.Jobs;
+import org.oran.pmproducer.repository.Jobs.JobGroup;
+import org.oran.pmproducer.tasks.JobDataDistributor;
+import org.oran.pmproducer.tasks.NewFileEvent;
+import org.oran.pmproducer.tasks.ProducerRegstrationTask;
+import org.oran.pmproducer.tasks.TopicListener;
+import org.oran.pmproducer.tasks.TopicListeners;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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.boot.test.context.TestConfiguration;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
+import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.web.reactive.function.client.WebClientRequestException;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+@TestMethodOrder(MethodOrderer.MethodName.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+@TestPropertySource(properties = { //
+        "server.ssl.key-store=./config/keystore.jks", //
+        "app.webclient.trust-store=./config/truststore.jks", //
+        "app.webclient.trust-store-used=true", //
+        "app.configuration-filepath=./src/test/resources/test_application_configuration.json", //
+        "app.pm-files-path=/tmp/dmaapadaptor", //
+        "app.s3.endpointOverride=" //
+})
+class ApplicationTest {
+
+    @Autowired
+    private ApplicationConfig applicationConfig;
+
+    @Autowired
+    private Jobs jobs;
+
+    @Autowired
+    private InfoTypes types;
+
+    @Autowired
+    private IcsSimulatorController icsSimulatorController;
+
+    @Autowired
+    TopicListeners topicListeners;
+
+    @Autowired
+    ProducerRegstrationTask producerRegistrationTask;
+
+    @Autowired
+    private SecurityContext securityContext;
+
+    private com.google.gson.Gson gson = new com.google.gson.GsonBuilder().disableHtmlEscaping().create();
+
+    @LocalServerPort
+    int localServerHttpPort;
+
+    private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    static class TestApplicationConfig extends ApplicationConfig {
+
+        @Override
+        public String getIcsBaseUrl() {
+            return thisProcessUrl();
+        }
+
+        @Override
+        public String getDmaapBaseUrl() {
+            return thisProcessUrl();
+        }
+
+        @Override
+        public String getSelfUrl() {
+            return thisProcessUrl();
+        }
+
+        private String thisProcessUrl() {
+            final String url = "https://localhost:" + getLocalServerHttpPort();
+            return url;
+        }
+    }
+
+    /**
+     * Overrides the BeanFactory.
+     */
+    @TestConfiguration
+    static class TestBeanFactory extends BeanFactory {
+
+        @Override
+        @Bean
+        public ServletWebServerFactory servletContainer() {
+            return new TomcatServletWebServerFactory();
+        }
+
+        // @Override
+        @Bean
+        public ApplicationConfig getApplicationConfig() {
+            TestApplicationConfig cfg = new TestApplicationConfig();
+            return cfg;
+        }
+    }
+
+    @BeforeEach
+    public void init() {
+        this.applicationConfig.setLocalServerHttpPort(this.localServerHttpPort);
+        assertThat(this.jobs.size()).isZero();
+
+        DataStore fileStore = this.dataStore();
+        fileStore.create(DataStore.Bucket.FILES).block();
+        fileStore.create(DataStore.Bucket.LOCKS).block();
+    }
+
+    private DataStore dataStore() {
+        return DataStore.create(this.applicationConfig);
+    }
+
+    @AfterEach
+    void reset() {
+
+        for (Job job : this.jobs.getAll()) {
+            this.icsSimulatorController.deleteJob(job.getId(), restClient());
+        }
+        await().untilAsserted(() -> assertThat(this.jobs.size()).isZero());
+
+        this.icsSimulatorController.testResults.reset();
+
+        DataStore fileStore = DataStore.create(applicationConfig);
+        fileStore.deleteBucket(Bucket.FILES);
+        fileStore.deleteBucket(Bucket.LOCKS);
+
+    }
+
+    private AsyncRestClient restClient(boolean useTrustValidation) {
+        WebClientConfig config = this.applicationConfig.getWebClientConfig();
+        HttpProxyConfig httpProxyConfig = HttpProxyConfig.builder() //
+                .httpProxyHost("") //
+                .httpProxyPort(0) //
+                .build();
+        config = WebClientConfig.builder() //
+                .keyStoreType(config.getKeyStoreType()) //
+                .keyStorePassword(config.getKeyStorePassword()) //
+                .keyStore(config.getKeyStore()) //
+                .keyPassword(config.getKeyPassword()) //
+                .isTrustStoreUsed(useTrustValidation) //
+                .trustStore(config.getTrustStore()) //
+                .trustStorePassword(config.getTrustStorePassword()) //
+                .httpProxyConfig(httpProxyConfig).build();
+
+        AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config, securityContext);
+        return restClientFactory.createRestClientNoHttpProxy(baseUrl());
+    }
+
+    private AsyncRestClient restClient() {
+        return restClient(false);
+    }
+
+    private String baseUrl() {
+        return "https://localhost:" + this.applicationConfig.getLocalServerHttpPort();
+    }
+
+    private String quote(String str) {
+        final String q = "\"";
+        return q + str.replace(q, "\\\"") + q;
+    }
+
+    private Object toJson(String json) {
+        try {
+            return JsonParser.parseString(json).getAsJsonObject();
+        } catch (Exception e) {
+            throw new NullPointerException(e.toString());
+        }
+    }
+
+    private ConsumerJobInfo consumerJobInfo(String typeId, String infoJobId, Object filter) {
+        try {
+            return new ConsumerJobInfo(typeId, filter, "owner", "");
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private void waitForRegistration() {
+        // Register producer, Register types
+        await().untilAsserted(() -> assertThat(icsSimulatorController.testResults.registrationInfo).isNotNull());
+        producerRegistrationTask.supervisionTask().block();
+
+        assertThat(icsSimulatorController.testResults.registrationInfo.supportedTypeIds).hasSize(this.types.size());
+        assertThat(producerRegistrationTask.isRegisteredInIcs()).isTrue();
+        assertThat(icsSimulatorController.testResults.types).hasSize(this.types.size());
+    }
+
+    @Test
+    void generateApiDoc() throws IOException {
+        String url = "https://localhost:" + applicationConfig.getLocalServerHttpPort() + "/v3/api-docs";
+        ResponseEntity<String> resp = restClient().getForEntity(url).block();
+        assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
+        JSONObject jsonObj = new JSONObject(resp.getBody());
+        assertThat(jsonObj.remove("servers")).isNotNull();
+
+        String indented = (jsonObj).toString(4);
+        String docDir = "api/";
+        Files.createDirectories(Paths.get(docDir));
+        try (PrintStream out = new PrintStream(new FileOutputStream(docDir + "api.json"))) {
+            out.print(indented);
+        }
+    }
+
+    @Test
+    void testTrustValidation() throws IOException {
+        String url = "https://localhost:" + applicationConfig.getLocalServerHttpPort() + "/v3/api-docs";
+        ResponseEntity<String> resp = restClient(true).getForEntity(url).block();
+        assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
+    }
+
+    @Test
+    void testResponseCodes() throws Exception {
+        String supervisionUrl = baseUrl() + ProducerCallbacksController.SUPERVISION_URL;
+        ResponseEntity<String> resp = restClient().getForEntity(supervisionUrl).block();
+        assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
+
+        String jobUrl = baseUrl() + ProducerCallbacksController.JOB_URL;
+        resp = restClient().deleteForEntity(jobUrl + "/junk").block();
+        assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
+
+        ProducerJobInfo info = new ProducerJobInfo(null, "id", "typeId", "owner", "lastUpdated");
+        String body = gson.toJson(info);
+        testErrorCode(restClient().post(jobUrl, body, MediaType.APPLICATION_JSON), HttpStatus.NOT_FOUND,
+                "Could not find type");
+    }
+
+    @Test
+    void testFiltering() {
+        String path = "./src/test/resources/pm_report.json.gz";
+        DataStore fs = DataStore.create(this.applicationConfig);
+        fs.copyFileTo(Path.of(path), "pm_report.json.gz");
+
+        InfoType infoType = this.types.getAll().iterator().next();
+        TopicListener listener = spy(new TopicListener(this.applicationConfig, infoType));
+        NewFileEvent event = NewFileEvent.builder().filename("pm_report.json.gz").build();
+        ConsumerRecord<byte[], byte[]> cr = new ConsumerRecord<>("", 0, 0, new byte[0], gson.toJson(event).getBytes());
+        when(listener.receiveFromKafka(any())).thenReturn(Flux.just(cr));
+
+        KafkaDeliveryInfo deliveryInfo = KafkaDeliveryInfo.builder().topic("topic").bootStrapServers("").build();
+        JobGroup jobGroup = new JobGroup(infoType, deliveryInfo);
+        jobGroup.add(new Job("id", infoType, "owner", "lastUpdated",
+                Parameters.builder().filter(new FilterData()).build(), this.applicationConfig));
+        JobDataDistributor distributor = spy(new JobDataDistributor(jobGroup, this.applicationConfig));
+
+        doReturn(Mono.just("")).when(distributor).sendToClient(any());
+
+        distributor.start(listener.getFlux());
+
+        {
+            ArgumentCaptor<FilteredData> captor = ArgumentCaptor.forClass(FilteredData.class);
+            verify(distributor).sendToClient(captor.capture());
+            FilteredData data = captor.getValue();
+            PmReport report = PmReportFilter.parse(new String(data.value));
+            assertThat(report.event.getCommonEventHeader().getSourceName()).isEqualTo("O-DU-1122");
+        }
+    }
+
+    @Test
+    void testFilteringHistoricalData() {
+        DataStore fileStore = DataStore.create(this.applicationConfig);
+        fileStore.copyFileTo(Path.of("./src/test/resources/pm_report.json"),
+                "O-DU-1122/A20000626.2315+0200-2330+0200_HTTPS-6-73.json").block();
+
+        InfoType infoType = this.types.getAll().iterator().next();
+        TopicListener listener = spy(new TopicListener(this.applicationConfig, infoType));
+        Flux<ConsumerRecord<byte[], byte[]>> flux = Flux.just("") //
+                .delayElements(Duration.ofSeconds(10)).flatMap(s -> Flux.empty());
+        when(listener.receiveFromKafka(any())).thenReturn(flux);
+
+        PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+        filterData.getSourceNames().add("O-DU-1122");
+        filterData.setPmRopStartTime("1999-12-27T10:50:44.000-08:00");
+        filterData.setPmRopEndTime(OffsetDateTime.now().toString());
+        KafkaDeliveryInfo deliveryInfo = KafkaDeliveryInfo.builder().topic("topic").bootStrapServers("").build();
+        JobGroup jobGroup = new JobGroup(infoType, deliveryInfo);
+        Parameters params = Parameters.builder().filter(filterData).build();
+        Job job = new Job("id", infoType, "owner", "lastUpdated", params, this.applicationConfig);
+        jobGroup.add(job);
+        JobDataDistributor distributor = spy(new JobDataDistributor(jobGroup, this.applicationConfig));
+
+        doReturn(Mono.just("")).when(distributor).sendToClient(any());
+
+        distributor.start(listener.getFlux());
+
+        {
+            ArgumentCaptor<FilteredData> captor = ArgumentCaptor.forClass(FilteredData.class);
+            verify(distributor, times(2)).sendToClient(captor.capture());
+            List<FilteredData> data = captor.getAllValues();
+            assertThat(data).hasSize(2);
+            PmReport report = PmReportFilter.parse(new String(data.get(0).value));
+            assertThat(report.event.getCommonEventHeader().getSourceName()).isEqualTo("O-DU-1122");
+            assertThat(new String(data.get(1).value)).isEqualTo("{}");
+        }
+    }
+
+    @Test
+    void testCreateJob() throws Exception {
+        // Create a job
+        final String JOB_ID = "ID";
+
+        // Register producer, Register types
+        waitForRegistration();
+
+        assertThat(this.topicListeners.getTopicListeners()).hasSize(1);
+
+        // Create a job with a PM filter
+        PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+
+        filterData.addMeasTypes("UtranCell", "succImmediateAssignProcs");
+        filterData.getMeasObjInstIds().add("UtranCell=Gbg-997");
+        filterData.getSourceNames().add("O-DU-1122");
+        filterData.getMeasuredEntityDns().add("ManagedElement=RNC-Gbg-1");
+        Job.Parameters param = Job.Parameters.builder() //
+                .filter(filterData).deliveryInfo(KafkaDeliveryInfo.builder().bootStrapServers("").topic("").build()) //
+                .build();
+
+        String paramJson = gson.toJson(param);
+        System.out.println(paramJson);
+        ConsumerJobInfo jobInfo = consumerJobInfo("PmDataOverKafka", "EI_PM_JOB_ID", toJson(paramJson));
+
+        this.icsSimulatorController.addJob(jobInfo, JOB_ID, restClient());
+        await().untilAsserted(() -> assertThat(this.jobs.size()).isEqualTo(1));
+
+        assertThat(this.topicListeners.getDataDistributors().keySet()).hasSize(1);
+    }
+
+    @Test
+    void testPmFilteringKafka() throws Exception {
+        // Test that the schema for kafka and pm filtering is OK.
+
+        // Create a job
+        final String JOB_ID = "ID";
+
+        // Register producer, Register types
+        waitForRegistration();
+
+        // Create a job with a PM filter
+        PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+        filterData.addMeasTypes("ManagedElement", "succImmediateAssignProcs");
+        Job.Parameters param = Job.Parameters.builder() //
+                .filter(filterData).deliveryInfo(KafkaDeliveryInfo.builder().bootStrapServers("").topic("").build()) //
+                .build();
+
+        String paramJson = gson.toJson(param);
+
+        ConsumerJobInfo jobInfo = consumerJobInfo("PmDataOverKafka", "EI_PM_JOB_ID", toJson(paramJson));
+        this.icsSimulatorController.addJob(jobInfo, JOB_ID, restClient());
+        await().untilAsserted(() -> assertThat(this.jobs.size()).isEqualTo(1));
+    }
+
+    @Test
+    void testReRegister() throws Exception {
+        // Wait foir register types and producer
+        waitForRegistration();
+
+        // Clear the registration, should trigger a re-register
+        icsSimulatorController.testResults.reset();
+        producerRegistrationTask.supervisionTask().block();
+        await().untilAsserted(() -> assertThat(icsSimulatorController.testResults.registrationInfo).isNotNull());
+        assertThat(icsSimulatorController.testResults.registrationInfo.supportedTypeIds).hasSize(this.types.size());
+
+        // Just clear the registerred types, should trigger a re-register
+        icsSimulatorController.testResults.types.clear();
+        await().untilAsserted(() -> assertThat(icsSimulatorController.testResults.registrationInfo.supportedTypeIds)
+                .hasSize(this.types.size()));
+    }
+
+    @Test
+    @SuppressWarnings("squid:S2925") // "Thread.sleep" should not be used in tests.
+    void testZZActuator() throws Exception {
+        // The test must be run last, hence the "ZZ" in the name. All succeeding tests
+        // will fail.
+        AsyncRestClient client = restClient();
+        client.post("/actuator/loggers/org.oran.pmproducer", "{\"configuredLevel\":\"trace\"}").block();
+        String resp = client.get("/actuator/loggers/org.oran.pmproducer").block();
+        assertThat(resp).contains("TRACE");
+        client.post("/actuator/loggers/org.springframework.boot.actuate", "{\"configuredLevel\":\"trace\"}").block();
+        // This will stop the web server and all coming tests will fail.
+        client.post("/actuator/shutdown", "").block();
+        Thread.sleep(1000);
+
+        String url = "https://localhost:" + applicationConfig.getLocalServerHttpPort() + "/v3/api-docs";
+        StepVerifier.create(restClient().get(url)) // Any call
+                .expectSubscription() //
+                .expectErrorMatches(t -> t instanceof WebClientRequestException) //
+                .verify();
+    }
+
+    public static void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains) {
+        testErrorCode(request, expStatus, responseContains, true);
+    }
+
+    public static void testErrorCode(Mono<?> request, HttpStatus expStatus, String responseContains,
+            boolean expectApplicationProblemJsonMediaType) {
+        StepVerifier.create(request) //
+                .expectSubscription() //
+                .expectErrorMatches(
+                        t -> checkWebClientError(t, expStatus, responseContains, expectApplicationProblemJsonMediaType)) //
+                .verify();
+    }
+
+    private static boolean checkWebClientError(Throwable throwable, HttpStatus expStatus, String responseContains,
+            boolean expectApplicationProblemJsonMediaType) {
+        assertTrue(throwable instanceof WebClientResponseException);
+        WebClientResponseException responseException = (WebClientResponseException) throwable;
+        assertThat(responseException.getStatusCode()).isEqualTo(expStatus);
+        assertThat(responseException.getResponseBodyAsString()).contains(responseContains);
+        if (expectApplicationProblemJsonMediaType) {
+            assertThat(responseException.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
+        }
+        return true;
+    }
+}
diff --git a/pmproducer/src/test/java/org/oran/pmproducer/IcsSimulatorController.java b/pmproducer/src/test/java/org/oran/pmproducer/IcsSimulatorController.java
new file mode 100644 (file)
index 0000000..edfe708
--- /dev/null
@@ -0,0 +1,165 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+import java.lang.invoke.MethodHandles;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.json.JSONObject;
+import org.oran.pmproducer.clients.AsyncRestClient;
+import org.oran.pmproducer.exceptions.ServiceException;
+import org.oran.pmproducer.r1.ConsumerJobInfo;
+import org.oran.pmproducer.r1.ProducerInfoTypeInfo;
+import org.oran.pmproducer.r1.ProducerJobInfo;
+import org.oran.pmproducer.r1.ProducerRegistrationInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController("IcsSimulatorController")
+@Tag(name = "Information Coordinator Service Simulator (exists only in test)")
+public class IcsSimulatorController {
+
+    private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+    private final static Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+
+    public static class TestResults {
+
+        ProducerRegistrationInfo registrationInfo = null;
+        Map<String, ProducerInfoTypeInfo> types = Collections.synchronizedMap(new HashMap<>());
+        String infoProducerId = null;
+        ConsumerJobInfo createdJob = null;
+
+        public TestResults() {}
+
+        public synchronized void reset() {
+            registrationInfo = null;
+            types.clear();
+            infoProducerId = null;
+            createdJob = null;
+        }
+
+        public void setCreatedJob(ConsumerJobInfo informationJobObject) {
+            this.createdJob = informationJobObject;
+        }
+    }
+
+    final TestResults testResults = new TestResults();
+    public static final String API_ROOT = "/data-producer/v1";
+
+    @GetMapping(path = API_ROOT + "/info-producers/{infoProducerId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> getInfoProducer( //
+            @PathVariable("infoProducerId") String infoProducerId) {
+
+        if (testResults.registrationInfo != null) {
+            return new ResponseEntity<>(gson.toJson(testResults.registrationInfo), HttpStatus.OK);
+        } else {
+            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+        }
+    }
+
+    @PutMapping(path = API_ROOT + "/info-producers/{infoProducerId}", //
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> putInfoProducer( //
+            @PathVariable("infoProducerId") String infoProducerId, //
+            @RequestBody ProducerRegistrationInfo registrationInfo) {
+        testResults.registrationInfo = registrationInfo;
+        testResults.infoProducerId = infoProducerId;
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @PutMapping(path = API_ROOT + "/info-types/{infoTypeId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> putInfoType( //
+            @PathVariable("infoTypeId") String infoTypeId, //
+            @RequestBody ProducerInfoTypeInfo registrationInfo) {
+        testResults.types.put(infoTypeId, registrationInfo);
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    @PutMapping(path = "/data-consumer/v1/info-jobs/{infoJobId}", //
+            produces = MediaType.APPLICATION_JSON_VALUE, //
+            consumes = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> putIndividualInfoJob( //
+            @PathVariable("infoJobId") String jobId, //
+            @RequestBody ConsumerJobInfo informationJobObject) {
+        logger.debug("*** added consumer job {}", jobId);
+        testResults.setCreatedJob(informationJobObject);
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    public void addJob(ConsumerJobInfo job, String jobId, AsyncRestClient restClient) throws ServiceException {
+        String url = this.testResults.registrationInfo.jobCallbackUrl;
+        ProducerJobInfo request = new ProducerJobInfo(job.jobDefinition, jobId, job.infoTypeId, job.owner, "TIMESTAMP");
+        String body = gson.toJson(request);
+        ProducerInfoTypeInfo type = testResults.types.get(job.infoTypeId);
+        if (type == null) {
+            logger.error("type not found: {} size: {}", job.infoTypeId, testResults.types.size());
+        } else {
+            assertThat(type).isNotNull();
+            validateJsonObjectAgainstSchema(job.jobDefinition, type.jobDataSchema);
+            logger.debug("ICS Simulator PUT job: {}", body);
+            restClient.post(url, body, MediaType.APPLICATION_JSON).block();
+        }
+    }
+
+    private void validateJsonObjectAgainstSchema(Object object, Object schemaObj) throws ServiceException {
+        if (schemaObj != null) { // schema is optional for now
+            try {
+                ObjectMapper mapper = new ObjectMapper();
+
+                String schemaAsString = mapper.writeValueAsString(schemaObj);
+                JSONObject schemaJSON = new JSONObject(schemaAsString);
+                var schema = org.everit.json.schema.loader.SchemaLoader.load(schemaJSON);
+
+                String objectAsString = object.toString();
+                JSONObject json = new JSONObject(objectAsString);
+                schema.validate(json);
+            } catch (Exception e) {
+                logger.error("Json validation failure {}", e.toString());
+                throw new ServiceException("Json validation failure " + e.toString(), HttpStatus.BAD_REQUEST);
+            }
+        }
+    }
+
+    public void deleteJob(String jobId, AsyncRestClient restClient) {
+        String url = this.testResults.registrationInfo.jobCallbackUrl + "/" + jobId;
+        logger.debug("ICS Simulator DELETE job: {}", url);
+        restClient.delete(url).block();
+
+    }
+}
diff --git a/pmproducer/src/test/java/org/oran/pmproducer/IntegrationWithIcs.java b/pmproducer/src/test/java/org/oran/pmproducer/IntegrationWithIcs.java
new file mode 100644 (file)
index 0000000..e7875e2
--- /dev/null
@@ -0,0 +1,193 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.oran.pmproducer.clients.AsyncRestClient;
+import org.oran.pmproducer.clients.AsyncRestClientFactory;
+import org.oran.pmproducer.clients.SecurityContext;
+import org.oran.pmproducer.configuration.ApplicationConfig;
+import org.oran.pmproducer.configuration.WebClientConfig;
+import org.oran.pmproducer.configuration.WebClientConfig.HttpProxyConfig;
+import org.oran.pmproducer.filter.PmReportFilter;
+import org.oran.pmproducer.r1.ConsumerJobInfo;
+import org.oran.pmproducer.repository.Jobs;
+import org.oran.pmproducer.tasks.ProducerRegstrationTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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.boot.test.context.TestConfiguration;
+import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
+import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.test.context.TestPropertySource;
+
+@SuppressWarnings("java:S3577") // Rename class
+@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
+@TestPropertySource(properties = { //
+        "server.ssl.key-store=./config/keystore.jks", //
+        "app.webclient.trust-store=./config/truststore.jks", //
+        "app.configuration-filepath=./src/test/resources/test_application_configuration.json", //
+        "app.ics-base-url=https://localhost:8434" //
+})
+class IntegrationWithIcs {
+
+    private static final String JOB_ID = "JOB_ID";
+    private static final Logger logger = LoggerFactory.getLogger(Application.class);
+
+    @Autowired
+    private ApplicationConfig applicationConfig;
+
+    @Autowired
+    private ProducerRegstrationTask producerRegstrationTask;
+
+    @Autowired
+    private Jobs jobs;
+
+    @Autowired
+    private SecurityContext securityContext;
+
+    private static Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+
+    static class TestApplicationConfig extends ApplicationConfig {
+
+        @Override
+        public String getIcsBaseUrl() {
+            return "https://localhost:8434";
+        }
+
+        @Override
+        public String getDmaapBaseUrl() {
+            return thisProcessUrl();
+        }
+
+        @Override
+        public String getSelfUrl() {
+            return thisProcessUrl();
+        }
+
+        private String thisProcessUrl() {
+            final String url = "https://localhost:" + getLocalServerHttpPort();
+            return url;
+        }
+    }
+
+    /**
+     * Overrides the BeanFactory.
+     */
+    @TestConfiguration
+    static class TestBeanFactory extends BeanFactory {
+
+        @Override
+        @Bean
+        public ServletWebServerFactory servletContainer() {
+            return new TomcatServletWebServerFactory();
+        }
+
+        @Override
+        @Bean
+        public ApplicationConfig getApplicationConfig() {
+            TestApplicationConfig cfg = new TestApplicationConfig();
+            return cfg;
+        }
+    }
+
+    @AfterEach
+    void reset() {
+        assertThat(this.jobs.size()).isZero();
+    }
+
+    private AsyncRestClient restClient(boolean useTrustValidation) {
+        WebClientConfig config = this.applicationConfig.getWebClientConfig();
+        HttpProxyConfig httpProxyConfig = HttpProxyConfig.builder() //
+                .httpProxyHost("") //
+                .httpProxyPort(0) //
+                .build();
+        config = WebClientConfig.builder() //
+                .keyStoreType(config.getKeyStoreType()) //
+                .keyStorePassword(config.getKeyStorePassword()) //
+                .keyStore(config.getKeyStore()) //
+                .keyPassword(config.getKeyPassword()) //
+                .isTrustStoreUsed(useTrustValidation) //
+                .trustStore(config.getTrustStore()) //
+                .trustStorePassword(config.getTrustStorePassword()) //
+                .httpProxyConfig(httpProxyConfig).build();
+
+        AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config, securityContext);
+        return restClientFactory.createRestClientNoHttpProxy(selfBaseUrl());
+    }
+
+    private AsyncRestClient restClient() {
+        return restClient(false);
+    }
+
+    private String selfBaseUrl() {
+        return "https://localhost:" + this.applicationConfig.getLocalServerHttpPort();
+    }
+
+    private String icsBaseUrl() {
+        return applicationConfig.getIcsBaseUrl();
+    }
+
+    private String jobUrl(String jobId) {
+        return icsBaseUrl() + "/data-consumer/v1/info-jobs/" + jobId + "?typeCheck=true";
+    }
+
+    private void deleteInformationJobInIcs(String jobId) {
+        try {
+            restClient().delete(jobUrl(jobId)).block();
+        } catch (Exception e) {
+            logger.warn("Couldnot delete job: {}  reason: {}", jobId, e.getMessage());
+        }
+    }
+
+    private void createInformationJobInIcs(String jobId, ConsumerJobInfo jobInfo) {
+        String body = gson.toJson(jobInfo);
+        restClient().putForEntity(jobUrl(jobId), body).block();
+        logger.info("Created job {}, {}", jobId, body);
+    }
+
+    @Test
+    void testCreateJob() throws Exception {
+        await().untilAsserted(() -> assertThat(producerRegstrationTask.isRegisteredInIcs()).isTrue());
+        final String TYPE_ID = "PmDataOverKafka";
+
+        PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+
+        ConsumerJobInfo jobInfo = IntegrationWithKafka
+                .consumerJobInfoKafka(this.applicationConfig.getKafkaBootStrapServers(), TYPE_ID, filterData);
+
+        createInformationJobInIcs(JOB_ID, jobInfo);
+        await().untilAsserted(() -> assertThat(this.jobs.size()).isEqualTo(1));
+
+        deleteInformationJobInIcs(JOB_ID);
+        await().untilAsserted(() -> assertThat(this.jobs.size()).isZero());
+    }
+}
diff --git a/pmproducer/src/test/java/org/oran/pmproducer/IntegrationWithKafka.java b/pmproducer/src/test/java/org/oran/pmproducer/IntegrationWithKafka.java
new file mode 100644 (file)
index 0000000..5410485
--- /dev/null
@@ -0,0 +1,643 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
+
+import com.google.gson.JsonParser;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import lombok.Builder;
+
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.apache.kafka.common.serialization.ByteArraySerializer;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.oran.pmproducer.clients.AsyncRestClient;
+import org.oran.pmproducer.clients.AsyncRestClientFactory;
+import org.oran.pmproducer.clients.SecurityContext;
+import org.oran.pmproducer.configuration.ApplicationConfig;
+import org.oran.pmproducer.configuration.WebClientConfig;
+import org.oran.pmproducer.configuration.WebClientConfig.HttpProxyConfig;
+import org.oran.pmproducer.controllers.ProducerCallbacksController;
+import org.oran.pmproducer.controllers.ProducerCallbacksController.StatisticsCollection;
+import org.oran.pmproducer.datastore.DataStore;
+import org.oran.pmproducer.filter.PmReportFilter;
+import org.oran.pmproducer.r1.ConsumerJobInfo;
+import org.oran.pmproducer.repository.InfoType;
+import org.oran.pmproducer.repository.InfoTypes;
+import org.oran.pmproducer.repository.Job;
+import org.oran.pmproducer.repository.Job.Statistics;
+import org.oran.pmproducer.repository.Jobs;
+import org.oran.pmproducer.tasks.NewFileEvent;
+import org.oran.pmproducer.tasks.TopicListener;
+import org.oran.pmproducer.tasks.TopicListeners;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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.boot.test.context.TestConfiguration;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
+import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.test.context.TestPropertySource;
+
+import reactor.core.publisher.Flux;
+import reactor.kafka.sender.KafkaSender;
+import reactor.kafka.sender.SenderOptions;
+import reactor.kafka.sender.SenderRecord;
+
+@SuppressWarnings("java:S3577") // Rename class
+@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
+@TestPropertySource(properties = { //
+        "server.ssl.key-store=./config/keystore.jks", //
+        "app.webclient.trust-store=./config/truststore.jks", //
+        "app.configuration-filepath=./src/test/resources/test_application_configuration.json", //
+        "app.pm-files-path=./src/test/resources/", //
+        "app.s3.locksBucket=ropfilelocks", //
+        "app.pm-files-path=/tmp/dmaapadaptor", //
+        "app.s3.bucket=dmaaptest" //
+}) //
+class IntegrationWithKafka {
+
+    final static String PM_TYPE_ID = "PmDataOverKafka";
+
+    @Autowired
+    private ApplicationConfig applicationConfig;
+
+    @Autowired
+    private Jobs jobs;
+
+    @Autowired
+    private InfoTypes types;
+
+    @Autowired
+    private IcsSimulatorController icsSimulatorController;
+
+    @Autowired
+    private TopicListeners topicListeners;
+
+    @Autowired
+    private SecurityContext securityContext;
+
+    private static com.google.gson.Gson gson = new com.google.gson.GsonBuilder().disableHtmlEscaping().create();
+
+    private final Logger logger = LoggerFactory.getLogger(IntegrationWithKafka.class);
+
+    @LocalServerPort
+    int localServerHttpPort;
+
+    static class TestApplicationConfig extends ApplicationConfig {
+        @Override
+        public String getIcsBaseUrl() {
+            return thisProcessUrl();
+        }
+
+        @Override
+        public String getDmaapBaseUrl() {
+            return thisProcessUrl();
+        }
+
+        @Override
+        public String getSelfUrl() {
+            return thisProcessUrl();
+        }
+
+        private String thisProcessUrl() {
+            final String url = "https://localhost:" + getLocalServerHttpPort();
+            return url;
+        }
+    }
+
+    /**
+     * Overrides the BeanFactory.
+     */
+    @TestConfiguration
+    static class TestBeanFactory extends BeanFactory {
+
+        @Override
+        @Bean
+        public ServletWebServerFactory servletContainer() {
+            return new TomcatServletWebServerFactory();
+        }
+
+        @Override
+        @Bean
+        public ApplicationConfig getApplicationConfig() {
+            TestApplicationConfig cfg = new TestApplicationConfig();
+            return cfg;
+        }
+    }
+
+    private static class KafkaReceiver {
+        public final String OUTPUT_TOPIC;
+        private TopicListener.DataFromTopic receivedKafkaOutput;
+        private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+        private final ApplicationConfig applicationConfig;
+
+        int count = 0;
+
+        public KafkaReceiver(ApplicationConfig applicationConfig, String outputTopic, SecurityContext securityContext,
+                String groupId) {
+            this.applicationConfig = applicationConfig;
+            this.OUTPUT_TOPIC = outputTopic;
+
+            // Create a listener to the output topic. The TopicListener happens to be
+            // suitable for that,
+            InfoType type = InfoType.builder() //
+                    .id("TestReceiver_" + outputTopic) //
+                    .kafkaInputTopic(OUTPUT_TOPIC) //
+                    .build();
+
+            TopicListener topicListener = new TopicListener(applicationConfig, type);
+            if (groupId != null) {
+                topicListener.setKafkaGroupId(groupId);
+            }
+
+            topicListener.getFlux() //
+                    .map(this::unzip) //
+                    .doOnNext(this::set) //
+                    .doFinally(sig -> logger.info("Finally " + sig)) //
+                    .subscribe();
+        }
+
+        private TopicListener.DataFromTopic unzip(TopicListener.DataFromTopic receivedKafkaOutput) {
+            if (this.applicationConfig.isZipOutput() != receivedKafkaOutput.isZipped()) {
+                logger.error("********* ERROR received zipped: {}, exp zipped: {}", receivedKafkaOutput.isZipped(),
+                        this.applicationConfig.isZipOutput());
+            }
+
+            if (!receivedKafkaOutput.isZipped()) {
+                return receivedKafkaOutput;
+            }
+            try {
+                byte[] unzipped = TopicListener.unzip(receivedKafkaOutput.value);
+                return new TopicListener.DataFromTopic("typeId", null, unzipped, receivedKafkaOutput.key);
+            } catch (IOException e) {
+                logger.error("********* ERROR ", e.getMessage());
+                return null;
+            }
+        }
+
+        private void set(TopicListener.DataFromTopic receivedKafkaOutput) {
+            this.receivedKafkaOutput = receivedKafkaOutput;
+            this.count++;
+            if (logger.isDebugEnabled()) {
+                logger.debug("*** received data on topic: {}", OUTPUT_TOPIC);
+                logger.debug("*** received typeId: {}", receivedKafkaOutput.getTypeIdFromHeaders());
+            }
+        }
+
+        void reset() {
+            this.receivedKafkaOutput = new TopicListener.DataFromTopic("", null, null, null);
+            this.count = 0;
+        }
+    }
+
+    private static KafkaReceiver kafkaReceiver;
+    private static KafkaReceiver kafkaReceiver2;
+
+    @BeforeEach
+    void init() {
+        this.applicationConfig.setZipOutput(false);
+
+        if (kafkaReceiver == null) {
+            kafkaReceiver = new KafkaReceiver(this.applicationConfig, "ouputTopic", this.securityContext, null);
+            kafkaReceiver2 = new KafkaReceiver(this.applicationConfig, "ouputTopic2", this.securityContext, null);
+        }
+        kafkaReceiver.reset();
+        kafkaReceiver2.reset();
+
+        DataStore fileStore = this.dataStore();
+        fileStore.create(DataStore.Bucket.FILES).block();
+        fileStore.create(DataStore.Bucket.LOCKS).block();
+
+    }
+
+    @AfterEach
+    void reset() {
+        for (Job job : this.jobs.getAll()) {
+            this.icsSimulatorController.deleteJob(job.getId(), restClient());
+        }
+        await().untilAsserted(() -> assertThat(this.jobs.size()).isZero());
+        await().untilAsserted(() -> assertThat(this.topicListeners.getDataDistributors().keySet()).isEmpty());
+
+        this.icsSimulatorController.testResults.reset();
+
+        DataStore fileStore = dataStore();
+        fileStore.deleteBucket(DataStore.Bucket.FILES).block();
+        fileStore.deleteBucket(DataStore.Bucket.LOCKS).block();
+    }
+
+    private AsyncRestClient restClient(boolean useTrustValidation) {
+        WebClientConfig config = this.applicationConfig.getWebClientConfig();
+        HttpProxyConfig httpProxyConfig = HttpProxyConfig.builder() //
+                .httpProxyHost("") //
+                .httpProxyPort(0) //
+                .build();
+        config = WebClientConfig.builder() //
+                .keyStoreType(config.getKeyStoreType()) //
+                .keyStorePassword(config.getKeyStorePassword()) //
+                .keyStore(config.getKeyStore()) //
+                .keyPassword(config.getKeyPassword()) //
+                .isTrustStoreUsed(useTrustValidation) //
+                .trustStore(config.getTrustStore()) //
+                .trustStorePassword(config.getTrustStorePassword()) //
+                .httpProxyConfig(httpProxyConfig).build();
+
+        AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config, securityContext);
+        return restClientFactory.createRestClientNoHttpProxy(baseUrl());
+    }
+
+    private AsyncRestClient restClient() {
+        return restClient(false);
+    }
+
+    private String baseUrl() {
+        return "https://localhost:" + this.applicationConfig.getLocalServerHttpPort();
+    }
+
+    private static Object jsonObject(String json) {
+        try {
+            return JsonParser.parseString(json).getAsJsonObject();
+        } catch (Exception e) {
+            throw new NullPointerException(e.toString());
+        }
+    }
+
+    public static ConsumerJobInfo consumerJobInfoKafka(String kafkaBootstrapServers, String topic,
+            PmReportFilter.FilterData filterData) {
+        try {
+            Job.Parameters.KafkaDeliveryInfo deliveryInfo = Job.Parameters.KafkaDeliveryInfo.builder() //
+                    .topic(topic) //
+                    .bootStrapServers(kafkaBootstrapServers) //
+                    .build();
+            Job.Parameters param = Job.Parameters.builder() //
+                    .filter(filterData) //
+                    .deliveryInfo(deliveryInfo) //
+                    .build();
+
+            String str = gson.toJson(param);
+            Object parametersObj = jsonObject(str);
+
+            return new ConsumerJobInfo(PM_TYPE_ID, parametersObj, "owner", "");
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private SenderOptions<byte[], byte[]> kafkaSenderOptions() {
+        String bootstrapServers = this.applicationConfig.getKafkaBootStrapServers();
+
+        Map<String, Object> props = new HashMap<>();
+        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
+        // props.put(ProducerConfig.CLIENT_ID_CONFIG, "sample-producerx");
+        props.put(ProducerConfig.ACKS_CONFIG, "all");
+        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class);
+        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class);
+        return SenderOptions.create(props);
+    }
+
+    private SenderRecord<byte[], byte[], Integer> kafkaSenderRecord(String data, String key, String typeId) {
+        final InfoType infoType = this.types.get(typeId);
+        int correlationMetadata = 2;
+        return SenderRecord.create(new ProducerRecord<>(infoType.getKafkaInputTopic(), key.getBytes(), data.getBytes()),
+                correlationMetadata);
+    }
+
+    private void sendDataToKafka(Flux<SenderRecord<byte[], byte[], Integer>> dataToSend) {
+        final KafkaSender<byte[], byte[]> sender = KafkaSender.create(kafkaSenderOptions());
+
+        sender.send(dataToSend) //
+                .doOnError(e -> logger.error("Send failed", e)) //
+                .blockLast();
+
+        sender.close();
+    }
+
+    @SuppressWarnings("squid:S2925") // "Thread.sleep" should not be used in tests.
+    private static void waitForKafkaListener() throws InterruptedException {
+        Thread.sleep(4000);
+    }
+
+    private StatisticsCollection getStatistics() {
+        String targetUri = baseUrl() + ProducerCallbacksController.STATISTICS_URL;
+        String statsResp = restClient().get(targetUri).block();
+        StatisticsCollection stats = gson.fromJson(statsResp, StatisticsCollection.class);
+        return stats;
+    }
+
+    @Builder
+    static class CharacteristicsResult {
+        long noOfFilesPerSecond;
+        long noOfSentBytes;
+        long noOfSentGigaBytes;
+        long noOfSentObjects;
+        long inputFileSize;
+        long noOfReceivedFiles;
+        long noOfReceivedBytes;
+        long noOfSubscribers;
+        long sizeOfSentObj;
+        boolean zipOutput;
+    }
+
+    private CharacteristicsResult getCharacteristicsResult(Instant startTime) {
+        final long durationMs = Instant.now().toEpochMilli() - startTime.toEpochMilli();
+        StatisticsCollection stats = getStatistics();
+        long noOfSentBytes = 0;
+        long noOfSentObjs = 0;
+        for (Statistics s : stats.jobStatistics) {
+            noOfSentBytes += s.getNoOfSentBytes();
+            noOfSentObjs += s.getNoOfSentObjects();
+        }
+
+        Statistics oneJobsStats = stats.jobStatistics.iterator().next();
+
+        return CharacteristicsResult.builder() //
+                .noOfSentBytes(noOfSentBytes) //
+                .noOfSentObjects(noOfSentObjs) //
+                .noOfSentGigaBytes(noOfSentBytes / (1024 * 1024)) //
+                .noOfSubscribers(stats.jobStatistics.size()) //
+                .zipOutput(this.applicationConfig.isZipOutput()) //
+                .noOfFilesPerSecond((oneJobsStats.getNoOfReceivedObjects() * 1000) / durationMs) //
+                .noOfReceivedBytes(oneJobsStats.getNoOfReceivedBytes()) //
+                .inputFileSize(oneJobsStats.getNoOfReceivedBytes() / oneJobsStats.getNoOfReceivedObjects()) //
+                .noOfReceivedFiles(oneJobsStats.getNoOfReceivedObjects()) //
+                .sizeOfSentObj(oneJobsStats.getNoOfSentBytes() / oneJobsStats.getNoOfSentObjects()) //
+                .build();
+    }
+
+    private void printCharacteristicsResult(String str, Instant startTime, int noOfIterations) {
+        final long durationMs = Instant.now().toEpochMilli() - startTime.toEpochMilli();
+        logger.info("*** {} Duration ({} ms),  objects/second: {}", str, durationMs,
+                (noOfIterations * 1000) / durationMs);
+
+        System.out.println("--------------");
+        System.out.println(gson.toJson(getCharacteristicsResult(startTime)));
+        System.out.println("--------------");
+
+    }
+
+    @SuppressWarnings("squid:S2925") // "Thread.sleep" should not be used in tests.
+    @Test
+    void kafkaCharacteristics_pmFilter_s3() throws Exception {
+        // Filter PM reports and sent to two jobs over Kafka
+
+        final String JOB_ID = "kafkaCharacteristics";
+        final String JOB_ID2 = "kafkaCharacteristics2";
+
+        // Register producer, Register types
+        await().untilAsserted(() -> assertThat(icsSimulatorController.testResults.registrationInfo).isNotNull());
+        assertThat(icsSimulatorController.testResults.registrationInfo.supportedTypeIds).hasSize(this.types.size());
+
+        PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+
+        filterData.addMeasTypes("NRCellCU", "pmCounterNumber0");
+
+        this.icsSimulatorController.addJob(consumerJobInfoKafka(this.applicationConfig.getKafkaBootStrapServers(),
+                kafkaReceiver.OUTPUT_TOPIC, filterData), JOB_ID, restClient());
+        this.icsSimulatorController.addJob(consumerJobInfoKafka(this.applicationConfig.getKafkaBootStrapServers(),
+                kafkaReceiver2.OUTPUT_TOPIC, filterData), JOB_ID2, restClient());
+
+        await().untilAsserted(() -> assertThat(this.jobs.size()).isEqualTo(2));
+        waitForKafkaListener();
+
+        final int NO_OF_OBJECTS = 10;
+
+        Instant startTime = Instant.now();
+
+        final String FILE_NAME = "A20000626.2315+0200-2330+0200_HTTPS-6-73.json";
+
+        DataStore fileStore = dataStore();
+
+        fileStore.create(DataStore.Bucket.FILES).block();
+        fileStore.copyFileTo(Path.of("./src/test/resources/" + FILE_NAME), FILE_NAME).block();
+
+        String eventAsString = newFileEvent(FILE_NAME);
+        var dataToSend = Flux.range(1, NO_OF_OBJECTS).map(i -> kafkaSenderRecord(eventAsString, "key", PM_TYPE_ID));
+        sendDataToKafka(dataToSend);
+
+        while (kafkaReceiver.count < NO_OF_OBJECTS) {
+            logger.info("sleeping {}", kafkaReceiver.count);
+            Thread.sleep(1000 * 1);
+        }
+
+        String msgString = kafkaReceiver.receivedKafkaOutput.valueAsString();
+        assertThat(msgString).contains("pmCounterNumber0");
+        assertThat(msgString).doesNotContain("pmCounterNumber1");
+
+        printCharacteristicsResult("kafkaCharacteristics_pmFilter_s3", startTime, NO_OF_OBJECTS);
+        logger.info("***  kafkaReceiver2 :" + kafkaReceiver.count);
+
+        assertThat(kafkaReceiver.count).isEqualTo(NO_OF_OBJECTS);
+    }
+
+    @SuppressWarnings("squid:S2925") // "Thread.sleep" should not be used in tests.
+    @Test
+    void kafkaCharacteristics_manyPmJobs() throws Exception {
+        // Filter PM reports and sent to many jobs over Kafka
+
+        this.applicationConfig.setZipOutput(false);
+
+        // Register producer, Register types
+        await().untilAsserted(() -> assertThat(icsSimulatorController.testResults.registrationInfo).isNotNull());
+        assertThat(icsSimulatorController.testResults.registrationInfo.supportedTypeIds).hasSize(this.types.size());
+
+        PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+        final int NO_OF_COUNTERS = 2;
+        for (int i = 0; i < NO_OF_COUNTERS; ++i) {
+            filterData.addMeasTypes("NRCellCU", "pmCounterNumber" + i);
+        }
+
+        final int NO_OF_JOBS = 150;
+
+        ArrayList<KafkaReceiver> receivers = new ArrayList<>();
+        for (int i = 0; i < NO_OF_JOBS; ++i) {
+            final String outputTopic = "manyJobs_" + i;
+            this.icsSimulatorController.addJob(
+                    consumerJobInfoKafka(this.applicationConfig.getKafkaBootStrapServers(), outputTopic, filterData),
+                    outputTopic, restClient());
+            KafkaReceiver receiver = new KafkaReceiver(this.applicationConfig, outputTopic, this.securityContext, null);
+            receivers.add(receiver);
+        }
+
+        await().untilAsserted(() -> assertThat(this.jobs.size()).isEqualTo(NO_OF_JOBS));
+        waitForKafkaListener();
+
+        final int NO_OF_OBJECTS = 1000;
+
+        Instant startTime = Instant.now();
+
+        final String FILE_NAME = "A20000626.2315+0200-2330+0200_HTTPS-6-73.json.gz";
+
+        DataStore fileStore = dataStore();
+
+        fileStore.deleteBucket(DataStore.Bucket.FILES).block();
+        fileStore.create(DataStore.Bucket.FILES).block();
+        fileStore.copyFileTo(Path.of("./src/test/resources/" + FILE_NAME), FILE_NAME).block();
+
+        String eventAsString = newFileEvent(FILE_NAME);
+        var dataToSend = Flux.range(1, NO_OF_OBJECTS).map(i -> kafkaSenderRecord(eventAsString, "key", PM_TYPE_ID));
+        sendDataToKafka(dataToSend);
+
+        logger.info("sleeping {}", kafkaReceiver.count);
+        while (receivers.get(0).count < NO_OF_OBJECTS) {
+            if (kafkaReceiver.count > 0) {
+                logger.info("sleeping {}", receivers.get(0).count);
+            }
+
+            Thread.sleep(1000 * 1);
+        }
+
+        printCharacteristicsResult("kafkaCharacteristics_manyPmJobs", startTime, NO_OF_OBJECTS);
+
+        for (KafkaReceiver receiver : receivers) {
+            if (receiver.count != NO_OF_OBJECTS) {
+                System.out.println("** Unexpected no of jobs: " + receiver.OUTPUT_TOPIC + " " + receiver.count);
+            }
+        }
+    }
+
+    @SuppressWarnings("squid:S2925") // "Thread.sleep" should not be used in tests.
+    @Test
+    void kafkaCharacteristics_manyPmJobs_sharedTopic() throws Exception {
+        // Filter PM reports and sent to many jobs over Kafka
+
+        this.applicationConfig.setZipOutput(false);
+
+        // Register producer, Register types
+        await().untilAsserted(() -> assertThat(icsSimulatorController.testResults.registrationInfo).isNotNull());
+        assertThat(icsSimulatorController.testResults.registrationInfo.supportedTypeIds).hasSize(this.types.size());
+
+        final int NO_OF_JOBS = 150;
+        ArrayList<KafkaReceiver> receivers = new ArrayList<>();
+        for (int i = 0; i < NO_OF_JOBS; ++i) {
+            final String outputTopic = "kafkaCharacteristics_onePmJobs_manyReceivers";
+            final String jobId = "manyJobs_" + i;
+            PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+            filterData.addMeasTypes("NRCellCU", "pmCounterNumber" + i); // all counters will be added
+
+            this.icsSimulatorController.addJob(
+                    consumerJobInfoKafka(this.applicationConfig.getKafkaBootStrapServers(), outputTopic, filterData),
+                    jobId, restClient());
+
+            KafkaReceiver receiver =
+                    new KafkaReceiver(this.applicationConfig, outputTopic, this.securityContext, "group_" + i);
+            receivers.add(receiver);
+        }
+
+        await().untilAsserted(() -> assertThat(this.jobs.size()).isEqualTo(NO_OF_JOBS));
+        waitForKafkaListener();
+
+        final int NO_OF_OBJECTS = 1000;
+
+        Instant startTime = Instant.now();
+
+        final String FILE_NAME = "A20000626.2315+0200-2330+0200_HTTPS-6-73.json.gz";
+
+        DataStore fileStore = dataStore();
+
+        fileStore.deleteBucket(DataStore.Bucket.FILES).block();
+        fileStore.create(DataStore.Bucket.FILES).block();
+        fileStore.copyFileTo(Path.of("./src/test/resources/" + FILE_NAME), FILE_NAME).block();
+
+        String eventAsString = newFileEvent(FILE_NAME);
+        var dataToSend = Flux.range(1, NO_OF_OBJECTS).map(i -> kafkaSenderRecord(eventAsString, "key", PM_TYPE_ID));
+        sendDataToKafka(dataToSend);
+
+        logger.info("sleeping {}", kafkaReceiver.count);
+        for (KafkaReceiver receiver : receivers) {
+            while (receiver.count < NO_OF_OBJECTS) {
+                if (kafkaReceiver.count > 0) {
+                    logger.info("sleeping {}", receiver.count);
+                }
+
+                Thread.sleep(1000 * 1);
+            }
+        }
+
+        printCharacteristicsResult("kafkaCharacteristics_manyPmJobs", startTime, NO_OF_OBJECTS);
+
+        for (KafkaReceiver receiver : receivers) {
+            if (receiver.count != NO_OF_OBJECTS) {
+                System.out.println("** Unexpected no of objects: " + receiver.OUTPUT_TOPIC + " " + receiver.count);
+            }
+        }
+
+        Thread.sleep(1000 * 5);
+    }
+
+    private String newFileEvent(String fileName) {
+        NewFileEvent event = NewFileEvent.builder() //
+                .filename(fileName) //
+                .build();
+        return gson.toJson(event);
+    }
+
+    private DataStore dataStore() {
+        return DataStore.create(this.applicationConfig);
+    }
+
+    @Test
+    void testHistoricalData() throws Exception {
+        // test
+        final String JOB_ID = "testHistoricalData";
+
+        DataStore fileStore = dataStore();
+
+        fileStore.deleteBucket(DataStore.Bucket.FILES).block();
+        fileStore.create(DataStore.Bucket.FILES).block();
+        fileStore.create(DataStore.Bucket.LOCKS).block();
+
+        fileStore.copyFileTo(Path.of("./src/test/resources/pm_report.json"),
+                "O-DU-1122/A20000626.2315+0200-2330+0200_HTTPS-6-73.json").block();
+
+        fileStore.copyFileTo(Path.of("./src/test/resources/pm_report.json"), "OTHER_SOURCENAME/test.json").block();
+
+        await().untilAsserted(() -> assertThat(icsSimulatorController.testResults.registrationInfo).isNotNull());
+
+        PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+        filterData.getSourceNames().add("O-DU-1122");
+        filterData.setPmRopStartTime("1999-12-27T10:50:44.000-08:00");
+
+        filterData.setPmRopEndTime(OffsetDateTime.now().toString());
+
+        this.icsSimulatorController.addJob(consumerJobInfoKafka(this.applicationConfig.getKafkaBootStrapServers(),
+                kafkaReceiver.OUTPUT_TOPIC, filterData), JOB_ID, restClient());
+        await().untilAsserted(() -> assertThat(this.jobs.size()).isEqualTo(1));
+
+        await().untilAsserted(() -> assertThat(kafkaReceiver.count).isPositive());
+    }
+
+}
diff --git a/pmproducer/src/test/java/org/oran/pmproducer/filter/PmReportFilterTest.java b/pmproducer/src/test/java/org/oran/pmproducer/filter/PmReportFilterTest.java
new file mode 100644 (file)
index 0000000..01efe47
--- /dev/null
@@ -0,0 +1,287 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2023 Nordix Foundation
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oran.pmproducer.filter;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.protobuf.AbstractMessage.Builder;
+import com.google.protobuf.Message;
+import com.google.protobuf.MessageOrBuilder;
+import com.google.protobuf.util.JsonFormat;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Instant;
+
+import org.junit.jupiter.api.Test;
+import org.oran.pmproducer.tasks.TopicListener;
+import org.oran.pmproducer.tasks.TopicListener.DataFromTopic;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class PmReportFilterTest {
+
+    public static class ProtoJsonUtil {
+
+        /**
+         * Makes a Json from a given message or builder
+         *
+         * @param messageOrBuilder is the instance
+         * @return The string representation
+         * @throws IOException if any error occurs
+         */
+        public static String toJson(MessageOrBuilder messageOrBuilder) throws IOException {
+            return JsonFormat.printer().print(messageOrBuilder);
+        }
+
+        /**
+         * Makes a new instance of message based on the json and the class
+         *
+         * @param <T> is the class type
+         * @param json is the json instance
+         * @param clazz is the class instance
+         * @return An instance of T based on the json values
+         * @throws IOException if any error occurs
+         */
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        public static <T extends Message> T fromJson(String json, Class<T> clazz) throws IOException {
+            // https://stackoverflow.com/questions/27642021/calling-parsefrom-method-for-generic-protobuffer-class-in-java/33701202#33701202
+            Builder builder = null;
+            try {
+                // Since we are dealing with a Message type, we can call newBuilder()
+                builder = (Builder) clazz.getMethod("newBuilder").invoke(null);
+
+            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
+                    | NoSuchMethodException | SecurityException e) {
+                return null;
+            }
+
+            // The instance is placed into the builder values
+            JsonFormat.parser().ignoringUnknownFields().merge(json, builder);
+
+            // the instance will be from the build
+            return (T) builder.build();
+        }
+    }
+
+    private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    private static Gson gson = new GsonBuilder() //
+            .disableHtmlEscaping() //
+            .create(); //
+
+    private String filterReport(PmReportFilter filter) throws Exception {
+
+        TopicListener.DataFromTopic data =
+                new TopicListener.DataFromTopic("typeId", null, null, loadReport().getBytes());
+        FilteredData filtered = filter.filter(data);
+
+        String reportAfterFilter = gson.toJson(data.getCachedPmReport());
+        String reportBeforeFilter = gson.toJson(gson.fromJson(loadReport(), PmReport.class));
+
+        assertThat(reportAfterFilter).isEqualTo(reportBeforeFilter);
+
+        return filtered.getValueAString();
+    }
+
+    @Test
+    void testPmFilterMeasTypes() throws Exception {
+
+        PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+        filterData.addMeasTypes("UtranCell", "succImmediateAssignProcs");
+
+        PmReportFilter filter = new PmReportFilter(filterData);
+        String filtered = filterReport(filter);
+
+        assertThat(filtered).contains("succImmediateAssignProcs").doesNotContain("\"p\":2").contains("\"p\":1")
+                .contains("Gbg-997");
+
+        // Test that no report is returned if not meas types were found
+        filterData = new PmReportFilter.FilterData();
+        filterData.addMeasTypes("junk", "succImmediateAssignProcs");
+
+        filter = new PmReportFilter(filterData);
+        filtered = filterReport(filter);
+        assertThat(filtered).isEmpty();
+    }
+
+    @Test
+    void testMeasObjInstIds() throws Exception {
+        PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+        filterData.measObjInstIds.add("junk");
+        PmReportFilter filter = new PmReportFilter(filterData);
+        String filtered = filterReport(filter);
+        assertThat(filtered).isEmpty();
+
+        filterData = new PmReportFilter.FilterData();
+        filterData.measObjInstIds.add("UtranCell=Gbg-997");
+        filter = new PmReportFilter(filterData);
+        filtered = filterReport(filter);
+        assertThat(filtered).contains("Gbg-997").doesNotContain("Gbg-998");
+    }
+
+    @Test
+    void testMeasObjClass() throws Exception {
+        {
+            PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+            filterData.addMeasTypes("junk");
+            PmReportFilter filter = new PmReportFilter(filterData);
+            String filtered = filterReport(filter);
+            assertThat(filtered).isEmpty();
+        }
+
+        {
+            TopicListener.DataFromTopic data =
+                    new TopicListener.DataFromTopic("typeId", null, null, loadReport().getBytes());
+
+            PmReportFilter.FilterData utranCellFilter = new PmReportFilter.FilterData();
+            utranCellFilter.addMeasTypes("UtranCell");
+            FilteredData filtered = new PmReportFilter(utranCellFilter).filter(data);
+            assertThat(filtered.getValueAString()).contains("UtranCell").doesNotContain("ENodeBFunction");
+
+            PmReportFilter.FilterData eNodeBFilter = new PmReportFilter.FilterData();
+            eNodeBFilter.addMeasTypes("ENodeBFunction");
+            filtered = new PmReportFilter(eNodeBFilter).filter(data);
+            assertThat(filtered.getValueAString()).contains("ENodeBFunction").doesNotContain("UtranCell");
+        }
+    }
+
+    @Test
+    void testSourceNames() throws Exception {
+        PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+        filterData.sourceNames.add("junk");
+        PmReportFilter filter = new PmReportFilter(filterData);
+        String filtered = filterReport(filter);
+        assertThat(filtered).isEmpty();
+
+        filterData = new PmReportFilter.FilterData();
+        filterData.sourceNames.add("O-DU-1122");
+        filter = new PmReportFilter(filterData);
+        filtered = filterReport(filter);
+        assertThat(filtered).contains("O-DU-1122");
+    }
+
+    // @Test
+    void testSomeCharacteristics() throws Exception {
+        String path = "./src/test/resources/A20000626.2315+0200-2330+0200_HTTPS-6-73.json";
+
+        String pmReportJson = Files.readString(Path.of(path), Charset.defaultCharset());
+
+        int TIMES = 100000;
+
+        {
+            path = "./src/test/resources/A20000626.2315+0200-2330+0200_HTTPS-6-73.json.gz";
+            byte[] pmReportZipped = Files.readAllBytes(Path.of(path));
+
+            Instant startTime = Instant.now();
+            for (int i = 0; i < TIMES; ++i) {
+                TopicListener.unzip(pmReportZipped);
+            }
+
+            printDuration("Unzip", startTime, TIMES);
+        }
+        {
+
+            PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+            filterData.addMeasTypes("NRCellCU", "pmCounterNumber0");
+            PmReportFilter filter = new PmReportFilter(filterData);
+            DataFromTopic topicData = new DataFromTopic("typeId", null, null, pmReportJson.getBytes());
+
+            Instant startTime = Instant.now();
+            for (int i = 0; i < TIMES; ++i) {
+                filter.filter(topicData);
+            }
+            printDuration("PM Filter", startTime, TIMES);
+        }
+        {
+            Instant startTime = Instant.now();
+            for (int i = 0; i < TIMES; ++i) {
+                gson.fromJson(pmReportJson, PmReport.class);
+            }
+            printDuration("Json parsing", startTime, TIMES);
+        }
+
+    }
+
+    void printDuration(String str, Instant startTime, int noOfIterations) {
+        final long durationMs = Instant.now().toEpochMilli() - startTime.toEpochMilli();
+        logger.info("*** Duration (ms) " + str + " :" + durationMs + ", objects/second: "
+                + (noOfIterations * 1000) / durationMs);
+    }
+
+    @Test
+    void testMeasuredEntityDns() throws Exception {
+        PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+        filterData.measuredEntityDns.add("junk");
+        PmReportFilter filter = new PmReportFilter(filterData);
+        String filtered = filterReport(filter);
+        assertThat(filtered).isEmpty();
+
+        filterData = new PmReportFilter.FilterData();
+        filterData.measuredEntityDns.add("ManagedElement=RNC-Gbg-1");
+        filter = new PmReportFilter(filterData);
+        filtered = filterReport(filter);
+        assertThat(filtered).contains("ManagedElement=RNC-Gbg-1");
+    }
+
+    @Test
+    void testCrapInput() {
+        PmReportFilter.FilterData filterData = new PmReportFilter.FilterData();
+        PmReportFilter filter = new PmReportFilter(filterData);
+
+        FilteredData filtered = filter.filter(new TopicListener.DataFromTopic("typeId", null, null, "junk".getBytes()));
+        assertThat(filtered.isEmpty()).isTrue();
+
+        filtered = filter
+                .filter(new TopicListener.DataFromTopic("typeId", null, null, reQuote("{'msg': 'test'}").getBytes()));
+        assertThat(filtered.isEmpty()).isTrue();
+
+    }
+
+    private String reQuote(String str) {
+        return str.replaceAll("'", "\\\"");
+    }
+
+    @Test
+    void testParse() throws Exception {
+        com.google.gson.Gson gson = new com.google.gson.GsonBuilder().disableHtmlEscaping().create();
+        PmReport report = gson.fromJson(loadReport(), PmReport.class);
+
+        String dn = report.event.getPerf3gppFields().getMeasDataCollection().getMeasuredEntityDn();
+        String json = gson.toJson(report);
+        report = gson.fromJson(json, PmReport.class);
+
+        // '=' is escaped to unicode by gson. but converted back
+        assertThat(report.event.getPerf3gppFields().getMeasDataCollection().getMeasuredEntityDn()).isEqualTo(dn);
+    }
+
+    private String loadReport() throws Exception {
+        String path = "./src/test/resources/pm_report.json";
+        return Files.readString(Path.of(path), Charset.defaultCharset());
+    }
+
+}
diff --git a/pmproducer/src/test/resources/A20000626.2315+0200-2330+0200_HTTPS-6-73.json b/pmproducer/src/test/resources/A20000626.2315+0200-2330+0200_HTTPS-6-73.json
new file mode 100644 (file)
index 0000000..451279a
--- /dev/null
@@ -0,0 +1 @@
+{"event":{"commonEventHeader":{"domain":"","eventId":"","sequence":0,"eventName":"","sourceName":"HTTPST2-0","reportingEntityName":"","priority":"","startEpochMicrosec":15198378,"lastEpochMicrosec":151983,"version":"","vesEventListenerVersion":"","timeZoneOffset":"UTC+05.30"},"perf3gppFields":{"perf3gppFieldsVersion":"1.0","measDataCollection":{"granularityPeriod":900,"measuredEntityUserName":"","measuredEntityDn":"nodedntest","measuredEntitySoftwareVersion":"CXP2010174_1 R44B17","measInfoList":[{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=NRCellCU_GNBCUCP"},"measTypes":{"sMeasTypesList":["pmCounterNumber0","pmCounterNumber1","pmCounterNumber2","pmCounterNumber3","pmCounterNumber4","pmCounterNumber5","pmCounterNumber6","pmCounterNumber7","pmCounterNumber8","pmCounterNumber9","pmCounterNumber10","pmCounterNumber11","pmCounterNumber12","pmCounterNumber13","pmCounterNumber14","pmCounterNumber15","pmCounterNumber16","pmCounterNumber17","pmCounterNumber18","pmCounterNumber19","pmCounterNumber20","pmCounterNumber21","pmCounterNumber22","pmCounterNumber23","pmCounterNumber24","pmCounterNumber25","pmCounterNumber26","pmCounterNumber27","pmCounterNumber28","pmCounterNumber29","pmCounterNumber30","pmCounterNumber31","pmCounterNumber32","pmCounterNumber33","pmCounterNumber34","pmCounterNumber35","pmCounterNumber36","pmCounterNumber37","pmCounterNumber38","pmCounterNumber39","pmCounterNumber40","pmCounterNumber41","pmCounterNumber42","pmCounterNumber43","pmCounterNumber44","pmCounterNumber45","pmCounterNumber46","pmCounterNumber47","pmCounterNumber48","pmCounterNumber49","pmCounterNumber50","pmCounterNumber51","pmCounterNumber51Act","pmCounterNumber53","pmCounterNumber53Act","pmCounterNumber55","pmCounterNumber56","pmCounterNumber57","pmCounterNumber58","pmCounterNumber59","pmCounterNumber60","pmCounterNumber60IntgProt64kbps","pmCounterNumber62","pmCounterNumber63","pmCounterNumber64","pmCounterNumber65","pmCounterNumber66","pmCounterNumber67","pmCounterNumber68","pmCounterNumber69","pmCounterNumber70","pmCounterNumber71","pmCounterNumber72","pmCounterNumber73","pmCounterNumber74","pmCounterNumber75","pmCounterNumber76","pmCounterNumber77","pmCounterNumber78","pmCounterNumber79","pmCounterNumber80","pmCounterNumber81","pmCounterNumber82","pmCounterNumber83","pmCounterNumber84","pmCounterNumber84Em","pmCounterNumber84EmFbInd","pmCounterNumber87","pmCounterNumber88","pmCounterNumber89","pmCounterNumber90","pmCounterNumber91"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,GNBCUCPFunction=1,NRCellCU=32","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"180"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"},{"p":67,"sValue":"0"},{"p":68,"sValue":"0"},{"p":69,"sValue":"0"},{"p":70,"sValue":"0"},{"p":71,"sValue":"0"},{"p":72,"sValue":"0"},{"p":73,"sValue":"0"},{"p":74,"sValue":"0"},{"p":75,"sValue":"0"},{"p":76,"sValue":"0"},{"p":77,"sValue":"0"},{"p":78,"sValue":"0"},{"p":79,"sValue":"0"},{"p":80,"sValue":"0"},{"p":81,"sValue":"0"},{"p":82,"sValue":"0"},{"p":83,"sValue":"0"},{"p":84,"sValue":"0"},{"p":85,"sValue":"0"},{"p":86,"sValue":"0"},{"p":87,"sValue":"0"},{"p":88,"sValue":"0"},{"p":89,"sValue":"0"},{"p":90,"sValue":"0"},{"p":91,"sValue":"0"},{"p":92,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBCUCPFunction=1,NRCellCU=31","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"180"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"},{"p":67,"sValue":"0"},{"p":68,"sValue":"0"},{"p":69,"sValue":"0"},{"p":70,"sValue":"0"},{"p":71,"sValue":"0"},{"p":72,"sValue":"0"},{"p":73,"sValue":"0"},{"p":74,"sValue":"0"},{"p":75,"sValue":"0"},{"p":76,"sValue":"0"},{"p":77,"sValue":"0"},{"p":78,"sValue":"0"},{"p":79,"sValue":"0"},{"p":80,"sValue":"0"},{"p":81,"sValue":"0"},{"p":82,"sValue":"0"},{"p":83,"sValue":"0"},{"p":84,"sValue":"0"},{"p":85,"sValue":"0"},{"p":86,"sValue":"0"},{"p":87,"sValue":"0"},{"p":88,"sValue":"0"},{"p":89,"sValue":"0"},{"p":90,"sValue":"0"},{"p":91,"sValue":"0"},{"p":92,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBCUCPFunction=1,NRCellCU=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"180"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"},{"p":67,"sValue":"0"},{"p":68,"sValue":"0"},{"p":69,"sValue":"0"},{"p":70,"sValue":"0"},{"p":71,"sValue":"0"},{"p":72,"sValue":"0"},{"p":73,"sValue":"0"},{"p":74,"sValue":"0"},{"p":75,"sValue":"0"},{"p":76,"sValue":"0"},{"p":77,"sValue":"0"},{"p":78,"sValue":"0"},{"p":79,"sValue":"0"},{"p":80,"sValue":"0"},{"p":81,"sValue":"0"},{"p":82,"sValue":"0"},{"p":83,"sValue":"0"},{"p":84,"sValue":"0"},{"p":85,"sValue":"0"},{"p":86,"sValue":"0"},{"p":87,"sValue":"0"},{"p":88,"sValue":"0"},{"p":89,"sValue":"0"},{"p":90,"sValue":"0"},{"p":91,"sValue":"0"},{"p":92,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBCUCPFunction=1,NRCellCU=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"180"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"},{"p":67,"sValue":"0"},{"p":68,"sValue":"0"},{"p":69,"sValue":"0"},{"p":70,"sValue":"0"},{"p":71,"sValue":"0"},{"p":72,"sValue":"0"},{"p":73,"sValue":"0"},{"p":74,"sValue":"0"},{"p":75,"sValue":"0"},{"p":76,"sValue":"0"},{"p":77,"sValue":"0"},{"p":78,"sValue":"0"},{"p":79,"sValue":"0"},{"p":80,"sValue":"0"},{"p":81,"sValue":"0"},{"p":82,"sValue":"0"},{"p":83,"sValue":"0"},{"p":84,"sValue":"0"},{"p":85,"sValue":"0"},{"p":86,"sValue":"0"},{"p":87,"sValue":"0"},{"p":88,"sValue":"0"},{"p":89,"sValue":"0"},{"p":90,"sValue":"0"},{"p":91,"sValue":"0"},{"p":92,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=NRCellDU_GNBDU"},"measTypes":{"sMeasTypesList":["pmCounterNumber92","pmCounterNumber93","pmCounterNumber94","pmCounterNumber95","pmCounterNumber96","pmCounterNumber97","pmCounterNumber98","pmCounterNumber99","pmCounterNumber100","pmCounterNumber101","pmCounterNumber102","pmCounterNumber103","pmCounterNumber104","pmCounterNumber105","pmCounterNumber106","pmCounterNumber107","pmCounterNumber108","pmCounterNumber108Init","pmCounterNumber110","pmCounterNumber110Init","pmCounterNumber112","pmCounterNumber112Init","pmCounterNumber114","pmCounterNumber114Init","pmCounterNumber116","pmCounterNumber116Init","pmCounterNumber118","pmCounterNumber118Init","pmCounterNumber120","pmCounterNumber120Init","pmCounterNumber122","pmCounterNumber122Init","pmCounterNumber124","pmCounterNumber125","pmCounterNumber125Init","pmCounterNumber127","pmCounterNumber127Init","pmCounterNumber129","pmCounterNumber129Init","pmCounterNumber131","pmCounterNumber131Init","pmCounterNumber133","pmCounterNumber133Init","pmCounterNumber135","pmCounterNumber135Init","pmCounterNumber137","pmCounterNumber137Init","pmCounterNumber139","pmCounterNumber139Init","pmCounterNumber141","pmCounterNumber141Init","pmCounterNumber143","pmCounterNumber143Init","pmCounterNumber145","pmCounterNumber145Init","pmCounterNumber147","pmCounterNumber147Init","pmCounterNumber149","pmCounterNumber150","pmCounterNumber150Init","pmCounterNumber152","pmCounterNumber152Init","pmCounterNumber154","pmCounterNumber154Init","pmCounterNumber156","pmCounterNumber156Init","pmCounterNumber158","pmCounterNumber158Ext","pmCounterNumber160","pmCounterNumber161","pmCounterNumber162","pmCounterNumber163","pmCounterNumber164","pmCounterNumber165","pmCounterNumber166","pmCounterNumber1666","pmCounterNumber168","pmCounterNumber169","pmCounterNumber170","pmCounterNumber171","pmCounterNumber172","pmCounterNumber173","pmCounterNumber174","pmCounterNumber175","pmCounterNumber176","pmCounterNumber177","pmCounterNumber178","pmCounterNumber179","pmCounterNumber180","pmCounterNumber181","pmCounterNumber182","pmCounterNumber183","pmCounterNumber184","pmCounterNumber185","pmCounterNumber185Qos","pmCounterNumber185Samp","pmCounterNumber185SampQos","pmCounterNumber189","pmCounterNumber189Qos","pmCounterNumber191","pmCounterNumber191Qos","pmCounterNumber193","pmCounterNumber193Samp","pmCounterNumber195","pmCounterNumber195Ext","pmCounterNumber197","pmCounterNumber198","pmCounterNumber199","pmCounterNumber200","pmCounterNumber200BsrGrant","pmCounterNumber200PreemptGrant","pmCounterNumber200PucchSrGrant","pmCounterNumber204","pmCounterNumber205","pmCounterNumber206","pmCounterNumber207","pmCounterNumber208","pmCounterNumber209","pmCounterNumber210","pmCounterNumber211","pmCounterNumber212","pmCounterNumber213","pmCounterNumber214","pmCounterNumber215","pmCounterNumber216","pmCounterNumber216MacCe","pmCounterNumber218","pmCounterNumber219","pmCounterNumber220","pmCounterNumber221","pmCounterNumber222","pmCounterNumber223","pmCounterNumber224","pmCounterNumber225","pmCounterNumber226","pmCounterNumber227","pmCounterNumber228","pmCounterNumber228Forced","pmCounterNumber230","pmCounterNumber231","pmCounterNumber232","pmCounterNumber233","pmCounterNumber234","pmCounterNumber235","pmCounterNumber236","pmCounterNumber237","pmCounterNumber238","pmCounterNumber239","pmCounterNumber240","pmCounterNumber241","pmCounterNumber242","pmCounterNumber243","pmCounterNumber244","pmCounterNumber245","pmCounterNumber246","pmCounterNumber247","pmCounterNumber248","pmCounterNumber249","pmCounterNumber250","pmCounterNumber251","pmCounterNumber252","pmCounterNumber252Qos","pmCounterNumber254","pmCounterNumber254Qos"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=32","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":9,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":12,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":13,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":14,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"},{"p":67,"sValue":"0"},{"p":68,"sValue":"0"},{"p":69,"sValue":"0"},{"p":70,"sValue":"0"},{"p":71,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":72,"sValue":"0"},{"p":73,"sValue":"0"},{"p":74,"sValue":"0"},{"p":75,"sValue":"0"},{"p":76,"sValue":"0"},{"p":77,"sValue":"0"},{"p":78,"sValue":"0"},{"p":79,"sValue":"2880"},{"p":80,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":81,"sValue":"0,0,0,0,0,0,0,0"},{"p":82,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":83,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":84,"sValue":"0,0,0,0,0,0,0,0"},{"p":85,"sValue":"0,0,0,0,0,0,0,0"},{"p":86,"sValue":"0,0,0,0,0,0,0,0"},{"p":87,"sValue":"0,0,0,0,0,0,0,0"},{"p":88,"sValue":"0,0,0,0,0,0,0,0"},{"p":89,"sValue":"0,0,0,0,0,0,0,0"},{"p":90,"sValue":"0,0,0,0,0,0,0,0"},{"p":91,"sValue":"0,0,0,0,0,0,0,0"},{"p":92,"sValue":"0,0,0,0,0,0,0,0"},{"p":93,"sValue":"0"},{"p":94,"sValue":"0"},{"p":95,"sValue":"0"},{"p":96,"sValue":"0"},{"p":97,"sValue":"0"},{"p":98,"sValue":"0"},{"p":99,"sValue":"0"},{"p":100,"sValue":"0"},{"p":101,"sValue":"0"},{"p":102,"sValue":"0"},{"p":103,"sValue":"0"},{"p":104,"sValue":"0"},{"p":105,"sValue":"0"},{"p":106,"sValue":"0"},{"p":107,"sValue":"0"},{"p":108,"sValue":"0"},{"p":109,"sValue":"0"},{"p":110,"sValue":"0"},{"p":111,"sValue":"0"},{"p":112,"sValue":"0"},{"p":113,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":114,"sValue":"0"},{"p":115,"sValue":"0"},{"p":116,"sValue":"0"},{"p":117,"sValue":"179"},{"p":118,"sValue":"0"},{"p":119,"sValue":"0,0,0,0"},{"p":120,"sValue":"0,0,0,0"},{"p":121,"sValue":"0"},{"p":122,"sValue":"0"},{"p":123,"sValue":"0"},{"p":124,"sValue":"0"},{"p":125,"sValue":"0"},{"p":126,"sValue":"0"},{"p":127,"sValue":"0"},{"p":128,"sValue":"0"},{"p":129,"sValue":"0"},{"p":130,"sValue":"0"},{"p":131,"sValue":"0"},{"p":132,"sValue":"0"},{"p":133,"sValue":"0"},{"p":134,"sValue":"900,0,0,0,0"},{"p":135,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":136,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":137,"sValue":"1440001"},{"p":138,"sValue":"0"},{"p":139,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":140,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":141,"sValue":"0"},{"p":142,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":143,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":144,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":145,"sValue":"0"},{"p":146,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":147,"sValue":"0,0,0,0,0,0,0,0,0,0,0,1800001,0,0,0,0,0,0,0,0,0"},{"p":148,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":149,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":150,"sValue":"0,0,0,0,0,0,0,0"},{"p":151,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":152,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":153,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0"},{"p":154,"sValue":"0"},{"p":155,"sValue":"0"},{"p":156,"sValue":"0"},{"p":157,"sValue":"0"},{"p":158,"sValue":"0"},{"p":159,"sValue":"0"},{"p":160,"sValue":"0"},{"p":161,"sValue":"0"},{"p":162,"sValue":"0"},{"p":163,"sValue":"0"},{"p":164,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=31","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":9,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":12,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":13,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":14,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"},{"p":67,"sValue":"0"},{"p":68,"sValue":"0"},{"p":69,"sValue":"0"},{"p":70,"sValue":"0"},{"p":71,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":72,"sValue":"0"},{"p":73,"sValue":"0"},{"p":74,"sValue":"0"},{"p":75,"sValue":"0"},{"p":76,"sValue":"5376"},{"p":77,"sValue":"0"},{"p":78,"sValue":"0"},{"p":79,"sValue":"0"},{"p":80,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":81,"sValue":"0,0,0,0,0,0,0,0"},{"p":82,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":83,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":84,"sValue":"0,0,0,0,0,0,0,0"},{"p":85,"sValue":"0,0,0,0,0,0,0,0"},{"p":86,"sValue":"0,0,0,0,0,0,0,0"},{"p":87,"sValue":"0,0,0,0,0,0,0,0"},{"p":88,"sValue":"0,0,0,0,0,0,0,0"},{"p":89,"sValue":"0,0,0,0,0,0,0,0"},{"p":90,"sValue":"0,0,0,0,0,0,0,0"},{"p":91,"sValue":"0,0,0,0,0,0,0,0"},{"p":92,"sValue":"0,0,0,0,0,0,0,0"},{"p":93,"sValue":"0"},{"p":94,"sValue":"0"},{"p":95,"sValue":"0"},{"p":96,"sValue":"0"},{"p":97,"sValue":"0"},{"p":98,"sValue":"0"},{"p":99,"sValue":"0"},{"p":100,"sValue":"0"},{"p":101,"sValue":"0"},{"p":102,"sValue":"0"},{"p":103,"sValue":"0"},{"p":104,"sValue":"0"},{"p":105,"sValue":"0"},{"p":106,"sValue":"0"},{"p":107,"sValue":"0"},{"p":108,"sValue":"0"},{"p":109,"sValue":"0"},{"p":110,"sValue":"0"},{"p":111,"sValue":"0"},{"p":112,"sValue":"0"},{"p":113,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":114,"sValue":"0"},{"p":115,"sValue":"0"},{"p":116,"sValue":"0"},{"p":117,"sValue":"179"},{"p":118,"sValue":"0"},{"p":119,"sValue":"0,0,0,0"},{"p":120,"sValue":"0,0,0,0"},{"p":121,"sValue":"0"},{"p":122,"sValue":"0"},{"p":123,"sValue":"0"},{"p":124,"sValue":"0"},{"p":125,"sValue":"0"},{"p":126,"sValue":"0"},{"p":127,"sValue":"0"},{"p":128,"sValue":"0"},{"p":129,"sValue":"0"},{"p":130,"sValue":"0"},{"p":131,"sValue":"0"},{"p":132,"sValue":"0"},{"p":133,"sValue":"0"},{"p":134,"sValue":"900,0,0,0,0"},{"p":135,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":136,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":137,"sValue":"1440001"},{"p":138,"sValue":"0"},{"p":139,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":140,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":141,"sValue":"0"},{"p":142,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":143,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":144,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":145,"sValue":"0"},{"p":146,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":147,"sValue":"0,0,0,0,0,0,0,0,0,0,0,1800001,0,0,0,0,0,0,0,0,0"},{"p":148,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":149,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":150,"sValue":"0,0,0,0,0,0,0,0"},{"p":151,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":152,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":153,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0"},{"p":154,"sValue":"0"},{"p":155,"sValue":"0"},{"p":156,"sValue":"0"},{"p":157,"sValue":"0"},{"p":158,"sValue":"0"},{"p":159,"sValue":"0"},{"p":160,"sValue":"0"},{"p":161,"sValue":"0"},{"p":162,"sValue":"0"},{"p":163,"sValue":"0"},{"p":164,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":9,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":12,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":13,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":14,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"},{"p":67,"sValue":"0"},{"p":68,"sValue":"0"},{"p":69,"sValue":"0"},{"p":70,"sValue":"0"},{"p":71,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":72,"sValue":"0"},{"p":73,"sValue":"0"},{"p":74,"sValue":"0"},{"p":75,"sValue":"0"},{"p":76,"sValue":"0"},{"p":77,"sValue":"0"},{"p":78,"sValue":"0"},{"p":79,"sValue":"0"},{"p":80,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":81,"sValue":"0,0,0,0,0,0,0,0"},{"p":82,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":83,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":84,"sValue":"0,0,0,0,0,0,0,0"},{"p":85,"sValue":"0,0,0,0,0,0,0,0"},{"p":86,"sValue":"0,0,0,0,0,0,0,0"},{"p":87,"sValue":"0,0,0,0,0,0,0,0"},{"p":88,"sValue":"0,0,0,0,0,0,0,0"},{"p":89,"sValue":"0,0,0,0,0,0,0,0"},{"p":90,"sValue":"0,0,0,0,0,0,0,0"},{"p":91,"sValue":"0,0,0,0,0,0,0,0"},{"p":92,"sValue":"0,0,0,0,0,0,0,0"},{"p":93,"sValue":"0"},{"p":94,"sValue":"0"},{"p":95,"sValue":"0"},{"p":96,"sValue":"0"},{"p":97,"sValue":"0"},{"p":98,"sValue":"0"},{"p":99,"sValue":"0"},{"p":100,"sValue":"0"},{"p":101,"sValue":"0"},{"p":102,"sValue":"0"},{"p":103,"sValue":"0"},{"p":104,"sValue":"0"},{"p":105,"sValue":"0"},{"p":106,"sValue":"0"},{"p":107,"sValue":"0"},{"p":108,"sValue":"0"},{"p":109,"sValue":"0"},{"p":110,"sValue":"0"},{"p":111,"sValue":"0"},{"p":112,"sValue":"0"},{"p":113,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":114,"sValue":"0"},{"p":115,"sValue":"0"},{"p":116,"sValue":"0"},{"p":117,"sValue":"0"},{"p":118,"sValue":"0"},{"p":119,"sValue":"0,0,0,0"},{"p":120,"sValue":"0,0,0,0"},{"p":121,"sValue":"0"},{"p":122,"sValue":"0"},{"p":123,"sValue":"0"},{"p":124,"sValue":"0"},{"p":125,"sValue":"0"},{"p":126,"sValue":"0"},{"p":127,"sValue":"0"},{"p":128,"sValue":"0"},{"p":129,"sValue":"0"},{"p":130,"sValue":"0"},{"p":131,"sValue":"0"},{"p":132,"sValue":"0"},{"p":133,"sValue":"0"},{"p":134,"sValue":"0,0,0,0,0"},{"p":135,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":136,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":137,"sValue":"0"},{"p":138,"sValue":"0"},{"p":139,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":140,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":141,"sValue":"0"},{"p":142,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":143,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":144,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":145,"sValue":"0"},{"p":146,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":147,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":148,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":149,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":150,"sValue":"0,0,0,0,0,0,0,0"},{"p":151,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":152,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":153,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0"},{"p":154,"sValue":"0"},{"p":155,"sValue":"0"},{"p":156,"sValue":"0"},{"p":157,"sValue":"0"},{"p":158,"sValue":"0"},{"p":159,"sValue":"0"},{"p":160,"sValue":"0"},{"p":161,"sValue":"0"},{"p":162,"sValue":"0"},{"p":163,"sValue":"0"},{"p":164,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":9,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":12,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":13,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":14,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"},{"p":67,"sValue":"0"},{"p":68,"sValue":"0"},{"p":69,"sValue":"0"},{"p":70,"sValue":"0"},{"p":71,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":72,"sValue":"0"},{"p":73,"sValue":"0"},{"p":74,"sValue":"0"},{"p":75,"sValue":"0"},{"p":76,"sValue":"0"},{"p":77,"sValue":"0"},{"p":78,"sValue":"0"},{"p":79,"sValue":"0"},{"p":80,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":81,"sValue":"0,0,0,0,0,0,0,0"},{"p":82,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":83,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":84,"sValue":"0,0,0,0,0,0,0,0"},{"p":85,"sValue":"0,0,0,0,0,0,0,0"},{"p":86,"sValue":"0,0,0,0,0,0,0,0"},{"p":87,"sValue":"0,0,0,0,0,0,0,0"},{"p":88,"sValue":"0,0,0,0,0,0,0,0"},{"p":89,"sValue":"0,0,0,0,0,0,0,0"},{"p":90,"sValue":"0,0,0,0,0,0,0,0"},{"p":91,"sValue":"0,0,0,0,0,0,0,0"},{"p":92,"sValue":"0,0,0,0,0,0,0,0"},{"p":93,"sValue":"0"},{"p":94,"sValue":"0"},{"p":95,"sValue":"0"},{"p":96,"sValue":"0"},{"p":97,"sValue":"0"},{"p":98,"sValue":"0"},{"p":99,"sValue":"0"},{"p":100,"sValue":"0"},{"p":101,"sValue":"0"},{"p":102,"sValue":"0"},{"p":103,"sValue":"0"},{"p":104,"sValue":"0"},{"p":105,"sValue":"0"},{"p":106,"sValue":"0"},{"p":107,"sValue":"0"},{"p":108,"sValue":"0"},{"p":109,"sValue":"0"},{"p":110,"sValue":"0"},{"p":111,"sValue":"0"},{"p":112,"sValue":"0"},{"p":113,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":114,"sValue":"0"},{"p":115,"sValue":"0"},{"p":116,"sValue":"0"},{"p":117,"sValue":"0"},{"p":118,"sValue":"0"},{"p":119,"sValue":"0,0,0,0"},{"p":120,"sValue":"0,0,0,0"},{"p":121,"sValue":"0"},{"p":122,"sValue":"0"},{"p":123,"sValue":"0"},{"p":124,"sValue":"0"},{"p":125,"sValue":"0"},{"p":126,"sValue":"0"},{"p":127,"sValue":"0"},{"p":128,"sValue":"0"},{"p":129,"sValue":"0"},{"p":130,"sValue":"0"},{"p":131,"sValue":"0"},{"p":132,"sValue":"0"},{"p":133,"sValue":"0"},{"p":134,"sValue":"0,0,0,0,0"},{"p":135,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":136,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":137,"sValue":"0"},{"p":138,"sValue":"0"},{"p":139,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":140,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":141,"sValue":"0"},{"p":142,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":143,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":144,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":145,"sValue":"0"},{"p":146,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":147,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":148,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":149,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":150,"sValue":"0,0,0,0,0,0,0,0"},{"p":151,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":152,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":153,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0"},{"p":154,"sValue":"0"},{"p":155,"sValue":"0"},{"p":156,"sValue":"0"},{"p":157,"sValue":"0"},{"p":158,"sValue":"0"},{"p":159,"sValue":"0"},{"p":160,"sValue":"0"},{"p":161,"sValue":"0"},{"p":162,"sValue":"0"},{"p":163,"sValue":"0"},{"p":164,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=NRSectorCarrier_GNBDU"},"measTypes":{"sMeasTypesList":["pmCounterNumber256","pmCounterNumber257","pmCounterNumber258","pmCounterNumber259","pmCounterNumber260","pmCounterNumber261","pmCounterNumber262","pmCounterNumber263","pmCounterNumber263On","pmCounterNumber265","pmCounterNumber266","pmCounterNumber267","pmCounterNumber267Auto","pmCounterNumber267AutoCbrs","pmCounterNumber267Man","pmCounterNumber267ManCbrs"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRSectorCarrier=32","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0,0,0,0,15,0,0,0,0,0"},{"p":2,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":5,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"10000"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRSectorCarrier=31","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,15,0"},{"p":2,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":5,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"15000"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRSectorCarrier=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"15,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":2,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":5,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRSectorCarrier=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"15,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":2,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":5,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=EUtranCellFDD"},"measTypes":{"sMeasTypesList":["pmCounterNumber272","pmCounterNumber273","pmCounterNumber274","pmCounterNumber275","pmCounterNumber276","pmCounterNumber277","pmCounterNumber278"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,ENodeBFunction=1,EUtranCellFDD=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0"},{"p":2,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":3,"sValue":"0,0,0,0"},{"p":4,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":5,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":6,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"}]},{"measObjInstId":"ManagedElement=nodedntest,ENodeBFunction=1,EUtranCellFDD=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0"},{"p":2,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":3,"sValue":"0,0,0,0"},{"p":4,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":5,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":6,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=NRCellDU_GNBDU"},"measTypes":{"sMeasTypesList":["pmCounterNumber279","pmCounterNumber280","pmCounterNumber281","pmCounterNumber282","pmCounterNumber283","pmCounterNumber284","pmCounterNumber285","pmCounterNumber286","pmCounterNumber287","pmCounterNumber288","pmCounterNumber289","pmCounterNumber290","pmCounterNumber291","pmCounterNumber292","pmCounterNumber293","pmCounterNumber294","pmCounterNumber295","pmCounterNumber296","pmCounterNumber274"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=32","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":2,"sValue":"0,0,0,0"},{"p":3,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":4,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":5,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":6,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":9,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0,0,0,0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=31","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":2,"sValue":"0,0,0,0"},{"p":3,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":4,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":5,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":6,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":9,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0,0,0,0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":2,"sValue":"0,0,0,0"},{"p":3,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":4,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":5,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":6,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":9,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0,0,0,0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":2,"sValue":"0,0,0,0"},{"p":3,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":4,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":5,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":6,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":9,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0,0,0,0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=EUtranCellFDD"},"measTypes":{"sMeasTypesList":["pmCounterNumber297"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,ENodeBFunction=1,EUtranCellFDD=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"}]},{"measObjInstId":"ManagedElement=nodedntest,ENodeBFunction=1,EUtranCellFDD=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=NRCellDU_GNBDU"},"measTypes":{"sMeasTypesList":["pmCounterNumber297F0Distr","pmCounterNumber297F2Distr"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=32","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":2,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=31","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":2,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":2,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":2,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=GNBCUCPFunction_GNBCUCP"},"measTypes":{"sMeasTypesList":["pmCounterNumber300","pmCounterNumber301","pmCounterNumber302","pmCounterNumber303","pmCounterNumber304","pmCounterNumber305"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,GNBCUCPFunction=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"180"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=NRCellCU_GNBCUCP"},"measTypes":{"sMeasTypesList":["pmCounterNumber306","pmCounterNumber307","pmCounterNumber308","pmCounterNumber309","pmCounterNumber310","pmCounterNumber311","pmCounterNumber312","pmCounterNumber313","pmCounterNumber314","pmCounterNumber315","pmCounterNumber316","pmCounterNumber317","pmCounterNumber318","pmCounterNumber319","pmCounterNumber320","pmCounterNumber321","pmCounterNumber322","pmCounterNumber323","pmCounterNumber324","pmCounterNumber324Act","pmCounterNumber326","pmCounterNumber326Act","pmCounterNumber328","pmCounterNumber329","pmCounterNumber330","pmCounterNumber331","pmCounterNumber332","pmCounterNumber333","pmCounterNumber334","pmCounterNumber335","pmCounterNumber336","pmCounterNumber337","pmCounterNumber338","pmCounterNumber339","pmCounterNumber340","pmCounterNumber341","pmCounterNumber342","pmCounterNumber343","pmCounterNumber343Mos","pmCounterNumber345","pmCounterNumber345Mos","pmCounterNumber347","pmCounterNumber348","pmCounterNumber349","pmCounterNumber350","pmCounterNumber350Mos","pmCounterNumber350Reatt","pmCounterNumber350ReattMos","pmCounterNumber354","pmCounterNumber354Mos","pmCounterNumber301","pmCounterNumber302","pmCounterNumber303","pmCounterNumber304","pmCounterNumber305","pmCounterNumber356","pmCounterNumber357","pmCounterNumber358"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,GNBCUCPFunction=1,NRCellCU=32","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"180"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"180"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"180"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBCUCPFunction=1,NRCellCU=31","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"180"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"180"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"180"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBCUCPFunction=1,NRCellCU=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"180"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"180"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"180"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBCUCPFunction=1,NRCellCU=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"180"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"180"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"180"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=GNBDUFunction_GNBDU"},"measTypes":{"sMeasTypesList":["pmCounterNumber359","pmCounterNumber360","pmCounterNumber361","pmCounterNumber362","pmCounterNumber363","pmCounterNumber364","pmCounterNumber365","pmCounterNumber366","pmCounterNumber367","pmCounterNumber368","pmCounterNumber369","pmCounterNumber370","pmCounterNumber371","pmCounterNumber372","pmCounterNumber373","pmCounterNumber374","pmCounterNumber375","pmCounterNumber376","pmCounterNumber377"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"32"},{"p":6,"sValue":"16"},{"p":7,"sValue":"0"},{"p":8,"sValue":"16"},{"p":9,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=NRCellDU_GNBDU"},"measTypes":{"sMeasTypesList":["pmCounterNumber378","pmCounterNumber379","pmCounterNumber380","pmCounterNumber381","pmCounterNumber382","pmCounterNumber383","pmCounterNumber384","pmCounterNumber385","pmCounterNumber386","pmCounterNumber306","pmCounterNumber307","pmCounterNumber307ual","pmCounterNumber388","pmCounterNumber389","pmCounterNumber390","pmCounterNumber391","pmCounterNumber392","pmCounterNumber393","pmCounterNumber394","pmCounterNumber395","pmCounterNumber396","pmCounterNumber397","pmCounterNumber398","pmCounterNumber399","pmCounterNumber399Broadcasting","pmCounterNumber401","pmCounterNumber402","pmCounterNumber403","pmCounterNumber404","pmCounterNumber405","pmCounterNumber406","pmCounterNumber407","pmCounterNumber407Qos","pmCounterNumber409","pmCounterNumber410","pmCounterNumber410Drb","pmCounterNumber410DrbQos","pmCounterNumber413","pmCounterNumber413ResUe","pmCounterNumber415","pmCounterNumber416","pmCounterNumber417","pmCounterNumber418","pmCounterNumber419","pmCounterNumber420","pmCounterNumber421","pmCounterNumber422","pmCounterNumber423","pmCounterNumber424","pmCounterNumber425","pmCounterNumber426","pmCounterNumber427","pmCounterNumber428","pmCounterNumber429","pmCounterNumber430","pmCounterNumber431","pmCounterNumber432","pmCounterNumber433","pmCounterNumber434","pmCounterNumber435","pmCounterNumber436","pmCounterNumber437","pmCounterNumber438","pmCounterNumber439","pmCounterNumber440","pmCounterNumber441"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=32","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"1800000"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"1800001"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"3032642268"},{"p":18,"sValue":"20160056"},{"p":19,"sValue":"15120000"},{"p":20,"sValue":"816482268"},{"p":21,"sValue":"0"},{"p":22,"sValue":"2880"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"3780540"},{"p":26,"sValue":"2520"},{"p":27,"sValue":"0"},{"p":28,"sValue":"900,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"900,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"5760004"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":43,"sValue":"1440004"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":46,"sValue":"2,1,1,2,2,3,2,1,1,0,0,0"},{"p":47,"sValue":"15"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"60"},{"p":54,"sValue":"15"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"179"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=31","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"1800000"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"1800001"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"5110563822"},{"p":18,"sValue":"20160056"},{"p":19,"sValue":"15120000"},{"p":20,"sValue":"1375923822"},{"p":21,"sValue":"0"},{"p":22,"sValue":"5376"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"3780504"},{"p":26,"sValue":"2352"},{"p":27,"sValue":"0"},{"p":28,"sValue":"900,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"900,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"5760004"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":43,"sValue":"1440004"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":46,"sValue":"3,1,2,1,2,0,3,2,0,0,0,0"},{"p":47,"sValue":"14"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"56"},{"p":54,"sValue":"14"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"179"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"900"},{"p":12,"sValue":"900"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":46,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBDUFunction=1,NRCellDU=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"900"},{"p":12,"sValue":"900"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":46,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=PpControlLink_GNBCUUP"},"measTypes":{"sMeasTypesList":["pmCounterNumber310","pmCounterNumber311","pmCounterNumber312","pmCounterNumber313","pmCounterNumber314","pmCounterNumber347","pmCounterNumber348","pmCounterNumber349"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,GNBCUUPFunction=1,PpControlTermination=1,PpControlLink=internal","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"900"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"900"},{"p":8,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=EUtranCellFDD"},"measTypes":{"sMeasTypesList":["pmCounterNumber442","pmCounterNumber443","pmCounterNumber444","pmCounterNumber445","pmCounterNumber446","pmCounterNumber447","pmCounterNumber448","pmCounterNumber448PCell","pmCounterNumber448SCell","pmCounterNumber448Volte","pmCounterNumber452","pmCounterNumber453","pmCounterNumber454","pmCounterNumber455","pmCounterNumber114","pmCounterNumber456","pmCounterNumber457","pmCounterNumber458","pmCounterNumber122","pmCounterNumber459","pmCounterNumber460","pmCounterNumber461","pmCounterNumber462","pmCounterNumber131","pmCounterNumber463","pmCounterNumber464","pmCounterNumber143","pmCounterNumber145","pmCounterNumber147","pmCounterNumber14916qam","pmCounterNumber149256Qam","pmCounterNumber14964Qam","pmCounterNumber149Iua16qam","pmCounterNumber149IuaQpsk","pmCounterNumber149Qpsk","pmCounterNumber471","pmCounterNumber472","pmCounterNumber473","pmCounterNumber474","pmCounterNumber475","pmCounterNumber476","pmCounterNumber477","pmCounterNumber478","pmCounterNumber248","pmCounterNumber249","pmCounterNumber479","pmCounterNumber250","pmCounterNumber251"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,ENodeBFunction=1,EUtranCellFDD=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,ENodeBFunction=1,EUtranCellFDD=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=FieldReplaceableUnit"},"measTypes":{"sMeasTypesList":["pmCounterNumber480","pmCounterNumber481"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S3-1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"4,4,4"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S2-1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"4,4,4"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S1-1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"4,4,4"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":" , , "}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1","suspectFlag":"false","measResults":[{"p":1,"sValue":" "},{"p":2,"sValue":"4,4,4"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=RiEthernetPort"},"measTypes":{"sMeasTypesList":["pmCounterNumber482","pmCounterNumber483","pmCounterNumber484","pmCounterNumber485","pmCounterNumber486","pmCounterNumber487","pmCounterNumber488","pmCounterNumber489","pmCounterNumber490","pmCounterNumber491"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,RiPort=P,RiEthernetPort=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"51885"},{"p":3,"sValue":"4628315"},{"p":4,"sValue":"9190"},{"p":5,"sValue":"0"},{"p":6,"sValue":"15493"},{"p":7,"sValue":"1956503"},{"p":8,"sValue":"7326"},{"p":9,"sValue":"0"},{"p":10,"sValue":" "}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,RiPort=N,RiEthernetPort=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"37309"},{"p":3,"sValue":"2868954"},{"p":4,"sValue":"4551"},{"p":5,"sValue":"0"},{"p":6,"sValue":"915"},{"p":7,"sValue":"380361"},{"p":8,"sValue":"4549"},{"p":9,"sValue":"0"},{"p":10,"sValue":" "}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,RiPort=B,RiEthernetPort=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"15490"},{"p":3,"sValue":"1512626"},{"p":4,"sValue":"7326"},{"p":5,"sValue":"0"},{"p":6,"sValue":"51883"},{"p":7,"sValue":"3492085"},{"p":8,"sValue":"9190"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,RiPort=A,RiEthernetPort=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"916"},{"p":3,"sValue":"260228"},{"p":4,"sValue":"4549"},{"p":5,"sValue":"0"},{"p":6,"sValue":"37309"},{"p":7,"sValue":"2097258"},{"p":8,"sValue":"4551"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=RiLink"},"measTypes":{"sMeasTypesList":["pmCounterNumber492","pmCounterNumber493","pmCounterNumber494","pmCounterNumber495"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,RiLink=S3-1","suspectFlag":"false","measResults":[{"p":1,"sValue":"1077"},{"p":2,"sValue":"1076"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,RiLink=S2-1","suspectFlag":"false","measResults":[{"p":1,"sValue":"450"},{"p":2,"sValue":"450"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,RiLink=S1-1","suspectFlag":"false","measResults":[{"p":1,"sValue":"427"},{"p":2,"sValue":"427"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,RiLink=R608-2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"2147483647"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,RiLink=R608-1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"2147483647"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=RiPort"},"measTypes":{"sMeasTypesList":["pmCounterNumber496","pmCounterNumber497","pmCounterNumber498"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S3-1,RiPort=DATA_2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S3-1,RiPort=DATA_1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S2-1,RiPort=DATA_2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S2-1,RiPort=DATA_1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S1-1,RiPort=DATA_2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S1-1,RiPort=DATA_1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,RiPort=P","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,RiPort=N","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,RiPort=B","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,RiPort=A","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,RiPort=K","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,RiPort=C","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,RiPort=B","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,RiPort=A","suspectFlag":"false","measResults":[{"p":1,"sValue":"0,0,0,0,0,0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=SfpChannel"},"measTypes":{"sMeasTypesList":["pmCounterNumber499","pmCounterNumber500","pmCounterNumber501"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,SfpModule=P,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"2951"},{"p":2,"sValue":"3600"},{"p":3,"sValue":"2922"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,SfpModule=N,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"3018"},{"p":2,"sValue":"4200"},{"p":3,"sValue":"2953"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=K,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"2809"},{"p":2,"sValue":"3500"},{"p":3,"sValue":"2812"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S3-1,SfpModule=DATA_2,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S2-1,SfpModule=DATA_2,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S1-1,SfpModule=DATA_2,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S3-1,SfpModule=DATA_1,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"2810"},{"p":2,"sValue":"3100"},{"p":3,"sValue":"2830"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S2-1,SfpModule=DATA_1,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"2717"},{"p":2,"sValue":"3600"},{"p":3,"sValue":"2775"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S1-1,SfpModule=DATA_1,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"2700"},{"p":2,"sValue":"3900"},{"p":3,"sValue":"2773"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=C,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,SfpModule=B,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"2744"},{"p":2,"sValue":"3200"},{"p":3,"sValue":"2775"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=B,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"2863"},{"p":2,"sValue":"3800"},{"p":3,"sValue":"2898"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,SfpModule=A,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"2684"},{"p":2,"sValue":"3900"},{"p":3,"sValue":"2737"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=A,SfpChannel=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"2892"},{"p":2,"sValue":"4400"},{"p":3,"sValue":"2893"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=SfpModule"},"measTypes":{"sMeasTypesList":["pmCounterNumber502","pmCounterNumber503"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S3-1,SfpModule=DATA_2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S3-1,SfpModule=DATA_1","suspectFlag":"false","measResults":[{"p":1,"sValue":"1310"},{"p":2,"sValue":"32552"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S2-1,SfpModule=DATA_2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S2-1,SfpModule=DATA_1","suspectFlag":"false","measResults":[{"p":1,"sValue":"1430"},{"p":2,"sValue":"32904"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S1-1,SfpModule=DATA_2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S1-1,SfpModule=DATA_1","suspectFlag":"false","measResults":[{"p":1,"sValue":"1410"},{"p":2,"sValue":"33141"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,SfpModule=P","suspectFlag":"false","measResults":[{"p":1,"sValue":"1388"},{"p":2,"sValue":"32558"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,SfpModule=N","suspectFlag":"false","measResults":[{"p":1,"sValue":"1384"},{"p":2,"sValue":"32505"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,SfpModule=B","suspectFlag":"false","measResults":[{"p":1,"sValue":"1297"},{"p":2,"sValue":"32999"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,SfpModule=A","suspectFlag":"false","measResults":[{"p":1,"sValue":"1297"},{"p":2,"sValue":"33041"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=K","suspectFlag":"false","measResults":[{"p":1,"sValue":"1474"},{"p":2,"sValue":"32588"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=C","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=B","suspectFlag":"false","measResults":[{"p":1,"sValue":"1463"},{"p":2,"sValue":"32493"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=A","suspectFlag":"false","measResults":[{"p":1,"sValue":"1464"},{"p":2,"sValue":"32534"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=EthernetPort"},"measTypes":{"sMeasTypesList":["pmCounterNumber482","pmCounterNumber483","pmCounterNumber484","pmCounterNumber485","pmCounterNumber486","pmCounterNumber487","pmCounterNumber488","pmCounterNumber489","pmCounterNumber504","pmCounterNumber490","pmCounterNumber505","pmCounterNumber506","pmCounterNumber507","pmCounterNumber491"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Transport=1,EthernetPort=TN_IDL_B_1","suspectFlag":"false","measResults":[{"p":1,"sValue":"22345"},{"p":2,"sValue":"1057"},{"p":3,"sValue":"2921772"},{"p":4,"sValue":"4472"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"9147991"},{"p":8,"sValue":"17099"},{"p":9,"sValue":"0"},{"p":10,"sValue":"7627"},{"p":11,"sValue":"3582"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=InterfaceIPv4"},"measTypes":{"sMeasTypesList":["pmCounterNumber508","pmCounterNumber509","pmCounterNumber510","pmCounterNumber511","pmCounterNumber512","pmCounterNumber513","pmCounterNumber514","pmCounterNumber515","pmCounterNumber516","pmCounterNumber517"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Transport=1,Router=vr_OAM,InterfaceIPv4=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"314834"},{"p":2,"sValue":"4561"},{"p":3,"sValue":"8697703"},{"p":4,"sValue":"16811"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Transport=1,Router=vr_NR,InterfaceIPv4=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"19900"},{"p":2,"sValue":"388"},{"p":3,"sValue":"1260"},{"p":4,"sValue":"30"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Transport=1,Router=vr_LTE,InterfaceIPv4=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"26656"},{"p":2,"sValue":"574"},{"p":3,"sValue":"14580"},{"p":4,"sValue":"243"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Transport=1,Router=Node_Internal_F1,InterfaceIPv4=NRDU","suspectFlag":"false","measResults":[{"p":1,"sValue":" "},{"p":2,"sValue":" "},{"p":3,"sValue":" "},{"p":4,"sValue":" "},{"p":5,"sValue":" "},{"p":6,"sValue":" "},{"p":7,"sValue":" "},{"p":8,"sValue":" "},{"p":9,"sValue":" "},{"p":10,"sValue":" "}]},{"measObjInstId":"ManagedElement=nodedntest,Transport=1,Router=Node_Internal_F1,InterfaceIPv4=NRCUCP","suspectFlag":"false","measResults":[{"p":1,"sValue":" "},{"p":2,"sValue":" "},{"p":3,"sValue":" "},{"p":4,"sValue":" "},{"p":5,"sValue":" "},{"p":6,"sValue":" "},{"p":7,"sValue":" "},{"p":8,"sValue":" "},{"p":9,"sValue":" "},{"p":10,"sValue":" "}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=SctpAssociation"},"measTypes":{"sMeasTypesList":["pmCounterNumber518","pmCounterNumber519","pmCounterNumber520","pmCounterNumber521","pmCounterNumber522","pmCounterNumber523","pmCounterNumber524","pmCounterNumber525","pmCounterNumber526","pmCounterNumber527","pmCounterNumber528","pmCounterNumber529"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Transport=1,SctpEndpoint=F1_NRDU,SctpAssociation=38472-10.0.0.1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"833"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"87820"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"87820"},{"p":12,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Transport=1,SctpEndpoint=F1_NRCUCP,SctpAssociation=38472-10.0.0.2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"833"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"87820"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"87820"},{"p":12,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=VlanPort"},"measTypes":{"sMeasTypesList":["pmCounterNumber482","pmCounterNumber483","pmCounterNumber484","pmCounterNumber485","pmCounterNumber486","pmCounterNumber487","pmCounterNumber488","pmCounterNumber489","pmCounterNumber507"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Transport=1,VlanPort=vr_OAM.IF1","suspectFlag":"false","measResults":[{"p":1,"sValue":"1996"},{"p":2,"sValue":"341"},{"p":3,"sValue":"657544"},{"p":4,"sValue":"4205"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"9067777"},{"p":8,"sValue":"16814"},{"p":9,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Transport=1,VlanPort=vr_NR.IF1","suspectFlag":"false","measResults":[{"p":1,"sValue":"8457"},{"p":2,"sValue":"343"},{"p":3,"sValue":"711892"},{"p":4,"sValue":"33"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"2368"},{"p":8,"sValue":"37"},{"p":9,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,Transport=1,VlanPort=vr_LTE.IF1","suspectFlag":"false","measResults":[{"p":1,"sValue":"11894"},{"p":2,"sValue":"343"},{"p":3,"sValue":"1006906"},{"p":4,"sValue":"234"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"20246"},{"p":8,"sValue":"248"},{"p":9,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=ConsumedEnergyMeasurement"},"measTypes":{"sMeasTypesList":["pmCounterNumber530","pmCounterNumber530Accumulated","pmCounterNumber532"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,NodeSupport=1,ConsumedEnergyMeasurement=1","suspectFlag":"true","measResults":[{"p":1,"sValue":" "},{"p":2,"sValue":" "},{"p":3,"sValue":" , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , "}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=EnergyMeter"},"measTypes":{"sMeasTypesList":["pmCounterNumber530","pmCounterNumber530Accumulated","pmCounterNumber532","pmCounterNumber533"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,EnergyMeter=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"83"},{"p":2,"sValue":"34846"},{"p":3,"sValue":"333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,334,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333"},{"p":4,"sValue":"52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S1-1,EnergyMeter=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"12"},{"p":2,"sValue":"4947"},{"p":3,"sValue":"47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47"},{"p":4,"sValue":"53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S2-1,EnergyMeter=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"13"},{"p":2,"sValue":"5394"},{"p":3,"sValue":"52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52"},{"p":4,"sValue":"54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=Radio-S3-1,EnergyMeter=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"22"},{"p":2,"sValue":"9338"},{"p":3,"sValue":"89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89"},{"p":4,"sValue":"53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,52,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,52,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=R608,EnergyMeter=1","suspectFlag":"true","measResults":[{"p":1,"sValue":" "},{"p":2,"sValue":" "},{"p":3,"sValue":" , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , "},{"p":4,"sValue":" , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , "}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=SupportUnit"},"measTypes":{"sMeasTypesList":["pmCounterNumber534"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,SupportUnit=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"18,18,18"}]},{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,SupportUnit=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"30,30,30"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=BbProcessingResource"},"measTypes":{"sMeasTypesList":["pmCounterNumber535","pmCounterNumber367","pmCounterNumber368","pmCounterNumber369","pmCounterNumber370","pmCounterNumber536","pmCounterNumber537","pmCounterNumber538","pmCounterNumber539","pmCounterNumber540","pmCounterNumber541","pmCounterNumber542","pmCounterNumber543","pmCounterNumber374","pmCounterNumber375","pmCounterNumber376","pmCounterNumber377","pmCounterNumber544","pmCounterNumber545","pmCounterNumber546","pmCounterNumber547","pmCounterNumber548","pmCounterNumber549","pmCounterNumber550","pmCounterNumber551"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,BbProcessingResource=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":20,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=ENodeBFunction"},"measTypes":{"sMeasTypesList":["pmCounterNumber359Aas","pmCounterNumber360Aas","pmCounterNumber554","pmCounterNumber555","pmCounterNumber365Actual","pmCounterNumber366Actual","pmCounterNumber558","pmCounterNumber559","pmCounterNumber560","pmCounterNumber561","pmCounterNumber562","pmCounterNumber563","pmCounterNumber564","pmCounterNumber565","pmCounterNumber566","pmCounterNumber567","pmCounterNumber568","pmCounterNumber569","pmCounterNumber570","pmCounterNumber571","pmCounterNumber572","pmCounterNumber573"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,ENodeBFunction=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"5"},{"p":8,"sValue":"900,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":9,"sValue":"180"},{"p":10,"sValue":"0"},{"p":11,"sValue":"8000"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"10000"},{"p":15,"sValue":"1800"},{"p":16,"sValue":"10000"},{"p":17,"sValue":"1800"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=EUtranCellFDD"},"measTypes":{"sMeasTypesList":["pmCounterNumber306","pmCounterNumber307","pmCounterNumber574","pmCounterNumber575","pmCounterNumber576","pmCounterNumber577","pmCounterNumber578","pmCounterNumber579","pmCounterNumber580","pmCounterNumber581","pmCounterNumber582","pmCounterNumber33","pmCounterNumber34","pmCounterNumber329","pmCounterNumber330","pmCounterNumber583","pmCounterNumber583Arp","pmCounterNumber583Csfb","pmCounterNumber583CsfbArp","pmCounterNumber583CsfbQci","pmCounterNumber583HoOngoing","pmCounterNumber583HoOngoingArp","pmCounterNumber583HoOngoingQci","pmCounterNumber583Qci","pmCounterNumber592","pmCounterNumber592Arp","pmCounterNumber594","pmCounterNumber595","pmCounterNumber596","pmCounterNumber597","pmCounterNumber598","pmCounterNumber599","pmCounterNumber599Arp","pmCounterNumber599Qci","pmCounterNumber602","pmCounterNumber602Arp","pmCounterNumber604","pmCounterNumber605","pmCounterNumber606","pmCounterNumber607","pmCounterNumber608","pmCounterNumber609","pmCounterNumber610","pmCounterNumber611","pmCounterNumber612","pmCounterNumber613","pmCounterNumber614","pmCounterNumber615","pmCounterNumber616","pmCounterNumber617","pmCounterNumber618","pmCounterNumber619","pmCounterNumber620","pmCounterNumber621","pmCounterNumber409","pmCounterNumber622","pmCounterNumber623","pmCounterNumber624","pmCounterNumber625","pmCounterNumber626","pmCounterNumber627","pmCounterNumber413ResUe","pmCounterNumber570","pmCounterNumber213Ce","pmCounterNumber629","pmCounterNumber630","pmCounterNumber631","pmCounterNumber631Uu","pmCounterNumber633","pmCounterNumber634","pmCounterNumber635","pmCounterNumber636","pmCounterNumber637","pmCounterNumber637FiltQci","pmCounterNumber637LastTTI","pmCounterNumber637LastTTIQci","pmCounterNumber641","pmCounterNumber642","pmCounterNumber643","pmCounterNumber350","pmCounterNumber350Ce","pmCounterNumber350Dta","pmCounterNumber350DtaCe","pmCounterNumber350Reatt","pmCounterNumber350ReattCe","pmCounterNumber350ReattDta","pmCounterNumber350ReattDtaCe","pmCounterNumber650","pmCounterNumber650ActiveUsers","pmCounterNumber652","pmCounterNumber652Ce","pmCounterNumber654","pmCounterNumber654Ce","pmCounterNumber354","pmCounterNumber354Ce","pmCounterNumber354Dta","pmCounterNumber354DtaCe","pmCounterNumber354GummeiNative","pmCounterNumber660","pmCounterNumber661","pmCounterNumber662","pmCounterNumber663","pmCounterNumber664","pmCounterNumber664Ce","pmCounterNumber664Dta","pmCounterNumber664DtaCe","pmCounterNumber664Em","pmCounterNumber664Hpa","pmCounterNumber664Mod","pmCounterNumber664ModCe","pmCounterNumber664Mos","pmCounterNumber664Mta","pmCounterNumber664MtaCe","pmCounterNumber675","pmCounterNumber675Ce","pmCounterNumber677","pmCounterNumber677Ce","pmCounterNumber677Dta","pmCounterNumber677DtaCe","pmCounterNumber677Em","pmCounterNumber677Hpa","pmCounterNumber677Mod","pmCounterNumber677ModCe","pmCounterNumber677Mos","pmCounterNumber677Mta","pmCounterNumber677MtaCe","pmCounterNumber688","pmCounterNumber689","pmCounterNumber690","pmCounterNumber691","pmCounterNumber692","pmCounterNumber693","pmCounterNumber694","pmCounterNumber694Em","pmCounterNumber696","pmCounterNumber696Em","pmCounterNumber698","pmCounterNumber699","pmCounterNumber699Em","pmCounterNumber701","pmCounterNumber701Em","pmCounterNumber703","pmCounterNumber704","pmCounterNumber705","pmCounterNumber706","pmCounterNumber7062","pmCounterNumber708","pmCounterNumber709","pmCounterNumber710","pmCounterNumber711","pmCounterNumber712","pmCounterNumber713","pmCounterNumber714","pmCounterNumber715","pmCounterNumber716","pmCounterNumber717","pmCounterNumber718","pmCounterNumber719","pmCounterNumber720"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,ENodeBFunction=1,EUtranCellFDD=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"900"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0,0,0,0"},{"p":48,"sValue":"0,0,0,0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0,0,0,0"},{"p":54,"sValue":"0,0,0,0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":59,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0,0,0,0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"},{"p":67,"sValue":"0"},{"p":68,"sValue":"0"},{"p":69,"sValue":"0"},{"p":70,"sValue":"0"},{"p":71,"sValue":"0"},{"p":72,"sValue":"0"},{"p":73,"sValue":"0"},{"p":74,"sValue":"0"},{"p":75,"sValue":"0"},{"p":76,"sValue":"0"},{"p":77,"sValue":"0"},{"p":78,"sValue":"0"},{"p":79,"sValue":"0"},{"p":80,"sValue":"0"},{"p":81,"sValue":"0,0,0,0"},{"p":82,"sValue":"0"},{"p":83,"sValue":"0,0,0,0"},{"p":84,"sValue":"0"},{"p":85,"sValue":"0,0,0,0"},{"p":86,"sValue":"0"},{"p":87,"sValue":"0,0,0,0"},{"p":88,"sValue":"0"},{"p":89,"sValue":"0"},{"p":90,"sValue":"0"},{"p":91,"sValue":"0,0,0,0"},{"p":92,"sValue":"0"},{"p":93,"sValue":"0,0,0,0"},{"p":94,"sValue":"0"},{"p":95,"sValue":"0,0,0,0"},{"p":96,"sValue":"0"},{"p":97,"sValue":"0,0,0,0"},{"p":98,"sValue":"0"},{"p":99,"sValue":"0"},{"p":100,"sValue":"0"},{"p":101,"sValue":"0"},{"p":102,"sValue":"0"},{"p":103,"sValue":"0"},{"p":104,"sValue":"0,0,0,0"},{"p":105,"sValue":"0"},{"p":106,"sValue":"0,0,0,0"},{"p":107,"sValue":"0"},{"p":108,"sValue":"0"},{"p":109,"sValue":"0"},{"p":110,"sValue":"0,0,0,0"},{"p":111,"sValue":"0"},{"p":112,"sValue":"0"},{"p":113,"sValue":"0,0,0,0"},{"p":114,"sValue":"0"},{"p":115,"sValue":"0,0,0,0"},{"p":116,"sValue":"0"},{"p":117,"sValue":"0,0,0,0"},{"p":118,"sValue":"0"},{"p":119,"sValue":"0,0,0,0"},{"p":120,"sValue":"0"},{"p":121,"sValue":"0"},{"p":122,"sValue":"0"},{"p":123,"sValue":"0,0,0,0"},{"p":124,"sValue":"0"},{"p":125,"sValue":"0"},{"p":126,"sValue":"0,0,0,0"},{"p":127,"sValue":"0"},{"p":128,"sValue":"0"},{"p":129,"sValue":"0"},{"p":130,"sValue":"0"},{"p":131,"sValue":"0"},{"p":132,"sValue":"0"},{"p":133,"sValue":"0"},{"p":134,"sValue":"0"},{"p":135,"sValue":"0"},{"p":136,"sValue":"0"},{"p":137,"sValue":"0"},{"p":138,"sValue":"0"},{"p":139,"sValue":"0"},{"p":140,"sValue":"0"},{"p":141,"sValue":"0"},{"p":142,"sValue":"0"},{"p":143,"sValue":"0"},{"p":144,"sValue":"0"},{"p":145,"sValue":"0"},{"p":146,"sValue":"0"},{"p":147,"sValue":"0"},{"p":148,"sValue":"0"},{"p":149,"sValue":"0"},{"p":150,"sValue":"0"},{"p":151,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":152,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":153,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":154,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":155,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":156,"sValue":"0"},{"p":157,"sValue":"0"},{"p":158,"sValue":"0,0,0,0,0,0,0,0"},{"p":159,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,ENodeBFunction=1,EUtranCellFDD=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"900"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0,0,0,0"},{"p":48,"sValue":"0,0,0,0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0,0,0,0"},{"p":54,"sValue":"0,0,0,0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":59,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0,0,0,0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"},{"p":67,"sValue":"0"},{"p":68,"sValue":"0"},{"p":69,"sValue":"0"},{"p":70,"sValue":"0"},{"p":71,"sValue":"0"},{"p":72,"sValue":"0"},{"p":73,"sValue":"0"},{"p":74,"sValue":"0"},{"p":75,"sValue":"0"},{"p":76,"sValue":"0"},{"p":77,"sValue":"0"},{"p":78,"sValue":"0"},{"p":79,"sValue":"0"},{"p":80,"sValue":"0"},{"p":81,"sValue":"0,0,0,0"},{"p":82,"sValue":"0"},{"p":83,"sValue":"0,0,0,0"},{"p":84,"sValue":"0"},{"p":85,"sValue":"0,0,0,0"},{"p":86,"sValue":"0"},{"p":87,"sValue":"0,0,0,0"},{"p":88,"sValue":"0"},{"p":89,"sValue":"0"},{"p":90,"sValue":"0"},{"p":91,"sValue":"0,0,0,0"},{"p":92,"sValue":"0"},{"p":93,"sValue":"0,0,0,0"},{"p":94,"sValue":"0"},{"p":95,"sValue":"0,0,0,0"},{"p":96,"sValue":"0"},{"p":97,"sValue":"0,0,0,0"},{"p":98,"sValue":"0"},{"p":99,"sValue":"0"},{"p":100,"sValue":"0"},{"p":101,"sValue":"0"},{"p":102,"sValue":"0"},{"p":103,"sValue":"0"},{"p":104,"sValue":"0,0,0,0"},{"p":105,"sValue":"0"},{"p":106,"sValue":"0,0,0,0"},{"p":107,"sValue":"0"},{"p":108,"sValue":"0"},{"p":109,"sValue":"0"},{"p":110,"sValue":"0,0,0,0"},{"p":111,"sValue":"0"},{"p":112,"sValue":"0"},{"p":113,"sValue":"0,0,0,0"},{"p":114,"sValue":"0"},{"p":115,"sValue":"0,0,0,0"},{"p":116,"sValue":"0"},{"p":117,"sValue":"0,0,0,0"},{"p":118,"sValue":"0"},{"p":119,"sValue":"0,0,0,0"},{"p":120,"sValue":"0"},{"p":121,"sValue":"0"},{"p":122,"sValue":"0"},{"p":123,"sValue":"0,0,0,0"},{"p":124,"sValue":"0"},{"p":125,"sValue":"0"},{"p":126,"sValue":"0,0,0,0"},{"p":127,"sValue":"0"},{"p":128,"sValue":"0"},{"p":129,"sValue":"0"},{"p":130,"sValue":"0"},{"p":131,"sValue":"0"},{"p":132,"sValue":"0"},{"p":133,"sValue":"0"},{"p":134,"sValue":"0"},{"p":135,"sValue":"0"},{"p":136,"sValue":"0"},{"p":137,"sValue":"0"},{"p":138,"sValue":"0"},{"p":139,"sValue":"0"},{"p":140,"sValue":"0"},{"p":141,"sValue":"0"},{"p":142,"sValue":"0"},{"p":143,"sValue":"0"},{"p":144,"sValue":"0"},{"p":145,"sValue":"0"},{"p":146,"sValue":"0"},{"p":147,"sValue":"0"},{"p":148,"sValue":"0"},{"p":149,"sValue":"0"},{"p":150,"sValue":"0"},{"p":151,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":152,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":153,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":154,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":155,"sValue":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"},{"p":156,"sValue":"0"},{"p":157,"sValue":"0"},{"p":158,"sValue":"0,0,0,0,0,0,0,0"},{"p":159,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=BbProcessingResource"},"measTypes":{"sMeasTypesList":["pmCounterNumber550Burst","pmCounterNumber550Format","pmCounterNumber550X2Fwd","pmCounterNumber724","pmCounterNumber725"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,Equipment=1,FieldReplaceableUnit=BB-1,BbProcessingResource=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=EUtranCellFDD"},"measTypes":{"sMeasTypesList":["pmCounterNumber726","pmCounterNumber727","pmCounterNumber728","pmCounterNumber729","pmCounterNumber730","pmCounterNumber731","pmCounterNumber732","pmCounterNumber733","pmCounterNumber734","pmCounterNumber735","pmCounterNumber629CatMDrxNoSyncQci","pmCounterNumber629CatMDrxSyncQci","pmCounterNumber629CatMNoDrxNoSyncQci","pmCounterNumber629CatMNoDrxSyncQci","pmCounterNumber629DrxNoSyncQci","pmCounterNumber629DrxSync","pmCounterNumber629DrxSyncQci","pmCounterNumber629NoDrxNoSyncQci","pmCounterNumber629NoDrxSyncQci","pmCounterNumber629Qci","pmCounterNumber630CatMDrxNoSyncQci","pmCounterNumber630CatMDrxSyncQci","pmCounterNumber630CatMNoDrxNoSyncQci","pmCounterNumber630CatMNoDrxSyncQci","pmCounterNumber630DrxNoSyncQci","pmCounterNumber630DrxSync","pmCounterNumber630DrxSyncQci","pmCounterNumber630NoDrxNoSyncQci","pmCounterNumber630NoDrxSyncQci","pmCounterNumber630Qci","pmCounterNumber756","pmCounterNumber756Qci","pmCounterNumber758","pmCounterNumber758Qci","pmCounterNumber631Qci","pmCounterNumber631UuQci","pmCounterNumber762","pmCounterNumber633Limitations","pmCounterNumber633MissingPdus2Qci","pmCounterNumber633Qci","pmCounterNumber633RohcFail2Qci","pmCounterNumber633SrbTooLarge","pmCounterNumber768","pmCounterNumber769","pmCounterNumber770","pmCounterNumber771","pmCounterNumber771Qci","pmCounterNumber634Qci","pmCounterNumber635Qci","pmCounterNumber775","pmCounterNumber637Ca","pmCounterNumber637LastTTICa","pmCounterNumber637Qci","pmCounterNumber637TransPlmn0","pmCounterNumber637TransPlmn1","pmCounterNumber637TransPlmn2","pmCounterNumber637TransPlmn3","pmCounterNumber637TransPlmn4","pmCounterNumber637TransPlmn5","pmCounterNumber637TransPlmn6","pmCounterNumber637TransQci","pmCounterNumber787","pmCounterNumber641Trans","pmCounterNumber789","pmCounterNumber642Plmn0","pmCounterNumber642Plmn1","pmCounterNumber642Plmn2","pmCounterNumber642Plmn3","pmCounterNumber642Plmn4","pmCounterNumber642Plmn5","pmCounterNumber642Plmn6","pmCounterNumber642Qci","pmCounterNumber798"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,ENodeBFunction=1,EUtranCellFDD=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":6,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":9,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"},{"p":67,"sValue":"0"},{"p":68,"sValue":"0"},{"p":69,"sValue":"0"},{"p":70,"sValue":"0"},{"p":71,"sValue":"0"},{"p":72,"sValue":"0"},{"p":73,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,ENodeBFunction=1,EUtranCellFDD=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":6,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":7,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":8,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":9,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":10,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":11,"sValue":"0"},{"p":12,"sValue":"0"},{"p":13,"sValue":"0"},{"p":14,"sValue":"0"},{"p":15,"sValue":"0"},{"p":16,"sValue":"0"},{"p":17,"sValue":"0"},{"p":18,"sValue":"0"},{"p":19,"sValue":"0"},{"p":20,"sValue":"0"},{"p":21,"sValue":"0"},{"p":22,"sValue":"0"},{"p":23,"sValue":"0"},{"p":24,"sValue":"0"},{"p":25,"sValue":"0"},{"p":26,"sValue":"0"},{"p":27,"sValue":"0"},{"p":28,"sValue":"0"},{"p":29,"sValue":"0"},{"p":30,"sValue":"0"},{"p":31,"sValue":"0"},{"p":32,"sValue":"0"},{"p":33,"sValue":"0"},{"p":34,"sValue":"0"},{"p":35,"sValue":"0"},{"p":36,"sValue":"0"},{"p":37,"sValue":"0"},{"p":38,"sValue":"0"},{"p":39,"sValue":"0"},{"p":40,"sValue":"0"},{"p":41,"sValue":"0"},{"p":42,"sValue":"0,0,0,0,0,0,0,0,0,0"},{"p":43,"sValue":"0"},{"p":44,"sValue":"0"},{"p":45,"sValue":"0"},{"p":46,"sValue":"0"},{"p":47,"sValue":"0"},{"p":48,"sValue":"0"},{"p":49,"sValue":"0"},{"p":50,"sValue":"0"},{"p":51,"sValue":"0"},{"p":52,"sValue":"0"},{"p":53,"sValue":"0"},{"p":54,"sValue":"0"},{"p":55,"sValue":"0"},{"p":56,"sValue":"0"},{"p":57,"sValue":"0"},{"p":58,"sValue":"0"},{"p":59,"sValue":"0"},{"p":60,"sValue":"0"},{"p":61,"sValue":"0"},{"p":62,"sValue":"0"},{"p":63,"sValue":"0"},{"p":64,"sValue":"0"},{"p":65,"sValue":"0"},{"p":66,"sValue":"0"},{"p":67,"sValue":"0"},{"p":68,"sValue":"0"},{"p":69,"sValue":"0"},{"p":70,"sValue":"0"},{"p":71,"sValue":"0"},{"p":72,"sValue":"0"},{"p":73,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=GNBCUUPFunction_GNBCUUP"},"measTypes":{"sMeasTypesList":["pmCounterNumber799","pmCounterNumber799Gtpu","pmCounterNumber799NoCtxt","pmCounterNumber802","pmCounterNumber803","pmCounterNumber804","pmCounterNumber805","pmCounterNumber806","pmCounterNumber807","pmCounterNumber808"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,GNBCUUPFunction=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"},{"p":3,"sValue":"0"},{"p":4,"sValue":"0"},{"p":5,"sValue":"0"},{"p":6,"sValue":"0"},{"p":7,"sValue":"0"},{"p":8,"sValue":"0"},{"p":9,"sValue":"0"},{"p":10,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=NRCellCU_GNBCUCP"},"measTypes":{"sMeasTypesList":["pmEndcUeCapabilityUlPdcpDelay"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,GNBCUCPFunction=1,NRCellCU=32","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBCUCPFunction=1,NRCellCU=31","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBCUCPFunction=1,NRCellCU=2","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"}]},{"measObjInstId":"ManagedElement=nodedntest,GNBCUCPFunction=1,NRCellCU=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"}]}]},{"measInfoId":{"sMeasInfoId":"PM=1,PmGroup=X2UTermination_GNBCUUP"},"measTypes":{"sMeasTypesList":["pmCounterNumber802","pmCounterNumber803"]},"measValuesList":[{"measObjInstId":"ManagedElement=nodedntest,GNBCUUPFunction=1,X2UTermination=1","suspectFlag":"false","measResults":[{"p":1,"sValue":"0"},{"p":2,"sValue":"0"}]}]}]}}}}
\ No newline at end of file
diff --git a/pmproducer/src/test/resources/A20000626.2315+0200-2330+0200_HTTPS-6-73.json.gz b/pmproducer/src/test/resources/A20000626.2315+0200-2330+0200_HTTPS-6-73.json.gz
new file mode 100644 (file)
index 0000000..9e65368
Binary files /dev/null and b/pmproducer/src/test/resources/A20000626.2315+0200-2330+0200_HTTPS-6-73.json.gz differ
diff --git a/pmproducer/src/test/resources/A20220418.1900-1915_seliitdus00487.xml b/pmproducer/src/test/resources/A20220418.1900-1915_seliitdus00487.xml
new file mode 100644 (file)
index 0000000..eed7585
--- /dev/null
@@ -0,0 +1,4023 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="MeasDataCollection.xsl"?>
+<measCollecFile xmlns="http://www.3gpp.org/ftp/specs/archive/32_series/32.435#measCollec"
+                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                xsi:schemaLocation="http://www.3gpp.org/ftp/specs/archive/32_series/32.435#measCollec">
+  <fileHeader fileFormatVersion="32.435 V10.0"
+              vendorName="Ericsson AB"
+              dnPrefix="SubNetwork=G3">
+    <fileSender localDn="seliitdus00487"
+                elementType="RadioNode"/>
+    <measCollec beginTime="2022-04-18T19:00:00+00:00"/>
+  </fileHeader>
+  <measData>
+    <managedElement localDn="seliitdus00487"
+                    swVersion="CXP2010174_1 R44B17"/>
+    <measInfo measInfoId="PM=1,PmGroup=NRCellCU_GNBCUCP">
+      <job jobId="nr_all"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmAnrNcgiMeasFailUeCap</measType>
+      <measType p="2">pmAnrNcgiMeasRcvDrx</measType>
+      <measType p="3">pmAnrNcgiMeasRcvNoSib1NoTacCell</measType>
+      <measType p="4">pmAnrNcgiMeasTriggerDrx</measType>
+      <measType p="5">pmAnrNeighbRelAddNr</measType>
+      <measType p="6">pmAnrNeighbRelRemNr</measType>
+      <measType p="7">pmCaConfigAtt</measType>
+      <measType p="8">pmCaConfigDlSamp</measType>
+      <measType p="9">pmCaConfigDlSumEndcDistr</measType>
+      <measType p="10">pmCaConfigDlSumSaDistr</measType>
+      <measType p="11">pmCaConfigSucc</measType>
+      <measType p="12">pmCaDeconfigAtt</measType>
+      <measType p="13">pmCaDeconfigSucc</measType>
+      <measType p="14">pmDrbEstabAttArp</measType>
+      <measType p="15">pmDrbEstabFailIntgProt</measType>
+      <measType p="16">pmDrbEstabSuccArp</measType>
+      <measType p="17">pmDrbRelAbnormalAmfActArp</measType>
+      <measType p="18">pmDrbRelAbnormalAmfArp</measType>
+      <measType p="19">pmDrbRelAbnormalGnbActArp</measType>
+      <measType p="20">pmDrbRelAbnormalGnbArp</measType>
+      <measType p="21">pmDrbRelNormalArp</measType>
+      <measType p="22">pmDrbRopEndEndc</measType>
+      <measType p="23">pmDrbRopEndSa</measType>
+      <measType p="24">pmEndcCaConfigAtt</measType>
+      <measType p="25">pmEndcCaConfigSucc</measType>
+      <measType p="26">pmEndcCaDeconfigAtt</measType>
+      <measType p="27">pmEndcCaDeconfigSucc</measType>
+      <measType p="28">pmEndcMnHoPSCellKeepAtt</measType>
+      <measType p="29">pmEndcMnHoPSCellKeepSucc</measType>
+      <measType p="30">pmEndcPSCellChangeRejInIntraGnbBbIntens</measType>
+      <measType p="31">pmEndcPSCellChangeRejInIntraGnbMpLoad</measType>
+      <measType p="32">pmEndcPSCellChangeResAllocReqInterSgnb</measType>
+      <measType p="33">pmEndcPSCellChangeResAllocSuccInterSgnb</measType>
+      <measType p="34">pmEndcSetupScgUeAtt</measType>
+      <measType p="35">pmEndcSetupScgUeSucc</measType>
+      <measType p="36">pmHoExeAttOutInterGnbTs</measType>
+      <measType p="37">pmHoExeAttOutIntraGnbTs</measType>
+      <measType p="38">pmHoExeSuccOutInterGnbTs</measType>
+      <measType p="39">pmHoExeSuccOutIntraGnbTs</measType>
+      <measType p="40">pmHoPrepAttOutInterGnbTs</measType>
+      <measType p="41">pmHoPrepAttOutIntraGnbTs</measType>
+      <measType p="42">pmHoPrepRejInInterGnbBbIntens</measType>
+      <measType p="43">pmHoPrepRejInInterGnbMpLoad</measType>
+      <measType p="44">pmHoPrepRejInIntraGnbBbIntens</measType>
+      <measType p="45">pmHoPrepRejInIntraGnbMpLoad</measType>
+      <measType p="46">pmHoPrepSuccOutInterGnbTs</measType>
+      <measType p="47">pmHoPrepSuccOutIntraGnbTs</measType>
+      <measType p="48">pmNgSigConnEstabAttEm</measType>
+      <measType p="49">pmNgSigConnEstabSuccEm</measType>
+      <measType p="50">pmNrdcRelRelocUeMnAtt</measType>
+      <measType p="51">pmNrdcRelRelocUeMnSucc</measType>
+      <measType p="52">pmNrdcRelUeSnAbnormalMnInit</measType>
+      <measType p="53">pmNrdcRelUeSnAbnormalMnInitAct</measType>
+      <measType p="54">pmNrdcRelUeSnAbnormalSnInit</measType>
+      <measType p="55">pmNrdcRelUeSnAbnormalSnInitAct</measType>
+      <measType p="56">pmNrdcRelUeSnNormal</measType>
+      <measType p="57">pmNrdcSetupUeMnAtt</measType>
+      <measType p="58">pmNrdcSetupUeMnSucc</measType>
+      <measType p="59">pmNrdcSetupUeSnAtt</measType>
+      <measType p="60">pmNrdcSetupUeSnSucc</measType>
+      <measType p="61">pmPduSessionEstabAtt</measType>
+      <measType p="62">pmPduSessionEstabAttIntgProt64kbps</measType>
+      <measType p="63">pmPduSessionEstabSucc</measType>
+      <measType p="64">pmPduSessionModifyAtt</measType>
+      <measType p="65">pmPduSessionModifySucc</measType>
+      <measType p="66">pmPduSessionRelAbnormalAmf</measType>
+      <measType p="67">pmPduSessionRelAbnormalGnb</measType>
+      <measType p="68">pmPduSessionRelNormal</measType>
+      <measType p="69">pmPwsCancelReqCmas</measType>
+      <measType p="70">pmPwsCancelReqEtws</measType>
+      <measType p="71">pmPwsCancelRespCmas</measType>
+      <measType p="72">pmPwsCancelRespEtws</measType>
+      <measType p="73">pmPwsReqAttCmas</measType>
+      <measType p="74">pmPwsReqAttEtwsPrimary</measType>
+      <measType p="75">pmPwsReqAttEtwsSecondary</measType>
+      <measType p="76">pmPwsReqSuccCmas</measType>
+      <measType p="77">pmPwsReqSuccEtwsPrimary</measType>
+      <measType p="78">pmPwsReqSuccEtwsSecondary</measType>
+      <measType p="79">pmRrcConnEstabAttEm</measType>
+      <measType p="80">pmRrcConnEstabAttReattEm</measType>
+      <measType p="81">pmRrcConnEstabSuccEm</measType>
+      <measType p="82">pmRrcConnLevelMaxNrDc</measType>
+      <measType p="83">pmRrcConnLevelSumNrDc</measType>
+      <measType p="84">pmRwrEutranUeNonPerfEpsfbEm</measType>
+      <measType p="85">pmRwrEutranUeSuccEpsfb</measType>
+      <measType p="86">pmRwrEutranUeSuccEpsfbEm</measType>
+      <measType p="87">pmRwrEutranUeSuccEpsfbEmFbInd</measType>
+      <measType p="88">pmRwrEutranUeSuccNrCoverage</measType>
+      <measType p="89">pmRwrIntraNrUeSuccCoverage</measType>
+      <measType p="90">pmUeCtxtRelAbnormalAmf</measType>
+      <measType p="91">pmUeCtxtRelAbnormalGnb</measType>
+      <measType p="92">pmUeCtxtRelNormal</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUCPFunction=1,NRCellCU=32">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">180</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+        <r p="67">0</r>
+        <r p="68">0</r>
+        <r p="69">0</r>
+        <r p="70">0</r>
+        <r p="71">0</r>
+        <r p="72">0</r>
+        <r p="73">0</r>
+        <r p="74">0</r>
+        <r p="75">0</r>
+        <r p="76">0</r>
+        <r p="77">0</r>
+        <r p="78">0</r>
+        <r p="79">0</r>
+        <r p="80">0</r>
+        <r p="81">0</r>
+        <r p="82">0</r>
+        <r p="83">0</r>
+        <r p="84">0</r>
+        <r p="85">0</r>
+        <r p="86">0</r>
+        <r p="87">0</r>
+        <r p="88">0</r>
+        <r p="89">0</r>
+        <r p="90">0</r>
+        <r p="91">0</r>
+        <r p="92">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUCPFunction=1,NRCellCU=31">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">180</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+        <r p="67">0</r>
+        <r p="68">0</r>
+        <r p="69">0</r>
+        <r p="70">0</r>
+        <r p="71">0</r>
+        <r p="72">0</r>
+        <r p="73">0</r>
+        <r p="74">0</r>
+        <r p="75">0</r>
+        <r p="76">0</r>
+        <r p="77">0</r>
+        <r p="78">0</r>
+        <r p="79">0</r>
+        <r p="80">0</r>
+        <r p="81">0</r>
+        <r p="82">0</r>
+        <r p="83">0</r>
+        <r p="84">0</r>
+        <r p="85">0</r>
+        <r p="86">0</r>
+        <r p="87">0</r>
+        <r p="88">0</r>
+        <r p="89">0</r>
+        <r p="90">0</r>
+        <r p="91">0</r>
+        <r p="92">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUCPFunction=1,NRCellCU=2">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">180</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+        <r p="67">0</r>
+        <r p="68">0</r>
+        <r p="69">0</r>
+        <r p="70">0</r>
+        <r p="71">0</r>
+        <r p="72">0</r>
+        <r p="73">0</r>
+        <r p="74">0</r>
+        <r p="75">0</r>
+        <r p="76">0</r>
+        <r p="77">0</r>
+        <r p="78">0</r>
+        <r p="79">0</r>
+        <r p="80">0</r>
+        <r p="81">0</r>
+        <r p="82">0</r>
+        <r p="83">0</r>
+        <r p="84">0</r>
+        <r p="85">0</r>
+        <r p="86">0</r>
+        <r p="87">0</r>
+        <r p="88">0</r>
+        <r p="89">0</r>
+        <r p="90">0</r>
+        <r p="91">0</r>
+        <r p="92">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUCPFunction=1,NRCellCU=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">180</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+        <r p="67">0</r>
+        <r p="68">0</r>
+        <r p="69">0</r>
+        <r p="70">0</r>
+        <r p="71">0</r>
+        <r p="72">0</r>
+        <r p="73">0</r>
+        <r p="74">0</r>
+        <r p="75">0</r>
+        <r p="76">0</r>
+        <r p="77">0</r>
+        <r p="78">0</r>
+        <r p="79">0</r>
+        <r p="80">0</r>
+        <r p="81">0</r>
+        <r p="82">0</r>
+        <r p="83">0</r>
+        <r p="84">0</r>
+        <r p="85">0</r>
+        <r p="86">0</r>
+        <r p="87">0</r>
+        <r p="88">0</r>
+        <r p="89">0</r>
+        <r p="90">0</r>
+        <r p="91">0</r>
+        <r p="92">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=NRCellDU_GNBDU">
+      <job jobId="nr_all"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmDrxSleepTime</measType>
+      <measType p="2">pmDrxTotalTime</measType>
+      <measType p="3">pmDuCellDownLockAuto</measType>
+      <measType p="4">pmDuCellDownLockMan</measType>
+      <measType p="5">pmDuCellDownUnlockMan</measType>
+      <measType p="6">pmMacBsrLcg0Distr</measType>
+      <measType p="7">pmMacBsrLcg1Distr</measType>
+      <measType p="8">pmMacBsrLcg2Distr</measType>
+      <measType p="9">pmMacBsrLcg3Distr</measType>
+      <measType p="10">pmMacBsrLcg4Distr</measType>
+      <measType p="11">pmMacBsrLcg5Distr</measType>
+      <measType p="12">pmMacBsrLcg6Distr</measType>
+      <measType p="13">pmMacBsrLcg7Distr</measType>
+      <measType p="14">pmMacContentionDelayDlDistr</measType>
+      <measType p="15">pmMacContentionDelayDlMaxQos</measType>
+      <measType p="16">pmMacContentionDelayUlDistr</measType>
+      <measType p="17">pmMacHarqDlAck16Qam</measType>
+      <measType p="18">pmMacHarqDlAck16QamInit</measType>
+      <measType p="19">pmMacHarqDlAck256Qam</measType>
+      <measType p="20">pmMacHarqDlAck256QamInit</measType>
+      <measType p="21">pmMacHarqDlAck64Qam</measType>
+      <measType p="22">pmMacHarqDlAck64QamInit</measType>
+      <measType p="23">pmMacHarqDlAckQpsk</measType>
+      <measType p="24">pmMacHarqDlAckQpskInit</measType>
+      <measType p="25">pmMacHarqDlDtx16Qam</measType>
+      <measType p="26">pmMacHarqDlDtx16QamInit</measType>
+      <measType p="27">pmMacHarqDlDtx256Qam</measType>
+      <measType p="28">pmMacHarqDlDtx256QamInit</measType>
+      <measType p="29">pmMacHarqDlDtx64Qam</measType>
+      <measType p="30">pmMacHarqDlDtx64QamInit</measType>
+      <measType p="31">pmMacHarqDlDtxQpsk</measType>
+      <measType p="32">pmMacHarqDlDtxQpskInit</measType>
+      <measType p="33">pmMacHarqDlFail</measType>
+      <measType p="34">pmMacHarqDlNack16Qam</measType>
+      <measType p="35">pmMacHarqDlNack16QamInit</measType>
+      <measType p="36">pmMacHarqDlNack256Qam</measType>
+      <measType p="37">pmMacHarqDlNack256QamInit</measType>
+      <measType p="38">pmMacHarqDlNack64Qam</measType>
+      <measType p="39">pmMacHarqDlNack64QamInit</measType>
+      <measType p="40">pmMacHarqDlNackQpsk</measType>
+      <measType p="41">pmMacHarqDlNackQpskInit</measType>
+      <measType p="42">pmMacHarqUlAck16Qam</measType>
+      <measType p="43">pmMacHarqUlAck16QamInit</measType>
+      <measType p="44">pmMacHarqUlAck256Qam</measType>
+      <measType p="45">pmMacHarqUlAck256QamInit</measType>
+      <measType p="46">pmMacHarqUlAck64Qam</measType>
+      <measType p="47">pmMacHarqUlAck64QamInit</measType>
+      <measType p="48">pmMacHarqUlAckQpsk</measType>
+      <measType p="49">pmMacHarqUlAckQpskInit</measType>
+      <measType p="50">pmMacHarqUlDtx16Qam</measType>
+      <measType p="51">pmMacHarqUlDtx16QamInit</measType>
+      <measType p="52">pmMacHarqUlDtx256Qam</measType>
+      <measType p="53">pmMacHarqUlDtx256QamInit</measType>
+      <measType p="54">pmMacHarqUlDtx64Qam</measType>
+      <measType p="55">pmMacHarqUlDtx64QamInit</measType>
+      <measType p="56">pmMacHarqUlDtxQpsk</measType>
+      <measType p="57">pmMacHarqUlDtxQpskInit</measType>
+      <measType p="58">pmMacHarqUlFail</measType>
+      <measType p="59">pmMacHarqUlNack16Qam</measType>
+      <measType p="60">pmMacHarqUlNack16QamInit</measType>
+      <measType p="61">pmMacHarqUlNack256Qam</measType>
+      <measType p="62">pmMacHarqUlNack256QamInit</measType>
+      <measType p="63">pmMacHarqUlNack64Qam</measType>
+      <measType p="64">pmMacHarqUlNack64QamInit</measType>
+      <measType p="65">pmMacHarqUlNackQpsk</measType>
+      <measType p="66">pmMacHarqUlNackQpskInit</measType>
+      <measType p="67">pmMacOffloadVolDlSCell</measType>
+      <measType p="68">pmMacOffloadVolDlSCellExt</measType>
+      <measType p="69">pmMacPdcchBlockingPdschOccasions</measType>
+      <measType p="70">pmMacPdcchBlockingPuschOccasions</measType>
+      <measType p="71">pmMacPucchSrPuschGrantLatDistr</measType>
+      <measType p="72">pmMacRBSymPucchSchedPusch</measType>
+      <measType p="73">pmMacRBSymRachSchedPusch</measType>
+      <measType p="74">pmMacRBSymUsedNrEfficiency</measType>
+      <measType p="75">pmMacRBSymUsedPdcchTypeAAggrLvl1</measType>
+      <measType p="76">pmMacRBSymUsedPdcchTypeAAggrLvl16</measType>
+      <measType p="77">pmMacRBSymUsedPdcchTypeAAggrLvl2</measType>
+      <measType p="78">pmMacRBSymUsedPdcchTypeAAggrLvl4</measType>
+      <measType p="79">pmMacRBSymUsedPdcchTypeAAggrLvl8</measType>
+      <measType p="80">pmMacRBSymUsedPdschAdvMimoUserDistr</measType>
+      <measType p="81">pmMacRBSymUsedPdschAdvSuMimoLayerDistr</measType>
+      <measType p="82">pmMacRBSymUsedPdschMimoLayerDistr</measType>
+      <measType p="83">pmMacRBSymUsedPdschMimoUserDistr</measType>
+      <measType p="84">pmMacRBSymUsedPuschMimoLayerDistr</measType>
+      <measType p="85">pmMacRBSymUsedPuschMimoUserDistr</measType>
+      <measType p="86">pmMacSchedSlotDlCellSchedDistr</measType>
+      <measType p="87">pmMacSchedSlotDlSCellActivatedDistr</measType>
+      <measType p="88">pmMacSchedSlotDlTempAlarmSCellDistr</measType>
+      <measType p="89">pmMacSchedSlotUlCellSchedDistr</measType>
+      <measType p="90">pmMacSchedSlotUlSCellActivatedDistr</measType>
+      <measType p="91">pmMacSchedSlotUlTempAlarmSCellDistr</measType>
+      <measType p="92">pmMacTempAlarmSCellDistr</measType>
+      <measType p="93">pmMacThpUlUeMax</measType>
+      <measType p="94">pmMacTimeDlDrbLastSlot</measType>
+      <measType p="95">pmMacTimeDlDrbLastSlotQos</measType>
+      <measType p="96">pmMacTimeDlDrbLastSlotSamp</measType>
+      <measType p="97">pmMacTimeDlDrbLastSlotSampQos</measType>
+      <measType p="98">pmMacTimeDlDrbSamp</measType>
+      <measType p="99">pmMacTimeDlDrbSampQos</measType>
+      <measType p="100">pmMacVolDlDrbLastSlot</measType>
+      <measType p="101">pmMacVolDlDrbLastSlotQos</measType>
+      <measType p="102">pmMacVolDlDrbSingleBurst</measType>
+      <measType p="103">pmMacVolDlDrbSingleBurstSamp</measType>
+      <measType p="104">pmMacVolDlSCell</measType>
+      <measType p="105">pmMacVolDlSCellExt</measType>
+      <measType p="106">pmMacVolUlResUeLastSlot</measType>
+      <measType p="107">pmMacVolUlResUeLate</measType>
+      <measType p="108">pmMacVolUlSCell</measType>
+      <measType p="109">pmMacVolUlUnresUe</measType>
+      <measType p="110">pmMacVolUlUnresUeBsrGrant</measType>
+      <measType p="111">pmMacVolUlUnresUePreemptGrant</measType>
+      <measType p="112">pmMacVolUlUnresUePucchSrGrant</measType>
+      <measType p="113">pmMeasGapDurationDistr</measType>
+      <measType p="114">pmMeasGapUeMaxDistr</measType>
+      <measType p="115">pmMeasGapUeMnMaxDistr</measType>
+      <measType p="116">pmMeasGapUeMnSumDistr</measType>
+      <measType p="117">pmMeasGapUeSamp</measType>
+      <measType p="118">pmMeasGapUeSumDistr</measType>
+      <measType p="119">pmPagBundledUeDistr</measType>
+      <measType p="120">pmPagBundledUeSampDistr</measType>
+      <measType p="121">pmPagDiscarded</measType>
+      <measType p="122">pmPagReceived</measType>
+      <measType p="123">pmRadioBeamFailureRecoveryCurrentBeam</measType>
+      <measType p="124">pmRadioBeamFailureRecoveryNewBeam</measType>
+      <measType p="125">pmRadioBeamSwitchSucc</measType>
+      <measType p="126">pmRadioBeamSwitchSuccMacCe</measType>
+      <measType p="127">pmRadioBeamSwitchTargetNotAvailable</measType>
+      <measType p="128">pmRadioMacCpofdmTransUl</measType>
+      <measType p="129">pmRadioMacDftsTransUl</measType>
+      <measType p="130">pmRadioMacWvfSwitchAttUlCpofdm</measType>
+      <measType p="131">pmRadioMacWvfSwitchAttUlDfts</measType>
+      <measType p="132">pmRadioMacWvfSwitchSuccUlCpofdm</measType>
+      <measType p="133">pmRadioMacWvfSwitchSuccUlDfts</measType>
+      <measType p="134">pmRadioMaxDeltaIpNDistr</measType>
+      <measType p="135">pmRadioNarrowBeamAcqTimeDistr</measType>
+      <measType p="136">pmRadioPathlossUlDistr</measType>
+      <measType p="137">pmRadioPdschEmptySlots</measType>
+      <measType p="138">pmRadioPdschEmptySlotsForced</measType>
+      <measType p="139">pmRadioPdschTable1McsDistr</measType>
+      <measType p="140">pmRadioPdschTable2McsDistr</measType>
+      <measType p="141">pmRadioPucchFailSr</measType>
+      <measType p="142">pmRadioPuschPhrDistr</measType>
+      <measType p="143">pmRadioPuschTable1McsDistr</measType>
+      <measType p="144">pmRadioPuschTable2McsDistr</measType>
+      <measType p="145">pmRadioRaCbSuccMsg3KnownCrnti</measType>
+      <measType p="146">pmRadioRecInterferenceNoOfEventsPrbDistr</measType>
+      <measType p="147">pmRadioRecInterferencePowerDistr</measType>
+      <measType p="148">pmRadioRecInterferencePwrSumPrbDistr</measType>
+      <measType p="149">pmRadioServingBeamDlDistr</measType>
+      <measType p="150">pmRadioSinrPucchDistr</measType>
+      <measType p="151">pmRadioSinrPucchF0Distr</measType>
+      <measType p="152">pmRadioSinrPucchF2Distr</measType>
+      <measType p="153">pmRadioSinrPuschDistr</measType>
+      <measType p="154">pmRadioSrsResAvailAntSwitching</measType>
+      <measType p="155">pmRadioSrsResUsedAntSwitching</measType>
+      <measType p="156">pmRadioSsbBeamSwitch</measType>
+      <measType p="157">pmRlcArqDlAck</measType>
+      <measType p="158">pmRlcArqDlNack</measType>
+      <measType p="159">pmRlcArqUlAck</measType>
+      <measType p="160">pmRlcArqUlNack</measType>
+      <measType p="161">pmRlcDelayPktTransmitDl</measType>
+      <measType p="162">pmRlcDelayPktTransmitDlQos</measType>
+      <measType p="163">pmRlcDelayTimeDl</measType>
+      <measType p="164">pmRlcDelayTimeDlQos</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=32">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="9">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="12">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="13">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="14">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="15">0</r>
+        <r p="16">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+        <r p="67">0</r>
+        <r p="68">0</r>
+        <r p="69">0</r>
+        <r p="70">0</r>
+        <r p="71">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="72">0</r>
+        <r p="73">0</r>
+        <r p="74">0</r>
+        <r p="75">0</r>
+        <r p="76">0</r>
+        <r p="77">0</r>
+        <r p="78">0</r>
+        <r p="79">2880</r>
+        <r p="80">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="81">0,0,0,0,0,0,0,0</r>
+        <r p="82">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="83">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="84">0,0,0,0,0,0,0,0</r>
+        <r p="85">0,0,0,0,0,0,0,0</r>
+        <r p="86">0,0,0,0,0,0,0,0</r>
+        <r p="87">0,0,0,0,0,0,0,0</r>
+        <r p="88">0,0,0,0,0,0,0,0</r>
+        <r p="89">0,0,0,0,0,0,0,0</r>
+        <r p="90">0,0,0,0,0,0,0,0</r>
+        <r p="91">0,0,0,0,0,0,0,0</r>
+        <r p="92">0,0,0,0,0,0,0,0</r>
+        <r p="93">0</r>
+        <r p="94">0</r>
+        <r p="95">0</r>
+        <r p="96">0</r>
+        <r p="97">0</r>
+        <r p="98">0</r>
+        <r p="99">0</r>
+        <r p="100">0</r>
+        <r p="101">0</r>
+        <r p="102">0</r>
+        <r p="103">0</r>
+        <r p="104">0</r>
+        <r p="105">0</r>
+        <r p="106">0</r>
+        <r p="107">0</r>
+        <r p="108">0</r>
+        <r p="109">0</r>
+        <r p="110">0</r>
+        <r p="111">0</r>
+        <r p="112">0</r>
+        <r p="113">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="114">0</r>
+        <r p="115">0</r>
+        <r p="116">0</r>
+        <r p="117">179</r>
+        <r p="118">0</r>
+        <r p="119">0,0,0,0</r>
+        <r p="120">0,0,0,0</r>
+        <r p="121">0</r>
+        <r p="122">0</r>
+        <r p="123">0</r>
+        <r p="124">0</r>
+        <r p="125">0</r>
+        <r p="126">0</r>
+        <r p="127">0</r>
+        <r p="128">0</r>
+        <r p="129">0</r>
+        <r p="130">0</r>
+        <r p="131">0</r>
+        <r p="132">0</r>
+        <r p="133">0</r>
+        <r p="134">900,0,0,0,0</r>
+        <r p="135">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="136">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="137">1440001</r>
+        <r p="138">0</r>
+        <r p="139">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="140">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="141">0</r>
+        <r p="142">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="143">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="144">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="145">0</r>
+        <r p="146">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="147">0,0,0,0,0,0,0,0,0,0,0,1800001,0,0,0,0,0,0,0,0,0</r>
+        <r p="148">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="149">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="150">0,0,0,0,0,0,0,0</r>
+        <r p="151">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="152">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="153">0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="154">0</r>
+        <r p="155">0</r>
+        <r p="156">0</r>
+        <r p="157">0</r>
+        <r p="158">0</r>
+        <r p="159">0</r>
+        <r p="160">0</r>
+        <r p="161">0</r>
+        <r p="162">0</r>
+        <r p="163">0</r>
+        <r p="164">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=31">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="9">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="12">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="13">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="14">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="15">0</r>
+        <r p="16">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+        <r p="67">0</r>
+        <r p="68">0</r>
+        <r p="69">0</r>
+        <r p="70">0</r>
+        <r p="71">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="72">0</r>
+        <r p="73">0</r>
+        <r p="74">0</r>
+        <r p="75">0</r>
+        <r p="76">5376</r>
+        <r p="77">0</r>
+        <r p="78">0</r>
+        <r p="79">0</r>
+        <r p="80">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="81">0,0,0,0,0,0,0,0</r>
+        <r p="82">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="83">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="84">0,0,0,0,0,0,0,0</r>
+        <r p="85">0,0,0,0,0,0,0,0</r>
+        <r p="86">0,0,0,0,0,0,0,0</r>
+        <r p="87">0,0,0,0,0,0,0,0</r>
+        <r p="88">0,0,0,0,0,0,0,0</r>
+        <r p="89">0,0,0,0,0,0,0,0</r>
+        <r p="90">0,0,0,0,0,0,0,0</r>
+        <r p="91">0,0,0,0,0,0,0,0</r>
+        <r p="92">0,0,0,0,0,0,0,0</r>
+        <r p="93">0</r>
+        <r p="94">0</r>
+        <r p="95">0</r>
+        <r p="96">0</r>
+        <r p="97">0</r>
+        <r p="98">0</r>
+        <r p="99">0</r>
+        <r p="100">0</r>
+        <r p="101">0</r>
+        <r p="102">0</r>
+        <r p="103">0</r>
+        <r p="104">0</r>
+        <r p="105">0</r>
+        <r p="106">0</r>
+        <r p="107">0</r>
+        <r p="108">0</r>
+        <r p="109">0</r>
+        <r p="110">0</r>
+        <r p="111">0</r>
+        <r p="112">0</r>
+        <r p="113">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="114">0</r>
+        <r p="115">0</r>
+        <r p="116">0</r>
+        <r p="117">179</r>
+        <r p="118">0</r>
+        <r p="119">0,0,0,0</r>
+        <r p="120">0,0,0,0</r>
+        <r p="121">0</r>
+        <r p="122">0</r>
+        <r p="123">0</r>
+        <r p="124">0</r>
+        <r p="125">0</r>
+        <r p="126">0</r>
+        <r p="127">0</r>
+        <r p="128">0</r>
+        <r p="129">0</r>
+        <r p="130">0</r>
+        <r p="131">0</r>
+        <r p="132">0</r>
+        <r p="133">0</r>
+        <r p="134">900,0,0,0,0</r>
+        <r p="135">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="136">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="137">1440001</r>
+        <r p="138">0</r>
+        <r p="139">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="140">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="141">0</r>
+        <r p="142">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="143">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="144">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="145">0</r>
+        <r p="146">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="147">0,0,0,0,0,0,0,0,0,0,0,1800001,0,0,0,0,0,0,0,0,0</r>
+        <r p="148">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="149">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="150">0,0,0,0,0,0,0,0</r>
+        <r p="151">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="152">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="153">0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="154">0</r>
+        <r p="155">0</r>
+        <r p="156">0</r>
+        <r p="157">0</r>
+        <r p="158">0</r>
+        <r p="159">0</r>
+        <r p="160">0</r>
+        <r p="161">0</r>
+        <r p="162">0</r>
+        <r p="163">0</r>
+        <r p="164">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=2">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="9">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="12">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="13">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="14">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="15">0</r>
+        <r p="16">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+        <r p="67">0</r>
+        <r p="68">0</r>
+        <r p="69">0</r>
+        <r p="70">0</r>
+        <r p="71">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="72">0</r>
+        <r p="73">0</r>
+        <r p="74">0</r>
+        <r p="75">0</r>
+        <r p="76">0</r>
+        <r p="77">0</r>
+        <r p="78">0</r>
+        <r p="79">0</r>
+        <r p="80">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="81">0,0,0,0,0,0,0,0</r>
+        <r p="82">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="83">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="84">0,0,0,0,0,0,0,0</r>
+        <r p="85">0,0,0,0,0,0,0,0</r>
+        <r p="86">0,0,0,0,0,0,0,0</r>
+        <r p="87">0,0,0,0,0,0,0,0</r>
+        <r p="88">0,0,0,0,0,0,0,0</r>
+        <r p="89">0,0,0,0,0,0,0,0</r>
+        <r p="90">0,0,0,0,0,0,0,0</r>
+        <r p="91">0,0,0,0,0,0,0,0</r>
+        <r p="92">0,0,0,0,0,0,0,0</r>
+        <r p="93">0</r>
+        <r p="94">0</r>
+        <r p="95">0</r>
+        <r p="96">0</r>
+        <r p="97">0</r>
+        <r p="98">0</r>
+        <r p="99">0</r>
+        <r p="100">0</r>
+        <r p="101">0</r>
+        <r p="102">0</r>
+        <r p="103">0</r>
+        <r p="104">0</r>
+        <r p="105">0</r>
+        <r p="106">0</r>
+        <r p="107">0</r>
+        <r p="108">0</r>
+        <r p="109">0</r>
+        <r p="110">0</r>
+        <r p="111">0</r>
+        <r p="112">0</r>
+        <r p="113">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="114">0</r>
+        <r p="115">0</r>
+        <r p="116">0</r>
+        <r p="117">0</r>
+        <r p="118">0</r>
+        <r p="119">0,0,0,0</r>
+        <r p="120">0,0,0,0</r>
+        <r p="121">0</r>
+        <r p="122">0</r>
+        <r p="123">0</r>
+        <r p="124">0</r>
+        <r p="125">0</r>
+        <r p="126">0</r>
+        <r p="127">0</r>
+        <r p="128">0</r>
+        <r p="129">0</r>
+        <r p="130">0</r>
+        <r p="131">0</r>
+        <r p="132">0</r>
+        <r p="133">0</r>
+        <r p="134">0,0,0,0,0</r>
+        <r p="135">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="136">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="137">0</r>
+        <r p="138">0</r>
+        <r p="139">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="140">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="141">0</r>
+        <r p="142">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="143">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="144">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="145">0</r>
+        <r p="146">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="147">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="148">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="149">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="150">0,0,0,0,0,0,0,0</r>
+        <r p="151">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="152">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="153">0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="154">0</r>
+        <r p="155">0</r>
+        <r p="156">0</r>
+        <r p="157">0</r>
+        <r p="158">0</r>
+        <r p="159">0</r>
+        <r p="160">0</r>
+        <r p="161">0</r>
+        <r p="162">0</r>
+        <r p="163">0</r>
+        <r p="164">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="9">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="12">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="13">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="14">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="15">0</r>
+        <r p="16">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+        <r p="67">0</r>
+        <r p="68">0</r>
+        <r p="69">0</r>
+        <r p="70">0</r>
+        <r p="71">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="72">0</r>
+        <r p="73">0</r>
+        <r p="74">0</r>
+        <r p="75">0</r>
+        <r p="76">0</r>
+        <r p="77">0</r>
+        <r p="78">0</r>
+        <r p="79">0</r>
+        <r p="80">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="81">0,0,0,0,0,0,0,0</r>
+        <r p="82">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="83">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="84">0,0,0,0,0,0,0,0</r>
+        <r p="85">0,0,0,0,0,0,0,0</r>
+        <r p="86">0,0,0,0,0,0,0,0</r>
+        <r p="87">0,0,0,0,0,0,0,0</r>
+        <r p="88">0,0,0,0,0,0,0,0</r>
+        <r p="89">0,0,0,0,0,0,0,0</r>
+        <r p="90">0,0,0,0,0,0,0,0</r>
+        <r p="91">0,0,0,0,0,0,0,0</r>
+        <r p="92">0,0,0,0,0,0,0,0</r>
+        <r p="93">0</r>
+        <r p="94">0</r>
+        <r p="95">0</r>
+        <r p="96">0</r>
+        <r p="97">0</r>
+        <r p="98">0</r>
+        <r p="99">0</r>
+        <r p="100">0</r>
+        <r p="101">0</r>
+        <r p="102">0</r>
+        <r p="103">0</r>
+        <r p="104">0</r>
+        <r p="105">0</r>
+        <r p="106">0</r>
+        <r p="107">0</r>
+        <r p="108">0</r>
+        <r p="109">0</r>
+        <r p="110">0</r>
+        <r p="111">0</r>
+        <r p="112">0</r>
+        <r p="113">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="114">0</r>
+        <r p="115">0</r>
+        <r p="116">0</r>
+        <r p="117">0</r>
+        <r p="118">0</r>
+        <r p="119">0,0,0,0</r>
+        <r p="120">0,0,0,0</r>
+        <r p="121">0</r>
+        <r p="122">0</r>
+        <r p="123">0</r>
+        <r p="124">0</r>
+        <r p="125">0</r>
+        <r p="126">0</r>
+        <r p="127">0</r>
+        <r p="128">0</r>
+        <r p="129">0</r>
+        <r p="130">0</r>
+        <r p="131">0</r>
+        <r p="132">0</r>
+        <r p="133">0</r>
+        <r p="134">0,0,0,0,0</r>
+        <r p="135">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="136">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="137">0</r>
+        <r p="138">0</r>
+        <r p="139">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="140">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="141">0</r>
+        <r p="142">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="143">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="144">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="145">0</r>
+        <r p="146">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="147">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="148">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="149">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="150">0,0,0,0,0,0,0,0</r>
+        <r p="151">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="152">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="153">0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="154">0</r>
+        <r p="155">0</r>
+        <r p="156">0</r>
+        <r p="157">0</r>
+        <r p="158">0</r>
+        <r p="159">0</r>
+        <r p="160">0</r>
+        <r p="161">0</r>
+        <r p="162">0</r>
+        <r p="163">0</r>
+        <r p="164">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=NRSectorCarrier_GNBDU">
+      <job jobId="nr_all"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmBwAssignedDistr</measType>
+      <measType p="2">pmEmfPwrBackoffDynResThrDistr</measType>
+      <measType p="3">pmEmfPwrBackoffHardLimit</measType>
+      <measType p="4">pmEmfPwrBackoffOnPwrDistr</measType>
+      <measType p="5">pmEmfPwrBackoffPwrDistr</measType>
+      <measType p="6">pmEmfPwrBackoffSamp</measType>
+      <measType p="7">pmEmfPwrBackoffStepPwrDistr</measType>
+      <measType p="8">pmEmfPwrBackoffSum</measType>
+      <measType p="9">pmEmfPwrBackoffSumOn</measType>
+      <measType p="10">pmRadioPowerDlDistr</measType>
+      <measType p="11">pmRadioScaledTxPowerMax</measType>
+      <measType p="12">pmSectorCarrierDowntime</measType>
+      <measType p="13">pmSectorCarrierDowntimeAuto</measType>
+      <measType p="14">pmSectorCarrierDowntimeAutoCbrs</measType>
+      <measType p="15">pmSectorCarrierDowntimeMan</measType>
+      <measType p="16">pmSectorCarrierDowntimeManCbrs</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRSectorCarrier=32">
+        <r p="1">0,0,0,0,0,0,0,0,0,15,0,0,0,0,0</r>
+        <r p="2">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="3">0</r>
+        <r p="4">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="5">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="6">0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">10000</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRSectorCarrier=31">
+        <r p="1">0,0,0,0,0,0,0,0,0,0,0,0,0,15,0</r>
+        <r p="2">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="3">0</r>
+        <r p="4">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="5">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="6">0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">15000</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRSectorCarrier=2">
+        <r p="1">15,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="2">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="3">0</r>
+        <r p="4">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="5">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="6">0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRSectorCarrier=1">
+        <r p="1">15,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="2">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="3">0</r>
+        <r p="4">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="5">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="6">0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=EUtranCellFDD">
+      <job jobId="stat"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmRadioHomUeRepRankDistr</measType>
+      <measType p="2">pmRadioTxRankDistr</measType>
+      <measType p="3">pmRadioUeRepRankDistr</measType>
+      <measType p="4">pmRadioUeRepTm4Rank1PmiDistr</measType>
+      <measType p="5">pmRadioUeRepTm4Rank2PmiDistr</measType>
+      <measType p="6">pmRadioUeRepTm4Rank3PmiDistr</measType>
+      <measType p="7">pmRadioUeRepTm4Rank4PmiDistr</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,ENodeBFunction=1,EUtranCellFDD=2">
+        <r p="1">0,0,0,0</r>
+        <r p="2">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="3">0,0,0,0</r>
+        <r p="4">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="5">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="6">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,ENodeBFunction=1,EUtranCellFDD=1">
+        <r p="1">0,0,0,0</r>
+        <r p="2">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="3">0,0,0,0</r>
+        <r p="4">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="5">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="6">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=NRCellDU_GNBDU">
+      <job jobId="stat"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmMacRBSymUsedPdschAdvSuMimoTxRankDistr</measType>
+      <measType p="2">pmRadioSchedUlSuMimoRankDistr</measType>
+      <measType p="3">pmRadioUeRepCqi256QamRank1Distr</measType>
+      <measType p="4">pmRadioUeRepCqi256QamRank2Distr</measType>
+      <measType p="5">pmRadioUeRepCqi256QamRank3Distr</measType>
+      <measType p="6">pmRadioUeRepCqi256QamRank4Distr</measType>
+      <measType p="7">pmRadioUeRepCqi64QamRank1Distr</measType>
+      <measType p="8">pmRadioUeRepCqi64QamRank2Distr</measType>
+      <measType p="9">pmRadioUeRepCqi64QamRank3Distr</measType>
+      <measType p="10">pmRadioUeRepCqi64QamRank4Distr</measType>
+      <measType p="11">pmRadioUeRepPmiPrimaryCsiRsRank1Distr</measType>
+      <measType p="12">pmRadioUeRepPmiPrimaryCsiRsRank2Distr</measType>
+      <measType p="13">pmRadioUeRepPmiPrimaryCsiRsRank3Distr</measType>
+      <measType p="14">pmRadioUeRepPmiPrimaryCsiRsRank4Distr</measType>
+      <measType p="15">pmRadioUeRepPmiSecondaryCsiRsRank1Distr</measType>
+      <measType p="16">pmRadioUeRepPmiSecondaryCsiRsRank2Distr</measType>
+      <measType p="17">pmRadioUeRepPmiSecondaryCsiRsRank3Distr</measType>
+      <measType p="18">pmRadioUeRepPmiSecondaryCsiRsRank4Distr</measType>
+      <measType p="19">pmRadioUeRepRankDistr</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=32">
+        <r p="1">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="2">0,0,0,0</r>
+        <r p="3">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="4">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="5">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="6">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="9">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0,0,0,0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=31">
+        <r p="1">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="2">0,0,0,0</r>
+        <r p="3">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="4">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="5">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="6">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="9">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0,0,0,0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=2">
+        <r p="1">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="2">0,0,0,0</r>
+        <r p="3">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="4">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="5">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="6">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="9">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0,0,0,0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=1">
+        <r p="1">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="2">0,0,0,0</r>
+        <r p="3">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="4">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="5">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="6">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="9">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0,0,0,0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=EUtranCellFDD">
+      <job jobId="inter"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmRadioRecInterferencePwrPucch</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,ENodeBFunction=1,EUtranCellFDD=2">
+        <r p="1">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,ENodeBFunction=1,EUtranCellFDD=1">
+        <r p="1">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=NRCellDU_GNBDU">
+      <job jobId="inter"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmRadioRecInterferencePwrPucchF0Distr</measType>
+      <measType p="2">pmRadioRecInterferencePwrPucchF2Distr</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=32">
+        <r p="1">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="2">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=31">
+        <r p="1">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="2">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=2">
+        <r p="1">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="2">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=1">
+        <r p="1">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="2">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=GNBCUCPFunction_GNBCUCP">
+      <job jobId="PREDEF_5GRC"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmEndcInitAccessFailMpOverload</measType>
+      <measType p="2">pmRrcConnLevelMaxEnDc</measType>
+      <measType p="3">pmRrcConnLevelMaxSa</measType>
+      <measType p="4">pmRrcConnLevelSamp</measType>
+      <measType p="5">pmRrcConnLevelSumEnDc</measType>
+      <measType p="6">pmRrcConnLevelSumSa</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUCPFunction=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">180</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=NRCellCU_GNBCUCP">
+      <job jobId="PREDEF_5GRC"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmCellDowntimeAuto</measType>
+      <measType p="2">pmCellDowntimeMan</measType>
+      <measType p="3">pmDrbEstabAtt5qi</measType>
+      <measType p="4">pmDrbEstabSucc5qi</measType>
+      <measType p="5">pmDrbLevelMaxEndc</measType>
+      <measType p="6">pmDrbLevelMaxSa</measType>
+      <measType p="7">pmDrbLevelSamp</measType>
+      <measType p="8">pmDrbLevelSumEndc</measType>
+      <measType p="9">pmDrbLevelSumSa</measType>
+      <measType p="10">pmDrbRelAbnormalAmf5qi</measType>
+      <measType p="11">pmDrbRelAbnormalAmfAct5qi</measType>
+      <measType p="12">pmDrbRelAbnormalGnb5qi</measType>
+      <measType p="13">pmDrbRelAbnormalGnbAct5qi</measType>
+      <measType p="14">pmDrbRelNormal5qi</measType>
+      <measType p="15">pmEndcPSCellChangeAttInterSgnb</measType>
+      <measType p="16">pmEndcPSCellChangeAttIntraSgnb</measType>
+      <measType p="17">pmEndcPSCellChangeSuccInterSgnb</measType>
+      <measType p="18">pmEndcPSCellChangeSuccIntraSgnb</measType>
+      <measType p="19">pmEndcRelUeAbnormalMenb</measType>
+      <measType p="20">pmEndcRelUeAbnormalMenbAct</measType>
+      <measType p="21">pmEndcRelUeAbnormalSgnb</measType>
+      <measType p="22">pmEndcRelUeAbnormalSgnbAct</measType>
+      <measType p="23">pmEndcRelUeNormal</measType>
+      <measType p="24">pmEndcSetupUeAtt</measType>
+      <measType p="25">pmEndcSetupUeSucc</measType>
+      <measType p="26">pmHoExeAttOutEutran</measType>
+      <measType p="27">pmHoExeAttOutInterGnb</measType>
+      <measType p="28">pmHoExeAttOutIntraGnb</measType>
+      <measType p="29">pmHoExeSuccOutEutran</measType>
+      <measType p="30">pmHoExeSuccOutInterGnb</measType>
+      <measType p="31">pmHoExeSuccOutIntraGnb</measType>
+      <measType p="32">pmHoPrepAttOutEutran</measType>
+      <measType p="33">pmHoPrepAttOutInterGnb</measType>
+      <measType p="34">pmHoPrepAttOutIntraGnb</measType>
+      <measType p="35">pmHoPrepSuccOutEutran</measType>
+      <measType p="36">pmHoPrepSuccOutInterGnb</measType>
+      <measType p="37">pmHoPrepSuccOutIntraGnb</measType>
+      <measType p="38">pmNgSigConnEstabAtt</measType>
+      <measType p="39">pmNgSigConnEstabAttMos</measType>
+      <measType p="40">pmNgSigConnEstabSucc</measType>
+      <measType p="41">pmNgSigConnEstabSuccMos</measType>
+      <measType p="42">pmPduSessionLevelMax</measType>
+      <measType p="43">pmPduSessionLevelSamp</measType>
+      <measType p="44">pmPduSessionLevelSum</measType>
+      <measType p="45">pmRrcConnEstabAtt</measType>
+      <measType p="46">pmRrcConnEstabAttMos</measType>
+      <measType p="47">pmRrcConnEstabAttReatt</measType>
+      <measType p="48">pmRrcConnEstabAttReattMos</measType>
+      <measType p="49">pmRrcConnEstabSucc</measType>
+      <measType p="50">pmRrcConnEstabSuccMos</measType>
+      <measType p="51">pmRrcConnLevelMaxEnDc</measType>
+      <measType p="52">pmRrcConnLevelMaxSa</measType>
+      <measType p="53">pmRrcConnLevelSamp</measType>
+      <measType p="54">pmRrcConnLevelSumEnDc</measType>
+      <measType p="55">pmRrcConnLevelSumSa</measType>
+      <measType p="56">pmSessionTimeDrb5qi</measType>
+      <measType p="57">pmUeCtxtEstabAtt</measType>
+      <measType p="58">pmUeCtxtEstabSucc</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUCPFunction=1,NRCellCU=32">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">180</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">180</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">180</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUCPFunction=1,NRCellCU=31">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">180</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">180</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">180</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUCPFunction=1,NRCellCU=2">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">180</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">180</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">180</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUCPFunction=1,NRCellCU=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">180</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">180</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">180</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=GNBDUFunction_GNBDU">
+      <job jobId="PREDEF_5GRP"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmLic10MHzBwHwacFdd</measType>
+      <measType p="2">pmLic10MHzBwHwacTdd</measType>
+      <measType p="3">pmLic25Plus25MHzScActual</measType>
+      <measType p="4">pmLic5MHzBwHwacFdd</measType>
+      <measType p="5">pmLic5MHzBwHwacTdd</measType>
+      <measType p="6">pmLic5Plus5MHzScActual</measType>
+      <measType p="7">pmLic5Plus5MHzScFdd</measType>
+      <measType p="8">pmLic5Plus5MHzScTdd</measType>
+      <measType p="9">pmLicDlCapDistr</measType>
+      <measType p="10">pmLicDlCapUsedMax</measType>
+      <measType p="11">pmLicDlCapUsedSamp</measType>
+      <measType p="12">pmLicDlCapUsedSum</measType>
+      <measType p="13">pmLicLteNrDynSpectrumSharingActual</measType>
+      <measType p="14">pmLicLteNrDynSpectrumSharingFdd</measType>
+      <measType p="15">pmLicLteNrSpectrumSharingFdd</measType>
+      <measType p="16">pmLicUlCapDistr</measType>
+      <measType p="17">pmLicUlCapUsedMax</measType>
+      <measType p="18">pmLicUlCapUsedSamp</measType>
+      <measType p="19">pmLicUlCapUsedSum</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">32</r>
+        <r p="6">16</r>
+        <r p="7">0</r>
+        <r p="8">16</r>
+        <r p="9">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=NRCellDU_GNBDU">
+      <job jobId="PREDEF_5GRP"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmActiveUeDlMax</measType>
+      <measType p="2">pmActiveUeDlSamp</measType>
+      <measType p="3">pmActiveUeDlSum</measType>
+      <measType p="4">pmActiveUeDlTrueSamp</measType>
+      <measType p="5">pmActiveUeUlMax</measType>
+      <measType p="6">pmActiveUeUlSamp</measType>
+      <measType p="7">pmActiveUeUlSum</measType>
+      <measType p="8">pmActiveUeUlTrueSamp</measType>
+      <measType p="9">pmCellDlSlots</measType>
+      <measType p="10">pmCellDowntimeAuto</measType>
+      <measType p="11">pmCellDowntimeMan</measType>
+      <measType p="12">pmCellDowntimeManual</measType>
+      <measType p="13">pmMacLatTimeDlDrxSyncQos</measType>
+      <measType p="14">pmMacLatTimeDlDrxSyncSampQos</measType>
+      <measType p="15">pmMacLatTimeDlNoDrxSyncQos</measType>
+      <measType p="16">pmMacLatTimeDlNoDrxSyncSampQos</measType>
+      <measType p="17">pmMacRBSymAvailDl</measType>
+      <measType p="18">pmMacRBSymAvailPucch</measType>
+      <measType p="19">pmMacRBSymAvailRach</measType>
+      <measType p="20">pmMacRBSymAvailUl</measType>
+      <measType p="21">pmMacRBSymCsiRs</measType>
+      <measType p="22">pmMacRBSymUsedPdcchTypeA</measType>
+      <measType p="23">pmMacRBSymUsedPdcchTypeB</measType>
+      <measType p="24">pmMacRBSymUsedPdschTypeA</measType>
+      <measType p="25">pmMacRBSymUsedPdschTypeABroadcasting</measType>
+      <measType p="26">pmMacRBSymUsedPuschTypeA</measType>
+      <measType p="27">pmMacRBSymUsedPuschTypeB</measType>
+      <measType p="28">pmMacRBSymUtilDlDistr</measType>
+      <measType p="29">pmMacRBSymUtilDlMax</measType>
+      <measType p="30">pmMacRBSymUtilUlDistr</measType>
+      <measType p="31">pmMacRBSymUtilUlMax</measType>
+      <measType p="32">pmMacTimeDlDrb</measType>
+      <measType p="33">pmMacTimeDlDrbQos</measType>
+      <measType p="34">pmMacTimeUlResUe</measType>
+      <measType p="35">pmMacVolDl</measType>
+      <measType p="36">pmMacVolDlDrb</measType>
+      <measType p="37">pmMacVolDlDrbQos</measType>
+      <measType p="38">pmMacVolUl</measType>
+      <measType p="39">pmMacVolUlResUe</measType>
+      <measType p="40">pmPdschAvailTime</measType>
+      <measType p="41">pmPdschSchedActivity</measType>
+      <measType p="42">pmPdschSchedEntityDistr</measType>
+      <measType p="43">pmPuschAvailTime</measType>
+      <measType p="44">pmPuschSchedActivity</measType>
+      <measType p="45">pmPuschSchedEntityDistr</measType>
+      <measType p="46">pmRadioRaAttTaDistr</measType>
+      <measType p="47">pmRadioRaCbAttMsg2</measType>
+      <measType p="48">pmRadioRaCbFailMsg1MaxMsg3Sched</measType>
+      <measType p="49">pmRadioRaCbFailMsg1Ooc</measType>
+      <measType p="50">pmRadioRaCbFailMsg2Disc</measType>
+      <measType p="51">pmRadioRaCbFailMsg3Crc</measType>
+      <measType p="52">pmRadioRaCbFailMsg3Crnti</measType>
+      <measType p="53">pmRadioRaCbFailMsg3Dtx</measType>
+      <measType p="54">pmRadioRaCbPreambles</measType>
+      <measType p="55">pmRadioRaCbSuccMsg3</measType>
+      <measType p="56">pmRadioThpVolDl</measType>
+      <measType p="57">pmRadioThpVolUl</measType>
+      <measType p="58">pmRrcAccessFailBbIntens</measType>
+      <measType p="59">pmRrcAccessFailMpLoad</measType>
+      <measType p="60">pmSchedActivityCellDl</measType>
+      <measType p="61">pmSchedActivityCellUl</measType>
+      <measType p="62">pmUeCtxtAllocatedMax</measType>
+      <measType p="63">pmUeCtxtAllocatedSamp</measType>
+      <measType p="64">pmUeCtxtAllocatedSum</measType>
+      <measType p="65">pmUeCtxtSetupAtt</measType>
+      <measType p="66">pmUeCtxtSetupSucc</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=32">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">1800000</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">1800001</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">3032642268</r>
+        <r p="18">20160056</r>
+        <r p="19">15120000</r>
+        <r p="20">816482268</r>
+        <r p="21">0</r>
+        <r p="22">2880</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">3780540</r>
+        <r p="26">2520</r>
+        <r p="27">0</r>
+        <r p="28">900,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="29">0</r>
+        <r p="30">900,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">5760004</r>
+        <r p="41">0</r>
+        <r p="42">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="43">1440004</r>
+        <r p="44">0</r>
+        <r p="45">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="46">2,1,1,2,2,3,2,1,1,0,0,0</r>
+        <r p="47">15</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">60</r>
+        <r p="54">15</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">179</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=31">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">1800000</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">1800001</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">5110563822</r>
+        <r p="18">20160056</r>
+        <r p="19">15120000</r>
+        <r p="20">1375923822</r>
+        <r p="21">0</r>
+        <r p="22">5376</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">3780504</r>
+        <r p="26">2352</r>
+        <r p="27">0</r>
+        <r p="28">900,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="29">0</r>
+        <r p="30">900,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">5760004</r>
+        <r p="41">0</r>
+        <r p="42">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="43">1440004</r>
+        <r p="44">0</r>
+        <r p="45">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="46">3,1,2,1,2,0,3,2,0,0,0,0</r>
+        <r p="47">14</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">56</r>
+        <r p="54">14</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">179</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=2">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">900</r>
+        <r p="12">900</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="29">0</r>
+        <r p="30">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="46">0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBDUFunction=1,NRCellDU=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">900</r>
+        <r p="12">900</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="29">0</r>
+        <r p="30">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="46">0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=PpControlLink_GNBCUUP">
+      <job jobId="PREDEF_5GPP"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmDrbLevelMaxEndc</measType>
+      <measType p="2">pmDrbLevelMaxSa</measType>
+      <measType p="3">pmDrbLevelSamp</measType>
+      <measType p="4">pmDrbLevelSumEndc</measType>
+      <measType p="5">pmDrbLevelSumSa</measType>
+      <measType p="6">pmPduSessionLevelMax</measType>
+      <measType p="7">pmPduSessionLevelSamp</measType>
+      <measType p="8">pmPduSessionLevelSum</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUUPFunction=1,PpControlTermination=1,PpControlLink=internal">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">900</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">900</r>
+        <r p="8">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=EUtranCellFDD">
+      <job jobId="ltearq"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmDlAssigsDetHarqMcPttNDO</measType>
+      <measType p="2">pmDlAssigsDetHarqNonMcPttNDO</measType>
+      <measType p="3">pmDlAssigsDetectedHarqNDO</measType>
+      <measType p="4">pmDlAssigsDetectedHarqVolteNDO</measType>
+      <measType p="5">pmDlAssigsWithDetHarqAckMcPtt</measType>
+      <measType p="6">pmDlAssigsWithDetHarqAckNonMcPtt</measType>
+      <measType p="7">pmDlAssigsWithDetectedHarqAck</measType>
+      <measType p="8">pmDlAssigsWithDetectedHarqAckPCell</measType>
+      <measType p="9">pmDlAssigsWithDetectedHarqAckSCell</measType>
+      <measType p="10">pmDlAssigsWithDetectedHarqAckVolte</measType>
+      <measType p="11">pmMacHarqDlAck16qam</measType>
+      <measType p="12">pmMacHarqDlAck256qam</measType>
+      <measType p="13">pmMacHarqDlAck64qam</measType>
+      <measType p="14">pmMacHarqDlAckComp</measType>
+      <measType p="15">pmMacHarqDlAckQpsk</measType>
+      <measType p="16">pmMacHarqDlDtx16qam</measType>
+      <measType p="17">pmMacHarqDlDtx256qam</measType>
+      <measType p="18">pmMacHarqDlDtx64qam</measType>
+      <measType p="19">pmMacHarqDlDtxQpsk</measType>
+      <measType p="20">pmMacHarqDlNack16qam</measType>
+      <measType p="21">pmMacHarqDlNack256qam</measType>
+      <measType p="22">pmMacHarqDlNack64qam</measType>
+      <measType p="23">pmMacHarqDlNackComp</measType>
+      <measType p="24">pmMacHarqDlNackQpsk</measType>
+      <measType p="25">pmMacHarqFail</measType>
+      <measType p="26">pmMacHarqUlDtx16qam</measType>
+      <measType p="27">pmMacHarqUlDtx256Qam</measType>
+      <measType p="28">pmMacHarqUlDtx64Qam</measType>
+      <measType p="29">pmMacHarqUlDtxQpsk</measType>
+      <measType p="30">pmMacHarqUlFail16qam</measType>
+      <measType p="31">pmMacHarqUlFail256Qam</measType>
+      <measType p="32">pmMacHarqUlFail64Qam</measType>
+      <measType p="33">pmMacHarqUlFailIua16qam</measType>
+      <measType p="34">pmMacHarqUlFailIuaQpsk</measType>
+      <measType p="35">pmMacHarqUlFailQpsk</measType>
+      <measType p="36">pmMacHarqUlSucc16qam</measType>
+      <measType p="37">pmMacHarqUlSucc256Qam</measType>
+      <measType p="38">pmMacHarqUlSucc64Qam</measType>
+      <measType p="39">pmMacHarqUlSuccQpsk</measType>
+      <measType p="40">pmRaAccHarqFailAfterContRes</measType>
+      <measType p="41">pmRaAccHarqFailWithinContRes</measType>
+      <measType p="42">pmRaRejHarqFailAfterContRes</measType>
+      <measType p="43">pmRaRejHarqFailWithinContRes</measType>
+      <measType p="44">pmRlcArqDlAck</measType>
+      <measType p="45">pmRlcArqDlNack</measType>
+      <measType p="46">pmRlcArqRetxDl</measType>
+      <measType p="47">pmRlcArqUlAck</measType>
+      <measType p="48">pmRlcArqUlNack</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,ENodeBFunction=1,EUtranCellFDD=2">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,ENodeBFunction=1,EUtranCellFDD=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=FieldReplaceableUnit">
+      <job jobId="PREDEF_Nc"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmPowerFailure</measType>
+      <measType p="2">pmUnitTemperatureLevel</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S3-1">
+        <r p="1">0</r>
+        <r p="2">4,4,4</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S2-1">
+        <r p="1">0</r>
+        <r p="2">4,4,4</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S1-1">
+        <r p="1">0</r>
+        <r p="2">4,4,4</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608">
+        <r p="1">0</r>
+        <r p="2"> , , </r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1">
+        <r p="1"> </r>
+        <r p="2">4,4,4</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=RiEthernetPort">
+      <job jobId="PREDEF_Nc"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">ifHCInBroadcastPkts</measType>
+      <measType p="2">ifHCInMulticastPkts</measType>
+      <measType p="3">ifHCInOctets</measType>
+      <measType p="4">ifHCInUcastPkts</measType>
+      <measType p="5">ifHCOutBroadcastPkts</measType>
+      <measType p="6">ifHCOutMulticastPkts</measType>
+      <measType p="7">ifHCOutOctets</measType>
+      <measType p="8">ifHCOutUcastPkts</measType>
+      <measType p="9">ifInErrors</measType>
+      <measType p="10">ifOutErrors</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,RiPort=P,RiEthernetPort=1">
+        <r p="1">0</r>
+        <r p="2">51885</r>
+        <r p="3">4628315</r>
+        <r p="4">9190</r>
+        <r p="5">0</r>
+        <r p="6">15493</r>
+        <r p="7">1956503</r>
+        <r p="8">7326</r>
+        <r p="9">0</r>
+        <r p="10"> </r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,RiPort=N,RiEthernetPort=1">
+        <r p="1">0</r>
+        <r p="2">37309</r>
+        <r p="3">2868954</r>
+        <r p="4">4551</r>
+        <r p="5">0</r>
+        <r p="6">915</r>
+        <r p="7">380361</r>
+        <r p="8">4549</r>
+        <r p="9">0</r>
+        <r p="10"> </r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,RiPort=B,RiEthernetPort=1">
+        <r p="1">0</r>
+        <r p="2">15490</r>
+        <r p="3">1512626</r>
+        <r p="4">7326</r>
+        <r p="5">0</r>
+        <r p="6">51883</r>
+        <r p="7">3492085</r>
+        <r p="8">9190</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,RiPort=A,RiEthernetPort=1">
+        <r p="1">0</r>
+        <r p="2">916</r>
+        <r p="3">260228</r>
+        <r p="4">4549</r>
+        <r p="5">0</r>
+        <r p="6">37309</r>
+        <r p="7">2097258</r>
+        <r p="8">4551</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=RiLink">
+      <job jobId="PREDEF_Nc"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmLinkDelayHigh</measType>
+      <measType p="2">pmLinkDelayLow</measType>
+      <measType p="3">pmLinkRestart</measType>
+      <measType p="4">pmLinkStabilityError</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,RiLink=S3-1">
+        <r p="1">1077</r>
+        <r p="2">1076</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,RiLink=S2-1">
+        <r p="1">450</r>
+        <r p="2">450</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,RiLink=S1-1">
+        <r p="1">427</r>
+        <r p="2">427</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,RiLink=R608-2">
+        <r p="1">0</r>
+        <r p="2">2147483647</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,RiLink=R608-1">
+        <r p="1">0</r>
+        <r p="2">2147483647</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=RiPort">
+      <job jobId="PREDEF_Nc"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmBitError</measType>
+      <measType p="2">pmLossOfFrame</measType>
+      <measType p="3">pmLossOfSignal</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S3-1,RiPort=DATA_2">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S3-1,RiPort=DATA_1">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S2-1,RiPort=DATA_2">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S2-1,RiPort=DATA_1">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S1-1,RiPort=DATA_2">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S1-1,RiPort=DATA_1">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,RiPort=P">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,RiPort=N">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,RiPort=B">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,RiPort=A">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,RiPort=K">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,RiPort=C">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,RiPort=B">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,RiPort=A">
+        <r p="1">0,0,0,0,0,0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=SfpChannel">
+      <job jobId="PREDEF_Nc"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmChannelRxPower</measType>
+      <measType p="2">pmChannelTxBias</measType>
+      <measType p="3">pmChannelTxPower</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,SfpModule=P,SfpChannel=1">
+        <r p="1">2951</r>
+        <r p="2">3600</r>
+        <r p="3">2922</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,SfpModule=N,SfpChannel=1">
+        <r p="1">3018</r>
+        <r p="2">4200</r>
+        <r p="3">2953</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=K,SfpChannel=1">
+        <r p="1">2809</r>
+        <r p="2">3500</r>
+        <r p="3">2812</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S3-1,SfpModule=DATA_2,SfpChannel=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S2-1,SfpModule=DATA_2,SfpChannel=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S1-1,SfpModule=DATA_2,SfpChannel=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S3-1,SfpModule=DATA_1,SfpChannel=1">
+        <r p="1">2810</r>
+        <r p="2">3100</r>
+        <r p="3">2830</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S2-1,SfpModule=DATA_1,SfpChannel=1">
+        <r p="1">2717</r>
+        <r p="2">3600</r>
+        <r p="3">2775</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S1-1,SfpModule=DATA_1,SfpChannel=1">
+        <r p="1">2700</r>
+        <r p="2">3900</r>
+        <r p="3">2773</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=C,SfpChannel=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,SfpModule=B,SfpChannel=1">
+        <r p="1">2744</r>
+        <r p="2">3200</r>
+        <r p="3">2775</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=B,SfpChannel=1">
+        <r p="1">2863</r>
+        <r p="2">3800</r>
+        <r p="3">2898</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,SfpModule=A,SfpChannel=1">
+        <r p="1">2684</r>
+        <r p="2">3900</r>
+        <r p="3">2737</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=A,SfpChannel=1">
+        <r p="1">2892</r>
+        <r p="2">4400</r>
+        <r p="3">2893</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=SfpModule">
+      <job jobId="PREDEF_Nc"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmSfpTemperature</measType>
+      <measType p="2">pmSfpVoltage</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S3-1,SfpModule=DATA_2">
+        <r p="1">0</r>
+        <r p="2">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S3-1,SfpModule=DATA_1">
+        <r p="1">1310</r>
+        <r p="2">32552</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S2-1,SfpModule=DATA_2">
+        <r p="1">0</r>
+        <r p="2">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S2-1,SfpModule=DATA_1">
+        <r p="1">1430</r>
+        <r p="2">32904</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S1-1,SfpModule=DATA_2">
+        <r p="1">0</r>
+        <r p="2">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S1-1,SfpModule=DATA_1">
+        <r p="1">1410</r>
+        <r p="2">33141</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,SfpModule=P">
+        <r p="1">1388</r>
+        <r p="2">32558</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,SfpModule=N">
+        <r p="1">1384</r>
+        <r p="2">32505</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,SfpModule=B">
+        <r p="1">1297</r>
+        <r p="2">32999</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,SfpModule=A">
+        <r p="1">1297</r>
+        <r p="2">33041</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=K">
+        <r p="1">1474</r>
+        <r p="2">32588</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=C">
+        <r p="1">0</r>
+        <r p="2">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=B">
+        <r p="1">1463</r>
+        <r p="2">32493</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,SfpModule=A">
+        <r p="1">1464</r>
+        <r p="2">32534</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=EthernetPort">
+      <job jobId="PREDEF_Rtn"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">ifHCInBroadcastPkts</measType>
+      <measType p="2">ifHCInMulticastPkts</measType>
+      <measType p="3">ifHCInOctets</measType>
+      <measType p="4">ifHCInUcastPkts</measType>
+      <measType p="5">ifHCOutBroadcastPkts</measType>
+      <measType p="6">ifHCOutMulticastPkts</measType>
+      <measType p="7">ifHCOutOctets</measType>
+      <measType p="8">ifHCOutUcastPkts</measType>
+      <measType p="9">ifInDiscards</measType>
+      <measType p="10">ifInErrors</measType>
+      <measType p="11">ifInUnknownProtos</measType>
+      <measType p="12">ifInUnknownTags</measType>
+      <measType p="13">ifOutDiscards</measType>
+      <measType p="14">ifOutErrors</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Transport=1,EthernetPort=TN_IDL_B_1">
+        <r p="1">22345</r>
+        <r p="2">1057</r>
+        <r p="3">2921772</r>
+        <r p="4">4472</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">9147991</r>
+        <r p="8">17099</r>
+        <r p="9">0</r>
+        <r p="10">7627</r>
+        <r p="11">3582</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=InterfaceIPv4">
+      <job jobId="PREDEF_Rtn"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">ipIfStatsHCInOctets</measType>
+      <measType p="2">ipIfStatsHCInReceives</measType>
+      <measType p="3">ipIfStatsHCOutOctets</measType>
+      <measType p="4">ipIfStatsHCOutTransmits</measType>
+      <measType p="5">ipIfStatsInAddrErrors</measType>
+      <measType p="6">ipIfStatsInDiscards</measType>
+      <measType p="7">ipIfStatsInHdrErrors</measType>
+      <measType p="8">ipIfStatsInNoRoutes</measType>
+      <measType p="9">ipIfStatsInTruncatedPkts</measType>
+      <measType p="10">ipIfStatsInUnknownProtos</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Transport=1,Router=vr_OAM,InterfaceIPv4=1">
+        <r p="1">314834</r>
+        <r p="2">4561</r>
+        <r p="3">8697703</r>
+        <r p="4">16811</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Transport=1,Router=vr_NR,InterfaceIPv4=1">
+        <r p="1">19900</r>
+        <r p="2">388</r>
+        <r p="3">1260</r>
+        <r p="4">30</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Transport=1,Router=vr_LTE,InterfaceIPv4=1">
+        <r p="1">26656</r>
+        <r p="2">574</r>
+        <r p="3">14580</r>
+        <r p="4">243</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Transport=1,Router=Node_Internal_F1,InterfaceIPv4=NRDU">
+        <r p="1"> </r>
+        <r p="2"> </r>
+        <r p="3"> </r>
+        <r p="4"> </r>
+        <r p="5"> </r>
+        <r p="6"> </r>
+        <r p="7"> </r>
+        <r p="8"> </r>
+        <r p="9"> </r>
+        <r p="10"> </r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Transport=1,Router=Node_Internal_F1,InterfaceIPv4=NRCUCP">
+        <r p="1"> </r>
+        <r p="2"> </r>
+        <r p="3"> </r>
+        <r p="4"> </r>
+        <r p="5"> </r>
+        <r p="6"> </r>
+        <r p="7"> </r>
+        <r p="8"> </r>
+        <r p="9"> </r>
+        <r p="10"> </r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=SctpAssociation">
+      <job jobId="PREDEF_Rtn"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">sctpAssocInAbnormalControlChunks</measType>
+      <measType p="2">sctpAssocInAbnormalDataChunks</measType>
+      <measType p="3">sctpAssocInControlChunks</measType>
+      <measType p="4">sctpAssocInDataChunks</measType>
+      <measType p="5">sctpAssocInDiscardedControlChunks</measType>
+      <measType p="6">sctpAssocInDiscardedDataChunks</measType>
+      <measType p="7">sctpAssocInOctets</measType>
+      <measType p="8">sctpAssocOutDataChunks</measType>
+      <measType p="9">sctpAssocOutDiscardedDataChunks</measType>
+      <measType p="10">sctpAssocOutDiscardedUserMsgs</measType>
+      <measType p="11">sctpAssocOutOctets</measType>
+      <measType p="12">sctpAssocRtxChunks</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Transport=1,SctpEndpoint=F1_NRDU,SctpAssociation=38472-10.0.0.1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">833</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">87820</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">87820</r>
+        <r p="12">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Transport=1,SctpEndpoint=F1_NRCUCP,SctpAssociation=38472-10.0.0.2">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">833</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">87820</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">87820</r>
+        <r p="12">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=VlanPort">
+      <job jobId="PREDEF_Rtn"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">ifHCInBroadcastPkts</measType>
+      <measType p="2">ifHCInMulticastPkts</measType>
+      <measType p="3">ifHCInOctets</measType>
+      <measType p="4">ifHCInUcastPkts</measType>
+      <measType p="5">ifHCOutBroadcastPkts</measType>
+      <measType p="6">ifHCOutMulticastPkts</measType>
+      <measType p="7">ifHCOutOctets</measType>
+      <measType p="8">ifHCOutUcastPkts</measType>
+      <measType p="9">ifOutDiscards</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Transport=1,VlanPort=vr_OAM.IF1">
+        <r p="1">1996</r>
+        <r p="2">341</r>
+        <r p="3">657544</r>
+        <r p="4">4205</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">9067777</r>
+        <r p="8">16814</r>
+        <r p="9">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Transport=1,VlanPort=vr_NR.IF1">
+        <r p="1">8457</r>
+        <r p="2">343</r>
+        <r p="3">711892</r>
+        <r p="4">33</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">2368</r>
+        <r p="8">37</r>
+        <r p="9">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Transport=1,VlanPort=vr_LTE.IF1">
+        <r p="1">11894</r>
+        <r p="2">343</r>
+        <r p="3">1006906</r>
+        <r p="4">234</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">20246</r>
+        <r p="8">248</r>
+        <r p="9">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=ConsumedEnergyMeasurement">
+      <job jobId="PREDEF_Apc"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmConsumedEnergy</measType>
+      <measType p="2">pmConsumedEnergyAccumulated</measType>
+      <measType p="3">pmPowerConsumption</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,NodeSupport=1,ConsumedEnergyMeasurement=1">
+        <r p="1"> </r>
+        <r p="2"> </r>
+        <r p="3"> , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , </r>
+        <suspect>true</suspect>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=EnergyMeter">
+      <job jobId="PREDEF_Apc"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmConsumedEnergy</measType>
+      <measType p="2">pmConsumedEnergyAccumulated</measType>
+      <measType p="3">pmPowerConsumption</measType>
+      <measType p="4">pmVoltage</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,EnergyMeter=1">
+        <r p="1">83</r>
+        <r p="2">34846</r>
+        <r p="3">333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,334,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333,333</r>
+        <r p="4">52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S1-1,EnergyMeter=1">
+        <r p="1">12</r>
+        <r p="2">4947</r>
+        <r p="3">47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47</r>
+        <r p="4">53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S2-1,EnergyMeter=1">
+        <r p="1">13</r>
+        <r p="2">5394</r>
+        <r p="3">52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52</r>
+        <r p="4">54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=Radio-S3-1,EnergyMeter=1">
+        <r p="1">22</r>
+        <r p="2">9338</r>
+        <r p="3">89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89</r>
+        <r p="4">53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,52,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,52,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=R608,EnergyMeter=1">
+        <r p="1"> </r>
+        <r p="2"> </r>
+        <r p="3"> , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , </r>
+        <r p="4"> , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , </r>
+        <suspect>true</suspect>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=SupportUnit">
+      <job jobId="PREDEF_Apc"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmFanSpeed</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,SupportUnit=2">
+        <r p="1">18,18,18</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,SupportUnit=1">
+        <r p="1">30,30,30</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=BbProcessingResource">
+      <job jobId="PREDEF_Lrat"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmLicDlCapActual</measType>
+      <measType p="2">pmLicDlCapDistr</measType>
+      <measType p="3">pmLicDlCapUsedMax</measType>
+      <measType p="4">pmLicDlCapUsedSamp</measType>
+      <measType p="5">pmLicDlCapUsedSum</measType>
+      <measType p="6">pmLicDlPrbCapActual</measType>
+      <measType p="7">pmLicDlPrbCapDistr</measType>
+      <measType p="8">pmLicDlPrbUsedDistr</measType>
+      <measType p="9">pmLicDlPrbUsedMax</measType>
+      <measType p="10">pmLicDlPrbUsedSamp</measType>
+      <measType p="11">pmLicDlPrbUsedSum</measType>
+      <measType p="12">pmLicPrbUnit</measType>
+      <measType p="13">pmLicUlCapActual</measType>
+      <measType p="14">pmLicUlCapDistr</measType>
+      <measType p="15">pmLicUlCapUsedMax</measType>
+      <measType p="16">pmLicUlCapUsedSamp</measType>
+      <measType p="17">pmLicUlCapUsedSum</measType>
+      <measType p="18">pmLicUlPrbCapActual</measType>
+      <measType p="19">pmLicUlPrbCapDistr</measType>
+      <measType p="20">pmLicUlPrbUsedDistr</measType>
+      <measType p="21">pmLicUlPrbUsedMax</measType>
+      <measType p="22">pmLicUlPrbUsedSamp</measType>
+      <measType p="23">pmLicUlPrbUsedSum</measType>
+      <measType p="24">pmPdcpPktDiscDlEth</measType>
+      <measType p="25">pmPdcpPktDiscUlEthPacing</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,BbProcessingResource=1">
+        <r p="1">0</r>
+        <r p="2">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="20">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=ENodeBFunction">
+      <job jobId="PREDEF_Lrat"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmLic10MHzBwHwacFddAas</measType>
+      <measType p="2">pmLic10MHzBwHwacTddAas</measType>
+      <measType p="3">pmLic5MHzBwHwacNonAas</measType>
+      <measType p="4">pmLic5MHzSectorCarrierActual</measType>
+      <measType p="5">pmLic5Plus5MHzScFddActual</measType>
+      <measType p="6">pmLic5Plus5MHzScTddActual</measType>
+      <measType p="7">pmLicConnectedUsersActual</measType>
+      <measType p="8">pmLicConnectedUsersDistr</measType>
+      <measType p="9">pmLicConnectedUsersLevSamp</measType>
+      <measType p="10">pmLicConnectedUsersLevSum</measType>
+      <measType p="11">pmLicConnectedUsersLicense</measType>
+      <measType p="12">pmLicConnectedUsersMax</measType>
+      <measType p="13">pmLicConnectedUsersTimeCong</measType>
+      <measType p="14">pmLicDlCapLicense</measType>
+      <measType p="15">pmLicDlPrbCapLicense</measType>
+      <measType p="16">pmLicUlCapLicense</measType>
+      <measType p="17">pmLicUlPrbCapLicense</measType>
+      <measType p="18">pmMoFootprintMax</measType>
+      <measType p="19">pmObsLocalEventDiscPmLoad</measType>
+      <measType p="20">pmRrcConnBrEnbLevSamp</measType>
+      <measType p="21">pmRrcConnBrEnbLevSum</measType>
+      <measType p="22">pmRrcConnBrEnbMax</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,ENodeBFunction=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">5</r>
+        <r p="8">900,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="9">180</r>
+        <r p="10">0</r>
+        <r p="11">8000</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">10000</r>
+        <r p="15">1800</r>
+        <r p="16">10000</r>
+        <r p="17">1800</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=EUtranCellFDD">
+      <job jobId="PREDEF_Lrat"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmCellDowntimeAuto</measType>
+      <measType p="2">pmCellDowntimeMan</measType>
+      <measType p="3">pmCellHoExeAttLteInterF</measType>
+      <measType p="4">pmCellHoExeAttLteIntraF</measType>
+      <measType p="5">pmCellHoExeSuccLteInterF</measType>
+      <measType p="6">pmCellHoExeSuccLteIntraF</measType>
+      <measType p="7">pmCellHoPrepAttLteInterF</measType>
+      <measType p="8">pmCellHoPrepAttLteIntraF</measType>
+      <measType p="9">pmCellHoPrepSuccLteInterF</measType>
+      <measType p="10">pmCellHoPrepSuccLteIntraF</measType>
+      <measType p="11">pmDrbThpTimeDlQci</measType>
+      <measType p="12">pmEndcSetupScgUeAtt</measType>
+      <measType p="13">pmEndcSetupScgUeSucc</measType>
+      <measType p="14">pmEndcSetupUeAtt</measType>
+      <measType p="15">pmEndcSetupUeSucc</measType>
+      <measType p="16">pmErabEstabAttAdded</measType>
+      <measType p="17">pmErabEstabAttAddedArp</measType>
+      <measType p="18">pmErabEstabAttAddedCsfb</measType>
+      <measType p="19">pmErabEstabAttAddedCsfbArp</measType>
+      <measType p="20">pmErabEstabAttAddedCsfbQci</measType>
+      <measType p="21">pmErabEstabAttAddedHoOngoing</measType>
+      <measType p="22">pmErabEstabAttAddedHoOngoingArp</measType>
+      <measType p="23">pmErabEstabAttAddedHoOngoingQci</measType>
+      <measType p="24">pmErabEstabAttAddedQci</measType>
+      <measType p="25">pmErabEstabAttInit</measType>
+      <measType p="26">pmErabEstabAttInitArp</measType>
+      <measType p="27">pmErabEstabFailAddedLic</measType>
+      <measType p="28">pmErabEstabFailAddedOngoingProc</measType>
+      <measType p="29">pmErabEstabFailGbrDlEnb</measType>
+      <measType p="30">pmErabEstabFailGbrUlEnb</measType>
+      <measType p="31">pmErabEstabFailInitLic</measType>
+      <measType p="32">pmErabEstabSuccAdded</measType>
+      <measType p="33">pmErabEstabSuccAddedArp</measType>
+      <measType p="34">pmErabEstabSuccAddedQci</measType>
+      <measType p="35">pmErabEstabSuccInit</measType>
+      <measType p="36">pmErabEstabSuccInitArp</measType>
+      <measType p="37">pmErabRelAbnormalEnbAct</measType>
+      <measType p="38">pmErabRelAbnormalMmeAct</measType>
+      <measType p="39">pmErabRelMmeAct</measType>
+      <measType p="40">pmErabRelNormalEnbAct</measType>
+      <measType p="41">pmErabRelNormalEnbArp</measType>
+      <measType p="42">pmEranCompUlAvoidedNack</measType>
+      <measType p="43">pmEranCompUlNack</measType>
+      <measType p="44">pmHoPrepRejInLicConnUsers</measType>
+      <measType p="45">pmHoPrepRejInLicMob</measType>
+      <measType p="46">pmHoPrepRejInSl</measType>
+      <measType p="47">pmLcgThpTimeUlLcg</measType>
+      <measType p="48">pmLcgThpVolUlLcg</measType>
+      <measType p="49">pmMacCellThpTimeDl</measType>
+      <measType p="50">pmMacCellThpTimeUl</measType>
+      <measType p="51">pmMacDrbThpTimeDlQci</measType>
+      <measType p="52">pmMacDrbThpVolDlQci</measType>
+      <measType p="53">pmMacLcgThpTimeUlLcg</measType>
+      <measType p="54">pmMacLcgThpVolUlLcg</measType>
+      <measType p="55">pmMacTimeUlResUe</measType>
+      <measType p="56">pmMacUeThpTimeDl</measType>
+      <measType p="57">pmMacUeThpTimeUl</measType>
+      <measType p="58">pmMacUeThpUlMbbHighVolDistr</measType>
+      <measType p="59">pmMacUeThpUlMbbLowVolDistr</measType>
+      <measType p="60">pmMacUeThpVolDl</measType>
+      <measType p="61">pmMacUeThpVolUl</measType>
+      <measType p="62">pmMacVolUlResUe</measType>
+      <measType p="63">pmObsLocalEventDiscPmLoad</measType>
+      <measType p="64">pmPagReceivedCe</measType>
+      <measType p="65">pmPdcpLatPktTransDl</measType>
+      <measType p="66">pmPdcpLatTimeDl</measType>
+      <measType p="67">pmPdcpPktDiscDlPelr</measType>
+      <measType p="68">pmPdcpPktDiscDlPelrUu</measType>
+      <measType p="69">pmPdcpPktLostUl</measType>
+      <measType p="70">pmPdcpPktReceivedUl</measType>
+      <measType p="71">pmPdcpPktTransDl</measType>
+      <measType p="72">pmPdcpUeThpTimeDl</measType>
+      <measType p="73">pmPdcpVolDlDrb</measType>
+      <measType p="74">pmPdcpVolDlDrbFiltQci</measType>
+      <measType p="75">pmPdcpVolDlDrbLastTTI</measType>
+      <measType p="76">pmPdcpVolDlDrbLastTTIQci</measType>
+      <measType p="77">pmPdcpVolDlSrb</measType>
+      <measType p="78">pmPdcpVolUlDrb</measType>
+      <measType p="79">pmPdcpVolUlSrb</measType>
+      <measType p="80">pmRrcConnEstabAtt</measType>
+      <measType p="81">pmRrcConnEstabAttCe</measType>
+      <measType p="82">pmRrcConnEstabAttDta</measType>
+      <measType p="83">pmRrcConnEstabAttDtaCe</measType>
+      <measType p="84">pmRrcConnEstabAttReatt</measType>
+      <measType p="85">pmRrcConnEstabAttReattCe</measType>
+      <measType p="86">pmRrcConnEstabAttReattDta</measType>
+      <measType p="87">pmRrcConnEstabAttReattDtaCe</measType>
+      <measType p="88">pmRrcConnEstabFailLic</measType>
+      <measType p="89">pmRrcConnEstabFailLicActiveUsers</measType>
+      <measType p="90">pmRrcConnEstabFailMmeOvlMod</measType>
+      <measType p="91">pmRrcConnEstabFailMmeOvlModCe</measType>
+      <measType p="92">pmRrcConnEstabFailMmeOvlMos</measType>
+      <measType p="93">pmRrcConnEstabFailMmeOvlMosCe</measType>
+      <measType p="94">pmRrcConnEstabSucc</measType>
+      <measType p="95">pmRrcConnEstabSuccCe</measType>
+      <measType p="96">pmRrcConnEstabSuccDta</measType>
+      <measType p="97">pmRrcConnEstabSuccDtaCe</measType>
+      <measType p="98">pmRrcConnEstabSuccGummeiNative</measType>
+      <measType p="99">pmRrcConnResumeAtt</measType>
+      <measType p="100">pmRrcConnResumeFallbackAtt</measType>
+      <measType p="101">pmRrcConnResumeFallbackSucc</measType>
+      <measType p="102">pmRrcConnResumeSucc</measType>
+      <measType p="103">pmS1SigConnEstabAtt</measType>
+      <measType p="104">pmS1SigConnEstabAttCe</measType>
+      <measType p="105">pmS1SigConnEstabAttDta</measType>
+      <measType p="106">pmS1SigConnEstabAttDtaCe</measType>
+      <measType p="107">pmS1SigConnEstabAttEm</measType>
+      <measType p="108">pmS1SigConnEstabAttHpa</measType>
+      <measType p="109">pmS1SigConnEstabAttMod</measType>
+      <measType p="110">pmS1SigConnEstabAttModCe</measType>
+      <measType p="111">pmS1SigConnEstabAttMos</measType>
+      <measType p="112">pmS1SigConnEstabAttMta</measType>
+      <measType p="113">pmS1SigConnEstabAttMtaCe</measType>
+      <measType p="114">pmS1SigConnEstabFailMmeOvlMos</measType>
+      <measType p="115">pmS1SigConnEstabFailMmeOvlMosCe</measType>
+      <measType p="116">pmS1SigConnEstabSucc</measType>
+      <measType p="117">pmS1SigConnEstabSuccCe</measType>
+      <measType p="118">pmS1SigConnEstabSuccDta</measType>
+      <measType p="119">pmS1SigConnEstabSuccDtaCe</measType>
+      <measType p="120">pmS1SigConnEstabSuccEm</measType>
+      <measType p="121">pmS1SigConnEstabSuccHpa</measType>
+      <measType p="122">pmS1SigConnEstabSuccMod</measType>
+      <measType p="123">pmS1SigConnEstabSuccModCe</measType>
+      <measType p="124">pmS1SigConnEstabSuccMos</measType>
+      <measType p="125">pmS1SigConnEstabSuccMta</measType>
+      <measType p="126">pmS1SigConnEstabSuccMtaCe</measType>
+      <measType p="127">pmSessionTimeDrb</measType>
+      <measType p="128">pmSessionTimeUe</measType>
+      <measType p="129">pmUeCtxtConnResumeAtt</measType>
+      <measType p="130">pmUeCtxtConnResumeSucc</measType>
+      <measType p="131">pmUeCtxtRelAbnormalEnbAct</measType>
+      <measType p="132">pmUeCtxtRelAbnormalMmeAct</measType>
+      <measType p="133">pmUeCtxtRelCsfbCdma1xRtt</measType>
+      <measType p="134">pmUeCtxtRelCsfbCdma1xRttEm</measType>
+      <measType p="135">pmUeCtxtRelCsfbGsm</measType>
+      <measType p="136">pmUeCtxtRelCsfbGsmEm</measType>
+      <measType p="137">pmUeCtxtRelCsfbLimitedDualRadioUeCdma</measType>
+      <measType p="138">pmUeCtxtRelCsfbTdScdma</measType>
+      <measType p="139">pmUeCtxtRelCsfbTdScdmaEm</measType>
+      <measType p="140">pmUeCtxtRelCsfbWcdma</measType>
+      <measType p="141">pmUeCtxtRelCsfbWcdmaEm</measType>
+      <measType p="142">pmUeCtxtRelMmeAct</measType>
+      <measType p="143">pmUeCtxtResumeAtt</measType>
+      <measType p="144">pmUeCtxtResumeSucc</measType>
+      <measType p="145">pmUeExclThpDlShortDrb</measType>
+      <measType p="146">pmUeExclThpDlShortDrb2</measType>
+      <measType p="147">pmUeExclThpUlRelativeHighVol</measType>
+      <measType p="148">pmUeExclThpUlRelativeLowVol</measType>
+      <measType p="149">pmUeExclThpUlShortTimeHighVol</measType>
+      <measType p="150">pmUeExclThpUlShortTimeLowVol</measType>
+      <measType p="151">pmUeThp2DlDistr</measType>
+      <measType p="152">pmUeThp2UlDistr</measType>
+      <measType p="153">pmUeThpDlDistr</measType>
+      <measType p="154">pmUeThpDlMbbFiltered2Distr</measType>
+      <measType p="155">pmUeThpDlMbbFilteredDistr</measType>
+      <measType p="156">pmUeThpTimeDl</measType>
+      <measType p="157">pmUeThpTimeUl</measType>
+      <measType p="158">pmUeThpUlDistr</measType>
+      <measType p="159">pmUeThpVolUl</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,ENodeBFunction=1,EUtranCellFDD=2">
+        <r p="1">0</r>
+        <r p="2">900</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0,0,0,0</r>
+        <r p="48">0,0,0,0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0,0,0,0</r>
+        <r p="54">0,0,0,0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="59">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0,0,0,0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+        <r p="67">0</r>
+        <r p="68">0</r>
+        <r p="69">0</r>
+        <r p="70">0</r>
+        <r p="71">0</r>
+        <r p="72">0</r>
+        <r p="73">0</r>
+        <r p="74">0</r>
+        <r p="75">0</r>
+        <r p="76">0</r>
+        <r p="77">0</r>
+        <r p="78">0</r>
+        <r p="79">0</r>
+        <r p="80">0</r>
+        <r p="81">0,0,0,0</r>
+        <r p="82">0</r>
+        <r p="83">0,0,0,0</r>
+        <r p="84">0</r>
+        <r p="85">0,0,0,0</r>
+        <r p="86">0</r>
+        <r p="87">0,0,0,0</r>
+        <r p="88">0</r>
+        <r p="89">0</r>
+        <r p="90">0</r>
+        <r p="91">0,0,0,0</r>
+        <r p="92">0</r>
+        <r p="93">0,0,0,0</r>
+        <r p="94">0</r>
+        <r p="95">0,0,0,0</r>
+        <r p="96">0</r>
+        <r p="97">0,0,0,0</r>
+        <r p="98">0</r>
+        <r p="99">0</r>
+        <r p="100">0</r>
+        <r p="101">0</r>
+        <r p="102">0</r>
+        <r p="103">0</r>
+        <r p="104">0,0,0,0</r>
+        <r p="105">0</r>
+        <r p="106">0,0,0,0</r>
+        <r p="107">0</r>
+        <r p="108">0</r>
+        <r p="109">0</r>
+        <r p="110">0,0,0,0</r>
+        <r p="111">0</r>
+        <r p="112">0</r>
+        <r p="113">0,0,0,0</r>
+        <r p="114">0</r>
+        <r p="115">0,0,0,0</r>
+        <r p="116">0</r>
+        <r p="117">0,0,0,0</r>
+        <r p="118">0</r>
+        <r p="119">0,0,0,0</r>
+        <r p="120">0</r>
+        <r p="121">0</r>
+        <r p="122">0</r>
+        <r p="123">0,0,0,0</r>
+        <r p="124">0</r>
+        <r p="125">0</r>
+        <r p="126">0,0,0,0</r>
+        <r p="127">0</r>
+        <r p="128">0</r>
+        <r p="129">0</r>
+        <r p="130">0</r>
+        <r p="131">0</r>
+        <r p="132">0</r>
+        <r p="133">0</r>
+        <r p="134">0</r>
+        <r p="135">0</r>
+        <r p="136">0</r>
+        <r p="137">0</r>
+        <r p="138">0</r>
+        <r p="139">0</r>
+        <r p="140">0</r>
+        <r p="141">0</r>
+        <r p="142">0</r>
+        <r p="143">0</r>
+        <r p="144">0</r>
+        <r p="145">0</r>
+        <r p="146">0</r>
+        <r p="147">0</r>
+        <r p="148">0</r>
+        <r p="149">0</r>
+        <r p="150">0</r>
+        <r p="151">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="152">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="153">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="154">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="155">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="156">0</r>
+        <r p="157">0</r>
+        <r p="158">0,0,0,0,0,0,0,0</r>
+        <r p="159">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,ENodeBFunction=1,EUtranCellFDD=1">
+        <r p="1">0</r>
+        <r p="2">900</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0,0,0,0</r>
+        <r p="48">0,0,0,0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0,0,0,0</r>
+        <r p="54">0,0,0,0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="59">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0,0,0,0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+        <r p="67">0</r>
+        <r p="68">0</r>
+        <r p="69">0</r>
+        <r p="70">0</r>
+        <r p="71">0</r>
+        <r p="72">0</r>
+        <r p="73">0</r>
+        <r p="74">0</r>
+        <r p="75">0</r>
+        <r p="76">0</r>
+        <r p="77">0</r>
+        <r p="78">0</r>
+        <r p="79">0</r>
+        <r p="80">0</r>
+        <r p="81">0,0,0,0</r>
+        <r p="82">0</r>
+        <r p="83">0,0,0,0</r>
+        <r p="84">0</r>
+        <r p="85">0,0,0,0</r>
+        <r p="86">0</r>
+        <r p="87">0,0,0,0</r>
+        <r p="88">0</r>
+        <r p="89">0</r>
+        <r p="90">0</r>
+        <r p="91">0,0,0,0</r>
+        <r p="92">0</r>
+        <r p="93">0,0,0,0</r>
+        <r p="94">0</r>
+        <r p="95">0,0,0,0</r>
+        <r p="96">0</r>
+        <r p="97">0,0,0,0</r>
+        <r p="98">0</r>
+        <r p="99">0</r>
+        <r p="100">0</r>
+        <r p="101">0</r>
+        <r p="102">0</r>
+        <r p="103">0</r>
+        <r p="104">0,0,0,0</r>
+        <r p="105">0</r>
+        <r p="106">0,0,0,0</r>
+        <r p="107">0</r>
+        <r p="108">0</r>
+        <r p="109">0</r>
+        <r p="110">0,0,0,0</r>
+        <r p="111">0</r>
+        <r p="112">0</r>
+        <r p="113">0,0,0,0</r>
+        <r p="114">0</r>
+        <r p="115">0,0,0,0</r>
+        <r p="116">0</r>
+        <r p="117">0,0,0,0</r>
+        <r p="118">0</r>
+        <r p="119">0,0,0,0</r>
+        <r p="120">0</r>
+        <r p="121">0</r>
+        <r p="122">0</r>
+        <r p="123">0,0,0,0</r>
+        <r p="124">0</r>
+        <r p="125">0</r>
+        <r p="126">0,0,0,0</r>
+        <r p="127">0</r>
+        <r p="128">0</r>
+        <r p="129">0</r>
+        <r p="130">0</r>
+        <r p="131">0</r>
+        <r p="132">0</r>
+        <r p="133">0</r>
+        <r p="134">0</r>
+        <r p="135">0</r>
+        <r p="136">0</r>
+        <r p="137">0</r>
+        <r p="138">0</r>
+        <r p="139">0</r>
+        <r p="140">0</r>
+        <r p="141">0</r>
+        <r p="142">0</r>
+        <r p="143">0</r>
+        <r p="144">0</r>
+        <r p="145">0</r>
+        <r p="146">0</r>
+        <r p="147">0</r>
+        <r p="148">0</r>
+        <r p="149">0</r>
+        <r p="150">0</r>
+        <r p="151">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="152">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="153">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="154">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="155">0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</r>
+        <r p="156">0</r>
+        <r p="157">0</r>
+        <r p="158">0,0,0,0,0,0,0,0</r>
+        <r p="159">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=BbProcessingResource">
+      <job jobId="nrcell"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmPdcpPktDiscDlEthBurst</measType>
+      <measType p="2">pmPdcpPktDiscDlEthFormat</measType>
+      <measType p="3">pmPdcpPktDiscDlEthX2Fwd</measType>
+      <measType p="4">pmPdcpPktDiscDlNoMbmsCtxt</measType>
+      <measType p="5">pmPdcpPktDiscDlNoUeCtxt</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,Equipment=1,FieldReplaceableUnit=BB-1,BbProcessingResource=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=EUtranCellFDD">
+      <job jobId="nrcell"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmPdcpBitrateDlDrbMax</measType>
+      <measType p="2">pmPdcpBitrateDlDrbMin</measType>
+      <measType p="3">pmPdcpBitrateUlDrbMax</measType>
+      <measType p="4">pmPdcpBitrateUlDrbMin</measType>
+      <measType p="5">pmPdcpInactSecDlMcPttDistr</measType>
+      <measType p="6">pmPdcpInactSecDlNonMcPttDistr</measType>
+      <measType p="7">pmPdcpInactSecDlVolteDistr</measType>
+      <measType p="8">pmPdcpInactSecUlMcPttDistr</measType>
+      <measType p="9">pmPdcpInactSecUlNonMcPttDistr</measType>
+      <measType p="10">pmPdcpInactSecUlVolteDistr</measType>
+      <measType p="11">pmPdcpLatPktTransDlCatMDrxNoSyncQci</measType>
+      <measType p="12">pmPdcpLatPktTransDlCatMDrxSyncQci</measType>
+      <measType p="13">pmPdcpLatPktTransDlCatMNoDrxNoSyncQci</measType>
+      <measType p="14">pmPdcpLatPktTransDlCatMNoDrxSyncQci</measType>
+      <measType p="15">pmPdcpLatPktTransDlDrxNoSyncQci</measType>
+      <measType p="16">pmPdcpLatPktTransDlDrxSync</measType>
+      <measType p="17">pmPdcpLatPktTransDlDrxSyncQci</measType>
+      <measType p="18">pmPdcpLatPktTransDlNoDrxNoSyncQci</measType>
+      <measType p="19">pmPdcpLatPktTransDlNoDrxSyncQci</measType>
+      <measType p="20">pmPdcpLatPktTransDlQci</measType>
+      <measType p="21">pmPdcpLatTimeDlCatMDrxNoSyncQci</measType>
+      <measType p="22">pmPdcpLatTimeDlCatMDrxSyncQci</measType>
+      <measType p="23">pmPdcpLatTimeDlCatMNoDrxNoSyncQci</measType>
+      <measType p="24">pmPdcpLatTimeDlCatMNoDrxSyncQci</measType>
+      <measType p="25">pmPdcpLatTimeDlDrxNoSyncQci</measType>
+      <measType p="26">pmPdcpLatTimeDlDrxSync</measType>
+      <measType p="27">pmPdcpLatTimeDlDrxSyncQci</measType>
+      <measType p="28">pmPdcpLatTimeDlNoDrxNoSyncQci</measType>
+      <measType p="29">pmPdcpLatTimeDlNoDrxSyncQci</measType>
+      <measType p="30">pmPdcpLatTimeDlQci</measType>
+      <measType p="31">pmPdcpPktDiscDlAqm</measType>
+      <measType p="32">pmPdcpPktDiscDlAqmQci</measType>
+      <measType p="33">pmPdcpPktDiscDlHo</measType>
+      <measType p="34">pmPdcpPktDiscDlHoQci</measType>
+      <measType p="35">pmPdcpPktDiscDlPelrQci</measType>
+      <measType p="36">pmPdcpPktDiscDlPelrUuQci</measType>
+      <measType p="37">pmPdcpPktFwdDl</measType>
+      <measType p="38">pmPdcpPktLostUlLimitations</measType>
+      <measType p="39">pmPdcpPktLostUlMissingPdus2Qci</measType>
+      <measType p="40">pmPdcpPktLostUlQci</measType>
+      <measType p="41">pmPdcpPktLostUlRohcFail2Qci</measType>
+      <measType p="42">pmPdcpPktLostUlSrbTooLarge</measType>
+      <measType p="43">pmPdcpPktPdbUlOkMcPtt</measType>
+      <measType p="44">pmPdcpPktPdbUlOkNonMcPtt</measType>
+      <measType p="45">pmPdcpPktPdbUlOkVoip</measType>
+      <measType p="46">pmPdcpPktReceivedDl</measType>
+      <measType p="47">pmPdcpPktReceivedDlQci</measType>
+      <measType p="48">pmPdcpPktReceivedUlQci</measType>
+      <measType p="49">pmPdcpPktTransDlQci</measType>
+      <measType p="50">pmPdcpVolDlCmpHdrQci</measType>
+      <measType p="51">pmPdcpVolDlDrbCa</measType>
+      <measType p="52">pmPdcpVolDlDrbLastTTICa</measType>
+      <measType p="53">pmPdcpVolDlDrbQci</measType>
+      <measType p="54">pmPdcpVolDlDrbTransPlmn0</measType>
+      <measType p="55">pmPdcpVolDlDrbTransPlmn1</measType>
+      <measType p="56">pmPdcpVolDlDrbTransPlmn2</measType>
+      <measType p="57">pmPdcpVolDlDrbTransPlmn3</measType>
+      <measType p="58">pmPdcpVolDlDrbTransPlmn4</measType>
+      <measType p="59">pmPdcpVolDlDrbTransPlmn5</measType>
+      <measType p="60">pmPdcpVolDlDrbTransPlmn6</measType>
+      <measType p="61">pmPdcpVolDlDrbTransQci</measType>
+      <measType p="62">pmPdcpVolDlHdrQci</measType>
+      <measType p="63">pmPdcpVolDlSrbTrans</measType>
+      <measType p="64">pmPdcpVolUlCmpHdrQci</measType>
+      <measType p="65">pmPdcpVolUlDrbPlmn0</measType>
+      <measType p="66">pmPdcpVolUlDrbPlmn1</measType>
+      <measType p="67">pmPdcpVolUlDrbPlmn2</measType>
+      <measType p="68">pmPdcpVolUlDrbPlmn3</measType>
+      <measType p="69">pmPdcpVolUlDrbPlmn4</measType>
+      <measType p="70">pmPdcpVolUlDrbPlmn5</measType>
+      <measType p="71">pmPdcpVolUlDrbPlmn6</measType>
+      <measType p="72">pmPdcpVolUlDrbQci</measType>
+      <measType p="73">pmPdcpVolUlHdrQci</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,ENodeBFunction=1,EUtranCellFDD=2">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="6">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="9">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+        <r p="67">0</r>
+        <r p="68">0</r>
+        <r p="69">0</r>
+        <r p="70">0</r>
+        <r p="71">0</r>
+        <r p="72">0</r>
+        <r p="73">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,ENodeBFunction=1,EUtranCellFDD=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="6">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="7">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="8">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="9">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="10">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="11">0</r>
+        <r p="12">0</r>
+        <r p="13">0</r>
+        <r p="14">0</r>
+        <r p="15">0</r>
+        <r p="16">0</r>
+        <r p="17">0</r>
+        <r p="18">0</r>
+        <r p="19">0</r>
+        <r p="20">0</r>
+        <r p="21">0</r>
+        <r p="22">0</r>
+        <r p="23">0</r>
+        <r p="24">0</r>
+        <r p="25">0</r>
+        <r p="26">0</r>
+        <r p="27">0</r>
+        <r p="28">0</r>
+        <r p="29">0</r>
+        <r p="30">0</r>
+        <r p="31">0</r>
+        <r p="32">0</r>
+        <r p="33">0</r>
+        <r p="34">0</r>
+        <r p="35">0</r>
+        <r p="36">0</r>
+        <r p="37">0</r>
+        <r p="38">0</r>
+        <r p="39">0</r>
+        <r p="40">0</r>
+        <r p="41">0</r>
+        <r p="42">0,0,0,0,0,0,0,0,0,0</r>
+        <r p="43">0</r>
+        <r p="44">0</r>
+        <r p="45">0</r>
+        <r p="46">0</r>
+        <r p="47">0</r>
+        <r p="48">0</r>
+        <r p="49">0</r>
+        <r p="50">0</r>
+        <r p="51">0</r>
+        <r p="52">0</r>
+        <r p="53">0</r>
+        <r p="54">0</r>
+        <r p="55">0</r>
+        <r p="56">0</r>
+        <r p="57">0</r>
+        <r p="58">0</r>
+        <r p="59">0</r>
+        <r p="60">0</r>
+        <r p="61">0</r>
+        <r p="62">0</r>
+        <r p="63">0</r>
+        <r p="64">0</r>
+        <r p="65">0</r>
+        <r p="66">0</r>
+        <r p="67">0</r>
+        <r p="68">0</r>
+        <r p="69">0</r>
+        <r p="70">0</r>
+        <r p="71">0</r>
+        <r p="72">0</r>
+        <r p="73">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=GNBCUUPFunction_GNBCUUP">
+      <job jobId="nrcell"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmPdcpPktDisc</measType>
+      <measType p="2">pmPdcpPktDiscGtpu</measType>
+      <measType p="3">pmPdcpPktDiscNoCtxt</measType>
+      <measType p="4">pmPdcpPktFwdRecDlDiscQos</measType>
+      <measType p="5">pmPdcpPktFwdRecDlQos</measType>
+      <measType p="6">pmPdcpPktInjected</measType>
+      <measType p="7">pmPdcpPktRecDlDiscInact</measType>
+      <measType p="8">pmPdcpPktRecDlInact</measType>
+      <measType p="9">pmPdcpRadioResSwitchDlMcg</measType>
+      <measType p="10">pmPdcpRadioResSwitchDlScg</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUUPFunction=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+        <r p="3">0</r>
+        <r p="4">0</r>
+        <r p="5">0</r>
+        <r p="6">0</r>
+        <r p="7">0</r>
+        <r p="8">0</r>
+        <r p="9">0</r>
+        <r p="10">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=NRCellCU_GNBCUCP">
+      <job jobId="nrcell"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmEndcUeCapabilityUlPdcpDelay</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUCPFunction=1,NRCellCU=32">
+        <r p="1">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUCPFunction=1,NRCellCU=31">
+        <r p="1">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUCPFunction=1,NRCellCU=2">
+        <r p="1">0</r>
+      </measValue>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUCPFunction=1,NRCellCU=1">
+        <r p="1">0</r>
+      </measValue>
+    </measInfo>
+    <measInfo measInfoId="PM=1,PmGroup=X2UTermination_GNBCUUP">
+      <job jobId="nrcell"/>
+      <granPeriod duration="PT900S"
+                  endTime="2022-04-18T19:15:00+00:00"/>
+      <repPeriod duration="PT900S"/>
+      <measType p="1">pmPdcpPktFwdRecDlDiscQos</measType>
+      <measType p="2">pmPdcpPktFwdRecDlQos</measType>
+      <measValue measObjLdn="ManagedElement=seliitdus00487,GNBCUUPFunction=1,X2UTermination=1">
+        <r p="1">0</r>
+        <r p="2">0</r>
+      </measValue>
+    </measInfo>
+  </measData>
+  <fileFooter>
+    <measCollec endTime="2022-04-18T19:15:00+00:00"/>
+  </fileFooter>
+</measCollecFile>
diff --git a/pmproducer/src/test/resources/pm_report.json b/pmproducer/src/test/resources/pm_report.json
new file mode 100644 (file)
index 0000000..402ffea
--- /dev/null
@@ -0,0 +1,223 @@
+{
+   "event": {
+      "commonEventHeader": {
+         "domain": "perf3gpp",
+         "eventName": "perf3gpp_gnb-Ericsson_pmMeasResult",
+         "sourceName": "O-DU-1122",
+         "reportingEntityName": "",
+         "startEpochMicrosec": 951912000000,
+         "lastEpochMicrosec": 951912900000,
+         "timeZoneOffset": "+00:00"
+      },
+      "perf3gppFields": {
+         "perf3gppFieldsVersion": "1.0",
+         "measDataCollection": {
+            "granularityPeriod": 900,
+            "measuredEntityUserName": "RNC Telecomville",
+            "measuredEntityDn": "SubNetwork=CountryNN,MeContext=MEC-Gbg-1,ManagedElement=RNC-Gbg-1",
+            "measuredEntitySoftwareVersion": "",
+            "measInfoList": [
+               {
+                  "measInfoId": {
+                     "sMeasInfoId": ""
+                  },
+                  "measTypes": {
+                     "sMeasTypesList": [
+                        "attTCHSeizures",
+                        "succTCHSeizures",
+                        "attImmediateAssignProcs",
+                        "succImmediateAssignProcs"
+                     ]
+                  },
+                  "measValuesList": [
+                     {
+                        "measObjInstId": "RncFunction=RF-1,UtranCell=Gbg-997",
+                        "suspectFlag": "false",
+                        "measResults": [
+                           {
+                              "p": 1,
+                              "sValue": "813"
+                           },
+                           {
+                              "p": 2,
+                              "sValue": "913"
+                           },
+                           {
+                              "p": 3,
+                              "sValue": "1013"
+                           },
+                           {
+                              "p": 4,
+                              "sValue": "1113"
+                           }
+                        ]
+                     },
+                     {
+                        "measObjInstId": "RncFunction=RF-1,UtranCell=Gbg-998",
+                        "suspectFlag": "false",
+                        "measResults": [
+                           {
+                              "p": 1,
+                              "sValue": "890"
+                           },
+                           {
+                              "p": 2,
+                              "sValue": "901"
+                           },
+                           {
+                              "p": 3,
+                              "sValue": "123"
+                           },
+                           {
+                              "p": 4,
+                              "sValue": "234"
+                           }
+                        ]
+                     },
+                     {
+                        "measObjInstId": "RncFunction=RF-1,UtranCell=Gbg-999",
+                        "suspectFlag": "true",
+                        "measResults": [
+                           {
+                              "p": 1,
+                              "sValue": "456"
+                           },
+                           {
+                              "p": 2,
+                              "sValue": "567"
+                           },
+                           {
+                              "p": 3,
+                              "sValue": "678"
+                           },
+                           {
+                              "p": 4,
+                              "sValue": "789"
+                           }
+                        ]
+                     }
+                  ]
+               },
+               {
+                  "measInfoId": {
+                     "sMeasInfoId": "ENodeBFunction"
+                  },
+                  "measTypes": {
+                     "sMeasTypesList": [
+                        "attTCHSeizures1",
+                        "succTCHSeizures2",
+                        "attImmediateAssignProcs3",
+                        "succImmediateAssignProcs4"
+                     ]
+                  },
+                  "measValuesList": [
+                     {
+                        "measObjInstId": "ManagedElement=RNC-Gbg-1,ENodeBFunction=1",
+                        "suspectFlag": "false",
+                        "measResults": [
+                           {
+                              "p": 1,
+                              "sValue": "4"
+                           },
+                           {
+                              "p": 2,
+                              "sValue": "86,87,2,6,77,96,75,33,24"
+                           },
+                           {
+                              "p": 3,
+                              "sValue": "40"
+                           },
+                           {
+                              "p": 4,
+                              "sValue": "90"
+                           }
+                        ]
+                     }
+                  ]
+               },
+               {
+                  "measInfoId": {
+                     "sMeasInfoId": ""
+                  },
+                  "measTypes": {
+                     "sMeasTypesList": [
+                        "attTCHSeizures5",
+                        "succTCHSeizures6",
+                        "attImmediateAssignProcs7",
+                        "succImmediateAssignProcs8"
+                     ]
+                  },
+                  "measValuesList": [
+                     {
+                        "measObjInstId": "RncFunction=RF-1,UtranCell=Gbg-997",
+                        "suspectFlag": "false",
+                        "measResults": [
+                           {
+                              "p": 1,
+                              "sValue": "238"
+                           },
+                           {
+                              "p": 2,
+                              "sValue": "344"
+                           },
+                           {
+                              "p": 3,
+                              "sValue": "563"
+                           },
+                           {
+                              "p": 4,
+                              "sValue": "787"
+                           }
+                        ]
+                     },
+                     {
+                        "measObjInstId": "RncFunction=RF-1,UtranCell=Gbg-998",
+                        "suspectFlag": "false",
+                        "measResults": [
+                           {
+                              "p": 1,
+                              "sValue": "898"
+                           },
+                           {
+                              "p": 2,
+                              "sValue": "905"
+                           },
+                           {
+                              "p": 3,
+                              "sValue": "127"
+                           },
+                           {
+                              "p": 4,
+                              "sValue": "238"
+                           }
+                        ]
+                     },
+                     {
+                        "measObjInstId": "RncFunction=RF-1,UtranCell=Gbg-999",
+                        "suspectFlag": "true",
+                        "measResults": [
+                           {
+                              "p": 1,
+                              "sValue": "454"
+                           },
+                           {
+                              "p": 2,
+                              "sValue": "569"
+                           },
+                           {
+                              "p": 3,
+                              "sValue": "672"
+                           },
+                           {
+                              "p": 4,
+                              "sValue": "785"
+                           }
+                        ]
+                     }
+                  ]
+               }
+            ]
+         }
+      }
+   }
+}
\ No newline at end of file
diff --git a/pmproducer/src/test/resources/pm_report.json.gz b/pmproducer/src/test/resources/pm_report.json.gz
new file mode 100644 (file)
index 0000000..2d191d1
Binary files /dev/null and b/pmproducer/src/test/resources/pm_report.json.gz differ
diff --git a/pmproducer/src/test/resources/test_application_configuration.json b/pmproducer/src/test/resources/test_application_configuration.json
new file mode 100644 (file)
index 0000000..540ddce
--- /dev/null
@@ -0,0 +1,18 @@
+{
+   "types": [
+      {
+         "id": "PmDataOverKafka",
+         "kafkaInputTopic": "FileReadyEvent",
+         "inputJobType": "xml-file-data-to-filestore",
+         "inputJobDefinition": {
+            "kafkaOutputTopic": "FileReadyEvent",
+            "filestore-output-bucket": "pm-files-json",
+            "filterType": "pmdata",
+            "filter": {
+               "inputCompression": "xml.gz",
+               "outputCompression": "none"
+            }
+         }
+      }
+   ]
+}
\ No newline at end of file