X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Fcallhome-provider%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Fcallhome%2Fmount%2FIetfZeroTouchCallHomeServerProvider.java;h=e2a99a34c34e683c7bcf607814ca67c34d6e79bf;hb=aab675bb3b93911cfd28f94e6102ddfa772db77a;hp=903dd9ce7154e5c38e116e33f42238c3893ea135;hpb=e7abbe8b97f494030992ea8f9cc81a982695852b;p=netconf.git diff --git a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/IetfZeroTouchCallHomeServerProvider.java b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/IetfZeroTouchCallHomeServerProvider.java index 903dd9ce71..e2a99a34c3 100644 --- a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/IetfZeroTouchCallHomeServerProvider.java +++ b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/IetfZeroTouchCallHomeServerProvider.java @@ -5,28 +5,31 @@ * 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 static com.google.common.base.Preconditions.checkArgument; - import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import io.netty.channel.nio.NioEventLoopGroup; import java.io.IOException; import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; import java.util.Set; -import org.opendaylight.controller.md.sal.binding.api.DataBroker; -import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; -import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; -import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; -import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import java.util.concurrent.ExecutionException; +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.ReadWriteTransaction; +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.CallHomeAuthorizationProvider; import org.opendaylight.netconf.callhome.protocol.NetconfCallHomeServer; import org.opendaylight.netconf.callhome.protocol.NetconfCallHomeServerBuilder; @@ -37,22 +40,26 @@ 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.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.rev161109.netconf.callhome.server.allowed.devices.device.Transport; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.device.transport.Ssh; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.device.transport.SshBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.device.transport.ssh.SshClientParams; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconf.callhome.server.rev161109.netconf.callhome.server.allowed.devices.device.transport.ssh.SshClientParamsBuilder; import org.opendaylight.yangtools.concepts.ListenerRegistration; -import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class IetfZeroTouchCallHomeServerProvider implements AutoCloseable, DataChangeListener { +public class IetfZeroTouchCallHomeServerProvider implements AutoCloseable, DataTreeChangeListener { private static final String APPNAME = "CallHomeServer"; static final InstanceIdentifier ALL_DEVICES = InstanceIdentifier.create(NetconfCallhomeServer.class) - .child(AllowedDevices.class); + .child(AllowedDevices.class); private static final Logger LOG = LoggerFactory.getLogger(IetfZeroTouchCallHomeServerProvider.class); private final DataBroker dataBroker; private final CallHomeMountDispatcher mountDispacher; - private CallHomeAuthProviderImpl authProvider; + private final CallHomeAuthProviderImpl authProvider; protected NetconfCallHomeServer server; @@ -60,9 +67,10 @@ public class IetfZeroTouchCallHomeServerProvider implements AutoCloseable, DataC private static final String CALL_HOME_PORT_KEY = "DefaultCallHomePort"; private int port = 0; // 0 = use default in NetconfCallHomeBuilder - private CallhomeStatusReporter statusReporter; + private final CallhomeStatusReporter statusReporter; - public IetfZeroTouchCallHomeServerProvider(DataBroker dataBroker, CallHomeMountDispatcher mountDispacher) { + public IetfZeroTouchCallHomeServerProvider(final DataBroker dataBroker, + final CallHomeMountDispatcher mountDispacher) { this.dataBroker = dataBroker; this.mountDispacher = mountDispacher; this.authProvider = new CallHomeAuthProviderImpl(dataBroker); @@ -74,15 +82,15 @@ public class IetfZeroTouchCallHomeServerProvider implements AutoCloseable, DataC try { LOG.info("Initializing provider for {}", APPNAME); initializeServer(); - dataBroker.registerDataChangeListener( - LogicalDatastoreType.CONFIGURATION, ALL_DEVICES, this, AsyncDataBroker.DataChangeScope.SUBTREE); + listenerReg = dataBroker.registerDataTreeChangeListener( + DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, ALL_DEVICES), this); LOG.info("Initialization complete for {}", APPNAME); } catch (IOException | Configuration.ConfigurationException e) { LOG.error("Unable to successfully initialize", e); } } - public void setPort(String portStr) { + public void setPort(final String portStr) { try { Configuration configuration = new Configuration(); configuration.set(CALL_HOME_PORT_KEY, portStr); @@ -100,11 +108,12 @@ public class IetfZeroTouchCallHomeServerProvider implements AutoCloseable, DataC private void initializeServer() throws IOException { LOG.info("Initializing Call Home server instance"); CallHomeAuthorizationProvider provider = this.getCallHomeAuthorization(); - NetconfCallHomeServerBuilder builder = new NetconfCallHomeServerBuilder( - provider, mountDispacher, statusReporter); + NetconfCallHomeServerBuilder builder = new NetconfCallHomeServerBuilder(provider, mountDispacher, + statusReporter); if (port > 0) { builder.setBindAddress(new InetSocketAddress(port)); } + builder.setNettyGroup(new NioEventLoopGroup()); server = builder.build(); server.bind(); mountDispacher.createTopology(); @@ -112,15 +121,15 @@ public class IetfZeroTouchCallHomeServerProvider implements AutoCloseable, DataC } @VisibleForTesting - void assertValid(Object obj, String description) { + void assertValid(final Object obj, final String description) { if (obj == null) { throw new RuntimeException( - String.format("Failed to find %s in IetfZeroTouchCallHomeProvider.initialize()", description)); + String.format("Failed to find %s in IetfZeroTouchCallHomeProvider.initialize()", description)); } } @Override - public void close() throws Exception { + public void close() { authProvider.close(); statusReporter.close(); @@ -136,93 +145,113 @@ public class IetfZeroTouchCallHomeServerProvider implements AutoCloseable, DataC } @Override - public void onDataChanged(AsyncDataChangeEvent, DataObject> change) { - + public void onDataTreeChanged(final Collection> changes) { // In case of any changes to the devices datatree, register the changed values with callhome server // As of now, no way to add a new callhome client key to the CallHomeAuthorization instance since // its created under CallHomeAuthorizationProvider. // Will have to redesign a bit here. // CallHomeAuthorization. - ReadOnlyTransaction roConfigTx = dataBroker.newReadOnlyTransaction(); - CheckedFuture, ReadFailedException> devicesFuture = - roConfigTx.read(LogicalDatastoreType.CONFIGURATION, IetfZeroTouchCallHomeServerProvider.ALL_DEVICES); + final ListenableFuture> devicesFuture; + try (ReadTransaction roConfigTx = dataBroker.newReadOnlyTransaction()) { + devicesFuture = roConfigTx.read(LogicalDatastoreType.CONFIGURATION, + IetfZeroTouchCallHomeServerProvider.ALL_DEVICES); + } - if (hasDeletedDevices(change)) { - handleDeletedDevices(change); + Set> deletedDevices = new HashSet<>(); + for (DataTreeModification change : changes) { + DataObjectModification rootNode = change.getRootNode(); + switch (rootNode.getModificationType()) { + case DELETE: + deletedDevices.add(change.getRootPath().getRootIdentifier()); + break; + default: + break; + } } + handleDeletedDevices(deletedDevices); + try { for (Device confDevice : getReadDevices(devicesFuture)) { readAndUpdateStatus(confDevice); } - } catch (ReadFailedException e) { - LOG.error("Error trying to read the whitelist devices: {}", e); + } catch (ExecutionException | InterruptedException e) { + LOG.error("Error trying to read the whitelist devices", e); } } - private boolean hasDeletedDevices(AsyncDataChangeEvent, DataObject> change) { - return change.getRemovedPaths() != null; - } - - private void handleDeletedDevices(AsyncDataChangeEvent, DataObject> change) { - checkArgument(change.getRemovedPaths() != null); - - ReadWriteTransaction opTx = dataBroker.newReadWriteTransaction(); + private void handleDeletedDevices(final Set> deletedDevices) { + if (deletedDevices.isEmpty()) { + return; + } - Set> removedDevices = change.getRemovedPaths(); - int numRemoved = removedDevices.size(); + WriteTransaction opTx = dataBroker.newWriteOnlyTransaction(); - Iterator> iterator = removedDevices.iterator(); - while (iterator.hasNext()) { - InstanceIdentifier removedIID = iterator.next(); + for (InstanceIdentifier removedIID : deletedDevices) { LOG.info("Deleting the entry for callhome device {}", removedIID); opTx.delete(LogicalDatastoreType.OPERATIONAL, removedIID); } - if (numRemoved > 0) { - opTx.submit(); - } - } + opTx.commit().addCallback(new FutureCallback() { + @Override + public void onSuccess(final CommitInfo result) { + LOG.debug("Device deletions committed"); + } - private List getReadDevices(CheckedFuture, ReadFailedException> devicesFuture) - throws ReadFailedException { - Optional opt = devicesFuture.checkedGet(); - if (opt.isPresent()) { - AllowedDevices confDevices = opt.get(); - if (confDevices != null) { - LOG.debug("Read {} devices", confDevices.getDevice().size()); - return confDevices.getDevice(); + @Override + public void onFailure(final Throwable cause) { + LOG.warn("Failed to commit device deletions", cause); } - } + }, MoreExecutors.directExecutor()); + } - LOG.debug("Failed to read devices"); - return new ArrayList<>(); + private static Collection getReadDevices(final ListenableFuture> devicesFuture) + throws InterruptedException, ExecutionException { + return devicesFuture.get().map(AllowedDevices::nonnullDevice).orElse(Collections.emptyMap()).values(); } - private void readAndUpdateStatus(Device cfgDevice) throws ReadFailedException { + private void readAndUpdateStatus(final Device cfgDevice) throws InterruptedException, ExecutionException { InstanceIdentifier deviceIID = InstanceIdentifier.create(NetconfCallhomeServer.class) - .child(AllowedDevices.class) - .child(Device.class, new DeviceKey(cfgDevice.getUniqueId())); + .child(AllowedDevices.class).child(Device.class, new DeviceKey(cfgDevice.getUniqueId())); ReadWriteTransaction tx = dataBroker.newReadWriteTransaction(); - CheckedFuture, ReadFailedException> deviceFuture = tx.read( - LogicalDatastoreType.OPERATIONAL, deviceIID); + ListenableFuture> deviceFuture = tx.read(LogicalDatastoreType.OPERATIONAL, deviceIID); - Optional opDevGet = deviceFuture.checkedGet(); - Device1 devStatus = new Device1Builder().setDeviceStatus(Device1.DeviceStatus.DISCONNECTED).build(); + final Device1 devStatus; + Optional opDevGet = deviceFuture.get(); if (opDevGet.isPresent()) { - Device opDevice = opDevGet.get(); - devStatus = opDevice.getAugmentation(Device1.class); + devStatus = opDevGet.get().augmentation(Device1.class); + } else { + devStatus = new Device1Builder().setDeviceStatus(Device1.DeviceStatus.DISCONNECTED).build(); } - Device newOpDevice = new DeviceBuilder() - .addAugmentation(Device1.class, devStatus) - .setSshHostKey(cfgDevice.getSshHostKey()) - .setUniqueId(cfgDevice.getUniqueId()).build(); + final Device opDevice = createOperationalDevice(cfgDevice, devStatus); + tx.merge(LogicalDatastoreType.OPERATIONAL, deviceIID, opDevice); + tx.commit().addCallback(new FutureCallback() { + @Override + public void onSuccess(final CommitInfo result) { + LOG.debug("Device {} status update committed", cfgDevice.key()); + } - cfgDevice = newOpDevice; + @Override + public void onFailure(final Throwable cause) { + LOG.warn("Failed to commit device {} status update", cfgDevice.key(), cause); + } + }, MoreExecutors.directExecutor()); + } - tx.merge(LogicalDatastoreType.OPERATIONAL, deviceIID, cfgDevice); - tx.submit(); + private Device createOperationalDevice(final Device cfgDevice, final Device1 devStatus) { + final DeviceBuilder deviceBuilder = new DeviceBuilder() + .addAugmentation(devStatus) + .setUniqueId(cfgDevice.getUniqueId()); + if (cfgDevice.getTransport() instanceof Ssh) { + final String hostKey = ((Ssh) cfgDevice.getTransport()).getSshClientParams().getHostKey(); + final SshClientParams params = new SshClientParamsBuilder().setHostKey(hostKey).build(); + final Transport sshTransport = new SshBuilder().setSshClientParams(params).build(); + deviceBuilder.setTransport(sshTransport); + } else if (cfgDevice.getSshHostKey() != null) { + deviceBuilder.setSshHostKey(cfgDevice.getSshHostKey()); + } + return deviceBuilder.build(); } }