Skip to content

Commit

Permalink
NMS-16388: Allow set to be invoked via LocationAwareSnmpClient
Browse files Browse the repository at this point in the history
  • Loading branch information
christianpape authored Aug 20, 2024
1 parent 16fbb64 commit 064c7fc
Show file tree
Hide file tree
Showing 22 changed files with 458 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public interface SnmpStrategy {
SnmpValue get(SnmpAgentConfig agentConfig, SnmpObjId oid);
SnmpValue[] get(SnmpAgentConfig agentConfig, SnmpObjId[] oids);
CompletableFuture<SnmpValue[]> getAsync(SnmpAgentConfig agentConfig, SnmpObjId[] oids);

CompletableFuture<SnmpValue[]> setAsync(SnmpAgentConfig agentConfig, SnmpObjId[] oids, SnmpValue[] values);
SnmpValue getNext(SnmpAgentConfig agentConfig, SnmpObjId oid);
SnmpValue[] getNext(SnmpAgentConfig agentConfig, SnmpObjId[] oids);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ public static CompletableFuture<SnmpValue[]> getAsync(SnmpAgentConfig agentConfi
return getStrategy().getAsync(agentConfig, oids);
}

public static CompletableFuture<SnmpValue[]> 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,18 @@
*/
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;
import org.opennms.netmgt.snmp.SnmpObjId;
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
Expand All @@ -55,4 +59,14 @@ public interface LocationAwareSnmpClient {
SNMPRequestBuilder<List<SnmpValue>> get(SnmpAgentConfig agent, SnmpObjId... oids);

SNMPRequestBuilder<List<SnmpValue>> get(SnmpAgentConfig agent, List<SnmpObjId> oids);

SNMPRequestBuilder<SnmpValue> set(SnmpAgentConfig agent, List<SnmpObjId> oids, List<SnmpValue> values);

default SNMPRequestBuilder<SnmpValue> set(SnmpAgentConfig agent, SnmpObjId oid, SnmpValue value) {
return set(agent, Collections.singletonList(oid), Collections.singletonList(value));
}

default SNMPRequestBuilder<SnmpValue> set(SnmpAgentConfig agent, SnmpObjId[] oids, SnmpValue[] values) {
return set(agent, Lists.newArrayList(oids), Lists.newArrayList(values));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,13 @@ public CompletableFuture<SnmpValue[]> getAsync(SnmpAgentConfig agentConfig, Snmp
return CompletableFuture.completedFuture(get(agentConfig, oids));
}

@Override
@Override
public CompletableFuture<SnmpValue[]> 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];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ public CompletableFuture<SnmpValue[]> getAsync(SnmpAgentConfig agentConfig, Snmp
return CompletableFuture.completedFuture(get(agentConfig, oids));
}

@Override
public CompletableFuture<SnmpValue[]> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ public String getVersionString() {
}
}

public String getWriteCommunity() {
return m_config.getWriteCommunity();
public OctetString getWriteCommunity() {
return convertCommunity(m_config.getWriteCommunity());
}

@Override
Expand Down Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,18 @@ public CompletableFuture<SnmpValue[]> getAsync(SnmpAgentConfig agentConfig, Snmp
return future;
}

@Override
public CompletableFuture<SnmpValue[]> setAsync(SnmpAgentConfig agentConfig, SnmpObjId[] oids, SnmpValue[] values) {
final CompletableFuture<SnmpValue[]> 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
*
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,19 @@ public abstract class AbstractSNMPRequestBuilder<T> implements SNMPRequestBuilde
private final SnmpAgentConfig agent;
private List<SnmpGetRequestDTO> gets;
private List<SnmpWalkRequestDTO> walks;
private List<SnmpSetRequestDTO> sets;
private String location;
private String systemId;
private String description;
private Long timeToLiveInMilliseconds = null;

public AbstractSNMPRequestBuilder(LocationAwareSnmpClientRpcImpl client,
SnmpAgentConfig agent, List<SnmpGetRequestDTO> gets, List<SnmpWalkRequestDTO> walks) {
SnmpAgentConfig agent, List<SnmpGetRequestDTO> gets, List<SnmpWalkRequestDTO> walks, List<SnmpSetRequestDTO> 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
Expand Down Expand Up @@ -89,6 +91,7 @@ public CompletableFuture<T> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -115,6 +116,11 @@ public SNMPRequestBuilder<List<SnmpValue>> get(SnmpAgentConfig agent, List<SnmpO
return new SNMPMultiGetBuilder(this, agent, oids);
}

@Override
public SNMPRequestBuilder<SnmpValue> set(SnmpAgentConfig agent, List<SnmpObjId> oids, List<SnmpValue> values) {
return new SNMPSetBuilder(this, agent, oids, values);
}

public CompletableFuture<SnmpMultiResponseDTO> execute(SnmpRequestDTO request) {
return delegate.execute(request);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
public class SNMPMultiGetBuilder extends AbstractSNMPRequestBuilder<List<SnmpValue>> {

public SNMPMultiGetBuilder(LocationAwareSnmpClientRpcImpl client, SnmpAgentConfig agent, List<SnmpObjId> oids) {
super(client, agent, buildGetRequests(oids), Collections.emptyList());
super(client, agent, buildGetRequests(oids), Collections.emptyList(), Collections.emptyList());
}

private static List<SnmpGetRequestDTO> buildGetRequests(List<SnmpObjId> oids) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<SnmpValue> {

public SNMPSetBuilder(LocationAwareSnmpClientRpcImpl client, SnmpAgentConfig agent, List<SnmpObjId> oids, List<SnmpValue> values) {
super(client, agent, Collections.emptyList(), Collections.emptyList(), buildGetRequests(oids, values));
}

private static List<SnmpSetRequestDTO> buildGetRequests(List<SnmpObjId> oids, List<SnmpValue> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
public class SNMPSingleGetBuilder extends AbstractSNMPRequestBuilder<SnmpValue> {

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<SnmpGetRequestDTO> buildGetRequests(SnmpObjId oid) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
public class SNMPWalkBuilder extends AbstractSNMPRequestBuilder<List<SnmpResult>> {

public SNMPWalkBuilder(LocationAwareSnmpClientRpcImpl client, SnmpAgentConfig agent, List<SnmpObjId> oids) {
super(client, agent, Collections.emptyList(), buildWalkRequests(oids));
super(client, agent, Collections.emptyList(), buildWalkRequests(oids), Collections.emptyList());
}

private static List<SnmpWalkRequestDTO> buildWalkRequests(List<SnmpObjId> oids) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class SNMPWalkWithTrackerBuilder extends AbstractSNMPRequestBuilder<Colle
private final CollectionTracker m_tracker;

public SNMPWalkWithTrackerBuilder(LocationAwareSnmpClientRpcImpl client, SnmpAgentConfig agent, CollectionTracker tracker) {
super(client, agent, Collections.emptyList(), buildWalkRequests(tracker));
super(client, agent, Collections.emptyList(), buildWalkRequests(tracker), Collections.emptyList());
m_tracker = tracker;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ public CompletableFuture<SnmpMultiResponseDTO> execute(SnmpRequestDTO request) {
return m;
});
}
for (SnmpSetRequestDTO setRequest : request.getSetRequest()) {
CompletableFuture<SnmpResponseDTO> future = set(request, setRequest);
combinedFuture = combinedFuture.thenCombine(future, (m, s) -> {
m.getResponses().add(s);
return m;
});
}
if (request.getWalkRequest().size() > 0) {
CompletableFuture<Collection<SnmpResponseDTO>> future = walk(request, request.getWalkRequest());
combinedFuture = combinedFuture.thenCombine(future, (m, s) -> {
Expand Down Expand Up @@ -204,6 +211,30 @@ private CompletableFuture<SnmpResponseDTO> get(SnmpRequestDTO request, SnmpGetRe
});
}

private CompletableFuture<SnmpResponseDTO> 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<SnmpValue[]> future = SnmpUtils.setAsync(request.getAgent(), oids, value);
return future.thenApply(values -> {
final List<SnmpResult> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ public class SnmpRequestDTO implements RpcRequest {
@XmlElement(name="walk")
private List<SnmpWalkRequestDTO> walks = new ArrayList<>(0);

@XmlElement(name="set")
private List<SnmpSetRequestDTO> sets = new ArrayList<>(0);

@XmlTransient
private Long timeToLive;

Expand Down Expand Up @@ -108,6 +111,15 @@ public List<SnmpWalkRequestDTO> getWalkRequest() {
return walks;
}

public void setSetRequests(List<SnmpSetRequestDTO> sets) {
this.sets = sets;
}

public List<SnmpSetRequestDTO> getSetRequest() {
return sets;
}


public String getDescription() {
return description;
}
Expand Down Expand Up @@ -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
Expand All @@ -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);
}
Expand Down
Loading

0 comments on commit 064c7fc

Please sign in to comment.