* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-
package org.opendaylight.netconf.callhome.mount;
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.MoreExecutors;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.List;
+import java.util.Collections;
+import java.util.Optional;
import java.util.concurrent.ExecutionException;
-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.binding.api.ReadOnlyTransaction;
-import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
-import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
-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.DataTreeChangeListener;
+import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
+import org.opendaylight.mdsal.binding.api.DataTreeModification;
+import org.opendaylight.mdsal.binding.api.ReadTransaction;
+import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.netconf.callhome.protocol.AuthorizedKeysDecoder;
import org.opendaylight.netconf.callhome.protocol.StatusRecorder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.callhome.device.status.rev170112.Device1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.callhome.device.status.rev170112.Device1.DeviceStatus;
import org.opendaylight.yang.gen.v1.urn.opendaylight.callhome.device.status.rev170112.Device1Builder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.network.topology.topology.topology.types.TopologyNetconf;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.NetconfCallhomeServer;
-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.allowed.devices.Device;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.DeviceBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.DeviceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev201015.NetconfCallhomeServer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev201015.netconf.callhome.server.AllowedDevices;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev201015.netconf.callhome.server.allowed.devices.Device;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev201015.netconf.callhome.server.allowed.devices.DeviceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev201015.netconf.callhome.server.allowed.devices.DeviceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev201015.netconf.callhome.server.allowed.devices.device.Transport;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev201015.netconf.callhome.server.allowed.devices.device.transport.Ssh;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev201015.netconf.callhome.server.allowed.devices.device.transport.SshBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev201015.netconf.callhome.server.allowed.devices.device.transport.ssh.SshClientParams;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev201015.netconf.callhome.server.allowed.devices.device.transport.ssh.SshClientParamsBuilder;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-class CallhomeStatusReporter implements DataTreeChangeListener<Node>, StatusRecorder, AutoCloseable {
+final class CallhomeStatusReporter implements DataTreeChangeListener<Node>, StatusRecorder, AutoCloseable {
private static final InstanceIdentifier<Topology> NETCONF_TOPO_IID =
InstanceIdentifier.create(NetworkTopology.class).child(Topology.class,
new TopologyKey(new TopologyId(TopologyNetconf.QNAME.getLocalName())));
CallhomeStatusReporter(final DataBroker broker) {
this.dataBroker = broker;
- this.reg = dataBroker.registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
+ this.reg = dataBroker.registerDataTreeChangeListener(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL,
NETCONF_TOPO_IID.child(Node.class)), this);
}
@Override
- public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Node>> changes) {
- for (DataTreeModification<Node> change: changes) {
+ public void onDataTreeChanged(final Collection<DataTreeModification<Node>> changes) {
+ for (DataTreeModification<Node> change : changes) {
final DataObjectModification<Node> rootNode = change.getRootNode();
final InstanceIdentifier<Node> identifier = change.getRootPath().getRootIdentifier();
switch (rootNode.getModificationType()) {
}
private static Device newDevice(final String id, final PublicKey serverKey, final Device1.DeviceStatus status) {
+ // used only for netconf devices that are connected via SSH transport and global credentials
String sshEncodedKey = serverKey.toString();
try {
sshEncodedKey = AuthorizedKeysDecoder.encodePublicKey(serverKey);
} catch (IOException e) {
LOG.warn("Unable to encode public key to ssh format.", e);
}
- Device1 d1 = new Device1Builder().setDeviceStatus(Device1.DeviceStatus.FAILEDNOTALLOWED).build();
- DeviceBuilder builder = new DeviceBuilder()
+ final SshClientParams sshParams = new SshClientParamsBuilder().setHostKey(sshEncodedKey).build();
+ final Transport transport = new SshBuilder().setSshClientParams(sshParams).build();
+ return new DeviceBuilder()
.setUniqueId(id)
.withKey(new DeviceKey(id))
- .setSshHostKey(sshEncodedKey)
- .addAugmentation(Device1.class, d1);
-
- return builder.build();
+ .setTransport(transport)
+ .addAugmentation(new Device1Builder().setDeviceStatus(status).build())
+ .build();
}
private Device readAndGetDevice(final NodeId nodeId) {
- return readDevice(nodeId).orNull();
+ return readDevice(nodeId).orElse(null);
}
- @Nonnull
private Optional<Device> readDevice(final NodeId nodeId) {
- ReadOnlyTransaction opTx = dataBroker.newReadOnlyTransaction();
-
- InstanceIdentifier<Device> deviceIID = buildDeviceInstanceIdentifier(nodeId);
- ListenableFuture<Optional<Device>> devFuture = opTx.read(LogicalDatastoreType.OPERATIONAL, deviceIID);
- try {
- return devFuture.get();
+ try (ReadTransaction opTx = dataBroker.newReadOnlyTransaction()) {
+ InstanceIdentifier<Device> deviceIID = buildDeviceInstanceIdentifier(nodeId);
+ return opTx.read(LogicalDatastoreType.OPERATIONAL, deviceIID).get();
} catch (InterruptedException | ExecutionException e) {
- return Optional.absent();
+ return Optional.empty();
}
}
private void writeDevice(final NodeId nodeId, final Device modifiedDevice) {
- ReadWriteTransaction opTx = dataBroker.newReadWriteTransaction();
+ WriteTransaction opTx = dataBroker.newWriteOnlyTransaction();
opTx.merge(LogicalDatastoreType.OPERATIONAL, buildDeviceInstanceIdentifier(nodeId), modifiedDevice);
- opTx.commit();
+ commit(opTx, modifiedDevice.key());
}
private static InstanceIdentifier<Device> buildDeviceInstanceIdentifier(final NodeId nodeId) {
return InstanceIdentifier.create(NetconfCallhomeServer.class)
- .child(AllowedDevices.class)
- .child(Device.class, new DeviceKey(nodeId.getValue()));
+ .child(AllowedDevices.class)
+ .child(Device.class, new DeviceKey(nodeId.getValue()));
}
private static Device withConnectedStatus(final Device opDev) {
- Device1 status = new Device1Builder().setDeviceStatus(Device1.DeviceStatus.CONNECTED).build();
- return new DeviceBuilder().addAugmentation(Device1.class, status).setUniqueId(opDev.getUniqueId())
- .setSshHostKey(opDev.getSshHostKey()).build();
+ return deviceWithStatus(opDev, Device1.DeviceStatus.CONNECTED);
}
private static Device withFailedStatus(final Device opDev) {
- Device1 status = new Device1Builder().setDeviceStatus(Device1.DeviceStatus.FAILED).build();
- return new DeviceBuilder().addAugmentation(Device1.class, status).setUniqueId(opDev.getUniqueId())
- .setSshHostKey(opDev.getSshHostKey()).build();
+ return deviceWithStatus(opDev, DeviceStatus.FAILED);
}
private static Device withDisconnectedStatus(final Device opDev) {
- Device1 status = new Device1Builder().setDeviceStatus(Device1.DeviceStatus.DISCONNECTED).build();
- return new DeviceBuilder().addAugmentation(Device1.class, status).setUniqueId(opDev.getUniqueId())
- .setSshHostKey(opDev.getSshHostKey()).build();
+ return deviceWithStatus(opDev, DeviceStatus.DISCONNECTED);
}
private static Device withFailedAuthStatus(final Device opDev) {
- Device1 status = new Device1Builder().setDeviceStatus(Device1.DeviceStatus.FAILEDAUTHFAILURE).build();
- return new DeviceBuilder().addAugmentation(Device1.class, status).setUniqueId(opDev.getUniqueId())
- .setSshHostKey(opDev.getSshHostKey()).build();
+ return deviceWithStatus(opDev, DeviceStatus.FAILEDAUTHFAILURE);
+ }
+
+ private static Device deviceWithStatus(final Device opDev, final DeviceStatus status) {
+ final DeviceBuilder deviceBuilder = new DeviceBuilder()
+ .setUniqueId(opDev.getUniqueId())
+ .addAugmentation(new Device1Builder().setDeviceStatus(status).build());
+ if (opDev.getTransport() != null) {
+ deviceBuilder.setTransport(opDev.getTransport());
+ } else {
+ deviceBuilder.setSshHostKey(opDev.getSshHostKey());
+ }
+ return deviceBuilder.build();
}
private void setDeviceStatus(final Device device) {
WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
- InstanceIdentifier<Device> deviceIId =
- InstanceIdentifier.create(NetconfCallhomeServer.class)
+ InstanceIdentifier<Device> deviceIId = InstanceIdentifier.create(NetconfCallhomeServer.class)
.child(AllowedDevices.class)
.child(Device.class, device.key());
tx.merge(LogicalDatastoreType.OPERATIONAL, deviceIId, device);
- tx.commit();
+ commit(tx, device.key());
+ }
+
+ private static void commit(final WriteTransaction tx, final DeviceKey device) {
+ tx.commit().addCallback(new FutureCallback<CommitInfo>() {
+ @Override
+ public void onSuccess(final CommitInfo result) {
+ LOG.debug("Device {} committed", device);
+ }
+
+ @Override
+ public void onFailure(final Throwable cause) {
+ LOG.warn("Failed to commit device {}", device, cause);
+ }
+ }, MoreExecutors.directExecutor());
}
private AllowedDevices getDevices() {
- ReadOnlyTransaction rxTransaction = dataBroker.newReadOnlyTransaction();
- ListenableFuture<Optional<AllowedDevices>> devicesFuture =
- rxTransaction.read(LogicalDatastoreType.OPERATIONAL, IetfZeroTouchCallHomeServerProvider.ALL_DEVICES);
- try {
- return devicesFuture.get().orNull();
+ try (ReadTransaction rxTransaction = dataBroker.newReadOnlyTransaction()) {
+ return rxTransaction.read(LogicalDatastoreType.OPERATIONAL, IetfZeroTouchCallHomeServerProvider.ALL_DEVICES)
+ .get().orElse(null);
} catch (ExecutionException | InterruptedException e) {
- LOG.error("Error trying to read the whitelist devices: {}", e);
+ LOG.error("Error trying to read the allowlist devices", e);
return null;
}
}
- private List<Device> getDevicesAsList() {
+ private Collection<Device> getDevicesAsList() {
AllowedDevices devices = getDevices();
- return devices == null ? new ArrayList<>() : devices.getDevice();
+ return devices == null ? Collections.emptyList() : devices.nonnullDevice().values();
}
@Override
public void reportFailedAuth(final PublicKey sshKey) {
AuthorizedKeysDecoder decoder = new AuthorizedKeysDecoder();
- for (Device device : getDevicesAsList()) {
- String keyString = device.getSshHostKey();
+ for (final Device device : getDevicesAsList()) {
+ final String keyString;
+ if (device.getTransport() instanceof Ssh) {
+ keyString = ((Ssh) device.getTransport()).getSshClientParams().getHostKey();
+ } else {
+ keyString = device.getSshHostKey();
+ }
+ if (keyString == null) {
+ LOG.info("Allowlist device {} does not have a host key, skipping it", device.getUniqueId());
+ continue;
+ }
try {
PublicKey pubKey = decoder.decodePublicKey(keyString);
}
}
- LOG.error("No match found for the failed auth device (should have been filtered by whitelist). Key: {}",
+ LOG.error("No match found for the failed auth device (should have been filtered by allowlist). Key: {}",
sshKey);
}