Add flexible mount point naming strategy
[netconf.git] / netconf / callhome-provider / src / main / java / org / opendaylight / netconf / callhome / mount / CallHomeAuthProviderImpl.java
index 7f50653eed4fdc143d8670d2074d3d899908a863..eda8ba2dd6b34ca47d23864ce15755789bd0f36c 100644 (file)
@@ -7,25 +7,23 @@
  */
 package org.opendaylight.netconf.callhome.mount;
 
-import com.google.common.base.Objects;
-import com.google.common.net.InetAddresses;
+import com.google.common.collect.Iterables;
 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;
@@ -34,6 +32,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.credentials.Credentials;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.AllowedDevices;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.Global;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.Global.MountPointNamingStrategy;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.Device;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -46,14 +45,14 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider,
     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();
@@ -64,7 +63,7 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider,
 
     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);
@@ -73,7 +72,8 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider,
 
     @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;
@@ -89,10 +89,11 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider,
                 statusReporter.asForceListedDevice(syntheticId, serverKey);
             } else {
                 Device opDevice = deviceOp.get(serverKey);
-                if (opDevice == null)
+                if (opDevice == null) {
                     statusReporter.asUnlistedDevice(syntheticId, serverKey);
-                else
+                } else {
                     LOG.info("Repeating rejection of unlisted device with id of {}", opDevice.getUniqueId());
+                }
                 return CallHomeAuthorization.rejected();
             }
         }
@@ -112,157 +113,164 @@ public class CallHomeAuthProviderImpl implements CallHomeAuthorizationProvider,
     }
 
     @Override
-    public void close() throws Exception {
+    public void close() {
         configReg.close();
         deviceReg.close();
         deviceOpReg.close();
     }
 
-    private String fromRemoteAddress(SocketAddress remoteAddress) {
+    private String fromRemoteAddress(final SocketAddress remoteAddress) {
         if (remoteAddress instanceof InetSocketAddress) {
             InetSocketAddress socketAddress = (InetSocketAddress) remoteAddress;
-            return InetAddresses.toAddrString(socketAddress.getAddress()) + ":" + socketAddress.getPort();
+            String ip = socketAddress.getAddress().getHostAddress();
+
+            final MountPointNamingStrategy strat = globalConfig.getMountPointNamingStrategy();
+            switch (strat) {
+                case IPONLY:
+                    return ip;
+                case IPPORT:
+                    return ip + ":" + socketAddress.getPort();
+                default:
+                    throw new IllegalStateException("Unhandled naming strategy " + strat);
+            }
         }
         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) {
-            for (DataTreeModification<Global> dataTreeModification : mods) {
-                current = dataTreeModification.getRootNode().getDataAfter();
+        public void onDataTreeChanged(final Collection<DataTreeModification<Global>> mods) {
+            if (!mods.isEmpty()) {
+                current = Iterables.getLast(mods).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;
+        }
+
+        MountPointNamingStrategy getMountPointNamingStrategy() {
+            final Global local = current;
+            final MountPointNamingStrategy strat = local != null ? local.getMountPointNamingStrategy() : null;
+            return strat == null ? MountPointNamingStrategy.IPPORT : strat;
         }
     }
 }