*/
package org.opendaylight.netconf.callhome.mount;
-import com.google.common.base.Objects;
import com.google.common.net.InetAddresses;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
+import java.security.GeneralSecurityException;
import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nonnull;
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
-import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
-import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
-import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.DataObjectModification;
+import org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
+import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
+import org.opendaylight.mdsal.binding.api.DataTreeModification;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.netconf.callhome.protocol.AuthorizedKeysDecoder;
import org.opendaylight.netconf.callhome.protocol.CallHomeAuthorization;
import org.opendaylight.netconf.callhome.protocol.CallHomeAuthorization.Builder;
private static final InstanceIdentifier<Global> GLOBAL_PATH =
InstanceIdentifier.create(NetconfCallhomeServer.class).child(Global.class);
private static final DataTreeIdentifier<Global> GLOBAL =
- new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, GLOBAL_PATH);
+ DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, GLOBAL_PATH);
private static final InstanceIdentifier<Device> ALLOWED_DEVICES_PATH =
InstanceIdentifier.create(NetconfCallhomeServer.class).child(AllowedDevices.class).child(Device.class);
private static final DataTreeIdentifier<Device> ALLOWED_DEVICES =
- new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, ALLOWED_DEVICES_PATH);
+ DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, ALLOWED_DEVICES_PATH);
private static final DataTreeIdentifier<Device> ALLOWED_OP_DEVICES =
- new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, ALLOWED_DEVICES_PATH);
+ DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, ALLOWED_DEVICES_PATH);
private final GlobalConfig globalConfig = new GlobalConfig();
private final DeviceConfig deviceConfig = new DeviceConfig();
private final CallhomeStatusReporter statusReporter;
- CallHomeAuthProviderImpl(DataBroker broker) {
+ CallHomeAuthProviderImpl(final DataBroker broker) {
configReg = broker.registerDataTreeChangeListener(GLOBAL, globalConfig);
deviceReg = broker.registerDataTreeChangeListener(ALLOWED_DEVICES, deviceConfig);
deviceOpReg = broker.registerDataTreeChangeListener(ALLOWED_OP_DEVICES, deviceOp);
@Nonnull
@Override
- public CallHomeAuthorization provideAuth(SocketAddress remoteAddress, PublicKey serverKey) {
+ public CallHomeAuthorization provideAuth(@Nonnull final SocketAddress remoteAddress,
+ @Nonnull final PublicKey serverKey) {
Device deviceSpecific = deviceConfig.get(serverKey);
String sessionName;
Credentials deviceCred;
}
@Override
- public void close() throws Exception {
+ public void close() {
configReg.close();
deviceReg.close();
deviceOpReg.close();
}
- private String fromRemoteAddress(SocketAddress remoteAddress) {
+ private static String fromRemoteAddress(final SocketAddress remoteAddress) {
if (remoteAddress instanceof InetSocketAddress) {
InetSocketAddress socketAddress = (InetSocketAddress) remoteAddress;
return InetAddresses.toAddrString(socketAddress.getAddress()) + ":" + socketAddress.getPort();
return remoteAddress.toString();
}
- private class DeviceConfig implements DataTreeChangeListener<Device> {
-
- private final AuthorizedKeysDecoder keyDecoder = new AuthorizedKeysDecoder();
-
- private ConcurrentMap<PublicKey, Device> byPublicKey = new ConcurrentHashMap<PublicKey, Device>();
+ private abstract static class AbstractDeviceListener implements DataTreeChangeListener<Device> {
@Override
- public void onDataTreeChanged(Collection<DataTreeModification<Device>> mods) {
+ public final void onDataTreeChanged(final Collection<DataTreeModification<Device>> mods) {
for (DataTreeModification<Device> dataTreeModification : mods) {
- DataObjectModification<Device> rootNode = dataTreeModification.getRootNode();
- process(rootNode);
- }
- }
-
- private void process(DataObjectModification<Device> deviceMod) {
- Device before = deviceMod.getDataBefore();
- Device after = deviceMod.getDataAfter();
-
- if (before == null) {
- putDevice(after);
- } else if (after == null) {
- // Delete
- removeDevice(before);
- } else {
- if (!Objects.equal(before.getSshHostKey(), after.getSshHostKey())) {
- // key changed // we should remove previous key.
- removeDevice(before);
+ final DataObjectModification<Device> deviceMod = dataTreeModification.getRootNode();
+ final ModificationType modType = deviceMod.getModificationType();
+ switch (modType) {
+ case DELETE:
+ deleteDevice(deviceMod.getDataBefore());
+ break;
+ case SUBTREE_MODIFIED:
+ case WRITE:
+ deleteDevice(deviceMod.getDataBefore());
+ writeDevice(deviceMod.getDataAfter());
+ break;
+ default:
+ throw new IllegalStateException("Unhandled modification type " + modType);
}
- putDevice(after);
}
}
- private void putDevice(Device device) {
- PublicKey key = publicKey(device);
- if (key == null) {
- return;
+ private void deleteDevice(final Device dataBefore) {
+ if (dataBefore != null) {
+ final String publicKey = dataBefore.getSshHostKey();
+ if (publicKey != null) {
+ LOG.debug("Removing device {}", dataBefore.getUniqueId());
+ removeDevice(publicKey, dataBefore);
+ } else {
+ LOG.debug("Ignoring removal of device {}, no host key present", dataBefore.getUniqueId());
+ }
}
- byPublicKey.put(key, device);
}
- private void removeDevice(Device device) {
- PublicKey key = publicKey(device);
- if (key == null) {
- return;
+ private void writeDevice(final Device dataAfter) {
+ final String publicKey = dataAfter.getSshHostKey();
+ if (publicKey != null) {
+ LOG.debug("Adding device {}", dataAfter.getUniqueId());
+ addDevice(publicKey, dataAfter);
+ } else {
+ LOG.debug("Ignoring addition of device {}, no host key present", dataAfter.getUniqueId());
}
- byPublicKey.remove(key);
}
- private PublicKey publicKey(Device device) {
- String hostKey = device.getSshHostKey();
- try {
- return keyDecoder.decodePublicKey(hostKey);
- } catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchProviderException e) {
- LOG.error("Unable to decode SSH key for {}. Ignoring update for this device", device.getUniqueId(), e);
- return null;
- }
- }
+ abstract void addDevice(String publicKey, Device device);
- private Device get(PublicKey key) {
- return byPublicKey.get(key);
- }
+ abstract void removeDevice(String publicKey, Device device);
}
- private class DeviceOp implements DataTreeChangeListener<Device> {
+ private static class DeviceConfig extends AbstractDeviceListener {
+ private final ConcurrentMap<PublicKey, Device> byPublicKey = new ConcurrentHashMap<>();
+ private final AuthorizedKeysDecoder keyDecoder = new AuthorizedKeysDecoder();
- private ConcurrentMap<String, Device> byPublicKey = new ConcurrentHashMap<>();
+ Device get(final PublicKey key) {
+ return byPublicKey.get(key);
+ }
@Override
- public void onDataTreeChanged(Collection<DataTreeModification<Device>> mods) {
- for (DataTreeModification<Device> dataTreeModification : mods) {
- DataObjectModification<Device> rootNode = dataTreeModification.getRootNode();
- process(rootNode);
+ void addDevice(final String publicKey, final Device device) {
+ final PublicKey key = publicKey(publicKey, device);
+ if (key != null) {
+ byPublicKey.put(key, device);
}
}
- private void process(DataObjectModification<Device> deviceMod) {
- Device before = deviceMod.getDataBefore();
- Device after = deviceMod.getDataAfter();
-
- if (before == null) {
- putDevice(after);
- } else if (after == null) {
- // Delete
- removeDevice(before);
- } else {
- if (!Objects.equal(before.getSshHostKey(), after.getSshHostKey())) {
- // key changed // we should remove previous key.
- removeDevice(before);
- }
- putDevice(after);
+ @Override
+ void removeDevice(final String publicKey, final Device device) {
+ final PublicKey key = publicKey(publicKey, device);
+ if (key != null) {
+ byPublicKey.remove(key);
}
}
- private void putDevice(Device device) {
- String key = device.getSshHostKey();
- byPublicKey.put(key, device);
- }
-
- private void removeDevice(Device device) {
- String key = device.getSshHostKey();
- byPublicKey.remove(key);
+ private PublicKey publicKey(final String hostKey, final Device device) {
+ try {
+ return keyDecoder.decodePublicKey(hostKey);
+ } catch (GeneralSecurityException e) {
+ LOG.error("Unable to decode SSH key for {}. Ignoring update for this device", device.getUniqueId(), e);
+ return null;
+ }
}
+ }
- Device get(PublicKey serverKey) {
- String skey = "";
+ private static class DeviceOp extends AbstractDeviceListener {
+ private final ConcurrentMap<String, Device> byPublicKey = new ConcurrentHashMap<>();
+ Device get(final PublicKey serverKey) {
+ final String skey;
try {
skey = AuthorizedKeysDecoder.encodePublicKey(serverKey);
- return byPublicKey.get(skey);
} catch (IOException | IllegalArgumentException e) {
- LOG.error("Unable to encode server key: {}", skey, e);
+ LOG.error("Unable to encode server key: {}", serverKey, e);
return null;
}
+
+ return byPublicKey.get(skey);
}
- }
- private class GlobalConfig implements DataTreeChangeListener<Global> {
+ @Override
+ void removeDevice(final String publicKey, final Device device) {
+ byPublicKey.remove(publicKey);
+ }
+ @Override
+ void addDevice(final String publicKey, final Device device) {
+ byPublicKey.put(publicKey, device);
+ }
+ }
+
+ private static class GlobalConfig implements DataTreeChangeListener<Global> {
private volatile Global current = null;
@Override
- public void onDataTreeChanged(Collection<DataTreeModification<Global>> mods) {
+ public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Global>> mods) {
for (DataTreeModification<Global> dataTreeModification : mods) {
current = dataTreeModification.getRootNode().getDataAfter();
}
}
boolean allowedUnknownKeys() {
- if (current == null) {
- return false;
- }
+ final Global local = current;
// Deal with null values.
- return Boolean.TRUE.equals(current.isAcceptAllSshKeys());
+ return local != null && Boolean.TRUE.equals(local.isAcceptAllSshKeys());
}
Credentials getCredentials() {
- return current != null ? current.getCredentials() : null;
+ final Global local = current;
+ return local != null ? local.getCredentials() : null;
}
}
}