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 df56548e186f..60f10d2fbc77 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 @@ -45,7 +45,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 161846269e2a..e455cfd04a90 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 @@ -96,6 +96,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 06d8b8601a48..54d418cd482c 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 @@ -28,7 +28,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; @@ -36,6 +38,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 @@ -62,4 +66,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 15e74bebadce..31c900ffe7e7 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 @@ -162,7 +162,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 22f1d515ea7e..5e1792a1e0de 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 @@ -133,6 +133,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 2fcbf85d2d7c..967baaf4fc6f 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 @@ -139,8 +139,8 @@ public String getVersionString() { } } - public String getWriteCommunity() { - return m_config.getWriteCommunity(); + public OctetString getWriteCommunity() { + return convertCommunity(m_config.getWriteCommunity()); } @Override @@ -267,27 +267,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 36ff1d832a93..d3c469552d90 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 @@ -271,6 +271,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 * @@ -352,7 +364,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 { @@ -381,7 +393,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 f58a081754fc..9f953ef7283c 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 @@ -44,17 +44,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 @@ -96,6 +98,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 c1db4446cdb6..be5db6e1965a 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 @@ -30,6 +30,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; @@ -122,6 +123,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 d810ede0ccbe..ce1399af56d2 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 @@ -40,7 +40,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..8cda943195b0 --- /dev/null +++ b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SNMPSetBuilder.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * This file is part of OpenNMS(R). + * + * Copyright (C) 2024 The OpenNMS Group, Inc. + * OpenNMS(R) is Copyright (C) 1999-2024 The OpenNMS Group, Inc. + * + * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. + * + * OpenNMS(R) is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * OpenNMS(R) is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with OpenNMS(R). If not, see: + * http://www.gnu.org/licenses/ + * + * For more information contact: + * OpenNMS(R) Licensing + * http://www.opennms.org/ + * http://www.opennms.com/ + *******************************************************************************/ + +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 cae1f0a15e9b..9b4ccdb7e84f 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 @@ -39,7 +39,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 c36ce91f2e5e..2dea7ae99ef2 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 @@ -38,7 +38,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 6a2ad84d77fb..c5002e6cc850 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 @@ -41,7 +41,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) -> { @@ -211,6 +218,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 08e393399475..7cd3385d8975 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 @@ -68,6 +68,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; @@ -115,6 +118,15 @@ public List getWalkRequest() { return walks; } + public void setSetRequests(List sets) { + this.sets = sets; + } + + public List getSetRequest() { + return sets; + } + + public String getDescription() { return description; } @@ -152,7 +164,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 @@ -169,6 +181,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..3d78b4f4f80b --- /dev/null +++ b/core/snmp/proxy-rpc-impl/src/main/java/org/opennms/netmgt/snmp/proxy/common/SnmpSetRequestDTO.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * This file is part of OpenNMS(R). + * + * Copyright (C) 2024 The OpenNMS Group, Inc. + * OpenNMS(R) is Copyright (C) 1999-2024 The OpenNMS Group, Inc. + * + * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. + * + * OpenNMS(R) is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * OpenNMS(R) is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with OpenNMS(R). If not, see: + * http://www.gnu.org/licenses/ + * + * For more information contact: + * OpenNMS(R) Licensing + * http://www.opennms.org/ + * http://www.opennms.com/ + *******************************************************************************/ + +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 5532ca718956..ff350891bf00 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 @@ -49,7 +49,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) { @@ -78,8 +79,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; @@ -158,6 +168,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 d11b429d03ca..fe8b1f9eb1a9 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 @@ -82,6 +82,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; @@ -301,9 +302,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 a3bd13310aeb..60eb43dc7eea 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 @@ -186,6 +186,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 0838f60d13e3..0385466685c8 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 @@ -144,6 +144,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..1b2ed1497b96 --- /dev/null +++ b/opennms-services/src/test/java/org/opennms/netmgt/collectd/SnmpSetIT.java @@ -0,0 +1,239 @@ +/******************************************************************************* + * This file is part of OpenNMS(R). + * + * Copyright (C) 2024 The OpenNMS Group, Inc. + * OpenNMS(R) is Copyright (C) 1999-2024 The OpenNMS Group, Inc. + * + * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. + * + * OpenNMS(R) is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * OpenNMS(R) is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with OpenNMS(R). If not, see: + * http://www.gnu.org/licenses/ + * + * For more information contact: + * OpenNMS(R) Licensing + * http://www.opennms.org/ + * http://www.opennms.com/ + *******************************************************************************/ + +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()); + } +}