This is an automated email from the ASF dual-hosted git repository. ppalaga pushed a commit to branch 2.13.x in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
commit d61ff2a070b2e8f0ad4b04008c6ffafc284f7fa9 Author: JiriOndrusek <[email protected]> AuthorDate: Thu May 4 17:25:28 2023 +0200 Snmp: cover snmp v3 for POLL operation #4881 --- .../component/snmp/QuarkusSnmpOIDPoller.java | 317 +++++++++++++++++++++ .../camel/quarkus/component/snmp/SnmpRecorder.java | 18 ++ .../quarkus/component/snmp/it/SnmpResource.java | 38 +++ .../camel/quarkus/component/snmp/it/SnmpRoute.java | 44 +-- .../camel/quarkus/component/snmp/it/SnmpTest.java | 82 +++--- .../component/snmp/it/SnmpTestResource.java | 51 +++- 6 files changed, 445 insertions(+), 105 deletions(-) diff --git a/extensions-jvm/snmp/runtime/src/main/java/org/apache/camel/quarkus/component/snmp/QuarkusSnmpOIDPoller.java b/extensions-jvm/snmp/runtime/src/main/java/org/apache/camel/quarkus/component/snmp/QuarkusSnmpOIDPoller.java new file mode 100644 index 0000000000..ac94362abb --- /dev/null +++ b/extensions-jvm/snmp/runtime/src/main/java/org/apache/camel/quarkus/component/snmp/QuarkusSnmpOIDPoller.java @@ -0,0 +1,317 @@ +/* + * 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; + +import java.util.List; + +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.component.snmp.SnmpActionType; +import org.apache.camel.component.snmp.SnmpEndpoint; +import org.apache.camel.component.snmp.SnmpOIDPoller; +import org.apache.camel.support.ScheduledPollConsumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.snmp4j.CommunityTarget; +import org.snmp4j.PDU; +import org.snmp4j.ScopedPDU; +import org.snmp4j.Snmp; +import org.snmp4j.Target; +import org.snmp4j.TransportMapping; +import org.snmp4j.UserTarget; +import org.snmp4j.event.ResponseEvent; +import org.snmp4j.event.ResponseListener; +import org.snmp4j.mp.MPv3; +import org.snmp4j.mp.SnmpConstants; +import org.snmp4j.security.AuthMD5; +import org.snmp4j.security.AuthSHA; +import org.snmp4j.security.Priv3DES; +import org.snmp4j.security.PrivAES128; +import org.snmp4j.security.PrivAES192; +import org.snmp4j.security.PrivAES256; +import org.snmp4j.security.PrivDES; +import org.snmp4j.security.SecurityModels; +import org.snmp4j.security.SecurityProtocols; +import org.snmp4j.security.USM; +import org.snmp4j.security.UsmUser; +import org.snmp4j.smi.Address; +import org.snmp4j.smi.GenericAddress; +import org.snmp4j.smi.OID; +import org.snmp4j.smi.OctetString; +import org.snmp4j.smi.VariableBinding; +import org.snmp4j.transport.DefaultTcpTransportMapping; +import org.snmp4j.transport.DefaultUdpTransportMapping; +import org.snmp4j.util.DefaultPDUFactory; +import org.snmp4j.util.TreeEvent; +import org.snmp4j.util.TreeUtils; + +/** + * A copy of SnmpOIDPoller fromm Camel + */ +public class QuarkusSnmpOIDPoller extends ScheduledPollConsumer implements ResponseListener { + + private static final Logger LOG = LoggerFactory.getLogger(SnmpOIDPoller.class); + + private Address targetAddress; + private TransportMapping<? extends Address> transport; + private Snmp snmp; + + private Target target; + private PDU pdu; + private SnmpEndpoint endpoint; + + public QuarkusSnmpOIDPoller(SnmpEndpoint endpoint, Processor processor) { + super(endpoint, processor); + this.endpoint = endpoint; + } + + @Override + protected void doStart() throws Exception { + super.doStart(); + + this.targetAddress = GenericAddress.parse(this.endpoint.getAddress()); + + // either tcp or udp + if ("tcp".equals(endpoint.getProtocol())) { + this.transport = new DefaultTcpTransportMapping(); + } else if ("udp".equals(endpoint.getProtocol())) { + this.transport = new DefaultUdpTransportMapping(); + } else { + throw new IllegalArgumentException("Unknown protocol: " + endpoint.getProtocol()); + } + + this.snmp = new Snmp(this.transport); + + if (SnmpConstants.version3 == endpoint.getSnmpVersion()) { + UserTarget userTarget = new UserTarget(); + + userTarget.setSecurityLevel(endpoint.getSecurityLevel()); + userTarget.setSecurityName(convertToOctetString(endpoint.getSecurityName())); + userTarget.setAddress(targetAddress); + userTarget.setRetries(endpoint.getRetries()); + userTarget.setTimeout(endpoint.getTimeout()); + userTarget.setVersion(endpoint.getSnmpVersion()); + + this.target = userTarget; + + USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0); + SecurityModels.getInstance().addSecurityModel(usm); + + OID authProtocol = convertAuthenticationProtocol(endpoint.getAuthenticationProtocol()); + + OctetString authPwd = convertToOctetString(endpoint.getAuthenticationPassphrase()); + + OID privProtocol = convertPrivacyProtocol(endpoint.getPrivacyProtocol()); + + OctetString privPwd = convertToOctetString(endpoint.getPrivacyPassphrase()); + + UsmUser user = new UsmUser( + convertToOctetString(endpoint.getSecurityName()), authProtocol, authPwd, privProtocol, privPwd); + + usm.addUser(convertToOctetString(endpoint.getSecurityName()), user); + + ScopedPDU scopedPDU = new ScopedPDU(); + + if (endpoint.getSnmpContextEngineId() != null) { + scopedPDU.setContextEngineID(new OctetString(endpoint.getSnmpContextEngineId())); + } + + if (endpoint.getSnmpContextName() != null) { + scopedPDU.setContextName(new OctetString(endpoint.getSnmpContextName())); + } + + this.pdu = scopedPDU; + } else { + CommunityTarget communityTarget = new CommunityTarget(); + + communityTarget.setCommunity(convertToOctetString(endpoint.getSnmpCommunity())); + communityTarget.setAddress(targetAddress); + communityTarget.setRetries(endpoint.getRetries()); + communityTarget.setTimeout(endpoint.getTimeout()); + communityTarget.setVersion(endpoint.getSnmpVersion()); + + this.target = communityTarget; + + this.pdu = new PDU(); + } + + // listen to the transport + if (LOG.isDebugEnabled()) { + LOG.debug("Starting OID poller on {} using {} protocol", endpoint.getAddress(), endpoint.getProtocol()); + } + this.transport.listen(); + if (LOG.isInfoEnabled()) { + LOG.info("Started OID poller on {} using {} protocol", endpoint.getAddress(), endpoint.getProtocol()); + } + } + + @Override + protected void doStop() throws Exception { + // stop listening to the transport + if (this.transport != null && this.transport.isListening()) { + LOG.info("Stopping OID poller on {}", targetAddress); + this.transport.close(); + LOG.info("Stopped OID poller on {}", targetAddress); + } + + super.doStop(); + } + + @Override + protected int poll() throws Exception { + this.pdu.clear(); + + int type = this.getPduType(this.endpoint.getType()); + + this.pdu.setType(type); + + if (!endpoint.isTreeList()) { + // prepare the request items + for (OID oid : this.endpoint.getOids()) { + this.pdu.add(new VariableBinding(oid)); + } + } else { + TreeUtils treeUtils = new TreeUtils(snmp, new DefaultPDUFactory()); + for (OID oid : this.endpoint.getOids()) { + List events = treeUtils.getSubtree(target, new OID(oid)); + for (Object eventObj : events) { + TreeEvent event = (TreeEvent) eventObj; + if (event == null) { + LOG.warn("Event is null"); + continue; + } + if (event.isError()) { + LOG.error("Error in event: {}", event.getErrorMessage()); + continue; + } + VariableBinding[] varBindings = event.getVariableBindings(); + if (varBindings == null || varBindings.length == 0) { + continue; + } + for (VariableBinding varBinding : varBindings) { + if (varBinding == null) { + continue; + } + this.pdu.add(varBinding); + } + } + } + } + + // send the request + snmp.send(pdu, target, null, this); + + return 1; + } + + @Override + public void onResponse(ResponseEvent event) { + // Always cancel async request when response has been received + // otherwise a memory leak is created! Not canceling a request + // immediately can be useful when sending a request to a broadcast address. + ((Snmp) event.getSource()).cancel(event.getRequest(), this); + + // check for valid response + if (event.getRequest() == null || event.getResponse() == null) { + // ignore null requests/responses + LOG.debug("Received invalid SNMP event. Request: {} / Response: {}", event.getRequest(), event.getResponse()); + return; + } + + PDU pdu = event.getResponse(); + processPDU(pdu); + } + + /** + * processes the pdu message + * + * @param pdu the pdu + */ + public void processPDU(PDU pdu) { + if (LOG.isDebugEnabled()) { + LOG.debug("Received response event for {} : {}", this.endpoint.getAddress(), pdu); + } + Exchange exchange = endpoint.createExchange(pdu); + try { + getProcessor().process(exchange); + } catch (Exception e) { + getExceptionHandler().handleException(e); + } + } + + /** + * * @return Returns the target. + */ + public Target getTarget() { + return this.target; + } + + /** + * @param target The target to set. + */ + public void setTarget(Target target) { + this.target = target; + } + + private OctetString convertToOctetString(String value) { + if (value == null) { + return null; + } + return new OctetString(value); + } + + private OID convertAuthenticationProtocol(String authenticationProtocol) { + if (authenticationProtocol == null) { + return null; + } + if ("MD5".equals(authenticationProtocol)) { + return AuthMD5.ID; + } else if ("SHA1".equals(authenticationProtocol)) { + return AuthSHA.ID; + } else { + throw new IllegalArgumentException("Unknown authentication protocol: " + authenticationProtocol); + } + } + + private OID convertPrivacyProtocol(String privacyProtocol) { + if (privacyProtocol == null) { + return null; + } + if ("DES".equals(privacyProtocol)) { + return PrivDES.ID; + } else if ("TRIDES".equals(privacyProtocol)) { + return Priv3DES.ID; + } else if ("AES128".equals(privacyProtocol)) { + return PrivAES128.ID; + } else if ("AES192".equals(privacyProtocol)) { + return PrivAES192.ID; + } else if ("AES256".equals(privacyProtocol)) { + return PrivAES256.ID; + } else { + throw new IllegalArgumentException("Unknown privacy protocol: " + privacyProtocol); + } + } + + private int getPduType(SnmpActionType type) { + if (SnmpActionType.GET_NEXT == type) { + return PDU.GETNEXT; + } else { + return PDU.GET; + } + } + +} diff --git a/extensions-jvm/snmp/runtime/src/main/java/org/apache/camel/quarkus/component/snmp/SnmpRecorder.java b/extensions-jvm/snmp/runtime/src/main/java/org/apache/camel/quarkus/component/snmp/SnmpRecorder.java index 8e357e5664..97f2f420c9 100644 --- a/extensions-jvm/snmp/runtime/src/main/java/org/apache/camel/quarkus/component/snmp/SnmpRecorder.java +++ b/extensions-jvm/snmp/runtime/src/main/java/org/apache/camel/quarkus/component/snmp/SnmpRecorder.java @@ -20,11 +20,14 @@ import java.util.Map; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; +import org.apache.camel.Consumer; import org.apache.camel.Endpoint; +import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.component.snmp.SnmpActionType; import org.apache.camel.component.snmp.SnmpComponent; import org.apache.camel.component.snmp.SnmpEndpoint; +import org.apache.camel.component.snmp.SnmpTrapConsumer; @Recorder public class SnmpRecorder { @@ -67,5 +70,20 @@ public class SnmpRecorder { return new QuarkusSnmpProducer(this, getType()); } } + + @Override + public Consumer createConsumer(Processor processor) throws Exception { + if (getType() == SnmpActionType.TRAP) { + SnmpTrapConsumer answer = new SnmpTrapConsumer(this, processor); + // As the SnmpTrapConsumer is not a polling consumer we don't need to call the configureConsumer here. + return answer; + } else if (getType() == SnmpActionType.POLL) { + QuarkusSnmpOIDPoller answer = new QuarkusSnmpOIDPoller(this, processor); + configureConsumer(answer); + return answer; + } else { + throw new IllegalArgumentException("The type '" + getType() + "' is not valid!"); + } + } } } 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 3d35fe115d..99ec6d8bd0 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 @@ -28,9 +28,12 @@ import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import org.apache.camel.ConsumerTemplate; +import org.apache.camel.Exchange; import org.apache.camel.ProducerTemplate; import org.apache.camel.component.snmp.SnmpMessage; import org.eclipse.microprofile.config.inject.ConfigProperty; @@ -60,6 +63,9 @@ public class SnmpResource { @Named("snmpTrapResults") Map<String, Deque<SnmpMessage>> snmpResults; + @Inject + ConsumerTemplate consumerTemplate; + @Inject ProducerTemplate producerTemplate; @@ -87,6 +93,9 @@ public class SnmpResource { String response = pdu.stream() .flatMap(m -> m.getSnmpMessage().getVariableBindings().stream()) + //snmp may add null oid to the result - because responder supports v3 + // (see https://camel.apache.org/components/3.20.x/snmp-component.html#_the_result_of_a_poll) + .filter(vb -> !"Null".equals(vb.getVariable().toString())) .map(vb -> vb.getVariable().toString()) .collect(Collectors.joining(",")); @@ -106,6 +115,35 @@ public class SnmpResource { return Response.ok().build(); } + @Path("/poll/{version}") + @POST + @Produces(MediaType.TEXT_PLAIN) + public Response poll(@PathParam("version") int version, + @QueryParam("user") String user, + @QueryParam("securityLevel") String securityLevel, + String oid) { + String url = String.format("snmp:%s?protocol=udp&snmpVersion=%d&type=POLL&oids=%s", snmpListenAddress, version, oid); + if (user != null) { + url = url + "&securityName=" + user; + } + if (securityLevel != null) { + url = url + "&securityLevel=" + securityLevel; + } + + //Even if routeBuilder is preferred instead of consumerTemplate, consumerTemplete can be used in a case, when the component uses polling consumers by default. + // In this case: + // - only polling consumer is used by the SNMP component + // - usage of the consumerTemplate reduces a lot of requests between SNMP providers significantly, thus making the tests more stable. + Exchange e = consumerTemplate.receive(url); + + String result = e.getIn().getBody(SnmpMessage.class).getSnmpMessage().getVariableBindings().stream() + //snmp may add null oid to the result - because responder supports v3 + // (see https://camel.apache.org/components/3.20.x/snmp-component.html#_the_result_of_a_poll) + .filter(vb -> !"Null".equals(vb.getVariable().toString())) + .map(v -> v.getVariable().toString()).collect(Collectors.joining(",")); + return Response.ok(result).build(); + } + @Path("/results/{from}") @POST @Produces(MediaType.TEXT_PLAIN) 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 index 3f906ab477..1862fe37ab 100644 --- 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 @@ -30,7 +30,6 @@ import javax.inject.Singleton; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.snmp.SnmpMessage; import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.snmp4j.mp.SnmpConstants; @ApplicationScoped public class SnmpRoute extends RouteBuilder { @@ -41,12 +40,9 @@ public class SnmpRoute extends RouteBuilder { @ConfigProperty(name = TRAP_V0_PORT) int trap0Port; - @ConfigProperty(name = "SnmpRoute_trap_v1") + @ConfigProperty(name = TRAP_V1_PORT) int trap1Port; - @ConfigProperty(name = "snmpListenAddress") - String snmpListenAddress; - @Inject @Named("snmpTrapResults") Map<String, Deque<SnmpMessage>> snmpResults; @@ -60,33 +56,6 @@ public class SnmpRoute extends RouteBuilder { //TRAP consumer snmpVersion=1 from("snmp:0.0.0.0:" + trap1Port + "?protocol=udp&type=TRAP&snmpVersion=1") .process(e -> snmpResults.get("v1_trap").add(e.getIn().getBody(SnmpMessage.class))); - - //POLL consumer 2 oids, snmpVersion=0 - from("snmp://" + snmpListenAddress + "?protocol=udp&snmpVersion=0&type=POLL&oids=" + - SnmpConstants.sysLocation + "," + SnmpConstants.sysContact) - .process(e -> snmpResults.get("v0_poll2oids").add(e.getIn().getBody(SnmpMessage.class))); - //POLL consumer 2 oids, snmpVersion=1 - from("snmp://" + snmpListenAddress + "?protocol=udp&snmpVersion=1&type=POLL&oids=" + - SnmpConstants.sysLocation + "," + SnmpConstants.sysContact) - .process(e -> snmpResults.get("v1_poll2oids").add(e.getIn().getBody(SnmpMessage.class))); - - // POLL consumer starting with dot snmpVersion=0 - from("snmp://" + snmpListenAddress - + "?protocol=udp&snmpVersion=0&type=POLL&oids=.1.3.6.1.4.1.6527.3.1.2.21.2.1.50") - .process(e -> snmpResults.get("v0_pollStartingDot").add(e.getIn().getBody(SnmpMessage.class))); - //POLL consumer startingWith dot snmpVersion=1 - from("snmp://" + snmpListenAddress - + "?protocol=udp&snmpVersion=1&type=POLL&oids=.1.3.6.1.4.1.6527.3.1.2.21.2.1.50") - .process(e -> snmpResults.get("v1_pollStartingDot").add(e.getIn().getBody(SnmpMessage.class))); - - //POLL consumer snmpVersion=0 - from("snmp://" + snmpListenAddress + "?protocol=udp&snmpVersion=0&type=POLL&oids=" - + SnmpConstants.sysDescr) - .process(e -> snmpResults.get("v0_poll").add(e.getIn().getBody(SnmpMessage.class))); - //POLL consumer snmpVersion=1 - from("snmp://" + snmpListenAddress + "?protocol=udp&snmpVersion=1&type=POLL&oids=" - + SnmpConstants.sysDescr) - .process(e -> snmpResults.get("v1_poll").add(e.getIn().getBody(SnmpMessage.class))); } static class Producers { @@ -95,15 +64,8 @@ public class SnmpRoute extends RouteBuilder { @Named("snmpTrapResults") Map<String, Deque<SnmpMessage>> snmpResults() { Map<String, Deque<SnmpMessage>> map = new ConcurrentHashMap<>(); - map.put("v0_trap", new ConcurrentLinkedDeque<SnmpMessage>()); - map.put("v1_trap", new ConcurrentLinkedDeque<SnmpMessage>()); - map.put("v0_poll", new ConcurrentLinkedDeque<SnmpMessage>()); - map.put("v1_poll", new ConcurrentLinkedDeque<SnmpMessage>()); - map.put("v3_poll", new ConcurrentLinkedDeque<SnmpMessage>()); - map.put("v0_pollStartingDot", new ConcurrentLinkedDeque<SnmpMessage>()); - map.put("v1_pollStartingDot", new ConcurrentLinkedDeque<SnmpMessage>()); - map.put("v0_poll2oids", new ConcurrentLinkedDeque<SnmpMessage>()); - map.put("v1_poll2oids", new ConcurrentLinkedDeque<SnmpMessage>()); + map.put("v0_trap", new ConcurrentLinkedDeque<>()); + map.put("v1_trap", 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 fac78e768c..1220e8aa44 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 @@ -22,9 +22,11 @@ import java.util.stream.Stream; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; +import io.restassured.specification.RequestSpecification; import org.hamcrest.Matchers; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.snmp4j.mp.SnmpConstants; import org.snmp4j.smi.OID; @@ -76,38 +78,44 @@ class SnmpTest { } @ParameterizedTest - @MethodSource("supportedVersions") + @ValueSource(ints = { 0, 1, 3 }) public void testPoll(int version) throws Exception { - String resultsName = "v" + version + "_poll"; + RequestSpecification rs = RestAssured.given() + .body(POLL_OID.toString()); - await().atMost(20L, TimeUnit.SECONDS).pollDelay(100, TimeUnit.MILLISECONDS).until(() -> { - String result = RestAssured.given() - .body(SnmpConstants.sysDescr.toString()) - .post("/snmp/results/" + resultsName) - .then() - .statusCode(200) - .extract().body().asString(); + if (version == 3) { + rs.queryParam("user", "test") + .queryParam("securityLevel", 1); + } - return result - .startsWith("My POLL Printer - response #1,My POLL Printer - response #2,My POLL Printer - response #3"); - }); + rs.post("/snmp/poll/" + version) + .then() + .statusCode(200) + .body(Matchers.equalTo("My POLL Printer - response #1")); } @ParameterizedTest @MethodSource("supportedVersions") - public void testPollStartingDot(int version) throws Exception { - String resultsName = "v" + version + "_pollStartingDot"; - - await().atMost(20L, TimeUnit.SECONDS).pollDelay(100, TimeUnit.MILLISECONDS).until(() -> { - String result = RestAssured.given() - .body(DOT_OID.toString()) - .post("/snmp/results/" + resultsName) - .then() - .statusCode(200) - .extract().body().asString(); + public void testPollWith2OIDs(int version) throws Exception { + RestAssured.given() + .body(TWO_OIDS_A + "," + TWO_OIDS_B) + .post("/snmp/poll/" + version) + .then() + .statusCode(200) + .body(Matchers.anyOf( + Matchers.equalTo("My 2 OIDs A Printer - response #1,My 2 OIDs B Printer - response #1"), + Matchers.equalTo("My 2 OIDs B Printer - response #1,My 2 OIDs A Printer - response #1"))); + } - return result.startsWith("My DOT Printer - response #1,My DOT Printer - response #2,My DOT Printer - response #3"); - }); + @ParameterizedTest + @MethodSource("supportedVersions") + public void testPollStartingDot(int version) throws Exception { + RestAssured.given() + .body("." + DOT_OID) + .post("/snmp/poll/" + version) + .then() + .statusCode(200) + .body(Matchers.equalTo("My DOT Printer - response #1")); } @ParameterizedTest @@ -134,30 +142,4 @@ class SnmpTest { .body(Matchers.endsWith("2,My GET_NEXT Printer - response #3")); } - @ParameterizedTest - @MethodSource("supportedVersions") - public void testPollWith2OIDs(int version) throws Exception { - String resultsName = "v" + version + "_poll2oids"; - - await().atMost(20L, TimeUnit.SECONDS).pollDelay(100, TimeUnit.MILLISECONDS).until(() -> { - String resultOid1 = RestAssured.given() - .body(TWO_OIDS_A.toString()) - .post("/snmp/results/" + resultsName) - .then() - .statusCode(200) - .extract().body().asString(); - String resultOid2 = RestAssured.given() - .body(TWO_OIDS_B.toString()) - .post("/snmp/results/" + resultsName) - .then() - .statusCode(200) - .extract().body().asString(); - - return resultOid1.startsWith( - "My 2 OIDs A Printer - response #1,My 2 OIDs A Printer - response #2,My 2 OIDs A Printer - response #3") && - resultOid2.startsWith( - "My 2 OIDs B Printer - response #1,My 2 OIDs B Printer - response #2,My 2 OIDs B Printer - response #3"); - }); - - } } 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 index 769c7824b6..c487d4acdc 100644 --- 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 @@ -34,8 +34,16 @@ import org.snmp4j.PDU; import org.snmp4j.PDUv1; import org.snmp4j.ScopedPDU; import org.snmp4j.Snmp; +import org.snmp4j.mp.CounterSupport; +import org.snmp4j.mp.DefaultCounterListener; +import org.snmp4j.mp.MPv3; import org.snmp4j.mp.SnmpConstants; import org.snmp4j.mp.StatusInformation; +import org.snmp4j.security.AuthSHA; +import org.snmp4j.security.SecurityModels; +import org.snmp4j.security.SecurityProtocols; +import org.snmp4j.security.USM; +import org.snmp4j.security.UsmUser; import org.snmp4j.smi.OID; import org.snmp4j.smi.OctetString; import org.snmp4j.smi.UdpAddress; @@ -51,6 +59,7 @@ public class SnmpTestResource implements QuarkusTestResourceLifecycleManager { @Override public Map<String, String> start() { + SecurityProtocols.getInstance().addDefaultProtocols(); DefaultUdpTransportMapping udpTransportMapping; try { udpTransportMapping = new DefaultUdpTransportMapping(new UdpAddress(LOCAL_ADDRESS)); @@ -59,6 +68,20 @@ public class SnmpTestResource implements QuarkusTestResourceLifecycleManager { TestCommandResponder responder = new TestCommandResponder(snmpResponder); snmpResponder.addCommandResponder(responder); + SecurityModels respSecModels = new SecurityModels() { + }; + + CounterSupport.getInstance().addCounterListener(new DefaultCounterListener()); + MPv3 mpv3CR = (MPv3) snmpResponder.getMessageDispatcher().getMessageProcessingModel(MPv3.ID); + mpv3CR.setLocalEngineID(MPv3.createLocalEngineID(new OctetString("responder"))); + respSecModels.addSecurityModel(new USM(SecurityProtocols.getInstance(), + new OctetString(mpv3CR.getLocalEngineID()), 0)); + mpv3CR.setSecurityModels(respSecModels); + + snmpResponder.getUSM().addUser( + new UsmUser(new OctetString("test"), AuthSHA.ID, new OctetString("changeit"), + AuthSHA.ID, new OctetString("changeit"))); + snmpResponder.listen(); } catch (Exception e) { throw new RuntimeException(e); @@ -75,6 +98,7 @@ public class SnmpTestResource implements QuarkusTestResourceLifecycleManager { @Override public void stop() { + SecurityProtocols.setSecurityProtocols(null); if (snmpResponder != null) { try { snmpResponder.close(); @@ -115,7 +139,7 @@ public class SnmpTestResource implements QuarkusTestResourceLifecycleManager { int numberOfSent = counts.getOrDefault(key, 0); try { - PDU response = makeResponse(++numberOfSent, SnmpConstants.version1, vbs); + PDU response = makeResponse(pdu, ++numberOfSent, SnmpConstants.version1, vbs); if (response != null) { response.setRequestID(pdu.getRequestID()); commandResponder.getMessageDispatcher().returnResponsePdu( @@ -130,18 +154,18 @@ public class SnmpTestResource implements QuarkusTestResourceLifecycleManager { counts.put(key, numberOfSent); } - private PDU makeResponse(int counter, int version, Vector<? extends VariableBinding> vbs) { - PDU responsePDU = new PDU(); + private PDU makeResponse(PDU originalPDU, int counter, int version, Vector<? extends VariableBinding> vbs) { + PDU responsePDU = (PDU) originalPDU.clone(); responsePDU.setType(PDU.RESPONSE); responsePDU.setErrorStatus(PDU.noError); responsePDU.setErrorIndex(0); if (vbs.isEmpty()) { - VariableBinding vb = generateResponseBinding(counter, SnmpTest.PRODUCE_PDU_OID); + VariableBinding vb = generateResponseBinding(counter, version, SnmpTest.PRODUCE_PDU_OID); if (vb != null) { responsePDU.add(vb); } } else { - vbs.stream().forEach(vb -> responsePDU.add(generateResponseBinding(counter, vb.getOid()))); + vbs.stream().forEach(vb -> responsePDU.add(generateResponseBinding(counter, version, vb.getOid()))); } if (responsePDU.getVariableBindings().isEmpty()) { return null; @@ -149,7 +173,7 @@ public class SnmpTestResource implements QuarkusTestResourceLifecycleManager { return responsePDU; } - private VariableBinding generateResponseBinding(int counter, OID oid) { + private VariableBinding generateResponseBinding(int counter, int version, OID oid) { //get next test if (SnmpTest.GET_NEXT_OID.equals(oid)) { //if counter < 2 return the same oid @@ -166,11 +190,12 @@ public class SnmpTestResource implements QuarkusTestResourceLifecycleManager { } if (SnmpTest.POLL_OID.equals(oid)) { - if (counter < 4) { - return new VariableBinding(SnmpTest.POLL_OID, - new OctetString("My POLL Printer - response #" + counter)); + if (counter > 1) { + throw new RuntimeException( + String.format("Not expected request #%d for poll of version %d.", counter, version)); } - + return new VariableBinding(SnmpTest.POLL_OID, + new OctetString("My POLL Printer - response #" + counter)); } if (SnmpTest.PRODUCE_PDU_OID.equals(oid)) { @@ -195,10 +220,8 @@ public class SnmpTestResource implements QuarkusTestResourceLifecycleManager { } if (SnmpTest.DOT_OID.equals(oid)) { - if (counter < 4) { - return new VariableBinding(SnmpTest.DOT_OID, - new OctetString("My DOT Printer - response #" + counter)); - } + return new VariableBinding(SnmpTest.DOT_OID, + new OctetString("My DOT Printer - response #" + counter)); } return null;
