Merge "Initial doc structure"
authorClaudio David Gasparini <claudio.gasparini@intl.att.com>
Fri, 21 May 2021 05:41:33 +0000 (05:41 +0000)
committerGerrit Code Review <gerrit@o-ran-sc.org>
Fri, 21 May 2021 05:41:33 +0000 (05:41 +0000)
73 files changed:
solution/docker-compose.yaml
ves-nf-oam-adopter/pom.xml
ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/application.yml [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/fm-ves-message-mapping.yaml [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/log4j2.yml [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/pm-ves-message-mapping.yaml [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-cert.pem [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-key.pem [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-keystore.jks [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-truststore.jks [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter.p12 [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/docs/api/swagger/openapi.yaml [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/pom.xml [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/AdapterApplication.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/config/HttpAsyncClientConfig.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/config/LoginAttemptsLogger.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/config/SecurityConfiguration.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/controller/AdapterController.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/controller/RestExceptionHandler.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/controller/TimeZoneServiceProvider.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/http/HttpCientFactory.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/properties/ServerProperties.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/properties/SslProperties.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/test/java/org/o/ran/oam/nf/oam/adopter/app/AdapterApplicationTest.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/test/resources/application.yml [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-artifacts/pom.xml
ves-nf-oam-adopter/ves-nf-oam-adopter-mock/configuration/application.yml
ves-nf-oam-adopter/ves-nf-oam-adopter-mock/docs/api/swagger/openapi.yaml
ves-nf-oam-adopter/ves-nf-oam-adopter-mock/pom.xml
ves-nf-oam-adopter/ves-nf-oam-adopter-parent/pom.xml
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/pom.xml [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/AdaptersDeployer.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/PerformanceManagementMapperConfigProvider.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/PerformanceManagementRestAgent.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/PerformanceManagementRestAgentFactory.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/api/HttpRestClient.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/api/PerformanceManagementAdaptersDeployer.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/configurations/PerformanceManagementRestManagerConfig.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/AlreadyPresentException.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/NotFoundException.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/PerformanceManagementEmptyOutputException.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/PerformanceManagementException.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/TokenGenerationException.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/ZoneIdException.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/mapper/CommonEventHeaderHandler.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/mapper/MeasurementFieldsHandler.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/mapper/PerformanceManagementFile2VesMapper.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/pojos/Adapter.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/pojos/CsvConfiguration.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/pojos/VesMappingConfiguration.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/properties/PerformanceManagementManagerProperties.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/HttpRestClientMock.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/JsonUtils.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/PerformanceManagementManagerTest.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/VesEventNotifierMock.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/resources/application.yaml [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/resources/json/PMVESMessage.json [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/resources/pm-ves-message-mapping.yaml [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/resources/zip/nfOamAdapter1.zip [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/pom.xml [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/DefaultHttpRestClient.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/http/DownloadPerformanceManagementFilesHandler.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/http/OffSetTimeZoneHandler.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/http/TokenHandler.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/pojos/TimeZoneResponse.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/pojos/TokenResponse.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/properties/PmEndpointsUrlsProperties.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/DefaultHttpRestClientTest.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/JsonUtils.java [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/resources/application.yaml [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/resources/json/timeZoneOffsetResponse.json [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/resources/json/tokenResponse.json [new file with mode: 0644]
ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker [new file with mode: 0644]

index 595f1f8..1490d22 100644 (file)
@@ -1,42 +1,44 @@
 version: "3.5"
 
 services:
-#  ves-collector:
-#    restart: unless-stopped
-#    ports:
-#      - "8080:8080/udp"
-#    image: nexus3.onap.org:10001/onap/org.onap.dcaegen2.collectors.ves.vescollector:latest
-#    container_name: vescollector
-#    environment:
-#      DMAAPHOST: 50.50.50.50
+  vescollector:
+    restart: unless-stopped
+    ports:
+      - "8080:8080/udp"
+    network_mode: host
+    image: nexus3.onap.org:10001/onap/org.onap.dcaegen2.collectors.ves.vescollector:latest
+    container_name: vescollector
+    environment:
+      DMAAPHOST: 50.50.50.50
 
   ves-nf-oam-adopter-ran-mock:
     ports:
-      - "444:443/tcp"
+      - "443:443/tcp"
     network_mode: host
     restart: unless-stopped
-    image: nexus3.o-ran-sc.org:10003/o-ran-sc/ves-nf-oam-adopter-ran-mock:latest
+    image: nexus3.o-ran-sc.org:10004/o-ran-sc/ves-nf-oam-adopter-ran-mock:latest
     container_name: ves-nf-oam-adopter-ran-mock
     environment:
       KEY_PASSWORD: nf-oam-adopter
       KEY_STORE_PASSWORD: nf-oam-adopter
       SNMP-DESTINY: 0.0.0.0/162
+      SERVER_PORT: 443
 
   ves-nf-oam-adopter:
     ports:
-      - "162:162/udp"
-      - "443:443/tcp"
+      - "443:444/tcp"
     network_mode: host
     restart: unless-stopped
-    image: nexus3.o-ran-sc.org:10003/o-ran-sc/ves-nf-oam-adopter:latest
+    image: nexus3.o-ran-sc.org:10004/o-ran-sc/ves-nf-oam-adopter:latest
     container_name: ves-nf-oam-adopter
     environment:
+      SERVER_PORT: 444
       USERNAME: admin
       PASSWORD: admin
       KEY_PASSWORD: nf-oam-adopter
       KEY_STORE_PASSWORD: nf-oam-adopter
       TRUST_STORE_PASSWORD: nf-oam-adopter
-      VES_COLLECTOR: http://vescollector:8080/eventListener/v7
+      VES_COLLECTOR: http://0.0.0.0:8080/eventListener/v7
       VES_ENCODED_AUTH: YWRtaW46YWRtaW4=
-      PM_SYNC_TIME_START: "06:05:00"
-      PM_SYNC_TIME_FREQ: "24:00:00"
\ No newline at end of file
+      PM_SYNC_TIME_START: "14:25:00"
+      PM_SYNC_TIME_FREQ: 30
\ No newline at end of file
index aa53ec3..890d575 100644 (file)
@@ -50,5 +50,8 @@
         <module>ves-nf-oam-adopter-api</module>
         <module>ves-nf-oam-adopter-event-notifier</module>
         <module>ves-nf-oam-adopter-snmp-manager</module>
+        <module>ves-nf-oam-adopter-pm-manager</module>
+        <module>ves-nf-oam-adopter-pm-sb-rest-client</module>
+        <module>ves-nf-oam-adopter-app</module>
     </modules>
 </project>
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/application.yml b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/application.yml
new file mode 100644 (file)
index 0000000..fee8a56
--- /dev/null
@@ -0,0 +1,34 @@
+spring.devtools.restart.log-condition-evaluation-delta: false
+logging.config: log4j2.yml
+server:
+  port : ${SERVER_PORT:443}
+  username: ${USERNAME:admin}
+  password: ${PASSWORD:admin}
+  ssl:
+    enabled: true
+    key-store-type: JKS
+    key-alias: nf-oam-adopter
+    key-store: ${KEYSTORE_PATH:ssl/nf-oam-adopter-keystore.jks}
+    key-store-password: ${KEY_STORE_PASSWORD:nf-oam-adopter}
+    key-password: ${KEY_PASSWORD:nf-oam-adopter}
+    trust-store: ${TRUST_STORE_PATH:ssl/nf-oam-adopter-truststore.jks}
+    trust-store-password: ${TRUST_STORE_PASSWORD:nf-oam-adopter}
+    enabled-protocols: TLSv1.3
+    ciphers: TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_CCM_8_SHA256, TLS_AES_128_CCM_SHA256
+ves-collector:
+  url: ${VES_COLLECTOR}
+  vesEncodedAuth: ${VES_ENCODED_AUTH}
+http-client:
+  conection-timeout: ${CONNECTION_TIMEOUT:600}
+  response-timeout: ${RESPONSE_TIMEOUT:600}
+pm-rest-manager:
+  synchronization-time-start: ${PM_SYNC_TIME_START}
+  synchronization-time-frequency: ${PM_SYNC_TIME_FREQ}
+  mapping-config-path: ${PM_MAPPING_FILE_PATH:mapping-configuration/pm-ves-message-mapping.yaml}
+  ran-token-endpoint: /auth/token
+  ran-pm-endpoint: /pm/files
+  ran-time-zone-offset-endpoint: /system/timeZone
+snmp-manager:
+  host: "0.0.0.0"
+  port: ${SNMP_PORT:162}
+  mapping-config-path: ${FM_MAPPING_FILE_PATH:mapping-configuration/fm-ves-message-mapping.yaml}
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/fm-ves-message-mapping.yaml b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/fm-ves-message-mapping.yaml
new file mode 100644 (file)
index 0000000..24fd5d7
--- /dev/null
@@ -0,0 +1,21 @@
+reporting-entity-name: "NF-OAM-ADOPTER"
+reporting-entity-id: "NF-OAM-ADOPTER"
+nf-vendor-name: "SOME-VENDOR"
+traps:
+  - oid: "default"
+    name: "SNMP_Fault"
+    event-severity: "CRITICAL"
+    event-source-type: "Unknown"
+  - oid: "1.3.6.1.4.1.1007.0.0.1.0.1"
+    name: "PortDOWN"
+    event-severity: "MAJOR"
+    oid-event-id: "1.3.6.1.4.1.1007.2.6.9.0"
+    oid-event-sequence: "0"
+    oid-reporting-entity-id: "1.3.6.1.4.1.1007.0.0.1.0.6"
+    oid-source-name: "1.3.6.1.4.1.1007.0.0.1.0.2"
+    oid-specific-problem-desc: "1.3.6.1.4.1.1007.0.0.1.0.3"
+    oid-alarm-interface-name: "1.3.6.1.4.1.1007.0.0.1.0.7"
+    oid-start-epoch-microsec: "1.3.6.1.4.1.1007.0.0.1.0.4"
+    oid-last-epoch-microsec: "1.3.6.1.4.1.1007.0.0.1.0.5"
+    event-category: "link"
+    event-source-type: "port"
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/log4j2.yml b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/log4j2.yml
new file mode 100644 (file)
index 0000000..c10a74b
--- /dev/null
@@ -0,0 +1,48 @@
+Configuration:
+  status: debug
+
+  appenders:
+    Console:
+      name: LogToConsole
+      PatternLayout:
+        Pattern: "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"
+
+    RollingFile:
+      - name: LogToRollingFile
+        fileName: logs/o-ran-ves-adapter.log
+        filePattern: "logs/$${date:yyyy-MM}/o-ran-ves-adapter-%d{MM-dd-yyyy}-%i.log.gz"
+        PatternLayout:
+          pattern: "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"
+        Policies:
+          SizeBasedTriggeringPolicy:
+            size: 100MB
+        DefaultRollOverStrategy:
+          max: 10
+
+  Loggers:
+    logger:
+      - name: org.o.ran.oam.nf.oam
+        level: debug
+        additivity: false
+        AppenderRef:
+          - ref: LogToConsole
+          - ref: LogToRollingFile
+      - name: org.springframework.boot.actuate.audit.listener
+        level: info
+        additivity: false
+        AppenderRef:
+          - ref: LogToConsole
+          - ref: LogToRollingFile
+      - name: org.springframework
+        level: warn
+        additivity: false
+        AppenderRef:
+          - ref: LogToConsole
+          - ref: LogToRollingFile
+
+    Root:
+      level: info
+      AppenderRef:
+        AppenderRef:
+          - ref: LogToConsole
+          - ref: LogToRollingFile
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/pm-ves-message-mapping.yaml b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/pm-ves-message-mapping.yaml
new file mode 100644 (file)
index 0000000..073e3c7
--- /dev/null
@@ -0,0 +1,26 @@
+reporting-entity-name: "NF-OAM-ADOPTER"
+reporting-entity-id: "ONAP-NF-OAM-SOME-VENDOR-ADAPTER"
+nf-vendor-name: "SOME-VENDOR"
+event-source-type: "SNMP Agent"
+event-name: "PM_Notification"
+measurement-interval: 40
+priority: "High"
+batch-size: 200
+csv:
+  source-name: Name
+  event-id:
+    - PortId
+    - Date
+    - Time
+  additional-fields:
+    - PortId
+    - Name
+    - Date
+    - Time
+  additional-measurements-name: "Port measurements"
+  additional-measurements:
+    - A_Parameter
+    - B_Parameter
+    - C_Parameter
+    - D_Parameter
+    - E_Parameter
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-cert.pem b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-cert.pem
new file mode 100644 (file)
index 0000000..6f3a7ac
--- /dev/null
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDizCCAnOgAwIBAgIUUR7hr58NdGQKpF3gmqUJRSdYwZgwDQYJKoZIhvcNAQEL
+BQAwVTELMAkGA1UEBhMCU0sxEzARBgNVBAgMCkJyYXRpc2xhdmExEzARBgNVBAcM
+CkJyYXRpc2xhdmExDTALBgNVBAoMBEFUJlQxDTALBgNVBAsMBEFUJlQwHhcNMjEw
+NTA0MDgxNjUzWhcNMzEwNTAyMDgxNjUzWjBVMQswCQYDVQQGEwJTSzETMBEGA1UE
+CAwKQnJhdGlzbGF2YTETMBEGA1UEBwwKQnJhdGlzbGF2YTENMAsGA1UECgwEQVQm
+VDENMAsGA1UECwwEQVQmVDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANgJZ0Vz0Aa0tFWAzJqeaVeT52zLweSWv2rbXFka/2ClAioUUSm7j0+y7lT9yTbK
+J27opYojlB0wPRBor5n587lpTP9yJYephIQvUdfsgP62xXANVcd1MVWRbI0ldxtc
+EOWaW/w2b3yDLa+o+zsG0KN7IAYmDfb7ZVc5fn+zU9oqO+UlhVbgPhJjRdlGJjoL
+zCZYP8ip6xEmRDDZZ+KTazevYZ4ZFe4VWVbOn3eRTBWVea3HAirmbNSEWG1HWZ0y
+BjGy+eI9HbHVLpuLObPUASpZCvEjwS2/IYwYkpBREwSmLUXu6qj8rp0F3GVTk8D0
+Y6xy6CtmcDFz2KOYWD9KKm8CAwEAAaNTMFEwHQYDVR0OBBYEFBALTA7uLbxLThTx
+a/R/0hcydIsjMB8GA1UdIwQYMBaAFBALTA7uLbxLThTxa/R/0hcydIsjMA8GA1Ud
+EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAD0czkrPMahhFNiJD5fmW3T
+o3QMCS/WQ4iCZ3/ROQ6P8zbNqbkZxy+yF6b1kii81wDd5jnoo1b/zhjHoUb7pUWg
+NFZXqF+zajKr0zXcDGbuksIlukVmB6Vz83jEowGxb5xakh9gluxukabkhO7yBUFY
+sc4B34n1keOKKQKdmgxG7FBS77n0Em+9NeuLzvP0S9AvkQdgDgXRX2yVjy8gVGEA
+Iz+IEIDE/oQQ3HaUQTIItnMNhg1orUzOzps5FdOtFEtd7XwjqSgu+NR77G6KOFcR
+RFw9V3uEd2XUn+sHUu81yfseBjMcneuvKBr/KIbwlj86kEAyaTedL2oRX6Ful6Q=
+-----END CERTIFICATE-----
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-key.pem b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-key.pem
new file mode 100644 (file)
index 0000000..011107e
--- /dev/null
@@ -0,0 +1,30 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIbmqjaTYftFoCAggA
+MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECIAcQM6jVuDQBIIEyOixt6jiuuRv
+6IYnMnmYvaeWIxoMpqvMHSoWFSvSdyCuGdTJv+qxit+HpVOGqhVRckSv0nAAbEdE
+jw0PVyAgfWX7vQca1YK2mxw5tGrNsSyQ5LPtS5QICe4fDew8VwiGXWdQhnxvIxUW
+xORfAOTar2qEgE0OBsYVBEjdU3mS3RFRKDtCVXYxiII6/24Nsf7bmz438VxShwog
+A5Zq6sQtqOjF3sKfRgkimOGLgBw7DACI852bEVffySdt8CdGZuAAfD6PucmQSTHp
+CSdmCELBA4p6juNFgpRqvm5qlG/7ZBJhcjGU7CAN1v8oYBH0TB44ek4hOFzT9b5P
+8icBXmg4w2ysWz6OPbwcQj8pG1yRb7WDUulntlSUijI2AXFV2SWIHZ2APFdVQ1c4
+qrivre66FIfo79/I+RTo5MMp2wmJbAjsSaO4571hwY9Cap0TdeJ+KmIKpK+HYwLQ
+iwJip2TtBUmd/ANPnJmgrG2pAeiahciJ0gMGwN2L5uxoiV+FSLwEQ7JCXZn2ET9B
+ai2ZodjIHjZTAT/dtX9gS/yJyHC20HpA3mVtjkenGHaBbs0ji2u1bK9JUgNb43R1
+I7r4BfsYav3+FTq1xoU/vFlP7vxX9cgUduda4rTu7ZNu56RgxKFeqsWl5gbUIX6y
+UU2W1R3TXVpoBPHzpEo2Vcig6exEEVEaO8HXu16UD1+wvwLYkSlwWjKby1pVOdNw
++ZnZv5aLIFk2UC1yJ7AJcxHLIcmJD6jBlzpGmQOqNvVb4Qig0NyMkz0bZAJ5SI/t
+6mZZXHDmgRLfjluawVYIoIVcflm/yfRh6nELUAfh/IAIVyDvaEtEwfK3SlWXrvP4
+j+F8EacnQm7O1rFgR+p2AxMtAdAm7E4JVoNpHSWbv36EI34RaoVPng7ueBSkG3Uk
+7qrbb/lWjXveKfizkkuUUFZWKiSyXAxYiXu9sQjBlN2EWSgBAix7WpqSh2CPROO4
+gn51n7TKyer5CcUqwzMz5SLhRSNkoBY82vIyBZyf7UrRztmJPrvbr4UQSoiqLrey
+DPaT0LHtyJZWuNZS2ki+WI1nnKkFtWgl7Yv57EtBwHqTpARvH4NiF8ryJbp8rl6R
+WMz5haeuBmFz/IXZtOsRXVR+kjhShBKygKYRtUGEc3sSDL2HsA47pCCaFW8907NP
+dfv1WeKyI6d0snBIMKypzFGo17CtdcxsNiAgcrwW2cvwsDyysimZQPa7dr1uAwEX
+WU2WAaoGmxShZB9fvdIeg4bydnJAlVOjotWs+kwPzCLFk++DSQ4HiElJnwawuPfB
+XWqk7ZaKZEiZXNJMyMGIqKo8w+88uIQTd4YNbfhBgvSlgf8Y5EaYbzx68yUkPeOR
+Urli4hXpSkkB3DtmRMquUg1xPn6Ao+vvEoXwiUPg61YQIO1RvTr2vVAWVj1WYcJI
+4k6sAi6HhhlGEVL1bxFMpkX9h/Jwd0YgDYAUFJ2DPnZ+18gbdpA7QZp/SBKwZpTh
+cbNXBkwv2Wa4u/yQsoqoyrxOTzpfiRYVvr4Xmi40TuLC4JEYqTdg+HE5ovjfIFTz
+dxOMEon6JwvwXxWdqZS655hVcG0pH1JvBVouGrWB5EOElVCzDcvquacDofaemcA9
+lkve4yZbTmJRwZrUBCmTOQ==
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-keystore.jks b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-keystore.jks
new file mode 100644 (file)
index 0000000..8712bd5
Binary files /dev/null and b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-keystore.jks differ
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-truststore.jks b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-truststore.jks
new file mode 100644 (file)
index 0000000..42cfc88
Binary files /dev/null and b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter-truststore.jks differ
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter.p12 b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter.p12
new file mode 100644 (file)
index 0000000..8b54595
Binary files /dev/null and b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/configuration/ssl/nf-oam-adopter.p12 differ
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/docs/api/swagger/openapi.yaml b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/docs/api/swagger/openapi.yaml
new file mode 100644 (file)
index 0000000..6a02e63
--- /dev/null
@@ -0,0 +1,124 @@
+#  ============LICENSE_START=======================================================
+#  Copyright © 2021 AT&T 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=========================================================
+openapi: 3.0.3
+info:
+  title: O-RAN OAM Adopter PM Manager
+  description: API for O-RAN OAM Adopter PM Manager service.
+  version: "1.0.0"
+  license:
+    name: "Apache 2.0"
+    url: "http://www.apache.org/licenses/LICENSE-2.0"
+
+servers:
+  - url: https://example.io/v1
+
+security:
+  - BasicAuth: []
+
+paths:
+  /:
+    get:
+      tags:
+        - controller
+      summary: Read all adapters host address
+      description: Returns a list of adapters host address
+      operationId: getAllAdapters
+      responses:
+        '200':
+          description: Successfully returned a list of adapters host address
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  type: string
+        '400':
+          $ref: '#/components/responses/400Error'
+
+
+  /adapter/{host}:
+    delete:
+      tags:
+        - controller
+      description: Delete an adapter by host address
+      operationId: removeAdapter
+      parameters:
+        - name: host
+          in: path
+          required: true
+          schema:
+            type: string
+
+      responses:
+        '200':
+          description: Successfully deleted an adapter
+        '400':
+          description: Invalid request
+        '401':
+          description: Unauthorized
+        '404':
+          description: adapter not found
+
+  /adapter:
+    post:
+      tags:
+        - controller
+      description: create an adapter instance
+      operationId: addAdapter
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/Adapter'
+      responses:
+        '200':
+          description: Successfully returned a list of adapters
+        '400':
+          $ref: '#/components/responses/400Error'
+
+components:
+  schemas:
+    Adapter:
+      type: object
+      properties:
+        host:
+          type: string
+        mechId:
+          type: object
+          properties:
+            username:
+              type: string
+            password:
+              type: string
+          required:
+            - username
+            - password
+      required:
+        - host
+        - mechId
+
+  responses:
+    400Error:
+      description: Invalid request
+      content:
+        application/json:
+          schema:
+            type: object
+            properties:
+              message:
+                type: string
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/pom.xml b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/pom.xml
new file mode 100644 (file)
index 0000000..c92c564
--- /dev/null
@@ -0,0 +1,239 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* ============LICENSE_START=======================================================
+* O-RAN-SC
+* ================================================================================
+* Copyright (C) 2021 AT&T 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============================================
+*
+-->
+<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.o-ran-sc.oam</groupId>
+        <artifactId>ves-nf-oam-adopter-parent</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+        <relativePath>../ves-nf-oam-adopter-parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ves-nf-oam-adopter-app</artifactId>
+
+    <properties>
+        <!--Image properties-->
+        <image.name>ves-nf-oam-adopter</image.name>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ves-nf-oam-adopter-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ves-nf-oam-adopter-event-notifier</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ves-nf-oam-adopter-snmp-manager</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ves-nf-oam-adopter-pm-manager</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ves-nf-oam-adopter-pm-sb-rest-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-log4j2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents.client5</groupId>
+            <artifactId>httpclient5</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+            <!-- Swagger code generation. -->
+            <plugin>
+                <groupId>io.swagger.codegen.v3</groupId>
+                <artifactId>swagger-codegen-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                        <configuration>
+                            <inputSpec>${project.basedir}/docs/api/swagger/openapi.yaml</inputSpec>
+                            <invokerPackage>org.o.ran.oam.nf.oam.adopter.app.controller</invokerPackage>
+                            <modelPackage>org.o.ran.oam.nf.oam.adopter.model</modelPackage>
+                            <apiPackage>org.o.ran.oam.nf.oam.adopter.api</apiPackage>
+                            <language>spring</language>
+                            <generateSupportingFiles>false</generateSupportingFiles>
+                            <configOptions>
+                                <sourceFolder>src/gen/java</sourceFolder>
+                                <dateLibrary>java11</dateLibrary>
+                                <interfaceOnly>true</interfaceOnly>
+                                <useTags>true</useTags>
+                                <serializableModel>true</serializableModel>
+                            </configOptions>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>docker</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>io.fabric8</groupId>
+                        <artifactId>docker-maven-plugin</artifactId>
+                        <version>0.34.1</version>
+                        <extensions>true</extensions>
+                        <configuration>
+                            <verbose>true</verbose>
+                            <images>
+                                <image>
+                                    <name>${nexus.repository}${image.name}</name>
+                                    <build>
+                                        <from>${nexus.repository.mirror}${base.image}</from>
+                                        <tags>
+                                            <tag>${project.version}</tag>
+                                        </tags>
+                                        <workdir>${image.workdir}</workdir>
+                                        <entryPoint>
+                                            <exec>
+                                                <args>java</args>
+                                                <args>-jar</args>
+                                                <args>${project.artifactId}-${project.version}.jar</args>
+                                            </exec>
+                                        </entryPoint>
+                                        <assembly>
+                                            <basedir>${image.workdir}</basedir>
+                                            <inline>
+                                                <dependencySets>
+                                                    <dependencySet>
+                                                        <useProjectArtifact>true</useProjectArtifact>
+                                                        <includes>
+                                                            <include>${project.groupId}:${project.artifactId}</include>
+                                                        </includes>
+                                                        <outputFileNameMapping>${project.build.finalName}.jar</outputFileNameMapping>
+                                                    </dependencySet>
+                                                </dependencySets>
+                                                <files>
+                                                    <file>
+                                                        <source>${base.config}application.yml</source>
+                                                        <outputDirectory>.</outputDirectory>
+                                                        <destName>application.yml</destName>
+                                                    </file>
+                                                    <file>
+                                                        <source>${base.config}fm-ves-message-mapping.yaml</source>
+                                                        <outputDirectory>./mapping-configuration</outputDirectory>
+                                                        <destName>fm-ves-message-mapping.yaml</destName>
+                                                    </file>
+                                                    <file>
+                                                        <source>${base.config}pm-ves-message-mapping.yaml</source>
+                                                        <outputDirectory>./mapping-configuration</outputDirectory>
+                                                        <destName>pm-ves-message-mapping.yaml</destName>
+                                                    </file>
+                                                    <file>
+                                                        <source>${base.config}log4j2.yml</source>
+                                                    </file>
+                                                    <file>
+                                                        <source>${base.config}/ssl/nf-oam-adopter-keystore.jks</source>
+                                                        <outputDirectory>./ssl</outputDirectory>
+                                                        <destName>nf-oam-adopter-keystore.jks</destName>
+                                                    </file>
+                                                    <file>
+                                                        <source>${base.config}/ssl/nf-oam-adopter-truststore.jks</source>
+                                                        <outputDirectory>./ssl</outputDirectory>
+                                                        <destName>nf-oam-adopter-truststore.jks</destName>
+                                                    </file>
+                                                </files>
+                                            </inline>
+                                        </assembly>
+                                    </build>
+                                </image>
+                            </images>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <id>build-docker-image</id>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>build</goal>
+                                </goals>
+                            </execution>
+                            <execution>
+                                <id>push-image</id>
+                                <phase>deploy</phase>
+                                <goals>
+                                    <goal>push</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/AdapterApplication.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/AdapterApplication.java
new file mode 100644 (file)
index 0000000..59ce697
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.app;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication(scanBasePackages = {"org.o.ran.oam.nf.oam.adopter"})
+public class AdapterApplication {
+
+    public static void main(final String[] args) {
+        SpringApplication.run(AdapterApplication.class, args);
+    }
+}
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/config/HttpAsyncClientConfig.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/config/HttpAsyncClientConfig.java
new file mode 100644 (file)
index 0000000..5404ca6
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.app.config;
+
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.o.ran.oam.nf.oam.adopter.app.http.HttpCientFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class HttpAsyncClientConfig {
+
+    @Value("${server.ssl.trust-store:#{null}}")
+    private String trustStore;
+    @Value("${server.ssl.trust-store-password:#{null}}")
+    private String trustStorePassword;
+    @Value("${http-client.conection-timeout:600}")
+    private Long conectionTimeout;
+    @Value("${http-client.response-timeout:600}")
+    private Long responseTimeout;
+
+    @Bean(initMethod = "start", destroyMethod = "close")
+    public CloseableHttpAsyncClient getHttpClient()
+            throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException,
+                           KeyManagementException {
+        return HttpCientFactory.createClient(trustStore, trustStorePassword, conectionTimeout, responseTimeout);
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/config/LoginAttemptsLogger.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/config/LoginAttemptsLogger.java
new file mode 100644 (file)
index 0000000..072fbb9
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.app.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.actuate.audit.AuditEvent;
+import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
+import org.springframework.boot.actuate.audit.listener.AuditApplicationEvent;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.event.EventListener;
+import org.springframework.security.web.authentication.WebAuthenticationDetails;
+import org.springframework.stereotype.Component;
+
+@Component
+public class LoginAttemptsLogger {
+    private static final Logger LOG = LoggerFactory.getLogger(LoginAttemptsLogger.class);
+
+    /**
+     *  audit Application Event.
+     */
+    @EventListener
+    public void auditEventHappened(final AuditApplicationEvent auditApplicationEvent) {
+        final AuditEvent auditEvent = auditApplicationEvent.getAuditEvent();
+        final WebAuthenticationDetails details = (WebAuthenticationDetails) auditEvent.getData().get("details");
+        if (details == null) {
+            LOG.info("AUDIT: User: {} Event Type: {}", auditEvent.getPrincipal(), auditEvent.getType());
+            return;
+        }
+        LOG.info("AUDIT: User: {} Event Type: {} Remote IP address: {}",
+                auditEvent.getPrincipal(), auditEvent.getType(), details.getRemoteAddress());
+    }
+
+    @Bean
+    public InMemoryAuditEventRepository auditEventRepository() throws Exception {
+        return new InMemoryAuditEventRepository();
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/config/SecurityConfiguration.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/config/SecurityConfiguration.java
new file mode 100644 (file)
index 0000000..2fc030f
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.app.config;
+
+import org.o.ran.oam.nf.oam.adopter.app.properties.ServerProperties;
+import org.o.ran.oam.nf.oam.adopter.app.properties.SslProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+@Configuration
+@EnableConfigurationProperties
+public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
+    private static final String ADMIN_ROLE = "ADMIN";
+    private final ServerProperties properties;
+
+    @Autowired
+    public SecurityConfiguration(final ServerProperties properties) {
+        this.properties = properties;
+    }
+
+    @Override
+    protected void configure(final HttpSecurity http) throws Exception {
+        final SslProperties ssl = properties.getSsl();
+        if (ssl != null && ssl.getEnabled() != null && ssl.getEnabled()) {
+            http.requiresChannel().anyRequest().requiresSecure();
+        }
+        http.csrf().disable()
+                .antMatcher("/adapters/**")
+                .authorizeRequests().anyRequest()
+                .hasRole(ADMIN_ROLE).and()
+                .httpBasic();
+    }
+
+    @Bean
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        return super.authenticationManagerBean();
+    }
+
+    @Autowired
+    public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
+        auth.inMemoryAuthentication().withUser(properties.getUsername()).password("{noop}" + properties.getPassword())
+                .roles(ADMIN_ROLE);
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/controller/AdapterController.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/controller/AdapterController.java
new file mode 100644 (file)
index 0000000..a6fb63d
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.app.controller;
+
+import java.util.List;
+import lombok.SneakyThrows;
+import org.o.ran.oam.nf.oam.adopter.api.ControllerApi;
+import org.o.ran.oam.nf.oam.adopter.model.Adapter;
+import org.o.ran.oam.nf.oam.adopter.model.AdapterMechId;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.api.PerformanceManagementAdaptersDeployer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@SpringBootApplication(scanBasePackages = {"org.o.ran.oam.adopter"})
+@RequestMapping(path = "/adapters")
+public class AdapterController implements ControllerApi {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AdapterController.class);
+
+    private final PerformanceManagementAdaptersDeployer deployer;
+
+    @Autowired
+    public AdapterController(final PerformanceManagementAdaptersDeployer deployer) {
+        this.deployer = deployer;
+    }
+
+    @Override
+    @SneakyThrows
+    public ResponseEntity<Void> addAdapter(final Adapter adapter) {
+        LOG.info("Request triggered: addAdapter");
+        final AdapterMechId mechIdDto = adapter.getMechId();
+        deployer.create(adapter.getHost(), mechIdDto.getUsername(), mechIdDto.getPassword());
+        return ResponseEntity.ok().build();
+    }
+
+    @Override
+    public ResponseEntity<List<String>> getAllAdapters() {
+        LOG.info("Request triggered: getAllAdapters");
+        return ResponseEntity.ok(deployer.getAll());
+    }
+
+    @Override
+    @SneakyThrows
+    public ResponseEntity<Void> removeAdapter(final String host) {
+        LOG.info("Request triggered: removeAdapter");
+        deployer.delete(host);
+        return ResponseEntity.ok().build();
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/controller/RestExceptionHandler.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/controller/RestExceptionHandler.java
new file mode 100644 (file)
index 0000000..a4a7d6f
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.app.controller;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.AlreadyPresentException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.NotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+@RestControllerAdvice
+public class RestExceptionHandler {
+    private static final Logger LOG = LoggerFactory.getLogger(RestExceptionHandler.class);
+
+    /**
+     * Handle Already Present Exceptions.
+     */
+    @ExceptionHandler({AlreadyPresentException.class})
+    public static ResponseEntity<Object> handleBadRequestExceptions(final AlreadyPresentException exception) {
+        LOG.error("Request failed", exception);
+        return ResponseEntity
+                .badRequest()
+                .body(exception.getMessage());
+    }
+
+    /**
+     * Handle Not Found Exceptions.
+     */
+    @ExceptionHandler({NotFoundException.class})
+    public static ResponseEntity<Object> handleNotFoundExceptions(final NotFoundException exception) {
+        LOG.error("Request failed", exception);
+        return ResponseEntity.notFound().build();
+    }
+
+    /**
+     * Handle MethodArgument Not Valid Exceptions.
+     */
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public Map<String, String> handleValidationExceptions(final MethodArgumentNotValidException ex) {
+        final Map<String, String> errors = new HashMap<>();
+        ex.getBindingResult().getAllErrors().forEach((error) -> {
+            final String fieldName = ((FieldError) error).getField();
+            final String errorMessage = error.getDefaultMessage();
+            errors.put(fieldName, errorMessage);
+        });
+        return errors;
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/controller/TimeZoneServiceProvider.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/controller/TimeZoneServiceProvider.java
new file mode 100644 (file)
index 0000000..c201ce6
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.app.controller;
+
+import java.time.ZoneId;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.api.PerformanceManagementAdaptersDeployer;
+import org.o.ran.oam.nf.oam.adopter.snmp.manager.api.TimeZoneOffsetService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class TimeZoneServiceProvider implements TimeZoneOffsetService {
+
+    private final PerformanceManagementAdaptersDeployer deployer;
+
+    @Autowired
+    public TimeZoneServiceProvider(final PerformanceManagementAdaptersDeployer deployer) {
+        this.deployer = deployer;
+    }
+
+    @Override
+    public ZoneId getTimeZone(final String host) {
+        return deployer.getTimeZone(host);
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/http/HttpCientFactory.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/http/HttpCientFactory.java
new file mode 100644 (file)
index 0000000..7184c28
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.app.http;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import javax.net.ssl.SSLContext;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.cookie.StandardCookieSpec;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
+import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
+import org.apache.hc.core5.ssl.SSLContextBuilder;
+import org.apache.hc.core5.util.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class HttpCientFactory {
+    private static final Logger LOG = LoggerFactory.getLogger(HttpCientFactory.class);
+
+    /**
+     * Generates a CloseableHttpAsyncClient.
+     */
+    public static CloseableHttpAsyncClient createClient(final String trustStore,
+            final String trustStorePassword, final Long conectionTimeout, final Long responseTimeout)
+            throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException, IOException,
+            CertificateException {
+        final SSLContext sslContext = getSslContext(new File(trustStore), trustStorePassword);
+        return trustTrustStore(sslContext, conectionTimeout, responseTimeout);
+    }
+
+    private static SSLContext getSslContext(final File trustStoreFilePath, final String trustStorePassword)
+            throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException,
+            KeyManagementException {
+        return new SSLContextBuilder()
+                .loadTrustMaterial(trustStoreFilePath.toURI().toURL(), trustStorePassword.toCharArray())
+                .build();
+    }
+
+    private static CloseableHttpAsyncClient trustTrustStore(final SSLContext sslContext,
+            final Long conectionTimeout, final Long responseTimeout) {
+        LOG.info("Trust all certificates under truststore");
+        final PoolingAsyncClientConnectionManager connectionManager =
+                PoolingAsyncClientConnectionManagerBuilder.create().setTlsStrategy(
+                        ClientTlsStrategyBuilder.create()
+                                .setSslContext(sslContext)
+                                .setHostnameVerifier(NoopHostnameVerifier.INSTANCE)
+                                .build())
+                        .build();
+
+        return HttpAsyncClients.custom()
+                .setConnectionManager(connectionManager)
+                .setDefaultRequestConfig(createDefaultRequestConfig(conectionTimeout, responseTimeout))
+                .setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
+                .build();
+    }
+
+    private static RequestConfig createDefaultRequestConfig(final Long conectionTimeout, final Long responseTimeout) {
+        return RequestConfig.custom()
+                .setConnectTimeout(Timeout.ofSeconds(conectionTimeout))
+                .setResponseTimeout(Timeout.ofSeconds(responseTimeout))
+                .setCookieSpec(StandardCookieSpec.STRICT)
+                .build();
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/properties/ServerProperties.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/properties/ServerProperties.java
new file mode 100644 (file)
index 0000000..1096cfb
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.app.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "server")
+@Data
+@NoArgsConstructor
+public class ServerProperties {
+    private int port;
+    private String username;
+    private String password;
+    private SslProperties ssl;
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/properties/SslProperties.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/main/java/org/o/ran/oam/nf/oam/adopter/app/properties/SslProperties.java
new file mode 100644 (file)
index 0000000..6e34bc9
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.app.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class SslProperties {
+    private Boolean enabled;
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/test/java/org/o/ran/oam/nf/oam/adopter/app/AdapterApplicationTest.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/test/java/org/o/ran/oam/nf/oam/adopter/app/AdapterApplicationTest.java
new file mode 100644 (file)
index 0000000..3b9cdf3
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.app;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import com.google.gson.Gson;
+import java.time.ZoneId;
+import java.util.Collections;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.o.ran.oam.nf.oam.adopter.app.controller.TimeZoneServiceProvider;
+import org.o.ran.oam.nf.oam.adopter.model.Adapter;
+import org.o.ran.oam.nf.oam.adopter.model.AdapterMechId;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.PerformanceManagementMapperConfigProvider;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.api.PerformanceManagementAdaptersDeployer;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.AlreadyPresentException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.NotFoundException;
+import org.o.ran.oam.nf.oam.adopter.snmp.manager.SnmpMappingConfigurationProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.MediaType;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@AutoConfigureMockMvc
+class AdapterApplicationTest {
+
+    private static final Gson GSON = new Gson();
+
+    @Autowired
+    private MockMvc mockMvc;
+    @Autowired
+    private TimeZoneServiceProvider timeZoneServiceProvider;
+    @MockBean
+    private PerformanceManagementAdaptersDeployer deployer;
+    @MockBean
+    private SnmpMappingConfigurationProvider snmpProvider;
+    @MockBean
+    private PerformanceManagementMapperConfigProvider pmProvider;
+    @Autowired
+    private WebApplicationContext context;
+
+
+    @BeforeEach
+    public void applySecurity() {
+        mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
+    }
+
+    @Test
+    @WithMockUser(username = "admin", roles = "ADMIN")
+    public void testGetAllAdapters() throws Exception {
+        when(deployer.getAll()).thenReturn(Collections.singletonList("mockResult"));
+
+        mockMvc.perform(get("/adapters/").secure(true).contentType(MediaType.APPLICATION_JSON)).andDo(print())
+                .andExpect(status().isOk()).andExpect(content().string(containsString("mockResult")));
+    }
+
+    @Test
+    @WithMockUser(username = "admin", roles = "ADMIN")
+    public void testDeleteAdapter() throws Exception {
+        mockMvc.perform(delete("/adapters/adapter/172.10.55.3").secure(true).contentType(MediaType.APPLICATION_JSON))
+                .andDo(print()).andExpect(status().isOk());
+    }
+
+    @Test
+    @WithMockUser(username = "admin", roles = "ADMIN")
+    public void testNotFound() throws Exception {
+        doThrow(NotFoundException.class).when(deployer).delete(anyString());
+
+        mockMvc.perform(delete("/adapters/adapter/172.10.55.3").secure(true).contentType(MediaType.APPLICATION_JSON))
+                .andDo(print()).andExpect(status().isNotFound());
+    }
+
+    @Test
+    @WithMockUser(username = "admin", roles = "ADMIN")
+    public void testAddAdapter() throws Exception {
+
+        final Adapter adapter = new Adapter();
+        adapter.setHost("172.10.55.3");
+
+        final AdapterMechId mechId = new AdapterMechId();
+        mechId.username("admin");
+        mechId.password("somePass");
+        adapter.setMechId(mechId);
+
+        mockMvc.perform(post("/adapters/adapter").secure(true).contentType(MediaType.APPLICATION_JSON)
+                                .content(GSON.toJson(adapter))).andDo(print()).andExpect(status().isOk());
+    }
+
+    @Test
+    @WithMockUser(username = "admin", roles = "ADMIN")
+    public void testAlreadyExist() throws Exception {
+
+        final Adapter adapter = new Adapter();
+        adapter.setHost("172.10.55.3");
+
+        final AdapterMechId mechId = new AdapterMechId();
+        mechId.username("admin");
+        mechId.password("somePass");
+        adapter.setMechId(mechId);
+
+        doThrow(AlreadyPresentException.class).when(deployer).create(anyString(), anyString(), anyString());
+
+        mockMvc.perform(post("/adapters/adapter").secure(true).contentType(MediaType.APPLICATION_JSON)
+                                .content(GSON.toJson(adapter))).andDo(print()).andExpect(status().isBadRequest());
+    }
+
+    @Test
+    @WithMockUser(username = "admin", roles = "ADMIN")
+    public void testMissingArguments() throws Exception {
+
+        final Adapter adapter = new Adapter();
+        adapter.setHost("172.10.55.3");
+
+        final AdapterMechId mechId = new AdapterMechId();
+        mechId.username("admin");
+        adapter.setMechId(mechId);
+
+
+        mockMvc.perform(post("/adapters/adapter").secure(true).contentType(MediaType.APPLICATION_JSON)
+                                .content(GSON.toJson(adapter))).andDo(print()).andExpect(status().isBadRequest());
+    }
+
+    @Test
+    public void test() {
+        final ZoneId zoneId = ZoneId.of("+02:00");
+        when(deployer.getTimeZone("172.10.55.3")).thenReturn(zoneId);
+        assertEquals(zoneId, timeZoneServiceProvider.getTimeZone("172.10.55.3"));
+    }
+}
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/test/resources/application.yml b/ves-nf-oam-adopter/ves-nf-oam-adopter-app/src/test/resources/application.yml
new file mode 100644 (file)
index 0000000..27b27ff
--- /dev/null
@@ -0,0 +1,33 @@
+spring.devtools.restart.log-condition-evaluation-delta: false
+logging.config: ./configuration/log4j2.yml
+server:
+  username: admin
+  password: admin
+  ssl:
+    enabled: true
+    key-store-type: JKS
+    key-alias: nf-oam-adopter
+    key-store: ./configuration/ssl/nf-oam-adopter-keystore.jks
+    key-store-password: nf-oam-adopter
+    key-password: nf-oam-adopter
+    trust-store: ./configuration/ssl/nf-oam-adopter-truststore.jks
+    trust-store-password: nf-oam-adopter
+    enabled-protocols: TLSv1.3
+    ciphers: TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_CCM_8_SHA256, TLS_AES_128_CCM_SHA256
+ves-collector:
+  url: someUrl
+  vesEncodedAuth: someAuth
+http-client:
+  conection-timeout: 600
+  response-timeout: 600
+pm-rest-manager:
+  synchronization-time-start: 20:00:00
+  synchronization-time-frequency: 5
+  mapping-config-path: mapping-configuration/pm-ves-message-mapping.yaml
+  ran-token-endpoint: /auth/token
+  ran-pm-endpoint: /pm/files
+  ran-time-zone-offset-endpoint: /system/timeZone
+snmp-manager:
+  host: "0.0.0.0"
+  port: 10162
+  mapping-config-path: mapping-configuration/fm-ves-message-mapping.yaml
\ No newline at end of file
index 63cb23f..7269b55 100644 (file)
                 <artifactId>ves-nf-oam-adopter-snmp-manager</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>ves-nf-oam-adopter-pm-manager</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>ves-nf-oam-adopter-pm-sb-rest-client</artifactId>
+                <version>${project.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 </project>
\ No newline at end of file
index 6bd01fd..c409443 100644 (file)
@@ -1,7 +1,7 @@
 spring.devtools.restart.log-condition-evaluation-delta: false
 scheduler:
-  fixedDelay: 8000
-  initialDelay: 4000
+  fixedDelay: 16000
+  initialDelay: 8000
 security:
   auth:
     username: admin
index 3f0aaaa..629703e 100644 (file)
@@ -48,7 +48,7 @@ paths:
         '401':
           $ref: '#/components/responses/401Error'
   /pm/files:
-    post:
+    get:
       tags:
         - controller
       summary: Read Performance Management Files
@@ -67,7 +67,7 @@ paths:
         '401':
           $ref: '#/components/responses/401Error'
   /system/timeZone:
-    post:
+    get:
       tags:
         - controller
       summary: Read time zone
index 7c8c356..0869dc6 100644 (file)
@@ -91,7 +91,6 @@
             <plugin>
                 <groupId>io.swagger.codegen.v3</groupId>
                 <artifactId>swagger-codegen-maven-plugin</artifactId>
-                <version>${swagger-codegen-maven-plugin.version}</version>
                 <executions>
                     <execution>
                         <goals>
                                 <image>
                                     <name>${nexus.repository}${image.name}</name>
                                     <build>
-                                        <from>${base.image}</from>
+                                        <from>${nexus.repository.mirror}${base.image}</from>
                                         <tags>
                                             <tag>${project.version}</tag>
                                         </tags>
index a694efe..a200391 100644 (file)
             ../ves-nf-oam-adopter-event-notifier/target/site/jacoco-ut/jacoco.xml,
             ../ves-nf-oam-adopter-event-notifier/target/site/jacoco-aggregate/jacoco.xml,
             ../ves-nf-oam-adopter-snmp-manager/target/site/jacoco-ut/jacoco.xml,
-            ../ves-nf-oam-adopter-snmp-manager/target/site/jacoco-aggregate/jacoco.xml
+            ../ves-nf-oam-adopter-snmp-manager/target/site/jacoco-aggregate/jacoco.xml,
+            ../ves-nf-oam-adopter-pm-manager/target/site/jacoco-ut/jacoco.xml,
+            ../ves-nf-oam-adopter-pm-manager/target/site/jacoco-aggregate/jacoco.xml,
+            ../ves-nf-oam-adopter-pm-sb-rest-client/target/site/jacoco-ut/jacoco.xml,
+            ../ves-nf-oam-adopter-pm-sb-rest-client/target/site/jacoco-aggregate/jacoco.xml
         </sonar.coverage.jacoco.xmlReportPaths>
         <sonar.scanner.version>3.8.0.2131</sonar.scanner.version>
         <!--Dependency Versions-->
@@ -52,6 +56,7 @@
         <commons-io.version>2.6</commons-io.version>
         <commons-beanutils.version>1.9.4</commons-beanutils.version>
         <gson.version>2.8.6</gson.version>
+        <guava.version>30.1-jre</guava.version>
         <httpclient5.version>5.0.3</httpclient5.version>
         <jdt.annotation.version>2.2.600</jdt.annotation.version>
         <lombok.version>1.18.20</lombok.version>
@@ -72,7 +77,8 @@
         <base.image>openjdk:11-jre-slim</base.image>
         <base.config>${project.basedir}/configuration/</base.config>
         <image.workdir>/o-ran-ves-adapter/</image.workdir>
-        <nexus.repository>nexus3.o-ran-sc.org:10003/o-ran-sc/</nexus.repository>
+        <nexus.repository.mirror>nexus3.o-ran-sc.org:10001/</nexus.repository.mirror>
+        <nexus.repository>nexus3.o-ran-sc.org:10004/o-ran-sc/</nexus.repository>
     </properties>
 
     <dependencyManagement>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
-            <dependency>
-                <groupId>io.swagger.core.v3</groupId>
-                <artifactId>swagger-annotations</artifactId>
-                <version>${swagger-annotations.version}</version>
-            </dependency>
             <dependency>
                 <groupId>com.google.code.gson</groupId>
                 <artifactId>gson</artifactId>
                 <artifactId>commons-beanutils</artifactId>
                 <version>${commons-beanutils.version}</version>
             </dependency>
+            <dependency>
+                <groupId>com.google.guava</groupId>
+                <artifactId>guava</artifactId>
+                <version>${guava.version}</version>
+            </dependency>
             <dependency>
                 <groupId>com.github.spotbugs</groupId>
                 <artifactId>spotbugs-annotations</artifactId>
                 <version>${spotbugs.version}</version>
             </dependency>
+            <dependency>
+                <groupId>io.swagger.core.v3</groupId>
+                <artifactId>swagger-annotations</artifactId>
+                <version>${swagger-annotations.version}</version>
+            </dependency>
             <dependency>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-starter</artifactId>
                         </execution>
                     </executions>
                 </plugin>
+                <plugin>
+                    <groupId>io.swagger.codegen.v3</groupId>
+                    <artifactId>swagger-codegen-maven-plugin</artifactId>
+                    <version>${swagger-codegen-maven-plugin.version}</version>
+                </plugin>
             </plugins>
         </pluginManagement>
         <plugins>
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/pom.xml b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/pom.xml
new file mode 100644 (file)
index 0000000..02b4f98
--- /dev/null
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* ============LICENSE_START=======================================================
+* O-RAN-SC
+* ================================================================================
+* Copyright (C) 2021 AT&T 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============================================
+*
+-->
+<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.o-ran-sc.oam</groupId>
+        <artifactId>ves-nf-oam-adopter-parent</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+        <relativePath>../ves-nf-oam-adopter-parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ves-nf-oam-adopter-pm-manager</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ves-nf-oam-adopter-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents.client5</groupId>
+            <artifactId>httpclient5</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-yaml</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-csv</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-configuration2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>30.1-jre</version>
+        </dependency>
+        <dependency>
+            <groupId>io.reactivex.rxjava3</groupId>
+            <artifactId>rxjava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.spotbugs</groupId>
+            <artifactId>spotbugs-annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/AdaptersDeployer.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/AdaptersDeployer.java
new file mode 100644 (file)
index 0000000..924373d
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager;
+
+import static org.eclipse.jdt.annotation.Checks.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.time.ZoneId;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.api.PerformanceManagementAdaptersDeployer;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.AlreadyPresentException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.NotFoundException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.Adapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public final class AdaptersDeployer implements PerformanceManagementAdaptersDeployer, AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AdaptersDeployer.class);
+
+    private final PerformanceManagementRestAgentFactory pmRestAgentFactory;
+    private final Map<String, PerformanceManagementRestAgent> adapters = new ConcurrentHashMap<>();
+
+    @Autowired
+    public AdaptersDeployer(final PerformanceManagementRestAgentFactory pmRestAgentFactory) {
+        this.pmRestAgentFactory = pmRestAgentFactory;
+    }
+
+    @Override
+    public synchronized void create(final String hostIpAddress, final String username, final String password)
+            throws AlreadyPresentException {
+        LOG.info("Create device PM adapter {}", hostIpAddress);
+        if (adapters.get(hostIpAddress) != null) {
+            throw new AlreadyPresentException(hostIpAddress);
+        }
+        final Adapter adapter =
+                Adapter.builder().username(username).password(password).hostIpAddress(hostIpAddress).build();
+        final PerformanceManagementRestAgent pmRestAgent =
+                pmRestAgentFactory.createPerformanceManagementRestAgent(adapter).blockingGet();
+        pmRestAgent.init();
+        adapters.put(hostIpAddress, pmRestAgent);
+    }
+
+    @Override
+    public synchronized void delete(final String host) throws NotFoundException {
+        LOG.info("Adapter PM adapter removed {}", requireNonNull(host));
+        final PerformanceManagementRestAgent adapter = adapters.remove(host);
+        if (adapter == null) {
+            throw new NotFoundException(host);
+        }
+        adapter.close();
+    }
+
+    @Override
+    public List<String> getAll() {
+        return ImmutableList.copyOf(adapters.keySet());
+    }
+
+    @Override
+    public ZoneId getTimeZone(final String host) {
+        LOG.debug("Read time zone for {}", host);
+        return Optional.ofNullable(adapters.get(host)).map(
+                PerformanceManagementRestAgent::getTimeZone).orElse(null);
+    }
+
+    @Override
+    public synchronized void close() {
+        for (final String host : ImmutableList.copyOf(adapters.keySet())) {
+            try {
+                delete(host);
+            } catch (final Exception e) {
+                LOG.warn("Failed to delete device PM adapter {}", host);
+            }
+        }
+        adapters.clear();
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/PerformanceManagementMapperConfigProvider.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/PerformanceManagementMapperConfigProvider.java
new file mode 100644 (file)
index 0000000..b7c24f8
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager;
+
+import static java.util.Objects.requireNonNull;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URI;
+import java.nio.file.Paths;
+import javax.annotation.PostConstruct;
+import org.apache.commons.configuration2.YAMLConfiguration;
+import org.apache.commons.configuration2.builder.ConfigurationBuilderEvent;
+import org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder;
+import org.apache.commons.configuration2.builder.fluent.Parameters;
+import org.apache.commons.configuration2.event.EventListener;
+import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.VesMappingConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Service
+public class PerformanceManagementMapperConfigProvider  {
+
+    private static final Logger LOG = LoggerFactory.getLogger(PerformanceManagementMapperConfigProvider.class);
+
+    private static final ObjectMapper YAML_READER = new ObjectMapper(new YAMLFactory());
+    @Value("${pm-rest-manager.mapping-config-path:#{null}}")
+    private String mappingFilePath;
+    private ReloadingFileBasedConfigurationBuilder<YAMLConfiguration> builder;
+
+    @Autowired
+    public PerformanceManagementMapperConfigProvider() {
+
+    }
+
+    /**
+     * Initialize Service.
+     */
+    @PostConstruct
+    public void init() throws IOException, ConfigurationException {
+        requireNonNull(mappingFilePath);
+        final URI filePath = Paths.get(mappingFilePath).toUri();
+        builder = new ReloadingFileBasedConfigurationBuilder<>(YAMLConfiguration.class)
+                          .configure(new Parameters().hierarchical().setURL(filePath.toURL()));
+        builder.addEventListener(ConfigurationBuilderEvent.CONFIGURATION_REQUEST, (EventListener) event -> {
+            builder.getReloadingController().checkForReloading(null);
+            LOG.debug("Reloading {}", filePath.toString());
+        });
+        //Test initial configuration
+        builder.getConfiguration();
+    }
+
+    /**
+     * Provide VES Mapping configuration.
+     */
+    public VesMappingConfiguration getVesMappingConfiguration() throws ConfigurationException, IOException {
+        final YAMLConfiguration configuration = builder.getConfiguration();
+        final StringWriter output = new StringWriter();
+        configuration.write(output);
+        return YAML_READER.readValue(output.toString(), VesMappingConfiguration.class);
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/PerformanceManagementRestAgent.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/PerformanceManagementRestAgent.java
new file mode 100644 (file)
index 0000000..66693fc
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager;
+
+import java.time.Duration;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+public final class PerformanceManagementRestAgent implements AutoCloseable {
+
+    private static final DateTimeFormatter TIME_INPUT_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss");
+    private final ScheduledExecutorService scheduler;
+    private final Runnable pmAgent;
+    private final LocalTime synchronizationTimeStart;
+    private final int synchronizationTimeFrequency;
+    private final ZoneId zoneId;
+
+
+    PerformanceManagementRestAgent(final Runnable pmAgent, final String synchronizationTimeStart,
+            final int synchronizationTimeFrequency, final ZoneId zoneId) {
+        this.scheduler = Executors.newSingleThreadScheduledExecutor();
+        this.pmAgent = pmAgent;
+        this.synchronizationTimeStart = LocalTime.parse(synchronizationTimeStart, TIME_INPUT_FORMAT);
+        this.synchronizationTimeFrequency = synchronizationTimeFrequency;
+        this.zoneId = zoneId;
+    }
+
+    /**
+     * Initialize service at fixed rate.
+     */
+    public void init() {
+        final long initialDelay = initialDelay();
+        scheduler.scheduleAtFixedRate(pmAgent, initialDelay, synchronizationTimeFrequency, TimeUnit.SECONDS);
+    }
+
+    private long initialDelay() {
+        final ZonedDateTime now = ZonedDateTime.now(zoneId);
+
+        ZonedDateTime nextRun = now
+                .withHour(synchronizationTimeStart.getHour())
+                .withMinute(synchronizationTimeStart.getMinute())
+                .withSecond(synchronizationTimeStart.getSecond());
+        if (now.compareTo(nextRun) > 0) {
+            nextRun = nextRun.plusSeconds(synchronizationTimeFrequency);
+        }
+        final Duration duration = Duration.between(now, nextRun);
+        return duration.getSeconds();
+    }
+
+    public ZoneId getTimeZone() {
+        return zoneId;
+    }
+
+    @Override
+    public void close() {
+        scheduler.shutdown();
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/PerformanceManagementRestAgentFactory.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/PerformanceManagementRestAgentFactory.java
new file mode 100644 (file)
index 0000000..0ebaa6c
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager;
+
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.Single;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+import org.o.ran.oam.nf.oam.adopter.api.VesEventNotifier;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.api.HttpRestClient;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.mapper.PerformanceManagementFile2VesMapper;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.Adapter;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.properties.PerformanceManagementManagerProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PerformanceManagementRestAgentFactory {
+    private static final Logger LOG = LoggerFactory.getLogger(PerformanceManagementRestAgentFactory.class);
+
+    private final VesEventNotifier eventListener;
+    private final PerformanceManagementManagerProperties properties;
+    private final PerformanceManagementFile2VesMapper pmFileMapper;
+    private final HttpRestClient httpRestClient;
+
+    /**
+     * Default constructor.
+     */
+    public PerformanceManagementRestAgentFactory(final VesEventNotifier eventListener,
+            final PerformanceManagementFile2VesMapper pmFileMapper,
+            final PerformanceManagementManagerProperties properties, final HttpRestClient httpRestClient) {
+        this.eventListener = eventListener;
+        this.pmFileMapper = pmFileMapper;
+        this.properties = properties;
+        this.httpRestClient = httpRestClient;
+    }
+
+    /**
+     * Generates new PM Agent which will get pm files via rest at specific time each day and
+     * send it as CommonEventFormat302ONAP event via rest.
+     * @param adapter IP address fo the adapter, adapter login username, adapter login password
+     * @return PMRestAgent
+     */
+    public final Single<PerformanceManagementRestAgent> createPerformanceManagementRestAgent(final Adapter adapter) {
+        return httpRestClient.getTimeZone(adapter).map(timeZone -> {
+            final PerformanceManagementAgentRunnable pmAgentRunnable =
+                    new PerformanceManagementAgentRunnable(httpRestClient, eventListener, pmFileMapper, adapter);
+            return new PerformanceManagementRestAgent(pmAgentRunnable, properties.getSynchronizationTimeStart(),
+                    properties.getSynchronizationTimeFrequency(), timeZone);
+        });
+    }
+
+    private static class PerformanceManagementAgentRunnable implements Runnable {
+        final HttpRestClient httpClient;
+        private final VesEventNotifier pmEventListener;
+        private final PerformanceManagementFile2VesMapper pmFileMapper;
+        private final Adapter adapter;
+
+        public PerformanceManagementAgentRunnable(final HttpRestClient httpClient,
+                final VesEventNotifier pmEventListener,
+                final PerformanceManagementFile2VesMapper pmFileMapper, final Adapter adapter) {
+            this.httpClient = httpClient;
+            this.pmEventListener = pmEventListener;
+            this.pmFileMapper = pmFileMapper;
+            this.adapter = adapter;
+        }
+
+        @Override
+        public synchronized void run() {
+            final String hostIp = adapter.getHostIpAddress();
+            httpClient.readFiles(adapter)
+                    .flatMap(zip -> pmFileMapper.map(zip, hostIp))
+                    .flatMapCompletable(events -> Observable.fromIterable(events)
+                            .concatMapCompletable(pmEventListener::notifyEvents))
+                    .doOnSubscribe(result -> LOG.info("PM VES notification forwarding for adapter {} started", hostIp))
+                    .doOnComplete(() -> LOG.info("PM VES notification forwarding for adapter {} finished", hostIp))
+                    .doOnError(error -> LOG.warn("PM VES notification forwarding for adapter {} failed", hostIp))
+                    .subscribeOn(Schedulers.single())
+                    .subscribe();
+        }
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/api/HttpRestClient.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/api/HttpRestClient.java
new file mode 100644 (file)
index 0000000..71a27ea
--- /dev/null
@@ -0,0 +1,13 @@
+package org.o.ran.oam.nf.oam.adopter.pm.rest.manager.api;
+
+import io.reactivex.rxjava3.core.Single;
+import java.time.ZoneId;
+import java.util.zip.ZipInputStream;
+import org.eclipse.jdt.annotation.NonNull;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.Adapter;
+
+public interface HttpRestClient {
+    @NonNull Single<ZipInputStream> readFiles(@NonNull Adapter adapter);
+
+    @NonNull Single<ZoneId> getTimeZone(@NonNull Adapter adapter);
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/api/PerformanceManagementAdaptersDeployer.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/api/PerformanceManagementAdaptersDeployer.java
new file mode 100644 (file)
index 0000000..01be898
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.api;
+
+import java.time.ZoneId;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.AlreadyPresentException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.NotFoundException;
+
+public interface PerformanceManagementAdaptersDeployer {
+
+    /**
+     * Creates PM Adapter.
+     *
+     * @param hostIpAddress adapter ip address
+     * @param username mechid username
+     * @param password mechid password
+     * @throws AlreadyPresentException if already present
+     */
+    void create(@NonNull String hostIpAddress, @NonNull String username, @NonNull String password)
+            throws AlreadyPresentException;
+
+    /**
+     * Removes PM Adapter by host ip address.
+     *
+     * @param host ip address
+     * @throws NotFoundException if not present
+     */
+    void delete(@NonNull String host) throws NotFoundException;
+
+    /**
+     * Returns list of Adapters host Ip.
+     */
+    @NonNull List<String> getAll();
+
+    /**
+     * Returns time zone of specific device.
+     */
+    @Nullable ZoneId getTimeZone(@NonNull String host);
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/configurations/PerformanceManagementRestManagerConfig.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/configurations/PerformanceManagementRestManagerConfig.java
new file mode 100644 (file)
index 0000000..0a88e80
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.configurations;
+
+import org.o.ran.oam.nf.oam.adopter.api.VesEventNotifier;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.PerformanceManagementRestAgentFactory;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.api.HttpRestClient;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.mapper.PerformanceManagementFile2VesMapper;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.properties.PerformanceManagementManagerProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@EnableConfigurationProperties
+public class PerformanceManagementRestManagerConfig {
+    private final PerformanceManagementManagerProperties pmManagerProperties;
+    private final VesEventNotifier eventListener;
+    private final PerformanceManagementFile2VesMapper pmFileMapper;
+
+    /**
+     * Default constructor.
+     */
+    @Autowired
+    public PerformanceManagementRestManagerConfig(final VesEventNotifier eventListener,
+            final PerformanceManagementFile2VesMapper pmFileMapper,
+            final PerformanceManagementManagerProperties pmManagerProperties) {
+        this.eventListener = eventListener;
+        this.pmFileMapper = pmFileMapper;
+        this.pmManagerProperties = pmManagerProperties;
+    }
+
+    @Bean
+    PerformanceManagementRestAgentFactory getPerformanceManagementRestManagerFactoryService(
+            final HttpRestClient httpRestClient) {
+        return new PerformanceManagementRestAgentFactory(eventListener, pmFileMapper, pmManagerProperties,
+                httpRestClient);
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/AlreadyPresentException.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/AlreadyPresentException.java
new file mode 100644 (file)
index 0000000..9199daa
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions;
+
+public class AlreadyPresentException extends PerformanceManagementException {
+    private static final long serialVersionUID = -1852996415384288431L;
+
+    public AlreadyPresentException(final String adapter) {
+        super("Adapter " + adapter + " already present.");
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/NotFoundException.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/NotFoundException.java
new file mode 100644 (file)
index 0000000..4179fb9
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions;
+
+public class NotFoundException extends PerformanceManagementException {
+    private static final long serialVersionUID = -1852996415384288431L;
+
+    public NotFoundException(final String adapter) {
+        super("Adapter " + adapter + " is not present.");
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/PerformanceManagementEmptyOutputException.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/PerformanceManagementEmptyOutputException.java
new file mode 100644 (file)
index 0000000..492e13c
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions;
+
+/**
+ * Object already exists exception.
+ */
+public class PerformanceManagementEmptyOutputException extends PerformanceManagementException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Create object already exists exception.
+     *
+     * @param message exception message
+     */
+    public PerformanceManagementEmptyOutputException(final String message) {
+        super(message);
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/PerformanceManagementException.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/PerformanceManagementException.java
new file mode 100644 (file)
index 0000000..f39dac8
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions;
+
+/**
+ * Object already exists exception.
+ */
+public class PerformanceManagementException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Create object already exists exception.
+     *
+     * @param message exception message
+     */
+    public PerformanceManagementException(final String message) {
+        super(message);
+    }
+
+    public PerformanceManagementException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/TokenGenerationException.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/TokenGenerationException.java
new file mode 100644 (file)
index 0000000..6441e98
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions;
+
+/**
+ * Object already exists exception.
+ */
+public class TokenGenerationException extends PerformanceManagementException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Create object already exists exception.
+     *
+     * @param message exception message
+     */
+    public TokenGenerationException(final String message) {
+        super(message);
+    }
+
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/ZoneIdException.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/exceptions/ZoneIdException.java
new file mode 100644 (file)
index 0000000..4552533
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions;
+
+/**
+ * Object already exists exception.
+ */
+public class ZoneIdException extends PerformanceManagementException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Create object already exists exception.
+     *
+     * @param message exception message
+     */
+    public ZoneIdException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/mapper/CommonEventHeaderHandler.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/mapper/CommonEventHeaderHandler.java
new file mode 100644 (file)
index 0000000..7049285
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.mapper;
+
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.o.ran.oam.nf.oam.adopter.api.CommonEventHeader;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.CsvConfiguration;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.VesMappingConfiguration;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+final class CommonEventHeaderHandler {
+    private static final String PM_NOTIFICATIONS = "PM_Notification";
+
+    static CommonEventHeader toCommonEventHeader(final VesMappingConfiguration config, final String hostIp,
+            final CsvConfiguration csv, final Map<String, String> record, final int sequence) {
+        final CommonEventHeader header = new CommonEventHeader();
+        setMandatoryFields(config, hostIp, csv, header, record, sequence);
+        setOptionalFields(config, header);
+        return header;
+    }
+
+    private static void setOptionalFields(final VesMappingConfiguration config, final CommonEventHeader header) {
+        header.setNfVendorName(Optional.ofNullable(config.getNfVendorName()).orElse(null));
+        header.setReportingEntityId(config.getReportingEntityId());
+        header.setNfNamingCode(null); //NOP
+        header.setNfcNamingCode(null); //NOP
+        header.setTimeZoneOffset(null); //NOP
+    }
+
+    private static void setMandatoryFields(final VesMappingConfiguration config, final String hostIp,
+            final CsvConfiguration csv, final CommonEventHeader header, final Map<String, String> record,
+            final int sequence) {
+        header.setDomain(CommonEventHeader.Domain.MEASUREMENT);
+        header.setEventName(CommonEventHeader.Domain.FAULT.name()
+                + "_" + config.getReportingEntityName()
+                + "_" + Optional.ofNullable(config.getEventName()).orElse(PM_NOTIFICATIONS));
+        header.setStartEpochMicrosec(System.currentTimeMillis());
+        header.setLastEpochMicrosec(System.currentTimeMillis());
+        header.setPriority(CommonEventHeader.Priority.fromValue(config.getPriority()));
+        header.setReportingEntityName(config.getReportingEntityName());
+        header.setSequence((long) sequence);
+        final String sourceNameField = csv.getSourceName();
+        final String sourceNameRecordValue = Optional.ofNullable(sourceNameField).map(record::get).orElse(hostIp);
+        final Optional<String> optRegex = Optional.ofNullable(csv.getSourceNameRegex());
+        header.setSourceName(optRegex.map(regex -> sourceNameRecordValue.replaceAll(regex, ""))
+            .orElse(sourceNameRecordValue));
+        header.setVersion(CommonEventHeader.Version._4_0);
+        header.setVesEventListenerVersion(Optional.ofNullable(config.getVesEventListenerVersion())
+                .map(CommonEventHeader.VesEventListenerVersion::fromValue)
+                .orElse(CommonEventHeader.VesEventListenerVersion._7_1));
+
+        final List<String> eventId = csv.getEventId();
+        final String keyIdConcat =  eventId.stream()
+                .filter(record::containsKey)
+                .map(record::get)
+                .collect(Collectors.joining());
+        header.setEventId(UUID.nameUUIDFromBytes(keyIdConcat.getBytes(StandardCharsets.UTF_8)).toString());
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/mapper/MeasurementFieldsHandler.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/mapper/MeasurementFieldsHandler.java
new file mode 100644 (file)
index 0000000..166af50
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.mapper;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.o.ran.oam.nf.oam.adopter.api.MeasurementFields;
+import org.o.ran.oam.nf.oam.adopter.api.NamedHashMap;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.CsvConfiguration;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.VesMappingConfiguration;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+final class MeasurementFieldsHandler {
+    private static void setOptionalsFields(final MeasurementFields measurementFields, final Map<String, String> records,
+            final VesMappingConfiguration config) {
+        final CsvConfiguration csv = config.getCsv();
+        measurementFields.setAdditionalFields(csv.getAdditionalFields()
+                .stream().filter(records::containsKey)
+                .collect(Collectors.toMap(Function.identity(), records::get)));
+
+        final NamedHashMap namedHashMap = new NamedHashMap();
+        namedHashMap.setName(csv.getAdditionalMeasurementsName());
+        namedHashMap.setHashMap(csv.getAdditionalMeasurements().stream()
+                .filter(records::containsKey)
+                .collect(Collectors.toMap(Function.identity(), records::get)));
+        measurementFields.setAdditionalMeasurements(Collections.singletonList(namedHashMap));
+        measurementFields.setAdditionalObjects(null);
+        measurementFields.setCodecUsageArray(null);
+        measurementFields.setConfiguredEntities(null);
+        measurementFields.setCpuUsageArray(null);
+        measurementFields.setDiskUsageArray(null);
+        measurementFields.setFeatureUsageArray(null);
+        measurementFields.setFilesystemUsageArray(null);
+        measurementFields.setHugePagesArray(null);
+        measurementFields.setIpmi(null);
+        measurementFields.setLatencyDistribution(null);
+        measurementFields.setLoadArray(null);
+        measurementFields.setMachineCheckExceptionArray(null);
+        measurementFields.setMeanRequestLatency(null);
+        measurementFields.setMemoryUsageArray(null);
+        measurementFields.setNfcScalingMetric(null);
+        measurementFields.setNicPerformanceArray(null);
+        measurementFields.setNumberOfMediaPortsInUse(null);
+        measurementFields.setProcessStatsArray(null);
+        measurementFields.setRequestRate(null);
+    }
+
+    static MeasurementFields toMeasurementFields(final VesMappingConfiguration config,
+            final Map<String, String> records) {
+        final MeasurementFields measurementFields = new MeasurementFields();
+        setMandatoryFields(measurementFields, config);
+        setOptionalsFields(measurementFields, records, config);
+        return measurementFields;
+    }
+
+    private static void setMandatoryFields(final MeasurementFields measurementFields,
+            final VesMappingConfiguration config) {
+        measurementFields.setMeasurementFieldsVersion(MeasurementFields.MeasurementFieldsVersion._4_0);
+        measurementFields.setMeasurementInterval(config.getMeasurementInterval());
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/mapper/PerformanceManagementFile2VesMapper.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/mapper/PerformanceManagementFile2VesMapper.java
new file mode 100644 (file)
index 0000000..6036a93
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.mapper;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.dataformat.csv.CsvMapper;
+import com.fasterxml.jackson.dataformat.csv.CsvSchema;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import io.reactivex.rxjava3.core.Single;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import org.o.ran.oam.nf.oam.adopter.api.CommonEventFormat302ONAP;
+import org.o.ran.oam.nf.oam.adopter.api.Event;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.PerformanceManagementMapperConfigProvider;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.CsvConfiguration;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.VesMappingConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class PerformanceManagementFile2VesMapper {
+    private static final Logger LOG = LoggerFactory.getLogger(PerformanceManagementFile2VesMapper.class);
+
+    private static final String CSV_EXTENSION = ".csv";
+    private final PerformanceManagementMapperConfigProvider pmConfigProvider;
+
+    @Autowired
+    public PerformanceManagementFile2VesMapper(final PerformanceManagementMapperConfigProvider pmConfigProvider) {
+        this.pmConfigProvider = pmConfigProvider;
+    }
+
+    /**
+     * Translate CSV in ZipInputStream format to list of CommonEventFormat302ONAP events.
+     *
+     * @param zipInputStream csv
+     * @param hostIp source Ip Address
+     * @return CommonEventFormat302ONAP events
+     */
+    @SuppressFBWarnings("REC_CATCH_EXCEPTION")
+    public Single<List<CommonEventFormat302ONAP>> map(final ZipInputStream zipInputStream, final String hostIp) {
+        LOG.info("Converting ZIP files to VES Message started");
+        final List<CommonEventFormat302ONAP> listOfNotifications = new ArrayList<>();
+        final CsvSchema schema = CsvSchema.emptySchema().withHeader();
+        final CsvMapper mapper = new CsvMapper();
+        mapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false);
+        try {
+            ZipEntry entry;
+            final VesMappingConfiguration mappingConfiguration = pmConfigProvider.getVesMappingConfiguration();
+            while ((entry = zipInputStream.getNextEntry()) != null) {
+                final String entryName = entry.getName();
+                if (!entryName.endsWith(CSV_EXTENSION)) {
+                    return Single.error(new Exception("Wrong file type :" + entryName));
+                }
+
+                final Iterator<Map<String, String>> iterator =
+                        mapper.readerFor(Map.class).with(schema).readValues(zipInputStream);
+                final List<List<Event>> mappedEvents = toEvent(mappingConfiguration, hostIp, iterator);
+
+                mappedEvents.forEach(mapped -> {
+                    final CommonEventFormat302ONAP eventFormat = new CommonEventFormat302ONAP();
+                    eventFormat.setEventList(mapped);
+                    listOfNotifications.add(eventFormat);
+                });
+            }
+        } catch (final Exception e) {
+            return Single.error(new Exception("Failed to process file", e));
+        } finally {
+            try {
+                zipInputStream.closeEntry();
+            } catch (final IOException e) {
+                LOG.warn("Failed to close zip stream", e);
+            }
+        }
+        LOG.info("Converting ZIP files to VES Message finished");
+        return Single.just(listOfNotifications);
+    }
+
+    private static List<List<Event>> toEvent(final VesMappingConfiguration mappingConfiguration, final String hostIp,
+            final Iterator<Map<String, String>> iterator) {
+        final List<List<Event>> globalList = new ArrayList<>();
+        final int batchSize = mappingConfiguration.getBatchSize();
+        int sequence = 0;
+        List<Event> events = new ArrayList<>();
+        final CsvConfiguration csv = mappingConfiguration.getCsv();
+        while (iterator.hasNext()) {
+            final Event event = new Event();
+            final Map<String, String> record = iterator.next();
+            event.setCommonEventHeader(
+                    CommonEventHeaderHandler.toCommonEventHeader(mappingConfiguration, hostIp, csv, record, sequence));
+            event.setMeasurementFields(MeasurementFieldsHandler.toMeasurementFields(mappingConfiguration, record));
+            events.add(event);
+            sequence++;
+            if (sequence % batchSize == 0) {
+                globalList.add(events);
+                events = new ArrayList<>();
+            }
+        }
+        if (!events.isEmpty()) {
+            globalList.add(events);
+        }
+        return globalList;
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/pojos/Adapter.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/pojos/Adapter.java
new file mode 100644 (file)
index 0000000..555634b
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos;
+
+import lombok.Builder;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NonNull;
+
+@EqualsAndHashCode
+@Getter
+@Builder
+public final class Adapter {
+    private @NonNull final String hostIpAddress;
+    private @NonNull final String username;
+    private @NonNull final String password;
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/pojos/CsvConfiguration.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/pojos/CsvConfiguration.java
new file mode 100644 (file)
index 0000000..cdb7617
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class CsvConfiguration {
+    @JsonProperty("additional-fields")
+    private List<String> additionalFields;
+    @JsonProperty("additional-measurements-name")
+    private String additionalMeasurementsName;
+    @JsonProperty("additional-measurements")
+    private List<String> additionalMeasurements;
+    @JsonProperty("event-id")
+    private List<String> eventId;
+    @JsonProperty("source-name")
+    private String sourceName;
+    @JsonProperty("source-name-regex")
+    private String sourceNameRegex;
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/pojos/VesMappingConfiguration.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/pojos/VesMappingConfiguration.java
new file mode 100644 (file)
index 0000000..3d4ea11
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class VesMappingConfiguration {
+    @JsonProperty("reporting-entity-name")
+    private String reportingEntityName;
+    @JsonProperty("reporting-entity-id")
+    private String reportingEntityId;
+    @JsonProperty("nf-vendor-name")
+    private String nfVendorName;
+    @JsonProperty("event-source-type")
+    private String eventSourceType;
+    @JsonProperty("event-name")
+    private String eventName;
+    @JsonProperty("measurement-interval")
+    private Long measurementInterval;
+    @JsonProperty("csv")
+    private CsvConfiguration csv;
+    @JsonProperty("batch-size")
+    private int batchSize;
+    @JsonProperty("priority")
+    private String priority;
+    @JsonProperty("ves-event-listener-version")
+    private String vesEventListenerVersion;
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/properties/PerformanceManagementManagerProperties.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/properties/PerformanceManagementManagerProperties.java
new file mode 100644 (file)
index 0000000..2a3af54
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager.properties;
+
+import javax.validation.constraints.NotEmpty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "pm-rest-manager")
+@Data
+@NoArgsConstructor
+public class PerformanceManagementManagerProperties {
+    @NotEmpty
+    private String synchronizationTimeStart;
+    private int synchronizationTimeFrequency;
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/HttpRestClientMock.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/HttpRestClientMock.java
new file mode 100644 (file)
index 0000000..095eafd
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager;
+
+import io.reactivex.rxjava3.core.Single;
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.time.ZoneId;
+import java.util.zip.ZipInputStream;
+import org.eclipse.jdt.annotation.NonNull;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.api.HttpRestClient;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.Adapter;
+
+public class HttpRestClientMock implements HttpRestClient {
+
+    @Override
+    public Single<ZipInputStream> readFiles(@NonNull final Adapter adapter) {
+        final InputStream file = HttpRestClientMock.class.getResourceAsStream("/zip/nfOamAdapter1.zip");
+        if (file == null) {
+            return Single.error(new Exception("Failed to read test file"));
+        }
+        final BufferedInputStream bis = new BufferedInputStream(file);
+        return Single.just(new ZipInputStream(bis));
+    }
+
+    @Override
+    public @NonNull Single<ZoneId> getTimeZone(@NonNull final Adapter adapter) {
+        return Single.just(ZoneId.of("+02:00"));
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/JsonUtils.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/JsonUtils.java
new file mode 100644 (file)
index 0000000..91bfb47
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import lombok.experimental.UtilityClass;
+import org.apache.commons.io.IOUtils;
+
+@UtilityClass
+final class JsonUtils {
+    private static final List<String> WHITE_LIST = Arrays.asList("eventId", "startEpochMicrosec", "lastEpochMicrosec");
+    private static final String EVENT_LIST = "eventList";
+    private static final String COMMON_EVENT_HEADER = "commonEventHeader";
+
+    static String readJson(final String url) throws IOException {
+        return IOUtils.toString(JsonUtils.class.getResourceAsStream(url), StandardCharsets.UTF_8);
+    }
+
+    public static void compareResult(final String expected, final String actual) {
+        final JsonObject expectedJO = JsonParser.parseString(expected).getAsJsonObject();
+        final JsonObject actualJO = JsonParser.parseString(actual).getAsJsonObject();
+        removeCommonEventHeaderFields(expectedJO.get(EVENT_LIST).getAsJsonArray(),
+                actualJO.get(EVENT_LIST).getAsJsonArray(), WHITE_LIST);
+        assertEquals(expectedJO, actualJO);
+    }
+
+    private static void removeCommonEventHeaderFields(final JsonArray expectedJO, final JsonArray actualJO,
+            final List<String> asList) {
+        asList.forEach(wipe -> {
+            expectedJO.forEach(jsonElement -> removeCommonEventHeaderFields(jsonElement, wipe));
+            actualJO.forEach(jsonElement -> removeCommonEventHeaderFields(jsonElement, wipe));
+        });
+    }
+
+    private static void removeCommonEventHeaderFields(final JsonElement jsonElement, final String wipe) {
+        jsonElement.getAsJsonObject().getAsJsonObject(COMMON_EVENT_HEADER).remove(wipe);
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/PerformanceManagementManagerTest.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/PerformanceManagementManagerTest.java
new file mode 100644 (file)
index 0000000..eb0112e
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager;
+
+import static java.lang.Thread.sleep;
+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.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.api.PerformanceManagementAdaptersDeployer;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.AlreadyPresentException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.NotFoundException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.mapper.PerformanceManagementFile2VesMapper;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.properties.PerformanceManagementManagerProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(classes = {VesEventNotifierMock.class, PerformanceManagementMapperConfigProvider.class,
+    PerformanceManagementFile2VesMapper.class, PerformanceManagementAdaptersDeployer.class})
+public class PerformanceManagementManagerTest {
+
+    @Autowired
+    @Qualifier("test")
+    private VesEventNotifierMock eventListener;
+    @Autowired
+    private PerformanceManagementFile2VesMapper fileMapper;
+    private PerformanceManagementAdaptersDeployer deployer;
+
+    /**
+     * Initialize test.
+     */
+    @BeforeEach
+    public void init() {
+        final HttpRestClientMock httpRestClientMock = new HttpRestClientMock();
+        final PerformanceManagementManagerProperties properties = new PerformanceManagementManagerProperties();
+        final ZonedDateTime now = ZonedDateTime.now(ZoneId.of("+02:00")).plusSeconds(5);
+        final String formattedString = now.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
+        properties.setSynchronizationTimeStart(formattedString);
+        properties.setSynchronizationTimeFrequency(30);
+        deployer = new AdaptersDeployer(
+                new PerformanceManagementRestAgentFactory(eventListener, fileMapper, properties, httpRestClientMock));
+    }
+
+    @Test
+    @Timeout(value = 10000, unit = TimeUnit.MILLISECONDS)
+    public void testMapping() throws IOException, InterruptedException, AlreadyPresentException {
+        assertTrue(deployer.getAll().isEmpty());
+        deployer.create("172.0.10.2", "admin", "admin");
+        assertFalse(deployer.getAll().isEmpty());
+
+        final String expected = JsonUtils.readJson("/json/PMVESMessage.json");
+        final List<String> notifications = getVesNotification(eventListener, 2);
+        final String actual = notifications.get(0);
+        JsonUtils.compareResult(expected, actual);
+    }
+
+    @Test
+    public void testDelete() throws AlreadyPresentException, NotFoundException {
+        assertTrue(deployer.getAll().isEmpty());
+        deployer.create("172.0.10.2", "admin", "admin");
+        assertFalse(deployer.getAll().isEmpty());
+
+        deployer.delete("172.0.10.2");
+        assertTrue(deployer.getAll().isEmpty());
+    }
+
+    @Test
+    public void testAlreadyPresent() throws AlreadyPresentException {
+        assertTrue(deployer.getAll().isEmpty());
+        deployer.create("172.0.10.2", "admin", "admin");
+        assertFalse(deployer.getAll().isEmpty());
+
+        final Exception alreadyPresentException = assertThrows(AlreadyPresentException.class,
+                () -> deployer.create("172.0.10.2", "admin", "admin"));
+        assertEquals(alreadyPresentException.getMessage(), "Adapter 172.0.10.2 already present.");
+    }
+
+    @Test
+    public void testNotPresent() {
+        final Exception exception = assertThrows(NotFoundException.class, () -> deployer.delete("172.0.10.2"));
+        assertEquals(exception.getMessage(), "Adapter 172.0.10.2 is not present.");
+    }
+
+    @Test
+    public void testTimeZone() throws AlreadyPresentException {
+        deployer.create("172.0.10.2", "admin", "admin");
+        assertEquals(deployer.getTimeZone("172.0.10.2"), ZoneId.of("+02:00"));
+    }
+
+    private static List<String> getVesNotification(final VesEventNotifierMock listener, final int expectedSize)
+            throws InterruptedException {
+        List<String> events = null;
+        for (int i = 0; i < 100000; i++) {
+            sleep(1000);
+            events = listener.getEvents();
+            if (events != null && !events.isEmpty() && events.size() == expectedSize) {
+                break;
+            }
+        }
+        return events;
+    }
+
+    @AfterEach
+    public final void after() {
+        ((AdaptersDeployer) deployer).close();
+        assertTrue(deployer.getAll().isEmpty());
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/VesEventNotifierMock.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/rest/manager/VesEventNotifierMock.java
new file mode 100644 (file)
index 0000000..ff79438
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.rest.manager;
+
+import com.google.gson.Gson;
+import io.reactivex.rxjava3.core.Completable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.o.ran.oam.nf.oam.adopter.api.CommonEventFormat302ONAP;
+import org.o.ran.oam.nf.oam.adopter.api.VesEventNotifier;
+import org.springframework.stereotype.Service;
+
+@Service("test")
+final class VesEventNotifierMock implements VesEventNotifier {
+
+    private static final Gson GSON = new Gson();
+    private final List<CommonEventFormat302ONAP> event = new ArrayList<>();
+
+    @Override
+    public synchronized Completable notifyEvents(final CommonEventFormat302ONAP event) {
+        this.event.add(event);
+        return Completable.complete();
+    }
+
+    protected synchronized List<String> getEvents() {
+        return event.stream().map(e -> GSON.toJson(e, CommonEventFormat302ONAP.class)).collect(Collectors.toList());
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/resources/application.yaml b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/resources/application.yaml
new file mode 100644 (file)
index 0000000..ea580db
--- /dev/null
@@ -0,0 +1,4 @@
+pm-rest-manager:
+  synchronization-time-start: "00:05:00"
+  synchronization-time-frequency: 60
+  mapping-config-path: "src/test/resources/pm-ves-message-mapping.yaml"
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/resources/json/PMVESMessage.json b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/resources/json/PMVESMessage.json
new file mode 100644 (file)
index 0000000..c9c2b6b
--- /dev/null
@@ -0,0 +1,160 @@
+{
+  "eventList":[
+    {
+      "commonEventHeader":{
+        "domain":"measurement",
+        "eventId":"ba10db33-9e27-3296-9bfd-a8d232bbe0a8",
+        "eventName":"FAULT_NF-OAM-ADOPTER_PM_Notification",
+        "lastEpochMicrosec":1618483581744,
+        "nfVendorName":"SOME-VENDOR",
+        "priority":"High",
+        "reportingEntityId":"ONAP-NF-OAM-SOME-VENDOR-ADAPTER",
+        "reportingEntityName":"NF-OAM-ADOPTER",
+        "sequence":0,
+        "sourceName":"OAM-BOX",
+        "startEpochMicrosec":1618483581744,
+        "version":"4.0",
+        "vesEventListenerVersion":"7.1"
+      },
+      "measurementFields":{
+        "additionalFields":{
+          "PortId":"2",
+          "Time":"12:02:12 AM",
+          "Date":"15/4/2021",
+          "Name":"OAM-BOX"
+        },
+        "additionalMeasurements":[
+          {
+            "name":"Port measurements",
+            "hashMap":{
+              "C_Parameter":"y26",
+              "A_Parameter":"1100",
+              "E_Parameter":"+10.2",
+              "B_Parameter":"0.5",
+              "D_Parameter":"x25"
+            }
+          }
+        ],
+        "measurementInterval":40,
+        "measurementFieldsVersion":"4.0"
+      }
+    },
+    {
+      "commonEventHeader":{
+        "domain":"measurement",
+        "eventId":"f75fd549-18dc-389c-90b6-f975f3ff2a70",
+        "eventName":"FAULT_NF-OAM-ADOPTER_PM_Notification",
+        "lastEpochMicrosec":1618483581763,
+        "nfVendorName":"SOME-VENDOR",
+        "priority":"High",
+        "reportingEntityId":"ONAP-NF-OAM-SOME-VENDOR-ADAPTER",
+        "reportingEntityName":"NF-OAM-ADOPTER",
+        "sequence":1,
+        "sourceName":"OAM-BOX",
+        "startEpochMicrosec":1618483581763,
+        "version":"4.0",
+        "vesEventListenerVersion":"7.1"
+      },
+      "measurementFields":{
+        "additionalFields":{
+          "PortId":"2",
+          "Time":"12:02:52 AM",
+          "Date":"15/4/2021",
+          "Name":"OAM-BOX"
+        },
+        "additionalMeasurements":[
+          {
+            "name":"Port measurements",
+            "hashMap":{
+              "C_Parameter":"y26",
+              "A_Parameter":"2100",
+              "E_Parameter":"+10.01",
+              "B_Parameter":"0.5",
+              "D_Parameter":"x25"
+            }
+          }
+        ],
+        "measurementInterval":40,
+        "measurementFieldsVersion":"4.0"
+      }
+    },
+    {
+      "commonEventHeader":{
+        "domain":"measurement",
+        "eventId":"da259c3e-b66d-311d-9fb9-946b241f2fed",
+        "eventName":"FAULT_NF-OAM-ADOPTER_PM_Notification",
+        "lastEpochMicrosec":1618483581763,
+        "nfVendorName":"SOME-VENDOR",
+        "priority":"High",
+        "reportingEntityId":"ONAP-NF-OAM-SOME-VENDOR-ADAPTER",
+        "reportingEntityName":"NF-OAM-ADOPTER",
+        "sequence":2,
+        "sourceName":"OAM-BOX",
+        "startEpochMicrosec":1618483581763,
+        "version":"4.0",
+        "vesEventListenerVersion":"7.1"
+      },
+      "measurementFields":{
+        "additionalFields":{
+          "PortId":"2",
+          "Time":"12:03:32 AM",
+          "Date":"15/4/2021",
+          "Name":"OAM-BOX"
+        },
+        "additionalMeasurements":[
+          {
+            "name":"Port measurements",
+            "hashMap":{
+              "C_Parameter":"y26",
+              "A_Parameter":"3100",
+              "E_Parameter":"+10.02",
+              "B_Parameter":"0.5",
+              "D_Parameter":"x25"
+            }
+          }
+        ],
+        "measurementInterval":40,
+        "measurementFieldsVersion":"4.0"
+      }
+    },
+    {
+      "commonEventHeader":{
+        "domain":"measurement",
+        "eventId":"e0bedb4a-4ded-3480-92c4-9c8cec77b28e",
+        "eventName":"FAULT_NF-OAM-ADOPTER_PM_Notification",
+        "lastEpochMicrosec":1618483581764,
+        "nfVendorName":"SOME-VENDOR",
+        "priority":"High",
+        "reportingEntityId":"ONAP-NF-OAM-SOME-VENDOR-ADAPTER",
+        "reportingEntityName":"NF-OAM-ADOPTER",
+        "sequence":3,
+        "sourceName":"OAM-BOX",
+        "startEpochMicrosec":1618483581764,
+        "version":"4.0",
+        "vesEventListenerVersion":"7.1"
+      },
+      "measurementFields":{
+        "additionalFields":{
+          "PortId":"2",
+          "Time":"12:04:12 AM",
+          "Date":"15/4/2021",
+          "Name":"OAM-BOX"
+        },
+        "additionalMeasurements":[
+          {
+            "name":"Port measurements",
+            "hashMap":{
+              "C_Parameter":"y26",
+              "A_Parameter":"4100",
+              "E_Parameter":"+10.03",
+              "B_Parameter":"0.5",
+              "D_Parameter":"x25"
+            }
+          }
+        ],
+        "measurementInterval":40,
+        "measurementFieldsVersion":"4.0"
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/resources/pm-ves-message-mapping.yaml b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/resources/pm-ves-message-mapping.yaml
new file mode 100644 (file)
index 0000000..aaeac4e
--- /dev/null
@@ -0,0 +1,26 @@
+reporting-entity-name: "NF-OAM-ADOPTER"
+reporting-entity-id: "ONAP-NF-OAM-SOME-VENDOR-ADAPTER"
+nf-vendor-name: "SOME-VENDOR"
+event-source-type: "SNMP Agent"
+event-name: "PM_Notification"
+measurement-interval: 40
+priority: "High"
+batch-size: 5
+csv:
+  source-name: Name
+  event-id:
+    - PortId
+    - Date
+    - Time
+  additional-fields:
+    - PortId
+    - Name
+    - Date
+    - Time
+  additional-measurements-name: "Port measurements"
+  additional-measurements:
+    - A_Parameter
+    - B_Parameter
+    - C_Parameter
+    - D_Parameter
+    - E_Parameter
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/resources/zip/nfOamAdapter1.zip b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/resources/zip/nfOamAdapter1.zip
new file mode 100644 (file)
index 0000000..6ea3cb8
Binary files /dev/null and b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-manager/src/test/resources/zip/nfOamAdapter1.zip differ
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/pom.xml b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/pom.xml
new file mode 100644 (file)
index 0000000..3dcd893
--- /dev/null
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* ============LICENSE_START=======================================================
+* O-RAN-SC
+* ================================================================================
+* Copyright (C) 2021 AT&T 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============================================
+*
+-->
+<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.o-ran-sc.oam</groupId>
+        <artifactId>ves-nf-oam-adopter-parent</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+        <relativePath>../ves-nf-oam-adopter-parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ves-nf-oam-adopter-pm-sb-rest-client</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ves-nf-oam-adopter-pm-manager</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents.client5</groupId>
+            <artifactId>httpclient5</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.reactivex.rxjava3</groupId>
+            <artifactId>rxjava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/DefaultHttpRestClient.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/DefaultHttpRestClient.java
new file mode 100644 (file)
index 0000000..b1d6008
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.sb.rest.client;
+
+import static org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.http.DownloadPerformanceManagementFilesHandler.readPerformanceManagementFiles;
+import static org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.http.OffSetTimeZoneHandler.readTimeZone;
+import static org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.http.TokenHandler.returnToken;
+import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import io.reactivex.rxjava3.core.Single;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.zip.ZipInputStream;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.core5.http.ConnectionClosedException;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.checkerframework.checker.lock.qual.GuardedBy;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.api.HttpRestClient;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.PerformanceManagementException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.TokenGenerationException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.ZoneIdException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.Adapter;
+import org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.properties.PmEndpointsUrlsProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public final class DefaultHttpRestClient implements HttpRestClient {
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultHttpRestClient.class);
+
+    public static final String HTTPS = "https://";
+    public static final String BEARER = "Bearer ";
+    private static final DateTimeFormatter OFFSET_FORMATTER = DateTimeFormatter.ofPattern("xxx");
+    private final CloseableHttpAsyncClient client;
+    @GuardedBy("this")
+    private final LoadingCache<Adapter, String> sessionCache =
+            CacheBuilder.newBuilder().refreshAfterWrite(59, TimeUnit.MINUTES).build(new CacheLoader<>() {
+                @Override
+                public String load(final Adapter adapter) throws ExecutionException, InterruptedException {
+                    try {
+                        return returnToken(DefaultHttpRestClient.this.client, DefaultHttpRestClient.this.tokenEndpoint,
+                                adapter);
+                    } catch (final Exception error) {
+                        LOG.error("Failed to read time zone", error);
+                        throw error;
+                    }
+                }
+            });
+
+    @GuardedBy("this")
+    private final LoadingCache<Adapter, ZoneId> zoneIdCache =
+            CacheBuilder.newBuilder().build(new CacheLoader<>() {
+                @Override
+                public ZoneId load(final Adapter adapter) {
+                    return readTimeZone(DefaultHttpRestClient.this, timeZoneEndpoint,  adapter)
+                                   .doOnError(error -> LOG.error("Failed to read time zone", error))
+                                   .blockingGet();
+                }
+            });
+    private final String pmFilesEndpoint;
+    private final String timeZoneEndpoint;
+    private final String tokenEndpoint;
+
+    /**
+     * Default constructor.
+     */
+    @Autowired
+    public DefaultHttpRestClient(final CloseableHttpAsyncClient httpAsyncClient,
+            final PmEndpointsUrlsProperties properties) {
+        this.client = httpAsyncClient;
+        this.pmFilesEndpoint = properties.getRanPmEndpoint();
+        this.timeZoneEndpoint = properties.getRanTimeZoneOffsetEndpoint();
+        this.tokenEndpoint = properties.getRanTokenEndpoint();
+    }
+
+
+    @Override
+    public synchronized Single<ZipInputStream> readFiles(final Adapter adapter) {
+        return readPerformanceManagementFiles(this, pmFilesEndpoint, adapter);
+    }
+
+    @Override
+    public Single<ZoneId> getTimeZone(final Adapter adapter) {
+        try {
+            final ZoneId zoneId = zoneIdCache.get(adapter);
+            LOG.info("Adapter {} has offset {}", adapter.getHostIpAddress(),
+                    OFFSET_FORMATTER.format(zoneId.getRules().getOffset(Instant.now())));
+            return Single.just(zoneId);
+        } catch (final Exception e) {
+            final Throwable cause = e.getCause();
+            if (cause instanceof PerformanceManagementException) {
+                return Single.error(cause);
+            }
+            return Single.error(new ZoneIdException("Failed to get Zone ID for " + adapter.getHostIpAddress(), cause));
+        }
+    }
+
+    /**
+     * Execute GET request on adapter endpoint.
+     * @param adapter destiny
+     * @param url endpoint
+     * @return response
+     */
+    public Single<SimpleHttpResponse> get(final Adapter adapter, final String url) {
+        return getToken(adapter).flatMap(token -> {
+            final SimpleHttpRequest request =
+                SimpleHttpRequests.get(HTTPS + adapter.getHostIpAddress() + url);
+            request.addHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON_VALUE);
+            request.addHeader(HttpHeaders.AUTHORIZATION, BEARER + token);
+            return Single.fromFuture(client.execute(request, null))
+                    .doOnSubscribe(result -> LOG.trace("GET Request started {} ...", request.toString()))
+                    .doOnSuccess(result -> LOG.trace("GET Request finished {}", request));
+        });
+    }
+
+    private Single<String> getToken(final Adapter adapter) {
+        try {
+            final String token = sessionCache.get(adapter);
+            return Single.just(token);
+        } catch (final Exception e) {
+            if (e.getCause() instanceof TokenGenerationException) {
+                return Single.error(e.getCause());
+            } else if (e.getCause() instanceof ConnectionClosedException) {
+                return Single.error(e.getCause());
+            }
+            return Single.error(e);
+        }
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/http/DownloadPerformanceManagementFilesHandler.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/http/DownloadPerformanceManagementFilesHandler.java
new file mode 100644 (file)
index 0000000..77957cb
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.http;
+
+import io.reactivex.rxjava3.core.Single;
+import java.io.ByteArrayInputStream;
+import java.util.zip.ZipInputStream;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.apache.hc.client5.http.async.methods.SimpleBody;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.message.StatusLine;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.PerformanceManagementException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.Adapter;
+import org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.DefaultHttpRestClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class DownloadPerformanceManagementFilesHandler {
+
+    private static final Logger LOG = LoggerFactory.getLogger(DownloadPerformanceManagementFilesHandler.class);
+
+    /**
+     * Reads and return ZIP CSV PM Files from device.
+     */
+    public static Single<ZipInputStream> readPerformanceManagementFiles(final DefaultHttpRestClient httpSession,
+            final String pmFilesEndpoint, final Adapter adapter) {
+        LOG.info("Download PM files from RAN {}", adapter.getHostIpAddress());
+        return httpSession.get(adapter, pmFilesEndpoint)
+                       .flatMap(response -> DownloadPerformanceManagementFilesHandler
+                                                    .validateGetZipFile(adapter, response))
+                       .map(entity -> new ZipInputStream(new ByteArrayInputStream(entity.getBodyBytes())));
+    }
+
+    private static Single<SimpleBody> validateGetZipFile(final Adapter adapter, final SimpleHttpResponse response) {
+        final String statusLine = new StatusLine(response).toString();
+        final ContentType contentType = response.getContentType();
+        final SimpleBody entity = response.getBody();
+        if (response.getCode() == HttpStatus.SC_OK && entity != null) {
+            if (ContentType.APPLICATION_OCTET_STREAM.getMimeType().equals(contentType.getMimeType())) {
+                return Single.just(entity);
+            }
+        }
+        return Single.error(new PerformanceManagementException(
+                "Download files from " + adapter.getHostIpAddress() + " failed: " + statusLine));
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/http/OffSetTimeZoneHandler.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/http/OffSetTimeZoneHandler.java
new file mode 100644 (file)
index 0000000..2bf8aeb
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.http;
+
+import com.google.gson.Gson;
+import io.reactivex.rxjava3.core.Single;
+import java.time.ZoneId;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.core5.http.HttpStatus;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.PerformanceManagementEmptyOutputException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.PerformanceManagementException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.Adapter;
+import org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.DefaultHttpRestClient;
+import org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.pojos.TimeZoneResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class OffSetTimeZoneHandler {
+
+    private static final Logger LOG = LoggerFactory.getLogger(OffSetTimeZoneHandler.class);
+
+    private static final Gson GSON = new Gson();
+
+    /**
+     * Returns time zone of the device.
+     */
+    public static Single<ZoneId> readTimeZone(final DefaultHttpRestClient httpRestClient, final String offsetEndpoint,
+            final Adapter adapter) {
+        LOG.debug("Read Time Zone from adapter {}", adapter.getHostIpAddress());
+        return httpRestClient.get(adapter, offsetEndpoint)
+                       .map(response -> validateGet(response, adapter))
+                       .map(ZoneId::of);
+    }
+
+    private static String validateGet(final SimpleHttpResponse response, final Adapter adapter) {
+        if (response.getCode() != HttpStatus.SC_OK) {
+            throw new PerformanceManagementException(
+                    "Get Zone offset failed for " + adapter.getHostIpAddress() + " Code: " + response.getCode());
+        }
+        final String output = response.getBody().getBodyText();
+        if (output.isEmpty()) {
+            throw new PerformanceManagementEmptyOutputException(
+                    "Get Zone offset failed for " + adapter.getHostIpAddress() + " . Empty output received");
+        }
+        return GSON.fromJson(output, TimeZoneResponse.class).getOffset();
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/http/TokenHandler.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/http/TokenHandler.java
new file mode 100644 (file)
index 0000000..26c62b5
--- /dev/null
@@ -0,0 +1,53 @@
+package org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.http;
+
+import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
+
+import com.google.gson.Gson;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.concurrent.ExecutionException;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.message.StatusLine;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.exceptions.TokenGenerationException;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.Adapter;
+import org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.pojos.TokenResponse;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class TokenHandler {
+
+    public static final Gson GSON = new Gson();
+    public static final String HTTPS = "https://";
+
+    /**
+     * Request Token under defined endpoint.
+     */
+    public static synchronized String returnToken(final CloseableHttpAsyncClient client, final String tokenEndpoint,
+            final Adapter adapter) throws ExecutionException, InterruptedException {
+        final String host = HTTPS + adapter.getHostIpAddress();
+        final SimpleHttpRequest request = SimpleHttpRequests.post(host + tokenEndpoint);
+        final String basicAuth = Base64.getEncoder().encodeToString(
+                (adapter.getUsername() + ":" + adapter.getPassword()).getBytes(StandardCharsets.UTF_8));
+        request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth);
+        request.addHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON_VALUE);
+        request.addHeader(HttpHeaders.ACCEPT, APPLICATION_JSON_VALUE);
+
+        final SimpleHttpResponse response = client.execute(request, null).get();
+        final String statusLine = new StatusLine(response).toString();
+        if (response.getCode() != HttpStatus.SC_OK) {
+            throw new TokenGenerationException("Failed to obtain a token for host " + host + ": " + statusLine);
+        }
+        final String output = response.getBody().getBodyText();
+        if (output.isEmpty()) {
+            throw new TokenGenerationException(
+                    "Failed to obtain a token for host " + host + ", empty output: " + statusLine);
+        }
+        return GSON.fromJson(output, TokenResponse.class).getToken();
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/pojos/TimeZoneResponse.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/pojos/TimeZoneResponse.java
new file mode 100644 (file)
index 0000000..853951d
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.pojos;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+
+@RequiredArgsConstructor
+@Getter
+@Setter
+public class TimeZoneResponse {
+    @SerializedName(value = "offset")
+    private @NonNull final String offset;
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/pojos/TokenResponse.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/pojos/TokenResponse.java
new file mode 100644 (file)
index 0000000..195922f
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  O-RAN-SC
+ *  ================================================================================
+ *  Copyright © 2021 AT&T 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.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.pojos;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class TokenResponse {
+    private String token;
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/properties/PmEndpointsUrlsProperties.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/main/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/properties/PmEndpointsUrlsProperties.java
new file mode 100644 (file)
index 0000000..65a6826
--- /dev/null
@@ -0,0 +1,23 @@
+package org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.properties;
+
+import javax.validation.constraints.NotEmpty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.annotation.Validated;
+
+@Component
+@ConfigurationProperties(prefix = "pm-rest-manager")
+@Data
+@NoArgsConstructor
+@Validated
+public class PmEndpointsUrlsProperties {
+
+    @NotEmpty
+    private String ranTokenEndpoint;
+    @NotEmpty
+    private String ranPmEndpoint;
+    @NotEmpty
+    private String ranTimeZoneOffsetEndpoint;
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/DefaultHttpRestClientTest.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/DefaultHttpRestClientTest.java
new file mode 100644 (file)
index 0000000..f9c115a
--- /dev/null
@@ -0,0 +1,180 @@
+package org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.when;
+
+import io.reactivex.rxjava3.observers.TestObserver;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.time.ZoneId;
+import java.util.Arrays;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+import java.util.zip.ZipInputStream;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpStatus;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.mockito.stubbing.Answer;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.api.HttpRestClient;
+import org.o.ran.oam.nf.oam.adopter.pm.rest.manager.pojos.Adapter;
+import org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client.properties.PmEndpointsUrlsProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.MethodMode;
+
+@SpringBootTest(classes = {DefaultHttpRestClient.class, PmEndpointsUrlsProperties.class})
+@EnableConfigurationProperties
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class DefaultHttpRestClientTest {
+
+    private static final Adapter ADAPTER =
+            Adapter.builder().hostIpAddress("150.62.25.26").username("admin").password("secretPassword").build();
+    @Autowired
+    public HttpRestClient restClient;
+
+    @MockBean
+    CloseableHttpAsyncClient client;
+
+    @Test
+    @DirtiesContext(methodMode = MethodMode.BEFORE_METHOD)
+    public void testGetFailedToken() {
+        final SimpleHttpResponse response =
+                SimpleHttpResponse.create(HttpStatus.SC_UNAUTHORIZED, "error", ContentType.APPLICATION_JSON);
+
+        when(client.execute(any(SimpleHttpRequest.class), nullable(FutureCallback.class)))
+                .thenAnswer((Answer<Future<SimpleHttpResponse>>) invocation -> {
+                    final SimpleHttpRequest req = (SimpleHttpRequest) invocation.getArguments()[0];
+                    if ("/auth/token".equals(req.getPath())) {
+                        return CompletableFuture.completedFuture(response);
+                    }
+                    throw new IllegalStateException("Unexpected value: " + req.getPath());
+                });
+
+
+        final TestObserver<ZoneId> observer = restClient.getTimeZone(ADAPTER).test();
+        observer.assertError(throwable -> throwable.getMessage()
+            .equals("Failed to obtain a token for host https://150.62.25.26: HTTP/1.1 401 Unauthorized"));
+    }
+
+    @Test
+    public void testReadFiles() throws IOException {
+        final String tokenJson = JsonUtils.readJson("/json/tokenResponse.json");
+        final SimpleHttpResponse response =
+                SimpleHttpResponse.create(HttpStatus.SC_OK, tokenJson, ContentType.APPLICATION_JSON);
+
+        final SimpleHttpResponse zipResponse =
+                SimpleHttpResponse.create(HttpStatus.SC_OK, "mockZip", ContentType.APPLICATION_OCTET_STREAM);
+
+        when(client.execute(any(SimpleHttpRequest.class), nullable(FutureCallback.class)))
+                .thenAnswer((Answer<Future<SimpleHttpResponse>>) invocation -> {
+                    final SimpleHttpRequest req = (SimpleHttpRequest) invocation.getArguments()[0];
+                    switch (req.getPath()) {
+                        case "/auth/token":
+                            return CompletableFuture.completedFuture(response);
+                        case "/pm/files":
+                            return CompletableFuture.completedFuture(zipResponse);
+                        default:
+                            throw new IllegalStateException("Unexpected value: " + req.getPath());
+                    }
+                });
+
+
+        final TestObserver<ZipInputStream> observer = restClient.readFiles(ADAPTER).test();
+        final ZipInputStream expected = new ZipInputStream(new ByteArrayInputStream("mockZip".getBytes()));
+        observer.assertValue(zip -> Arrays.equals(zip.readAllBytes(), expected.readAllBytes()));
+    }
+
+    @Test
+    public void testReadFilesResponseFail() throws IOException {
+        final String tokenJson = JsonUtils.readJson("/json/tokenResponse.json");
+        final SimpleHttpResponse response =
+                SimpleHttpResponse.create(HttpStatus.SC_OK, tokenJson, ContentType.APPLICATION_JSON);
+
+        final SimpleHttpResponse zipResponse = SimpleHttpResponse.create(HttpStatus.SC_BAD_REQUEST, "mockZip",
+                ContentType.APPLICATION_OCTET_STREAM);
+
+        when(client.execute(any(SimpleHttpRequest.class), nullable(FutureCallback.class)))
+                .thenAnswer((Answer<Future<SimpleHttpResponse>>) invocation -> {
+                    final SimpleHttpRequest req = (SimpleHttpRequest) invocation.getArguments()[0];
+                    switch (req.getPath()) {
+                        case "/auth/token":
+                            return CompletableFuture.completedFuture(response);
+                        case "/pm/files":
+                            return CompletableFuture.completedFuture(zipResponse);
+                        default:
+                            throw new IllegalStateException("Unexpected value: " + req.getPath());
+                    }
+                });
+
+
+        final TestObserver<ZipInputStream> observer = restClient.readFiles(ADAPTER).test();
+        observer.assertError(throwable -> throwable.getMessage()
+            .equals("Download files from 150.62.25.26 failed: HTTP/1.1 400 Bad Request"));
+    }
+
+    @Test
+    public void testGetTimeOffset() throws IOException {
+        final String tokenJson = JsonUtils.readJson("/json/tokenResponse.json");
+        final SimpleHttpResponse response =
+                SimpleHttpResponse.create(HttpStatus.SC_OK, tokenJson, ContentType.APPLICATION_JSON);
+
+        final String timeZoneOFfsetResponseJson = JsonUtils.readJson("/json/timeZoneOffsetResponse.json");
+        final SimpleHttpResponse timeOffsetResponse =
+                SimpleHttpResponse.create(HttpStatus.SC_OK, timeZoneOFfsetResponseJson, ContentType.APPLICATION_JSON);
+
+        when(client.execute(any(SimpleHttpRequest.class), nullable(FutureCallback.class)))
+                .thenAnswer((Answer<Future<SimpleHttpResponse>>) invocation -> {
+                    final SimpleHttpRequest req = (SimpleHttpRequest) invocation.getArguments()[0];
+                    switch (req.getPath()) {
+                        case "/auth/token":
+                            return CompletableFuture.completedFuture(response);
+                        case "/system/timeZone":
+                            return CompletableFuture.completedFuture(timeOffsetResponse);
+                        default:
+                            throw new IllegalStateException("Unexpected value: " + req.getPath());
+                    }
+                });
+
+
+        final TestObserver<ZoneId> observer = restClient.getTimeZone(ADAPTER).test();
+        observer.assertValues(ZoneId.of("+02:00"));
+    }
+
+    @Test
+    public void testGetTimeOffsetFail() throws IOException {
+        final String tokenJson = JsonUtils.readJson("/json/tokenResponse.json");
+        final SimpleHttpResponse response =
+                SimpleHttpResponse.create(HttpStatus.SC_OK, tokenJson, ContentType.APPLICATION_JSON);
+
+        final SimpleHttpResponse timeOffsetResponse =
+                SimpleHttpResponse.create(HttpStatus.SC_OK, "", ContentType.APPLICATION_JSON);
+
+        when(client.execute(any(SimpleHttpRequest.class), nullable(FutureCallback.class)))
+                .thenAnswer((Answer<Future<SimpleHttpResponse>>) invocation -> {
+                    final SimpleHttpRequest req = (SimpleHttpRequest) invocation.getArguments()[0];
+                    switch (req.getPath()) {
+                        case "/auth/token":
+                            return CompletableFuture.completedFuture(response);
+                        case "/system/timeZone":
+                            return CompletableFuture.completedFuture(timeOffsetResponse);
+                        default:
+                            throw new IllegalStateException("Unexpected value: " + req.getPath());
+                    }
+                });
+
+
+        final TestObserver<ZoneId> observer = restClient.getTimeZone(ADAPTER).test();
+        observer.assertError(throwable -> throwable.getMessage()
+            .equals("Get Zone offset failed for 150.62.25.26 . Empty output received"));
+    }
+}
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/JsonUtils.java b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/java/org/o/ran/oam/nf/oam/adopter/pm/sb/rest/client/JsonUtils.java
new file mode 100644 (file)
index 0000000..60bf0ef
--- /dev/null
@@ -0,0 +1,13 @@
+package org.o.ran.oam.nf.oam.adopter.pm.sb.rest.client;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import lombok.experimental.UtilityClass;
+import org.apache.commons.io.IOUtils;
+
+@UtilityClass
+final class JsonUtils {
+    static String readJson(final String url) throws IOException {
+        return IOUtils.toString(JsonUtils.class.getResourceAsStream(url), StandardCharsets.UTF_8);
+    }
+}
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/resources/application.yaml b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/resources/application.yaml
new file mode 100644 (file)
index 0000000..f81c8ff
--- /dev/null
@@ -0,0 +1,4 @@
+pm-rest-manager:
+  ran-token-endpoint: /auth/token
+  ran-pm-endpoint: /pm/files
+  ran-time-zone-offset-endpoint: /system/timeZone
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/resources/json/timeZoneOffsetResponse.json b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/resources/json/timeZoneOffsetResponse.json
new file mode 100644 (file)
index 0000000..c80769c
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "offset": "+02:00"
+}
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/resources/json/tokenResponse.json b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/resources/json/tokenResponse.json
new file mode 100644 (file)
index 0000000..2269aa0
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "token": "someRandomToken"
+}
\ No newline at end of file
diff --git a/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/ves-nf-oam-adopter/ves-nf-oam-adopter-pm-sb-rest-client/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644 (file)
index 0000000..ca6ee9c
--- /dev/null
@@ -0,0 +1 @@
+mock-maker-inline
\ No newline at end of file