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=d8a2c36e4fba2d282704e49443e99e9d0d88c6c6;hb=refs%2Fchanges%2F06%2F101606%2F2;hp=bcc5707b140668d20982b802db0334739337b116;hpb=217ac8deb9e5fb5b2ba34271cfe72dd7cd22d653;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 bcc5707b14..d8a2c36e4f 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,67 +5,73 @@ * 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.io.File; 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; 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.Device1Builder; -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.Tls; +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.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); + static final InstanceIdentifier ALL_DEVICES = InstanceIdentifier.create(NetconfCallhomeServer.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; private ListenerRegistration listenerReg = null; private static final String CALL_HOME_PORT_KEY = "DefaultCallHomePort"; - private static String configurationPath = "etc"+File.pathSeparator+"ztp-callhome-config.cfg"; 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); @@ -76,24 +82,23 @@ public class IetfZeroTouchCallHomeServerProvider implements AutoCloseable, DataC // Register itself as a listener to changes in Devices subtree try { LOG.info("Initializing provider for {}", APPNAME); - loadConfigurableValues(configurationPath); initializeServer(); - dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,ALL_DEVICES,this, AsyncDataBroker.DataChangeScope.SUBTREE); - LOG.info( "Initialization complete for {}", APPNAME); - } - catch(IOException | Configuration.ConfigurationException e) { + 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); } } - private void loadConfigurableValues(String configurationPath) - throws Configuration.ConfigurationException { + public void setPort(final String portStr) { try { - Configuration configuration = new Configuration(configurationPath); + Configuration configuration = new Configuration(); + configuration.set(CALL_HOME_PORT_KEY, portStr); port = configuration.getAsPort(CALL_HOME_PORT_KEY); - } - catch(Configuration.ConfigurationException e) { - LOG.error("Problem trying to load configuration values from {}", configurationPath, e); + LOG.info("Setting port for call home server to {}", portStr); + } catch (Configuration.ConfigurationException e) { + LOG.error("Problem trying to set port for call home server {}", portStr, e); } } @@ -102,34 +107,38 @@ public class IetfZeroTouchCallHomeServerProvider implements AutoCloseable, DataC } private void initializeServer() throws IOException { - LOG.info( "Initializing Call Home server instance"); + LOG.info("Initializing Call Home server instance"); CallHomeAuthorizationProvider provider = this.getCallHomeAuthorization(); - NetconfCallHomeServerBuilder builder = new NetconfCallHomeServerBuilder( - provider, mountDispacher, statusReporter); - if (port > 0) + 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(); - LOG.info( "Initialization complete for Call Home server instance"); + LOG.info("Initialization complete for Call Home server instance"); } @VisibleForTesting - void assertValid(Object obj, String description) { - if (obj == null) - throw new RuntimeException(String.format("Failed to find %s in IetfZeroTouchCallHomeProvider.initialize()", 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)); + } } @Override - public void close() throws Exception { + public void close() { authProvider.close(); statusReporter.close(); // FIXME unbind the server - if ( this.listenerReg != null ) { + if (this.listenerReg != null) { listenerReg.close(); } - if(server != null ) { + if (server != null) { server.close(); } @@ -137,91 +146,115 @@ 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); + } + + 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; + } + } - if (hasDeletedDevices(change)) - handleDeletedDevices(change); + handleDeletedDevices(deletedDevices); try { - for(Device confDevice : getReadDevices(devicesFuture)) { + 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(); - LOG.info("Deleting the entry for callhome device {}",removedIID); + 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 { - InstanceIdentifier deviceIID = InstanceIdentifier.create(NetconfCallhomeServer.class) - .child(AllowedDevices.class) - .child(Device.class, new DeviceKey(cfgDevice.getUniqueId())); + 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())); ReadWriteTransaction tx = dataBroker.newReadWriteTransaction(); - CheckedFuture, ReadFailedException> deviceFuture = tx.read(LogicalDatastoreType.OPERATIONAL, deviceIID); - - Optional opDevGet = deviceFuture.checkedGet(); - Device1 devStatus = new Device1Builder().setDeviceStatus(Device1.DeviceStatus.DISCONNECTED).build(); - if(opDevGet.isPresent()) { - Device opDevice = opDevGet.get(); - devStatus = opDevice.getAugmentation(Device1.class); + ListenableFuture> deviceFuture = tx.read(LogicalDatastoreType.OPERATIONAL, deviceIID); + + final Device1 devStatus; + Optional opDevGet = deviceFuture.get(); + if (opDevGet.isPresent()) { + 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.getTransport() instanceof Tls) { + deviceBuilder.setTransport(cfgDevice.getTransport()); + } else if (cfgDevice.getSshHostKey() != null) { + deviceBuilder.setSshHostKey(cfgDevice.getSshHostKey()); + } + return deviceBuilder.build(); } }