From 064c7fcc15d5e91336f2772a43de411999f19153 Mon Sep 17 00:00:00 2001 From: Christian Pape Date: Tue, 20 Aug 2024 09:35:11 +0200 Subject: [PATCH] NMS-16388: Allow set to be invoked via LocationAwareSnmpClient --- .../org/opennms/netmgt/snmp/SnmpStrategy.java | 2 +- .../org/opennms/netmgt/snmp/SnmpUtils.java | 4 + .../snmp/proxy/LocationAwareSnmpClient.java | 14 ++ .../netmgt/snmp/joesnmp/JoeSnmpStrategy.java | 8 +- .../netmgt/snmp/mock/MockSnmpStrategy.java | 5 + .../netmgt/snmp/snmp4j/Snmp4JAgentConfig.java | 20 +- .../netmgt/snmp/snmp4j/Snmp4JStrategy.java | 16 +- .../common/AbstractSNMPRequestBuilder.java | 5 +- .../LocationAwareSnmpClientRpcImpl.java | 6 + .../proxy/common/SNMPMultiGetBuilder.java | 2 +- .../snmp/proxy/common/SNMPSetBuilder.java | 33 +++ .../proxy/common/SNMPSingleGetBuilder.java | 2 +- .../snmp/proxy/common/SNMPWalkBuilder.java | 2 +- .../common/SNMPWalkWithTrackerBuilder.java | 2 +- .../snmp/proxy/common/SnmpProxyRpcModule.java | 31 +++ .../snmp/proxy/common/SnmpRequestDTO.java | 15 +- .../snmp/proxy/common/SnmpSetRequestDTO.java | 76 +++++++ .../snmp/proxy/common/MockSnmpStrategy.java | 18 +- .../netmgt/collectd/tca/TcaCollectorIT.java | 8 +- .../collectd/AbstractSnmpCollectorIT.java | 1 + .../collectd/SnmpCollectorMinMaxValIT.java | 1 + .../opennms/netmgt/collectd/SnmpSetIT.java | 211 ++++++++++++++++++ 22 files changed, 458 insertions(+), 24 deletions(-) create mode 100644 core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPSetBuilder.java create mode 100644 core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SnmpSetRequestDTO.java create mode 100644 opennms-services/src/test/java/org/opennms/netmgt/collectd/SnmpSetIT.java diff --git a/core/snmp/api/src/main/java/org/opennms/netmgt/snmp/SnmpStrategy.java b/core/snmp/api/src/main/java/org/opennms/netmgt/snmp/SnmpStrategy.java index 0772c70819c3..87954f3589d8 100644 --- a/core/snmp/api/src/main/java/org/opennms/netmgt/snmp/SnmpStrategy.java +++ b/core/snmp/api/src/main/java/org/opennms/netmgt/snmp/SnmpStrategy.java @@ -38,7 +38,7 @@ public interface SnmpStrategy { SnmpValue get(SnmpAgentConfig agentConfig, SnmpObjId oid); SnmpValue[] get(SnmpAgentConfig agentConfig, SnmpObjId[] oids); CompletableFuture getAsync(SnmpAgentConfig agentConfig, SnmpObjId[] oids); - + CompletableFuture setAsync(SnmpAgentConfig agentConfig, SnmpObjId[] oids, SnmpValue[] values); SnmpValue getNext(SnmpAgentConfig agentConfig, SnmpObjId oid); SnmpValue[] getNext(SnmpAgentConfig agentConfig, SnmpObjId[] oids); diff --git a/core/snmp/api/src/main/java/org/opennms/netmgt/snmp/SnmpUtils.java b/core/snmp/api/src/main/java/org/opennms/netmgt/snmp/SnmpUtils.java index a02c2103caa3..6a5388ea114c 100644 --- a/core/snmp/api/src/main/java/org/opennms/netmgt/snmp/SnmpUtils.java +++ b/core/snmp/api/src/main/java/org/opennms/netmgt/snmp/SnmpUtils.java @@ -89,6 +89,10 @@ public static CompletableFuture getAsync(SnmpAgentConfig agentConfi return getStrategy().getAsync(agentConfig, oids); } + public static CompletableFuture setAsync(SnmpAgentConfig agentConfig, SnmpObjId[] oids, SnmpValue[] values) { + return getStrategy().setAsync(agentConfig, oids, values); + } + public static SnmpValue getNext(SnmpAgentConfig agentConfig, SnmpObjId oid) { return getStrategy().getNext(agentConfig, oid); } diff --git a/core/snmp/api/src/main/java/org/opennms/netmgt/snmp/proxy/LocationAwareSnmpClient.java b/core/snmp/api/src/main/java/org/opennms/netmgt/snmp/proxy/LocationAwareSnmpClient.java index 25b2f759fea6..3da888c74315 100644 --- a/core/snmp/api/src/main/java/org/opennms/netmgt/snmp/proxy/LocationAwareSnmpClient.java +++ b/core/snmp/api/src/main/java/org/opennms/netmgt/snmp/proxy/LocationAwareSnmpClient.java @@ -21,7 +21,9 @@ */ package org.opennms.netmgt.snmp.proxy; +import java.util.Collections; import java.util.List; +import java.util.Map; import org.opennms.netmgt.snmp.CollectionTracker; import org.opennms.netmgt.snmp.SnmpAgentConfig; @@ -29,6 +31,8 @@ import org.opennms.netmgt.snmp.SnmpResult; import org.opennms.netmgt.snmp.SnmpValue; +import com.google.common.collect.Lists; + /** * Asynchronous SNMP client API that either executes the request locally, delegating * the request to the current {@link org.opennms.netmgt.snmp.SnmpStrategy}, or dispatches @@ -55,4 +59,14 @@ public interface LocationAwareSnmpClient { SNMPRequestBuilder> get(SnmpAgentConfig agent, SnmpObjId... oids); SNMPRequestBuilder> get(SnmpAgentConfig agent, List oids); + + SNMPRequestBuilder set(SnmpAgentConfig agent, List oids, List values); + + default SNMPRequestBuilder set(SnmpAgentConfig agent, SnmpObjId oid, SnmpValue value) { + return set(agent, Collections.singletonList(oid), Collections.singletonList(value)); + } + + default SNMPRequestBuilder set(SnmpAgentConfig agent, SnmpObjId[] oids, SnmpValue[] values) { + return set(agent, Lists.newArrayList(oids), Lists.newArrayList(values)); + } } diff --git a/core/snmp/impl-joesnmp/src/main/java/org/opennms/netmgt/snmp/joesnmp/JoeSnmpStrategy.java b/core/snmp/impl-joesnmp/src/main/java/org/opennms/netmgt/snmp/joesnmp/JoeSnmpStrategy.java index 7d89b91dd291..0eb086157f09 100644 --- a/core/snmp/impl-joesnmp/src/main/java/org/opennms/netmgt/snmp/joesnmp/JoeSnmpStrategy.java +++ b/core/snmp/impl-joesnmp/src/main/java/org/opennms/netmgt/snmp/joesnmp/JoeSnmpStrategy.java @@ -155,7 +155,13 @@ public CompletableFuture getAsync(SnmpAgentConfig agentConfig, Snmp return CompletableFuture.completedFuture(get(agentConfig, oids)); } - @Override + @Override + public CompletableFuture setAsync(SnmpAgentConfig agentConfig, SnmpObjId[] oids, SnmpValue[] values) { + LOG.warn("The JoeSnmpStrategy does not support asynchronous SNMP SET requests."); + return CompletableFuture.completedFuture(set(agentConfig, oids, values)); + } + + @Override public SnmpValue getNext(SnmpAgentConfig snmpAgentConfig, SnmpObjId oid) { SnmpObjId[] oids = { oid }; return getNext(snmpAgentConfig, oids)[0]; diff --git a/core/snmp/impl-mock/src/main/java/org/opennms/netmgt/snmp/mock/MockSnmpStrategy.java b/core/snmp/impl-mock/src/main/java/org/opennms/netmgt/snmp/mock/MockSnmpStrategy.java index d73fff5d8518..3f4627585e9b 100644 --- a/core/snmp/impl-mock/src/main/java/org/opennms/netmgt/snmp/mock/MockSnmpStrategy.java +++ b/core/snmp/impl-mock/src/main/java/org/opennms/netmgt/snmp/mock/MockSnmpStrategy.java @@ -126,6 +126,11 @@ public CompletableFuture getAsync(SnmpAgentConfig agentConfig, Snmp return CompletableFuture.completedFuture(get(agentConfig, oids)); } + @Override + public CompletableFuture setAsync(SnmpAgentConfig agentConfig, SnmpObjId[] oids, SnmpValue[] values) { + return CompletableFuture.completedFuture(set(agentConfig, oids, values)); + } + @Override public SnmpValue getNext(final SnmpAgentConfig agentConfig, final SnmpObjId oid) { final PropertyOidContainer oidContainer = getOidContainer(agentConfig); diff --git a/core/snmp/impl-snmp4j/src/main/java/org/opennms/netmgt/snmp/snmp4j/Snmp4JAgentConfig.java b/core/snmp/impl-snmp4j/src/main/java/org/opennms/netmgt/snmp/snmp4j/Snmp4JAgentConfig.java index 8c0904026583..bf2924d4a0fd 100644 --- a/core/snmp/impl-snmp4j/src/main/java/org/opennms/netmgt/snmp/snmp4j/Snmp4JAgentConfig.java +++ b/core/snmp/impl-snmp4j/src/main/java/org/opennms/netmgt/snmp/snmp4j/Snmp4JAgentConfig.java @@ -132,8 +132,8 @@ public String getVersionString() { } } - public String getWriteCommunity() { - return m_config.getWriteCommunity(); + public OctetString getWriteCommunity() { + return convertCommunity(m_config.getWriteCommunity()); } @Override @@ -260,27 +260,31 @@ private static OID convertAuthProtocol(String authProtocol) { @VisibleForTesting public Target getTarget() { - Target target = createTarget(); + return getTarget(false); + } + + public Target getTarget(final boolean useWriteCommunity) { + Target target = createTarget(useWriteCommunity); target.setVersion(getVersion()); target.setRetries(getRetries()); target.setTimeout(getTimeout()); target.setAddress(getAddress()); target.setMaxSizeRequestPDU(getMaxRequestSize()); - + return target; } - private Target createTarget() { - return (isSnmpV3() ? createUserTarget() : createCommunityTarget()); + private Target createTarget(final boolean useWriteCommunity) { + return (isSnmpV3() ? createUserTarget() : createCommunityTarget(useWriteCommunity)); } boolean isSnmpV3() { return m_config.getVersion() == SnmpConstants.version3; } - private Target createCommunityTarget() { + private Target createCommunityTarget(final boolean useWriteCommunity) { CommunityTarget target = new CommunityTarget(); - target.setCommunity(getReadCommunity()); + target.setCommunity(useWriteCommunity ? getWriteCommunity() : getReadCommunity()); return target; } diff --git a/core/snmp/impl-snmp4j/src/main/java/org/opennms/netmgt/snmp/snmp4j/Snmp4JStrategy.java b/core/snmp/impl-snmp4j/src/main/java/org/opennms/netmgt/snmp/snmp4j/Snmp4JStrategy.java index 597ac3b37de1..bf871cdb57d5 100644 --- a/core/snmp/impl-snmp4j/src/main/java/org/opennms/netmgt/snmp/snmp4j/Snmp4JStrategy.java +++ b/core/snmp/impl-snmp4j/src/main/java/org/opennms/netmgt/snmp/snmp4j/Snmp4JStrategy.java @@ -264,6 +264,18 @@ public CompletableFuture getAsync(SnmpAgentConfig agentConfig, Snmp return future; } + @Override + public CompletableFuture setAsync(SnmpAgentConfig agentConfig, SnmpObjId[] oids, SnmpValue[] values) { + final CompletableFuture future = new CompletableFuture<>(); + final Snmp4JAgentConfig snmp4jAgentConfig = new Snmp4JAgentConfig(agentConfig); + final PDU pdu = buildPdu(snmp4jAgentConfig, PDU.SET, oids, values); + if (pdu == null) { + future.completeExceptionally(new Exception("Invalid PDU for OIDs: " + Arrays.toString(oids))); + } + send(snmp4jAgentConfig, pdu, true, future); + return future; + } + /** * SNMP4J getNext implementation * @@ -345,7 +357,7 @@ private void send(Snmp4JAgentConfig agentConfig, PDU pdu, boolean expectResponse try { final Snmp mySession = session; - mySession.send(pdu, agentConfig.getTarget(), null, new ResponseListener() { + mySession.send(pdu, agentConfig.getTarget(pdu.getType() == PDU.SET), null, new ResponseListener() { @Override public void onResponse(final ResponseEvent responseEvent) { try { @@ -374,7 +386,7 @@ public void run() { } } else { // we're not expecting a response try { - session.send(pdu, agentConfig.getTarget()); + session.send(pdu, agentConfig.getTarget(pdu.getType() == PDU.SET)); future.complete(null); } catch (final Exception e) { LOG.error("send: error during SNMP operation", e); diff --git a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/AbstractSNMPRequestBuilder.java b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/AbstractSNMPRequestBuilder.java index 4034a8f1ebf1..87f547794154 100644 --- a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/AbstractSNMPRequestBuilder.java +++ b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/AbstractSNMPRequestBuilder.java @@ -37,17 +37,19 @@ public abstract class AbstractSNMPRequestBuilder implements SNMPRequestBuilde private final SnmpAgentConfig agent; private List gets; private List walks; + private List sets; private String location; private String systemId; private String description; private Long timeToLiveInMilliseconds = null; public AbstractSNMPRequestBuilder(LocationAwareSnmpClientRpcImpl client, - SnmpAgentConfig agent, List gets, List walks) { + SnmpAgentConfig agent, List gets, List walks, List sets) { this.client = Objects.requireNonNull(client); this.agent = Objects.requireNonNull(agent); this.gets = Objects.requireNonNull(gets); this.walks = Objects.requireNonNull(walks); + this.sets = Objects.requireNonNull(sets); } @Override @@ -89,6 +91,7 @@ public CompletableFuture execute() { snmpRequestDTO.setDescription(description); snmpRequestDTO.setGetRequests(gets); snmpRequestDTO.setWalkRequests(walks); + snmpRequestDTO.setSetRequests(sets); // TTL specified in agent configuration overwrites any previous ttls specified. if (agent.getTTL() != null) { timeToLiveInMilliseconds = agent.getTTL(); diff --git a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/LocationAwareSnmpClientRpcImpl.java b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/LocationAwareSnmpClientRpcImpl.java index 77ce45c80866..5697c46722f2 100644 --- a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/LocationAwareSnmpClientRpcImpl.java +++ b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/LocationAwareSnmpClientRpcImpl.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -115,6 +116,11 @@ public SNMPRequestBuilder> get(SnmpAgentConfig agent, List set(SnmpAgentConfig agent, List oids, List values) { + return new SNMPSetBuilder(this, agent, oids, values); + } + public CompletableFuture execute(SnmpRequestDTO request) { return delegate.execute(request); } diff --git a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPMultiGetBuilder.java b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPMultiGetBuilder.java index 2c1d41496569..8af513931006 100644 --- a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPMultiGetBuilder.java +++ b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPMultiGetBuilder.java @@ -33,7 +33,7 @@ public class SNMPMultiGetBuilder extends AbstractSNMPRequestBuilder> { public SNMPMultiGetBuilder(LocationAwareSnmpClientRpcImpl client, SnmpAgentConfig agent, List oids) { - super(client, agent, buildGetRequests(oids), Collections.emptyList()); + super(client, agent, buildGetRequests(oids), Collections.emptyList(), Collections.emptyList()); } private static List buildGetRequests(List oids) { diff --git a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPSetBuilder.java b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPSetBuilder.java new file mode 100644 index 000000000000..4945db5a45d5 --- /dev/null +++ b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPSetBuilder.java @@ -0,0 +1,33 @@ +package org.opennms.netmgt.snmp.proxy.common; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.opennms.netmgt.snmp.SnmpAgentConfig; +import org.opennms.netmgt.snmp.SnmpObjId; +import org.opennms.netmgt.snmp.SnmpResult; +import org.opennms.netmgt.snmp.SnmpValue; + +public class SNMPSetBuilder extends AbstractSNMPRequestBuilder { + + public SNMPSetBuilder(LocationAwareSnmpClientRpcImpl client, SnmpAgentConfig agent, List oids, List values) { + super(client, agent, Collections.emptyList(), Collections.emptyList(), buildGetRequests(oids, values)); + } + + private static List buildGetRequests(List oids, List values) { + final SnmpSetRequestDTO setRequest = new SnmpSetRequestDTO(); + setRequest.setOids(oids); + setRequest.setValues(values); + return Collections.singletonList(setRequest); + } + + @Override + protected SnmpValue processResponse(SnmpMultiResponseDTO response) { + return response.getResponses().stream() + .flatMap(res -> res.getResults().stream()) + .findFirst() + .map(SnmpResult::getValue) + .orElse(null); + } +} diff --git a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPSingleGetBuilder.java b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPSingleGetBuilder.java index a5176a642ead..86e456d1ecd3 100644 --- a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPSingleGetBuilder.java +++ b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPSingleGetBuilder.java @@ -32,7 +32,7 @@ public class SNMPSingleGetBuilder extends AbstractSNMPRequestBuilder { public SNMPSingleGetBuilder(LocationAwareSnmpClientRpcImpl client, SnmpAgentConfig agent, SnmpObjId oid) { - super(client, agent, buildGetRequests(oid), Collections.emptyList()); + super(client, agent, buildGetRequests(oid), Collections.emptyList(), Collections.emptyList()); } private static List buildGetRequests(SnmpObjId oid) { diff --git a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPWalkBuilder.java b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPWalkBuilder.java index ec1c8faa2b60..78b836fd91c5 100644 --- a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPWalkBuilder.java +++ b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPWalkBuilder.java @@ -31,7 +31,7 @@ public class SNMPWalkBuilder extends AbstractSNMPRequestBuilder> { public SNMPWalkBuilder(LocationAwareSnmpClientRpcImpl client, SnmpAgentConfig agent, List oids) { - super(client, agent, Collections.emptyList(), buildWalkRequests(oids)); + super(client, agent, Collections.emptyList(), buildWalkRequests(oids), Collections.emptyList()); } private static List buildWalkRequests(List oids) { diff --git a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPWalkWithTrackerBuilder.java b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPWalkWithTrackerBuilder.java index 6e4c339983b4..53ebcd7cdd89 100644 --- a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPWalkWithTrackerBuilder.java +++ b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPWalkWithTrackerBuilder.java @@ -34,7 +34,7 @@ public class SNMPWalkWithTrackerBuilder extends AbstractSNMPRequestBuilder execute(SnmpRequestDTO request) { return m; }); } + for (SnmpSetRequestDTO setRequest : request.getSetRequest()) { + CompletableFuture future = set(request, setRequest); + combinedFuture = combinedFuture.thenCombine(future, (m, s) -> { + m.getResponses().add(s); + return m; + }); + } if (request.getWalkRequest().size() > 0) { CompletableFuture> future = walk(request, request.getWalkRequest()); combinedFuture = combinedFuture.thenCombine(future, (m, s) -> { @@ -204,6 +211,30 @@ private CompletableFuture get(SnmpRequestDTO request, SnmpGetRe }); } + private CompletableFuture set(SnmpRequestDTO request, SnmpSetRequestDTO get) { + final SnmpObjId[] oids = get.getOids().toArray(new SnmpObjId[0]); + final SnmpValue[] value = get.getValues().toArray(new SnmpValue[0]); + final CompletableFuture future = SnmpUtils.setAsync(request.getAgent(), oids, value); + return future.thenApply(values -> { + final List results = new ArrayList<>(oids.length); + if (values.length < oids.length) { + // Should never reach here, should have thrown exception in SnmpUtils. + LOG.warn("Received error response from SNMP for the agent {} for oids = {}", request.getAgent(), oids); + final SnmpResponseDTO responseDTO = new SnmpResponseDTO(); + responseDTO.setCorrelationId(get.getCorrelationId()); + } else { + for (int i = 0; i < oids.length; i++) { + final SnmpResult result = new SnmpResult(oids[i], null, values[i]); + results.add(result); + } + } + final SnmpResponseDTO responseDTO = new SnmpResponseDTO(); + responseDTO.setCorrelationId(get.getCorrelationId()); + responseDTO.setResults(results); + return responseDTO; + }); + } + @Override public SnmpMultiResponseDTO createResponseWithException(Throwable ex) { return new SnmpMultiResponseDTO(ex); diff --git a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SnmpRequestDTO.java b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SnmpRequestDTO.java index c5b0104f31f6..3a9a79006437 100644 --- a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SnmpRequestDTO.java +++ b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SnmpRequestDTO.java @@ -61,6 +61,9 @@ public class SnmpRequestDTO implements RpcRequest { @XmlElement(name="walk") private List walks = new ArrayList<>(0); + @XmlElement(name="set") + private List sets = new ArrayList<>(0); + @XmlTransient private Long timeToLive; @@ -108,6 +111,15 @@ public List getWalkRequest() { return walks; } + public void setSetRequests(List sets) { + this.sets = sets; + } + + public List getSetRequest() { + return sets; + } + + public String getDescription() { return description; } @@ -145,7 +157,7 @@ public void addTracingInfo(String key, String value) { @Override public int hashCode() { - return Objects.hash(location, systemId, agent, gets, walks, description, timeToLive); + return Objects.hash(location, systemId, agent, gets, walks, sets, description, timeToLive); } @Override @@ -162,6 +174,7 @@ public boolean equals(Object obj) { && Objects.equals(this.agent, other.agent) && Objects.equals(this.gets, other.gets) && Objects.equals(this.walks, other.walks) + && Objects.equals(this.sets, other.sets) && Objects.equals(this.description, other.description) && Objects.equals(this.timeToLive, other.timeToLive); } diff --git a/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SnmpSetRequestDTO.java b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SnmpSetRequestDTO.java new file mode 100644 index 000000000000..c1f2f4376022 --- /dev/null +++ b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SnmpSetRequestDTO.java @@ -0,0 +1,76 @@ +package org.opennms.netmgt.snmp.proxy.common; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import org.opennms.netmgt.snmp.SnmpObjId; +import org.opennms.netmgt.snmp.SnmpObjIdXmlAdapter; +import org.opennms.netmgt.snmp.SnmpValue; + +@XmlRootElement(name="snmp-set-request") +@XmlAccessorType(XmlAccessType.NONE) +public class SnmpSetRequestDTO { + + @XmlAttribute(name="correlation-id") + private String correlationId; + + @XmlElement(name="oid") + @XmlJavaTypeAdapter(SnmpObjIdXmlAdapter.class) + private List oids = new ArrayList<>(0); + + @XmlElement(name="value") + @XmlJavaTypeAdapter(SnmpObjIdXmlAdapter.class) + private List values = new ArrayList<>(0); + + public String getCorrelationId() { + return correlationId; + } + + public void setCorrelationId(String correlationId) { + this.correlationId = correlationId; + } + + public List getOids() { + return oids; + } + + public void setOids(List oids) { + this.oids = oids; + } + + public List getValues() { + return values; + } + + public void setValues(List values) { + this.values = values; + } + + @Override + public int hashCode() { + return Objects.hash(correlationId, oids, values); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final SnmpSetRequestDTO other = (SnmpSetRequestDTO) obj; + return Objects.equals(this.correlationId, other.correlationId) + && Objects.equals(this.oids, other.oids) + && Objects.equals(this.values, other.values); + } +} diff --git a/core/snmp/proxy-rpc-tests/src/test/java/org/opennms/netmgt/snmp/proxy/common/MockSnmpStrategy.java b/core/snmp/proxy-rpc-tests/src/test/java/org/opennms/netmgt/snmp/proxy/common/MockSnmpStrategy.java index 7fb16689ab57..f6498eaad637 100644 --- a/core/snmp/proxy-rpc-tests/src/test/java/org/opennms/netmgt/snmp/proxy/common/MockSnmpStrategy.java +++ b/core/snmp/proxy-rpc-tests/src/test/java/org/opennms/netmgt/snmp/proxy/common/MockSnmpStrategy.java @@ -42,7 +42,8 @@ public class MockSnmpStrategy implements SnmpStrategy { - private static boolean firstCall = true; + private static boolean firstGetCall = true; + private static boolean firstSetCall = true; @Override public SnmpWalker createWalker(SnmpAgentConfig agentConfig, String name, CollectionTracker tracker) { @@ -71,8 +72,17 @@ public SnmpValue[] get(SnmpAgentConfig agentConfig, SnmpObjId[] oids) { @Override public CompletableFuture getAsync(SnmpAgentConfig agentConfig, SnmpObjId[] oids) { - if (firstCall) { - firstCall = false; + if (firstGetCall) { + firstGetCall = false; + return SnmpProxyRpcModuleTest.completedFuture; + } + return SnmpProxyRpcModuleTest.failedFuture; + } + + @Override + public CompletableFuture setAsync(SnmpAgentConfig agentConfig, SnmpObjId[] oids, SnmpValue[] values) { + if (firstSetCall) { + firstSetCall = false; return SnmpProxyRpcModuleTest.completedFuture; } return SnmpProxyRpcModuleTest.failedFuture; @@ -151,6 +161,6 @@ public byte[] getLocalEngineID() { } public static void setFirstCall(boolean firstCall) { - MockSnmpStrategy.firstCall = firstCall; + MockSnmpStrategy.firstGetCall = firstCall; } } diff --git a/features/juniper-tca-collector/src/test/java/org/opennms/netmgt/collectd/tca/TcaCollectorIT.java b/features/juniper-tca-collector/src/test/java/org/opennms/netmgt/collectd/tca/TcaCollectorIT.java index fffaab28cae8..4cc08be4cc31 100644 --- a/features/juniper-tca-collector/src/test/java/org/opennms/netmgt/collectd/tca/TcaCollectorIT.java +++ b/features/juniper-tca-collector/src/test/java/org/opennms/netmgt/collectd/tca/TcaCollectorIT.java @@ -75,6 +75,7 @@ import org.opennms.netmgt.model.OnmsIpInterface; import org.opennms.netmgt.model.OnmsNode; import org.opennms.netmgt.rrd.RrdStrategy; +import org.opennms.netmgt.snmp.SnmpAgentConfig; import org.opennms.netmgt.snmp.SnmpObjId; import org.opennms.netmgt.snmp.SnmpUtils; import org.opennms.netmgt.snmp.SnmpValue; @@ -294,9 +295,12 @@ public void testCollector() throws Exception { SnmpValue v1a = SnmpUtils.get(m_collectionAgent.getAgentConfig(), peer1); SnmpValue v2a = SnmpUtils.get(m_collectionAgent.getAgentConfig(), peer2); + final SnmpAgentConfig agentConfig = m_collectionAgent.getAgentConfig(); + agentConfig.setWriteCommunity("public"); + // Set New Values - SnmpUtils.set(m_collectionAgent.getAgentConfig(), peer1, valFac.getOctetString(sb.toString().getBytes())); - SnmpUtils.set(m_collectionAgent.getAgentConfig(), peer2, valFac.getOctetString(sb.toString().getBytes())); + SnmpUtils.set(agentConfig, peer1, valFac.getOctetString(sb.toString().getBytes())); + SnmpUtils.set(agentConfig, peer2, valFac.getOctetString(sb.toString().getBytes())); // Validate New Values SnmpValue v1b = SnmpUtils.get(m_collectionAgent.getAgentConfig(), peer1); diff --git a/opennms-services/src/test/java/org/opennms/netmgt/collectd/AbstractSnmpCollectorIT.java b/opennms-services/src/test/java/org/opennms/netmgt/collectd/AbstractSnmpCollectorIT.java index 295218a979af..047a19023760 100644 --- a/opennms-services/src/test/java/org/opennms/netmgt/collectd/AbstractSnmpCollectorIT.java +++ b/opennms-services/src/test/java/org/opennms/netmgt/collectd/AbstractSnmpCollectorIT.java @@ -179,6 +179,7 @@ public void setUp() throws Exception { m_pollOutagesDao, collector.getClass().getCanonicalName()); m_collectionAgent = DefaultSnmpCollectionAgent.create(iface.getId(), m_ipInterfaceDao, m_transactionManager); m_agentConfig = SnmpPeerFactory.getInstance().getAgentConfig(InetAddressUtils.getLocalHostAddress()); + m_agentConfig.setWriteCommunity("public"); } protected abstract AbstractSnmpCollector createSnmpCollector(); diff --git a/opennms-services/src/test/java/org/opennms/netmgt/collectd/SnmpCollectorMinMaxValIT.java b/opennms-services/src/test/java/org/opennms/netmgt/collectd/SnmpCollectorMinMaxValIT.java index 7f68a9ccc724..a46f048e1bd6 100644 --- a/opennms-services/src/test/java/org/opennms/netmgt/collectd/SnmpCollectorMinMaxValIT.java +++ b/opennms-services/src/test/java/org/opennms/netmgt/collectd/SnmpCollectorMinMaxValIT.java @@ -137,6 +137,7 @@ public void setUp() throws Exception { SnmpPeerFactory.setInstance(m_snmpPeerFactory); m_agentConfig = m_snmpPeerFactory.getAgentConfig(InetAddressUtils.addr(TEST_HOST_ADDRESS)); + m_agentConfig.setWriteCommunity("public"); m_rrdStrategy = new JRobinRrdStrategy(); diff --git a/opennms-services/src/test/java/org/opennms/netmgt/collectd/SnmpSetIT.java b/opennms-services/src/test/java/org/opennms/netmgt/collectd/SnmpSetIT.java new file mode 100644 index 000000000000..4e4ac9d9d4a6 --- /dev/null +++ b/opennms-services/src/test/java/org/opennms/netmgt/collectd/SnmpSetIT.java @@ -0,0 +1,211 @@ +package org.opennms.netmgt.collectd; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.opennms.core.rpc.mock.MockRpcClientFactory; +import org.opennms.netmgt.mock.OpenNMSITCase; +import org.opennms.netmgt.snmp.SnmpAgentConfig; +import org.opennms.netmgt.snmp.SnmpObjId; +import org.opennms.netmgt.snmp.SnmpUtils; +import org.opennms.netmgt.snmp.SnmpValue; +import org.opennms.netmgt.snmp.proxy.LocationAwareSnmpClient; +import org.opennms.netmgt.snmp.proxy.common.LocationAwareSnmpClientRpcImpl; +import org.opennms.netmgt.snmp.snmp4j.Snmp4JValueFactory; +import org.snmp4j.TransportMapping; +import org.snmp4j.agent.BaseAgent; +import org.snmp4j.agent.CommandProcessor; +import org.snmp4j.agent.DuplicateRegistrationException; +import org.snmp4j.agent.mo.MOAccessImpl; +import org.snmp4j.agent.mo.MOScalar; +import org.snmp4j.agent.mo.MOTableRow; +import org.snmp4j.agent.mo.snmp.RowStatus; +import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB; +import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB; +import org.snmp4j.agent.mo.snmp.SnmpTargetMIB; +import org.snmp4j.agent.mo.snmp.StorageType; +import org.snmp4j.agent.mo.snmp.VacmMIB; +import org.snmp4j.agent.security.MutableVACM; +import org.snmp4j.mp.MPv3; +import org.snmp4j.security.SecurityLevel; +import org.snmp4j.security.SecurityModel; +import org.snmp4j.security.SecurityProtocols; +import org.snmp4j.security.USM; +import org.snmp4j.smi.Address; +import org.snmp4j.smi.GenericAddress; +import org.snmp4j.smi.Integer32; +import org.snmp4j.smi.OID; +import org.snmp4j.smi.OctetString; +import org.snmp4j.smi.Variable; +import org.snmp4j.transport.TransportMappings; + +import com.google.common.collect.Lists; + +public class SnmpSetIT extends OpenNMSITCase { + private static class TestSnmpAgent extends BaseAgent { + private static final String ADDRESS = "127.0.0.1/9161"; + + private TestSnmpAgent(TemporaryFolder tempFolder) throws IOException { + super(tempFolder.newFile("conf.agent"), tempFolder.newFile("bootCounter.agent"), new CommandProcessor(new OctetString(MPv3.createLocalEngineID()))); + final MOScalar myScalar1 = new MOScalar(new OID(".1.3.0"), MOAccessImpl.ACCESS_READ_WRITE, new OctetString("initial1")); + final MOScalar myScalar2 = new MOScalar(new OID(".1.4.0"), MOAccessImpl.ACCESS_READ_WRITE, new OctetString("initial2")); + try { + server.register(myScalar1, null); + server.register(myScalar2, null); + } catch (DuplicateRegistrationException e) { + //ignore + } + } + + @Override + protected void initTransportMappings() throws IOException { + transportMappings = new TransportMapping[1]; + final Address addr = GenericAddress.parse(ADDRESS); + final TransportMapping tm = TransportMappings.getInstance().createTransportMapping(addr); + transportMappings[0] = tm; + } + + @Override + protected void registerManagedObjects() { + } + + @Override + protected void unregisterManagedObjects() { + } + + @Override + protected void addUsmUser(USM usm) { + } + + @Override + protected void addNotificationTargets(final SnmpTargetMIB snmpTargetMIB, final SnmpNotificationMIB snmpNotificationMIB) { + } + + public void start() throws IOException { + init(); + addShutdownHook(); + getServer().addContext(new OctetString("public")); + finishInit(); + SecurityProtocols.getInstance().addDefaultProtocols(); + run(); + sendColdStartNotification(); + } + + @Override + protected void addViews(final VacmMIB vacmMIB) { + // define read community access + vacmMIB.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c, new OctetString("cpublic"), new OctetString("v1v2group1"), StorageType.nonVolatile); + vacmMIB.addAccess(new OctetString("v1v2group1"), new OctetString("public"), SecurityModel.SECURITY_MODEL_SNMPv2c, SecurityLevel.NOAUTH_NOPRIV, MutableVACM.VACM_MATCH_EXACT, new OctetString("fullReadView1"), new OctetString("fullWriteView1"), new OctetString("fullNotifyView1"), StorageType.nonVolatile); + vacmMIB.addViewTreeFamily(new OctetString("fullReadView1"), new OID("1.3"), new OctetString(), VacmMIB.vacmViewIncluded, StorageType.nonVolatile); + vacmMIB.addViewTreeFamily(new OctetString("fullReadView1"), new OID("1.4"), new OctetString(), VacmMIB.vacmViewIncluded, StorageType.nonVolatile); + // define write community access + vacmMIB.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c, new OctetString("cprivate"), new OctetString("v1v2group2"), StorageType.nonVolatile); + vacmMIB.addAccess(new OctetString("v1v2group2"), new OctetString("public"), SecurityModel.SECURITY_MODEL_SNMPv2c, SecurityLevel.NOAUTH_NOPRIV, MutableVACM.VACM_MATCH_EXACT, new OctetString("fullReadView2"), new OctetString("fullWriteView2"), new OctetString("fullNotifyView2"), StorageType.nonVolatile); + vacmMIB.addViewTreeFamily(new OctetString("fullReadView2"), new OID("1.3"), new OctetString(), VacmMIB.vacmViewIncluded, StorageType.nonVolatile); + vacmMIB.addViewTreeFamily(new OctetString("fullWriteView2"), new OID("1.3"), new OctetString(), VacmMIB.vacmViewIncluded, StorageType.nonVolatile); + vacmMIB.addViewTreeFamily(new OctetString("fullReadView2"), new OID("1.4"), new OctetString(), VacmMIB.vacmViewIncluded, StorageType.nonVolatile); + vacmMIB.addViewTreeFamily(new OctetString("fullWriteView2"), new OID("1.4"), new OctetString(), VacmMIB.vacmViewIncluded, StorageType.nonVolatile); + } + + @Override + protected void addCommunities(final SnmpCommunityMIB communityMIB) { + // read community + final Variable[] com2sec = new Variable[]{new OctetString("public"), new OctetString("cpublic"), getAgent().getContextEngineID(), new OctetString("public"), new OctetString(), new Integer32(StorageType.nonVolatile), new Integer32(RowStatus.active)}; + final MOTableRow row = communityMIB.getSnmpCommunityEntry().createRow(new OctetString("public2public").toSubIndex(true), com2sec); + communityMIB.getSnmpCommunityEntry().addRow((SnmpCommunityMIB.SnmpCommunityEntryRow) row); + // write community + final Variable[] com2sec2 = new Variable[]{new OctetString("private"), new OctetString("cprivate"), getAgent().getContextEngineID(), new OctetString("public"), new OctetString(), new Integer32(StorageType.nonVolatile), new Integer32(RowStatus.active)}; + final MOTableRow row2 = communityMIB.getSnmpCommunityEntry().createRow(new OctetString("public2public").toSubIndex(true), com2sec2); + communityMIB.getSnmpCommunityEntry().addRow((SnmpCommunityMIB.SnmpCommunityEntryRow) row2); + } + } + + private final LocationAwareSnmpClient m_locationAwareSnmpClient = new LocationAwareSnmpClientRpcImpl(new MockRpcClientFactory()); + + private TestSnmpAgent testSnmpAgent; + + @Before + @Override + public void setUp() throws Exception { + setStartEventd(false); + super.setUp(); + + SnmpUtils.unsetStrategyResolver(); + System.getProperties().remove("org.opennms.snmp.strategyClass"); + + final TemporaryFolder temporaryFolder = new TemporaryFolder(); + temporaryFolder.create(); + + testSnmpAgent = new TestSnmpAgent(temporaryFolder); + testSnmpAgent.start(); + } + + @After + @Override + public void tearDown() throws Exception { + testSnmpAgent.stop(); + testSnmpAgent = null; + } + + @Test + public void testSnmpSet() throws InterruptedException, ExecutionException { + final SnmpAgentConfig snmpAgentConfig = new SnmpAgentConfig(); + snmpAgentConfig.setAddress(myLocalHost()); + snmpAgentConfig.setPort(9161); + snmpAgentConfig.setReadCommunity("public"); + snmpAgentConfig.setWriteCommunity("private"); + snmpAgentConfig.setVersion(SnmpAgentConfig.VERSION2C); + snmpAgentConfig.setRetries(2); + + // first get, text should be "initial1" + final SnmpValue result1 = m_locationAwareSnmpClient.get(snmpAgentConfig, SnmpObjId.get(".1.3.0")).execute().get(); + assertEquals("initial1", result1.toString()); + + // invoke set, return value should be "foobar" + final SnmpValue modifiedText = new Snmp4JValueFactory().getOctetString("foobar".getBytes()); + final SnmpValue result2 = m_locationAwareSnmpClient.set(snmpAgentConfig, SnmpObjId.get(".1.3.0"), modifiedText).execute().get(); + assertEquals("foobar", result2.toString()); + + // now get again, value should be "foobar" now + final SnmpValue result3 = m_locationAwareSnmpClient.get(snmpAgentConfig, SnmpObjId.get(".1.3.0")).execute().get(); + assertEquals("foobar", result3.toString()); + } + + @Test + public void testTwoSnmpSet() throws InterruptedException, ExecutionException { + final SnmpAgentConfig snmpAgentConfig = new SnmpAgentConfig(); + snmpAgentConfig.setAddress(myLocalHost()); + snmpAgentConfig.setPort(9161); + snmpAgentConfig.setReadCommunity("public"); + snmpAgentConfig.setWriteCommunity("private"); + snmpAgentConfig.setVersion(SnmpAgentConfig.VERSION2C); + snmpAgentConfig.setRetries(2); + + // first get, text should be "initial1" + final SnmpValue result1 = m_locationAwareSnmpClient.get(snmpAgentConfig, SnmpObjId.get(".1.3.0")).execute().get(); + assertEquals("initial1", result1.toString()); + final SnmpValue result2 = m_locationAwareSnmpClient.get(snmpAgentConfig, SnmpObjId.get(".1.4.0")).execute().get(); + assertEquals("initial2", result2.toString()); + + // invoke set, passing both variable changes at once, return value should be "foobar1" or "foobar2" + final SnmpValue modifiedText1 = new Snmp4JValueFactory().getOctetString("foobar1".getBytes()); + final SnmpValue modifiedText2 = new Snmp4JValueFactory().getOctetString("foobar2".getBytes()); + final SnmpValue result3 = m_locationAwareSnmpClient.set(snmpAgentConfig, Lists.newArrayList(SnmpObjId.get(".1.3.0"), SnmpObjId.get(".1.4.0")), Lists.newArrayList(modifiedText1, modifiedText2)).execute().get(); + assertTrue("foobar1".equals(result3.toString()) || "foobar2".equals(result3.toString())); + + // now get again, values should be "foobar1" and "foobar2" now + final SnmpValue result4 = m_locationAwareSnmpClient.get(snmpAgentConfig, SnmpObjId.get(".1.3.0")).execute().get(); + assertEquals("foobar1", result4.toString()); + final SnmpValue result5 = m_locationAwareSnmpClient.get(snmpAgentConfig, SnmpObjId.get(".1.4.0")).execute().get(); + assertEquals("foobar2", result5.toString()); + } +}