You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ja...@apache.org on 2023/04/21 06:46:21 UTC

[camel-quarkus] branch main updated: Snmp: Extend test coverage #4797

This is an automated email from the ASF dual-hosted git repository.

jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git


The following commit(s) were added to refs/heads/main by this push:
     new ee6cadb250 Snmp: Extend test coverage #4797
ee6cadb250 is described below

commit ee6cadb250713b78e8a3e45a5bee3bd508d04ca2
Author: JiriOndrusek <on...@gmail.com>
AuthorDate: Thu Apr 20 15:31:01 2023 +0200

    Snmp: Extend test coverage #4797
---
 integration-tests-jvm/snmp/pom.xml                 |   5 +
 .../quarkus/component/snmp/it/SnmpResource.java    | 120 ++++++++++++++++--
 .../camel/quarkus/component/snmp/it/SnmpRoute.java |  64 ++++++++++
 .../camel/quarkus/component/snmp/it/SnmpTest.java  |  67 +++++++++-
 .../component/snmp/it/SnmpTestResource.java        | 135 +++++++++++++++++++++
 5 files changed, 375 insertions(+), 16 deletions(-)

diff --git a/integration-tests-jvm/snmp/pom.xml b/integration-tests-jvm/snmp/pom.xml
index b64813cbfb..effa7b4291 100644
--- a/integration-tests-jvm/snmp/pom.xml
+++ b/integration-tests-jvm/snmp/pom.xml
@@ -46,6 +46,11 @@
             <artifactId>quarkus-junit5</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>io.rest-assured</groupId>
             <artifactId>rest-assured</artifactId>
diff --git a/integration-tests-jvm/snmp/src/main/java/org/apache/camel/quarkus/component/snmp/it/SnmpResource.java b/integration-tests-jvm/snmp/src/main/java/org/apache/camel/quarkus/component/snmp/it/SnmpResource.java
index 8ff7bcda6f..5b573a6f1d 100644
--- a/integration-tests-jvm/snmp/src/main/java/org/apache/camel/quarkus/component/snmp/it/SnmpResource.java
+++ b/integration-tests-jvm/snmp/src/main/java/org/apache/camel/quarkus/component/snmp/it/SnmpResource.java
@@ -16,35 +16,129 @@
  */
 package org.apache.camel.quarkus.component.snmp.it;
 
+import java.util.Deque;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
 import jakarta.enterprise.context.ApplicationScoped;
 import jakarta.inject.Inject;
+import jakarta.inject.Named;
 import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
-import org.apache.camel.CamelContext;
-import org.jboss.logging.Logger;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.component.snmp.SnmpMessage;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.snmp4j.PDU;
+import org.snmp4j.PDUv1;
+import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.TimeTicks;
+import org.snmp4j.smi.Variable;
+import org.snmp4j.smi.VariableBinding;
 
 @Path("/snmp")
 @ApplicationScoped
 public class SnmpResource {
 
-    private static final Logger LOG = Logger.getLogger(SnmpResource.class);
+    @ConfigProperty(name = "snmpListenAddress")
+    String snmpListenAddress;
+
+    @Inject
+    @Named("snmpTrapResults")
+    Map<String, Deque<SnmpMessage>> snmpResults;
 
-    private static final String COMPONENT_SNMP = "snmp";
     @Inject
-    CamelContext context;
+    ProducerTemplate producerTemplate;
 
-    @Path("/load/component/snmp")
+    @Path("/producePDU")
     @GET
     @Produces(MediaType.TEXT_PLAIN)
-    public Response loadComponentSnmp() throws Exception {
-        /* This is an autogenerated test */
-        if (context.getComponent(COMPONENT_SNMP) != null) {
-            return Response.ok().build();
-        }
-        LOG.warnf("Could not load [%s] from the Camel context", COMPONENT_SNMP);
-        return Response.status(500, COMPONENT_SNMP + " could not be loaded from the Camel context").build();
+    public Response producePDU() {
+        String url = String.format("snmp://%s?retries=1", snmpListenAddress);
+        SnmpMessage pdu = producerTemplate.requestBody(url, "", SnmpMessage.class);
+
+        String response = pdu.getSnmpMessage().getVariableBindings().stream()
+                .filter(vb -> vb.getOid().equals(SnmpConstants.sysDescr))
+                .map(vb -> vb.getVariable().toString())
+                .collect(Collectors.joining());
+
+        return Response.ok(response).build();
+    }
+
+    @Path("/sendPoll")
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response sendPoll() {
+        SnmpMessage pdu = producerTemplate.requestBody("direct:producePoll", "", SnmpMessage.class);
+
+        String response = pdu.getSnmpMessage().getVariableBindings().stream()
+                .filter(vb -> vb.getOid().equals(SnmpConstants.sysDescr))
+                .map(vb -> vb.getVariable().toString())
+                .collect(Collectors.joining());
+
+        return Response.ok(response).build();
+    }
+
+    @Path("/getNext")
+    @POST
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response getNext(String payload) {
+        String url = String.format("snmp://%s?type=GET_NEXT&retries=1&protocol=udp&oids=%s", snmpListenAddress,
+                SnmpConstants.sysDescr);
+        List<SnmpMessage> pdu = producerTemplate.requestBody(url, "", List.class);
+
+        String response = pdu.stream()
+                .flatMap(m -> m.getSnmpMessage().getVariableBindings().stream())
+                .filter(vb -> vb.getOid().equals(SnmpConstants.sysDescr))
+                .map(vb -> vb.getVariable().toString())
+                .collect(Collectors.joining(","));
+
+        return Response.ok(response).build();
+    }
+
+    @Path("/produceTrap")
+    @POST
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response sendTrap(String payload) {
+        String url = "snmp:127.0.0.1:1662?protocol=udp&type=TRAP&snmpVersion=0)";
+        PDU trap = createTrap(payload);
+
+        producerTemplate.sendBody(url, trap);
+
+        return Response.ok().build();
+    }
+
+    @Path("/results")
+    @POST
+    @Produces(MediaType.TEXT_PLAIN)
+    public Response results(String from) throws Exception {
+        OID oid = "trap".equals(from) ? new OID("1.2.3.4.5") : SnmpConstants.sysDescr;
+        String result = snmpResults.get(from).stream().map(m -> m.getSnmpMessage().getVariable(oid).toString())
+                .collect(Collectors.joining(","));
+
+        return Response.ok(result).build();
+    }
+
+    public PDU createTrap(String payload) {
+        PDUv1 trap = new PDUv1();
+        trap.setGenericTrap(PDUv1.ENTERPRISE_SPECIFIC);
+        trap.setSpecificTrap(1);
+
+        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))); // put your uptime here
+        trap.add(new VariableBinding(SnmpConstants.sysDescr, new OctetString("System Description")));
+        trap.setEnterprise(oid);
+
+        //Add Payload
+        Variable var = new OctetString(payload);
+        trap.add(new VariableBinding(oid, var));
+        return trap;
     }
 }
diff --git a/integration-tests-jvm/snmp/src/main/java/org/apache/camel/quarkus/component/snmp/it/SnmpRoute.java b/integration-tests-jvm/snmp/src/main/java/org/apache/camel/quarkus/component/snmp/it/SnmpRoute.java
new file mode 100644
index 0000000000..445d6a456f
--- /dev/null
+++ b/integration-tests-jvm/snmp/src/main/java/org/apache/camel/quarkus/component/snmp/it/SnmpRoute.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.snmp.it;
+
+import java.util.Deque;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.inject.Named;
+import jakarta.inject.Singleton;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.snmp.SnmpMessage;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+@ApplicationScoped
+public class SnmpRoute extends RouteBuilder {
+
+    @ConfigProperty(name = "snmpListenAddress")
+    String snmpListenAddress;
+
+    @Inject
+    @Named("snmpTrapResults")
+    Map<String, Deque<SnmpMessage>> snmpResults;
+
+    @Override
+    public void configure() {
+        //TRAP consumer
+        from("snmp:0.0.0.0:1662?protocol=udp&type=TRAP&snmpVersion=0")
+                .process(e -> snmpResults.get("trap").add(e.getIn().getBody(SnmpMessage.class)));
+
+        //POLL consumer
+        from("snmp://" + snmpListenAddress + "?protocol=udp&type=POLL&snmpVersion=0&oids=1.3.6.1.2.1.1.5.0")
+                .process(e -> snmpResults.get("poll").add(e.getIn().getBody(SnmpMessage.class)));
+    }
+
+    static class Producers {
+        @jakarta.enterprise.inject.Produces
+        @Singleton
+        @Named("snmpTrapResults")
+        Map<String, Deque<SnmpMessage>> snmpResults() {
+            Map<String, Deque<SnmpMessage>> map = new ConcurrentHashMap<>();
+            map.put("trap", new ConcurrentLinkedDeque());
+            map.put("poll", new ConcurrentLinkedDeque());
+            return map;
+        }
+    }
+}
diff --git a/integration-tests-jvm/snmp/src/test/java/org/apache/camel/quarkus/component/snmp/it/SnmpTest.java b/integration-tests-jvm/snmp/src/test/java/org/apache/camel/quarkus/component/snmp/it/SnmpTest.java
index 046ed5b620..83194f0a89 100644
--- a/integration-tests-jvm/snmp/src/test/java/org/apache/camel/quarkus/component/snmp/it/SnmpTest.java
+++ b/integration-tests-jvm/snmp/src/test/java/org/apache/camel/quarkus/component/snmp/it/SnmpTest.java
@@ -16,19 +16,80 @@
  */
 package org.apache.camel.quarkus.component.snmp.it;
 
+import java.util.concurrent.TimeUnit;
+
+import io.quarkus.test.common.QuarkusTestResource;
 import io.quarkus.test.junit.QuarkusTest;
 import io.restassured.RestAssured;
+import org.hamcrest.Matchers;
 import org.junit.jupiter.api.Test;
 
+import static org.awaitility.Awaitility.await;
+
+/**
+ * There is a responder defined in the test resource. Which returns 2 responses without a delay and the third one
+ * with delay longer then default timeout. This means following behavior:
+ * - send PDU will receive 1 response
+ * - get_next will receive 2 responses (the third one reaches timeout)
+ * - poll returns unending stream of responses
+ */
 @QuarkusTest
+@QuarkusTestResource(SnmpTestResource.class)
 class SnmpTest {
 
     @Test
-    public void loadComponentSnmp() {
-        /* A simple autogenerated test */
-        RestAssured.get("/snmp/load/component/snmp")
+    public void testSendReceiveTrap() throws Exception {
+
+        RestAssured.given()
+                .body("TEXT")
+                .post("/snmp/produceTrap")
                 .then()
                 .statusCode(200);
+
+        await().atMost(10L, TimeUnit.SECONDS).pollDelay(100, TimeUnit.MILLISECONDS).until(() -> {
+            String result = RestAssured.given()
+                    .body("trap")
+                    .post("/snmp/results")
+                    .then()
+                    .statusCode(200)
+                    .extract().body().asString();
+
+            return result.contains("TEXT");
+        });
+    }
+
+    @Test
+    public void testPoll() throws Exception {
+        await().atMost(10L, TimeUnit.SECONDS).pollDelay(100, TimeUnit.MILLISECONDS).until(() -> {
+            String result = RestAssured.given()
+                    .body("poll")
+                    .post("/snmp/results")
+                    .then()
+                    .statusCode(200)
+                    .extract().body().asString();
+
+            return result.startsWith("Response from the test #1,Response from the test #2,Response from the test #3");
+        });
     }
 
+    @Test
+    public void testProducePDU() {
+
+        RestAssured
+                .get("/snmp/producePDU")
+                .then()
+                .statusCode(200)
+                .body(Matchers.equalTo("Response from the test #1"));
+    }
+
+    @Test
+    public void testGetNext() {
+
+        RestAssured.given()
+                .body("TEXT")
+                .post("/snmp/getNext")
+                .then()
+                .statusCode(200)
+                .body(Matchers.equalTo("Response from the test #1,Response from the test #2"));
+    }
 }
diff --git a/integration-tests-jvm/snmp/src/test/java/org/apache/camel/quarkus/component/snmp/it/SnmpTestResource.java b/integration-tests-jvm/snmp/src/test/java/org/apache/camel/quarkus/component/snmp/it/SnmpTestResource.java
new file mode 100644
index 0000000000..504e4f6250
--- /dev/null
+++ b/integration-tests-jvm/snmp/src/test/java/org/apache/camel/quarkus/component/snmp/it/SnmpTestResource.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.snmp.it;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import org.apache.camel.util.CollectionHelper;
+import org.junit.jupiter.api.Assertions;
+import org.snmp4j.CommandResponder;
+import org.snmp4j.CommandResponderEvent;
+import org.snmp4j.MessageException;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
+import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.mp.StatusInformation;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.UdpAddress;
+import org.snmp4j.smi.VariableBinding;
+import org.snmp4j.transport.DefaultUdpTransportMapping;
+
+public class SnmpTestResource implements QuarkusTestResourceLifecycleManager {
+
+    public static final String LISTEN_ADDRESS = "snmpListenAddress";
+    public static final String LOCAL_ADDRESS = "127.0.0.1/0";
+
+    Snmp snmpResponder;
+
+    @Override
+    public Map<String, String> start() {
+        DefaultUdpTransportMapping udpTransportMapping;
+        try {
+            udpTransportMapping = new DefaultUdpTransportMapping(new UdpAddress(LOCAL_ADDRESS));
+            snmpResponder = new Snmp(udpTransportMapping);
+
+            TestCommandResponder responder = new TestCommandResponder(snmpResponder);
+            snmpResponder.addCommandResponder(responder);
+
+            snmpResponder.listen();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+        return CollectionHelper.mapOf(LISTEN_ADDRESS, udpTransportMapping.getListenAddress().toString().replaceFirst("/", ":"));
+    }
+
+    @Override
+    public void stop() {
+        if (snmpResponder != null) {
+            try {
+                snmpResponder.close();
+            } catch (IOException e) {
+                //do nothing
+            }
+        }
+    }
+
+    static class TestCommandResponder implements CommandResponder {
+
+        private final Snmp commandResponder;
+        private final Map<String, Integer> counts = new ConcurrentHashMap<>();
+
+        public TestCommandResponder(Snmp commandResponder) {
+            this.commandResponder = commandResponder;
+        }
+
+        @Override
+        public synchronized void processPdu(CommandResponderEvent event) {
+            PDU pdu = event.getPDU();
+            Vector<? extends VariableBinding> vbs = Optional.ofNullable(pdu.getVariableBindings()).orElse(new Vector<>(0));
+            String key = vbs.stream().sequential().map(vb -> vb.getOid().toString()).collect(Collectors.joining(","));
+            int numberOfSent = counts.getOrDefault(key, 0);
+
+            //if 3 responses were already sent for the OID, do not respond anymore
+            if (numberOfSent > 3) {
+                return;
+            }
+            //first 2 responses are quick, the third response takes 3000ms (so there is a timeout with default 1500ms) ->
+            //   getNext producer will receive only 2 messages
+            //   poll consumer should receive all of them
+            if (numberOfSent % 3 == 2) {
+                try {
+                    Thread.sleep(3000);
+                } catch (InterruptedException e) {
+                    //nothing
+                }
+            }
+
+            PDU response = makeResponse(++numberOfSent, SnmpConstants.version1);
+            if (response != null) {
+                try {
+                    response.setRequestID(pdu.getRequestID());
+                    commandResponder.getMessageDispatcher().returnResponsePdu(
+                            event.getMessageProcessingModel(), event.getSecurityModel(),
+                            event.getSecurityName(), event.getSecurityLevel(),
+                            response, event.getMaxSizeResponsePDU(),
+                            event.getStateReference(), new StatusInformation());
+                } catch (MessageException e) {
+                    Assertions.assertNull(e);
+                }
+                counts.put(key, numberOfSent);
+            }
+        }
+
+        private PDU makeResponse(int counter, int version) {
+            PDU responsePDU = new PDU();
+            responsePDU.setType(PDU.RESPONSE);
+            responsePDU.setErrorStatus(PDU.noError);
+            responsePDU.setErrorIndex(0);
+            responsePDU.add(new VariableBinding(new OID(SnmpConstants.sysDescr),
+                    new OctetString("Response from the test #" + counter)));
+            return responsePDU;
+        }
+    }
+}