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
<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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+-----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-----
--- /dev/null
+-----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-----
--- /dev/null
+# ============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
--- /dev/null
+<?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
--- /dev/null
+/*
+ * ============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
--- /dev/null
+/*
+ * ============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);
+ }
+}
--- /dev/null
+/*
+ * ============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();
+ }
+}
--- /dev/null
+/*
+ * ============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);
+ }
+}
--- /dev/null
+/*
+ * ============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();
+ }
+}
--- /dev/null
+/*
+ * ============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;
+ }
+}
--- /dev/null
+/*
+ * ============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);
+ }
+}
--- /dev/null
+/*
+ * ============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();
+ }
+}
--- /dev/null
+/*
+ * ============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;
+}
--- /dev/null
+/*
+ * ============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;
+}
--- /dev/null
+/*
+ * ============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
--- /dev/null
+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
<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
spring.devtools.restart.log-condition-evaluation-delta: false
scheduler:
- fixedDelay: 8000
- initialDelay: 4000
+ fixedDelay: 16000
+ initialDelay: 8000
security:
auth:
username: admin
'401':
$ref: '#/components/responses/401Error'
/pm/files:
- post:
+ get:
tags:
- controller
summary: Read Performance Management Files
'401':
$ref: '#/components/responses/401Error'
/system/timeZone:
- post:
+ get:
tags:
- controller
summary: Read time zone
<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>
../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-->
<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>
<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>
--- /dev/null
+<?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
--- /dev/null
+/*
+ * ============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();
+ }
+}
--- /dev/null
+/*
+ * ============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);
+ }
+}
--- /dev/null
+/*
+ * ============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();
+ }
+}
--- /dev/null
+/*
+ * ============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();
+ }
+ }
+}
--- /dev/null
+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);
+}
--- /dev/null
+/*
+ * ============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);
+}
--- /dev/null
+/*
+ * ============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);
+ }
+}
--- /dev/null
+/*
+ * ============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.");
+ }
+}
--- /dev/null
+/*
+ * ============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.");
+ }
+}
--- /dev/null
+/*
+ * ============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);
+ }
+}
--- /dev/null
+/*
+ * ============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);
+ }
+}
--- /dev/null
+/*
+ * ============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);
+ }
+
+}
--- /dev/null
+/*
+ * ============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);
+ }
+
+}
--- /dev/null
+/*
+ * ============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());
+ }
+}
--- /dev/null
+/*
+ * ============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());
+ }
+}
--- /dev/null
+/*
+ * ============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;
+ }
+}
--- /dev/null
+/*
+ * ============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;
+}
--- /dev/null
+/*
+ * ============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;
+}
--- /dev/null
+/*
+ * ============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;
+}
--- /dev/null
+/*
+ * ============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;
+}
--- /dev/null
+/*
+ * ============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"));
+ }
+}
--- /dev/null
+/*
+ * ============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);
+ }
+}
--- /dev/null
+/*
+ * ============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());
+ }
+}
--- /dev/null
+/*
+ * ============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());
+ }
+}
--- /dev/null
+pm-rest-manager:
+ synchronization-time-start: "00:05:00"
+ synchronization-time-frequency: 60
+ mapping-config-path: "src/test/resources/pm-ves-message-mapping.yaml"
--- /dev/null
+{
+ "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
--- /dev/null
+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
--- /dev/null
+<?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
--- /dev/null
+/*
+ * ============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);
+ }
+ }
+}
--- /dev/null
+/*
+ * ============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));
+ }
+}
--- /dev/null
+/*
+ * ============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();
+ }
+}
--- /dev/null
+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();
+ }
+}
--- /dev/null
+/*
+ * ============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;
+}
--- /dev/null
+/*
+ * ============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;
+}
--- /dev/null
+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;
+}
--- /dev/null
+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
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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
--- /dev/null
+{
+ "offset": "+02:00"
+}
\ No newline at end of file
--- /dev/null
+{
+ "token": "someRandomToken"
+}
\ No newline at end of file
--- /dev/null
+mock-maker-inline
\ No newline at end of file