<properties>
<!-- Code coverate & Sonar -->
<minimum.coverage>0.9</minimum.coverage>
- <jacoco.reportDirectory.aggregate>${project.reporting.outputDirectory}/jacoco-aggregate</jacoco.reportDirectory.aggregate>
+ <jacoco.reportDirectory.aggregate>
+ ${project.reporting.outputDirectory}/jacoco-aggregate
+ </jacoco.reportDirectory.aggregate>
<sonar.coverage.jacoco.xmlReportPaths>
../ves-nf-oam-adopter-event-notifier/target/site/jacoco-ut/jacoco.xml,
../ves-nf-oam-adopter-event-notifier/target/site/jacoco-aggregate/jacoco.xml
<gson.version>2.8.6</gson.version>
<httpclient5.version>5.0.3</httpclient5.version>
<jdt.annotation.version>2.2.600</jdt.annotation.version>
+ <junit.jupiter.version>5.7.1</junit.jupiter.version>
<lombok.version>1.18.20</lombok.version>
<rxjava.version>3.0.12</rxjava.version>
<spotbugs-maven-plugin.version>4.2.3</spotbugs-maven-plugin.version>
</exclusion>
</exclusions>
</dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <version>${spring.boot.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-logging</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <version>${junit.jupiter.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <version>${junit.jupiter.version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-validation</artifactId>
+ </dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</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
final UdpAddress address = (UdpAddress) cmdRespEvent.getPeerAddress();
final ZoneId optZoneId = timeZoneOffsetService.getTimeZone(address.getInetAddress().getHostAddress());
final String timeZone = Optional.ofNullable(optZoneId)
- .map(zoneId -> "UTC" + LocalDateTime.now().atZone(zoneId).getOffset()
- .toString()).orElse(null);
+ .map(zoneId -> "UTC" + LocalDateTime.now().atZone(zoneId).getOffset().toString()).orElse(null);
mapper.toEvent(address, timeZone, pdu)
.flatMapCompletable(vesEventNotifier::notifyEvents)
.doOnSubscribe(result -> LOG.debug("SNMP Trap processing started"))
.doOnComplete(() -> LOG.debug("SNMP Trap processed successfully"))
- .doOnError(error -> LOG.error("Failed to process SNMP Trap", error)).subscribe();
+ .doOnError(error -> LOG.error("Failed to process SNMP Trap", error))
+ .subscribe();
}
}
package org.o.ran.oam.nf.oam.adopter.snmp.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;
+import org.springframework.validation.annotation.Validated;
@Component
@ConfigurationProperties(prefix = "snmp-manager")
@Data
@NoArgsConstructor
+@Validated
public class SnmpManagerProperties {
+ @NotEmpty
private String host;
private int port;
}
--- /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.snmp.manager;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+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", "actualJO");
+ private static final List<String> WHITE_LIST_EPOCH =
+ Arrays.asList("eventId", "actualJO", "startEpochMicrosec", "lastEpochMicrosec");
+ private static final String COMMON_EVENT_HEADER = "commonEventHeader";
+ private static final String EVENT = "event";
+
+ 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, actualJO, WHITE_LIST);
+ assertEquals(expectedJO, actualJO);
+ }
+
+ private static void removeCommonEventHeaderFields(final JsonObject expectedJO, final JsonObject actualJO,
+ final List<String> asList) {
+ asList.forEach(wipe -> {
+ expectedJO.get(EVENT).getAsJsonObject().getAsJsonObject(COMMON_EVENT_HEADER).remove(wipe);
+ actualJO.get(EVENT).getAsJsonObject().getAsJsonObject(COMMON_EVENT_HEADER).remove(wipe);
+ });
+ }
+
+ public static void compareResultSkipEpoch(final String expected, final String actual) {
+ final JsonObject expectedJO = JsonParser.parseString(expected).getAsJsonObject();
+ final JsonObject actualJO = JsonParser.parseString(actual).getAsJsonObject();
+ removeCommonEventHeaderFields(expectedJO, actualJO, WHITE_LIST_EPOCH);
+ assertEquals(expectedJO, actualJO);
+ }
+}
--- /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.snmp.manager;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import java.time.ZoneId;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.o.ran.oam.nf.oam.adopter.snmp.manager.api.TimeZoneOffsetService;
+import org.o.ran.oam.nf.oam.adopter.snmp.manager.configurations.SnmpManagerConfig;
+import org.o.ran.oam.nf.oam.adopter.snmp.manager.mapper.SnmpMapperImpl;
+import org.o.ran.oam.nf.oam.adopter.snmp.manager.properties.SnmpManagerProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(classes = {VesEventNotifierMock.class, SnmpMappingConfigurationProvider.class,
+ SnmpMapperImpl.class, SnmpManagerProperties.class, SnmpManagerConfig.class})
+public class SnmpManagerTest {
+ @Autowired
+ @Qualifier("test")
+ private VesEventNotifierMock listener;
+ @Autowired
+ private SnmpManagerProperties snmpManagerProperties;
+ @Autowired
+ private SnmpManagerImpl snmpManager;
+ @MockBean
+ private TimeZoneOffsetService timeZoneOffsetService;
+
+ @BeforeEach
+ public void init() {
+ when(timeZoneOffsetService.getTimeZone(anyString())).thenReturn(ZoneId.of("+02:00"));
+ }
+
+ private static String getVesNotification(final VesEventNotifierMock notificationProvider)
+ throws InterruptedException {
+ for (int i = 0; i < 1000; i++) {
+ Thread.sleep(100);
+ if (notificationProvider.getEvent() != null) {
+ break;
+ }
+ }
+ final String event = notificationProvider.getEvent();
+ assertNotNull(event);
+ notificationProvider.clear();
+ return event;
+ }
+
+ @Test
+ public void testDefaultTrap() throws Exception {
+ SnmpTestUtil
+ .sendDefaultTrapV2(snmpManagerProperties.getHost(), Integer.toString(snmpManagerProperties.getPort()));
+ final String expected = JsonUtils.readJson("/json/VESMessageDefaultTrap.json");
+ final String actual = getVesNotification(listener);
+ JsonUtils.compareResultSkipEpoch(expected, actual);
+ }
+
+ @Test
+ public void testBoxDown() throws Exception {
+ SnmpTestUtil.sendPortDownTrapV2(snmpManagerProperties.getHost(),
+ Integer.toString(snmpManagerProperties.getPort()));
+ final String expected = JsonUtils.readJson("/json/PortDOWN.json");
+ final String actual = getVesNotification(listener);
+ JsonUtils.compareResult(expected, actual);
+ }
+}
--- /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.snmp.manager;
+
+import java.io.IOException;
+import lombok.experimental.UtilityClass;
+import org.snmp4j.CommunityTarget;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
+import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.smi.Address;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.TimeTicks;
+import org.snmp4j.smi.UdpAddress;
+import org.snmp4j.smi.Variable;
+import org.snmp4j.smi.VariableBinding;
+import org.snmp4j.transport.DefaultUdpTransportMapping;
+
+@UtilityClass
+public class SnmpTestUtil {
+ public static final OID PORT_DOWN_OID = new OID(new int[] {1, 3, 6, 1, 4, 1, 1007, 0, 0, 1, 0, 1});
+ public static final OID BOX_NAME = new OID(new int[] {1, 3, 6, 1, 4, 1, 1007, 0, 0, 1, 0, 2});
+ public static final OID NOTIFICATION_DESCRIPTION = new OID(new int[] {1, 3, 6, 1, 4, 1, 1007, 0, 0, 1, 0, 3});
+ public static final OID START_EPOCH = new OID(new int[] {1, 3, 6, 1, 4, 1, 1007, 0, 0, 1, 0, 4});
+ public static final OID LAST_EPOCH = new OID(new int[] {1, 3, 6, 1, 4, 1, 1007, 0, 0, 1, 0, 5});
+ public static final OID BOX_SERIAL = new OID(new int[] {1, 3, 6, 1, 4, 1, 1007, 0, 0, 1, 0, 6});
+ public static final OID NOTIFICATION_INTERFACE = new OID(new int[] {1, 3, 6, 1, 4, 1, 1007, 0, 0, 1, 0, 7});
+
+ private static void sndTrap(final PDU trap, final String host, final String port) throws IOException {
+ final Address targetaddress = new UdpAddress(host + "/" + port);
+ final CommunityTarget target = new CommunityTarget();
+ target.setCommunity(new OctetString("public"));
+ target.setVersion(SnmpConstants.version2c);
+ target.setAddress(targetaddress);
+
+ final Snmp snmp = new Snmp(new DefaultUdpTransportMapping());
+ snmp.send(trap, target, null, null);
+ snmp.close();
+ }
+
+ /**
+ * Mock Default trap.
+ */
+ public static void sendDefaultTrapV2(final String host, final String port) throws IOException {
+ final PDU trap = new PDU();
+ trap.setType(PDU.TRAP);
+
+ final OID oid = new OID("1.2.3.4.5");
+ trap.add(new VariableBinding(SnmpConstants.snmpTrapOID, oid));
+ trap.add(new VariableBinding(SnmpConstants.sysUpTime, new TimeTicks(5000)));
+ trap.add(new VariableBinding(SnmpConstants.sysDescr, new OctetString("System Description")));
+
+ // Add Payload
+ final Variable var = new OctetString("some string");
+ trap.add(new VariableBinding(oid, var));
+
+ sndTrap(trap, host, port);
+ }
+
+ /**
+ * Mock trap.
+ */
+ public static void sendPortDownTrapV2(final String host, final String port) throws IOException {
+ final PDU trap = new PDU();
+ trap.setType(PDU.TRAP);
+
+ trap.add(new VariableBinding(SnmpConstants.snmpTrapOID, PORT_DOWN_OID));
+ trap.add(new VariableBinding(SnmpConstants.sysUpTime, new TimeTicks(5000)));
+
+ // Add Payload
+ trap.add(new VariableBinding(BOX_NAME, new OctetString("OAM-BOX")));
+ trap.add(new VariableBinding(BOX_SERIAL, new OctetString("10283")));
+ trap.add(new VariableBinding(NOTIFICATION_DESCRIPTION, new OctetString("Port DOWN")));
+ trap.add(new VariableBinding(START_EPOCH, new OctetString("1613592976108380")));
+ trap.add(new VariableBinding(LAST_EPOCH, new OctetString("1613592976108880")));
+ trap.add(new VariableBinding(NOTIFICATION_INTERFACE, new OctetString("A0")));
+ sndTrap(trap, host, port);
+ }
+}
--- /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.snmp.manager;
+
+import com.google.gson.Gson;
+import io.reactivex.rxjava3.core.Completable;
+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")
+class VesEventNotifierMock implements VesEventNotifier {
+
+ private static final Gson GSON = new Gson();
+ private CommonEventFormat302ONAP event;
+
+ @Override
+ public synchronized Completable notifyEvents(final CommonEventFormat302ONAP event) {
+ this.event = event;
+ return Completable.complete();
+ }
+
+ protected String getEvent() {
+ if (event == null) {
+ return null;
+ }
+ return GSON.toJson(event, CommonEventFormat302ONAP.class);
+ }
+
+ protected void clear() {
+ event = null;
+ }
+}
--- /dev/null
+#########################################
+### FM Configuration ###
+#########################################
+snmp-manager:
+ host: "127.0.0.1"
+ port: 10162
+ mapping-config-path: "src/test/resources/fm-ves-message-mapping.yaml"
--- /dev/null
+reporting-entity-name: "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
+{
+ "event":{
+ "commonEventHeader":{
+ "domain":"fault",
+ "eventId":"1023555944",
+ "eventName":"FAULT_NF-OAM-ADOPTER_PortDOWN",
+ "lastEpochMicrosec":1613592976108880,
+ "nfVendorName":"SOME-VENDOR",
+ "priority":"High",
+ "reportingEntityId":"10283",
+ "reportingEntityName": "NF-OAM-ADOPTER",
+ "sequence":0,
+ "sourceName":"OAM-BOX",
+ "startEpochMicrosec":1613592976108380,
+ "timeZoneOffset":"UTC+02:00",
+ "version":"4.0",
+ "vesEventListenerVersion":"7.1"
+ },
+ "faultFields":{
+ "alarmAdditionalInformation":{
+ "1.3.6.1.4.1.1007.0.0.1.0.7":"A0",
+ "1.3.6.1.4.1.1007.0.0.1.0.5":"1613592976108880",
+ "1.3.6.1.6.3.1.1.4.1.0":"1.3.6.1.4.1.1007.0.0.1.0.1",
+ "1.3.6.1.4.1.1007.0.0.1.0.6":"10283",
+ "1.3.6.1.4.1.1007.0.0.1.0.3":"Port DOWN",
+ "1.3.6.1.4.1.1007.0.0.1.0.4":"1613592976108380",
+ "1.3.6.1.4.1.1007.0.0.1.0.2":"OAM-BOX",
+ "1.3.6.1.2.1.1.3.0":"0:00:50.00"
+ },
+ "alarmCondition":"PortDOWN",
+ "alarmInterfaceA":"A0",
+ "eventCategory":"link",
+ "eventSeverity":"MAJOR",
+ "eventSourceType":"port",
+ "faultFieldsVersion":"4.0",
+ "specificProblem":"Port DOWN",
+ "vfStatus":"Active"
+ }
+ }
+}
--- /dev/null
+{
+ "event": {
+ "commonEventHeader": {
+ "domain": "fault",
+ "eventId": "46102539",
+ "eventName":"FAULT_NF-OAM-ADOPTER_SNMP_Fault",
+ "lastEpochMicrosec": 1.601542425363E12,
+ "nfVendorName": "SOME-VENDOR",
+ "priority": "High",
+ "reportingEntityName": "NF-OAM-ADOPTER",
+ "sequence": 0,
+ "sourceName": "127.0.0.1",
+ "startEpochMicrosec":1613592976108380,
+ "timeZoneOffset":"UTC+02:00",
+ "version": "4.0",
+ "vesEventListenerVersion": "7.1"
+ },
+ "faultFields": {
+ "alarmAdditionalInformation": {
+ "1.3.6.1.6.3.1.1.4.1.0": "1.2.3.4.5",
+ "1.2.3.4.5": "some string",
+ "1.3.6.1.2.1.1.1.0": "System Description",
+ "1.3.6.1.2.1.1.3.0": "0:00:50.00"
+ },
+ "alarmCondition": "SNMP_Fault",
+ "eventSeverity": "CRITICAL",
+ "eventSourceType": "Unknown",
+ "faultFieldsVersion": "4.0",
+ "specificProblem": "SNMP_Fault",
+ "vfStatus": "Active"
+ }
+ }
+}