import static java.util.Objects.requireNonNull;
+import com.google.common.base.MoreObjects;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.netty.util.concurrent.Promise;
-import java.net.InetSocketAddress;
-import java.security.PublicKey;
import org.opendaylight.netconf.api.NetconfMessage;
import org.opendaylight.netconf.api.NetconfTerminationReason;
import org.opendaylight.netconf.callhome.protocol.CallHomeChannelActivator;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+// Non-final to allow mocking
class CallHomeMountSessionContext {
- public interface CloseCallback {
+ @FunctionalInterface
+ interface CloseCallback {
void onClosed(CallHomeMountSessionContext deviceContext);
}
this.onClose = requireNonNull(callback, "callback");
}
+ CallHomeProtocolSessionContext getProtocol() {
+ return protocol;
+ }
+
NodeId getId() {
return nodeId;
}
- public ContextKey getContextKey() {
+ ContextKey getContextKey() {
return key;
}
Node getConfigNode() {
- NodeBuilder builder = new NodeBuilder();
- return builder.setNodeId(getId()).addAugmentation(NetconfNode.class, configNetconfNode()).build();
- }
-
- private NetconfNode configNetconfNode() {
- NetconfNodeBuilder node = new NetconfNodeBuilder();
- node.setHost(new Host(key.getIpAddress()));
- node.setPort(key.getPort());
- node.setTcpOnly(Boolean.FALSE);
- node.setCredentials(new LoginPasswordBuilder().setUsername("ommited").setPassword("ommited").build());
- node.setSchemaless(Boolean.FALSE);
- return node.build();
+ return new NodeBuilder()
+ .setNodeId(getId())
+ .addAugmentation(NetconfNode.class, new NetconfNodeBuilder()
+ .setHost(new Host(key.getIpAddress()))
+ .setPort(key.getPort())
+ .setTcpOnly(Boolean.FALSE)
+ .setCredentials(new LoginPasswordBuilder()
+ .setUsername("ommited")
+ .setPassword("ommited")
+ .build())
+ .setSchemaless(Boolean.FALSE)
+ .build())
+ .build();
}
@SuppressWarnings("unchecked")
return (Promise<V>) activator.activate(wrap(sessionListener));
}
- @SuppressWarnings("deprecation")
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("address", protocol.getRemoteAddress())
+ .add("hostKey", protocol.getRemoteServerKey())
+ .toString();
+ }
+
private NetconfClientSessionListener wrap(final NetconfClientSessionListener delegate) {
return new NetconfClientSessionListener() {
@Override
private void removeSelf() {
onClose.onClosed(this);
}
-
- InetSocketAddress getRemoteAddress() {
- return protocol.getRemoteAddress();
- }
-
- PublicKey getRemoteServerKey() {
- return protocol.getRemoteServerKey();
- }
}
*/
package org.opendaylight.netconf.callhome.mount;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.MultimapBuilder;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.security.PublicKey;
-import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.netconf.callhome.mount.CallHomeMountSessionContext.CloseCallback;
import org.opendaylight.netconf.callhome.protocol.CallHomeChannelActivator;
import org.opendaylight.netconf.callhome.protocol.CallHomeProtocolSessionContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class CallHomeMountSessionManager implements CallHomeMountSessionContext.CloseCallback {
+ private static final Logger LOG = LoggerFactory.getLogger(CallHomeMountSessionManager.class);
private final ConcurrentMap<SocketAddress, CallHomeMountSessionContext> contextByAddress =
new ConcurrentHashMap<>();
- private final Multimap<PublicKey, CallHomeMountSessionContext> contextByPublicKey = MultimapBuilder.hashKeys()
- .hashSetValues().build();
+ private final ConcurrentMap<PublicKey, CallHomeMountSessionContext> contextByPublicKey = new ConcurrentHashMap<>();
- public @Nullable CallHomeMountSessionContext getByAddress(InetSocketAddress remoteAddr) {
+ @Nullable CallHomeMountSessionContext getByAddress(final InetSocketAddress remoteAddr) {
return contextByAddress.get(remoteAddr);
}
+ CallHomeMountSessionContext createSession(final CallHomeProtocolSessionContext session,
+ final CallHomeChannelActivator activator, final CloseCallback onCloseHandler) {
+ final CallHomeMountSessionContext deviceContext = new CallHomeMountSessionContext(session.getSessionName(),
+ session, activator, devCtxt -> onClosed(devCtxt, onCloseHandler));
- public @NonNull Collection<CallHomeMountSessionContext> getByPublicKey(PublicKey publicKey) {
- return contextByPublicKey.get(publicKey);
- }
-
- CallHomeMountSessionContext createSession(CallHomeProtocolSessionContext session,
- CallHomeChannelActivator activator, final CloseCallback onCloseHandler) {
-
- String name = session.getSessionName();
- CallHomeMountSessionContext deviceContext = new CallHomeMountSessionContext(name,
- session, activator, devCtxt -> {
- CallHomeMountSessionManager.this.onClosed(devCtxt);
- onCloseHandler.onClosed(devCtxt);
- });
-
- contextByAddress.put(deviceContext.getRemoteAddress(), deviceContext);
- contextByPublicKey.put(deviceContext.getRemoteServerKey(), deviceContext);
+ final PublicKey remoteKey = session.getRemoteServerKey();
+ final CallHomeMountSessionContext existing = contextByPublicKey.putIfAbsent(remoteKey, deviceContext);
+ if (existing != null) {
+ // Check if the sshkey of the incoming netconf server is present. If present return null, else store the
+ // session. The sshkey is the uniqueness of the callhome sessions not the uniqueid/devicename.
+ LOG.error("SSH Host Key {} is associated with existing session {}, closing session {}", remoteKey, existing,
+ session);
+ session.terminate();
+ return null;
+ }
+ final InetSocketAddress remoteAddress = session.getRemoteAddress();
+ final CallHomeMountSessionContext prev = contextByAddress.put(remoteAddress, deviceContext);
+ if (prev != null) {
+ LOG.warn("Remote {} replaced context {} with {}", remoteAddress, prev, deviceContext);
+ }
return deviceContext;
}
@Override
- public synchronized void onClosed(CallHomeMountSessionContext deviceContext) {
- contextByAddress.remove(deviceContext.getRemoteAddress());
- contextByPublicKey.remove(deviceContext.getRemoteServerKey(), deviceContext);
+ public void onClosed(final CallHomeMountSessionContext deviceContext) {
+ final CallHomeProtocolSessionContext session = deviceContext.getProtocol();
+ contextByAddress.remove(session.getRemoteAddress(), deviceContext);
+ contextByPublicKey.remove(session.getRemoteServerKey(), deviceContext);
+ }
+
+ @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+ justification = "https://github.com/spotbugs/spotbugs/issues/811")
+ private void onClosed(final CallHomeMountSessionContext deviceContext, final CloseCallback onCloseHandler) {
+ onClosed(deviceContext);
+ onCloseHandler.onClosed(deviceContext);
}
}