X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Fcallhome-provider%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Fcallhome%2Fmount%2FCallhomeStatusReporter.java;h=7bff5acd4a661b7c8b02ad07e83a5b8b18ef279b;hb=2401a0b4ea5e95292b6d7a23274992c5b57f5149;hp=016bd57de29601452938ea2f272a992f7eab71a6;hpb=9a59f915f530d8a9923b4a6284fb4597ea8ddaeb;p=netconf.git diff --git a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallhomeStatusReporter.java b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallhomeStatusReporter.java index 016bd57de2..7bff5acd4a 100644 --- a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallhomeStatusReporter.java +++ b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallhomeStatusReporter.java @@ -5,30 +5,28 @@ * 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.CheckedFuture; - +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; +import java.security.GeneralSecurityException; import java.security.PublicKey; -import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; +import java.util.Collection; import java.util.List; -import java.util.Map; - -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.binding.api.WriteTransaction; -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.Optional; +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.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; @@ -49,13 +47,11 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology. import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey; 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; - -class CallhomeStatusReporter implements DataChangeListener, StatusRecorder, AutoCloseable { +class CallhomeStatusReporter implements DataTreeChangeListener, StatusRecorder, AutoCloseable { private static final InstanceIdentifier NETCONF_TOPO_IID = InstanceIdentifier.create(NetworkTopology.class).child(Topology.class, new TopologyKey(new TopologyId(TopologyNetconf.QNAME.getLocalName()))); @@ -63,45 +59,54 @@ class CallhomeStatusReporter implements DataChangeListener, StatusRecorder, Auto private static final Logger LOG = LoggerFactory.getLogger(CallhomeStatusReporter.class); private final DataBroker dataBroker; - private final ListenerRegistration reg; + private final ListenerRegistration reg; - CallhomeStatusReporter(DataBroker broker) { + CallhomeStatusReporter(final DataBroker broker) { this.dataBroker = broker; - this.reg = dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, NETCONF_TOPO_IID.child(Node.class), - this, AsyncDataBroker.DataChangeScope.SUBTREE); + this.reg = dataBroker.registerDataTreeChangeListener(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, + NETCONF_TOPO_IID.child(Node.class)), this); } @Override - public void onDataChanged(AsyncDataChangeEvent, DataObject> change) { - for (InstanceIdentifier removedPath : change.getRemovedPaths()) { - if(removedPath.getTargetType() != NetconfNode.class) - continue; - - final NodeId nodeId = getNodeId(removedPath); - if (nodeId != null) { - handleDisconnectedNetconfNode(nodeId); - return; + public void onDataTreeChanged(final Collection> changes) { + for (DataTreeModification change: changes) { + final DataObjectModification rootNode = change.getRootNode(); + final InstanceIdentifier identifier = change.getRootPath().getRootIdentifier(); + switch (rootNode.getModificationType()) { + case WRITE: + case SUBTREE_MODIFIED: + if (isNetconfNode(rootNode.getDataAfter())) { + NodeId nodeId = getNodeId(identifier); + if (nodeId != null) { + NetconfNode nnode = rootNode.getDataAfter().augmentation(NetconfNode.class); + handledNetconfNode(nodeId, nnode); + } + } + break; + case DELETE: + if (isNetconfNode(rootNode.getDataBefore())) { + final NodeId nodeId = getNodeId(identifier); + if (nodeId != null) { + handleDisconnectedNetconfNode(nodeId); + } + } + break; + default: + break; } } + } - for (Map.Entry, DataObject> entry : change.getUpdatedData().entrySet()) { - if (entry.getKey().getTargetType() == NetconfNode.class) { - NodeId nodeId = getNodeId(entry.getKey()); - if (nodeId != null) { - NetconfNode nnode = (NetconfNode) entry.getValue(); - handledNetconfNode(nodeId, nnode); - return; - } - } - } + private static boolean isNetconfNode(final Node node) { + return node.augmentation(NetconfNode.class) != null; } - private NodeId getNodeId(final InstanceIdentifier path) { + private static NodeId getNodeId(final InstanceIdentifier path) { NodeKey key = path.firstKeyOf(Node.class); return key != null ? key.getNodeId() : null; } - private void handledNetconfNode(NodeId nodeId, NetconfNode nnode) { + private void handledNetconfNode(final NodeId nodeId, final NetconfNode nnode) { NetconfNodeConnectionStatus.ConnectionStatus csts = nnode.getConnectionStatus(); switch (csts) { @@ -117,7 +122,7 @@ class CallhomeStatusReporter implements DataChangeListener, StatusRecorder, Auto } } - private void handleConnectedNetconfNode(NodeId nodeId) { + private void handleConnectedNetconfNode(final NodeId nodeId) { // Fully connected, all services for remote device are // available from the MountPointService. LOG.debug("NETCONF Node: {} is fully connected", nodeId.getValue()); @@ -127,14 +132,15 @@ class CallhomeStatusReporter implements DataChangeListener, StatusRecorder, Auto LOG.warn("No corresponding callhome device found - exiting."); } else { Device modifiedDevice = withConnectedStatus(opDev); - if(modifiedDevice == null) - return ; + if (modifiedDevice == null) { + return; + } LOG.info("Setting successful status for callhome device id:{}.", nodeId); writeDevice(nodeId, modifiedDevice); } } - private void handleDisconnectedNetconfNode(NodeId nodeId) { + private void handleDisconnectedNetconfNode(final NodeId nodeId) { LOG.debug("NETCONF Node: {} disconnected", nodeId.getValue()); Device opDev = readAndGetDevice(nodeId); @@ -142,14 +148,15 @@ class CallhomeStatusReporter implements DataChangeListener, StatusRecorder, Auto LOG.warn("No corresponding callhome device found - exiting."); } else { Device modifiedDevice = withDisconnectedStatus(opDev); - if(modifiedDevice == null) + if (modifiedDevice == null) { return; + } LOG.info("Setting disconnected status for callhome device id:{}.", nodeId); writeDevice(nodeId, modifiedDevice); } } - private void handleUnableToConnectNetconfNode(NodeId nodeId) { + private void handleUnableToConnectNetconfNode(final NodeId nodeId) { // The maximum configured number of reconnect attempts // have been reached. No more reconnects will be // attempted by the Netconf Connector. @@ -160,158 +167,160 @@ class CallhomeStatusReporter implements DataChangeListener, StatusRecorder, Auto LOG.warn("No corresponding callhome device found - exiting."); } else { Device modifiedDevice = withFailedStatus(opDev); - if(modifiedDevice == null) + if (modifiedDevice == null) { return; + } LOG.info("Setting failed status for callhome device id:{}.", nodeId); writeDevice(nodeId, modifiedDevice); } } - void asForceListedDevice(String id, PublicKey serverKey) { + void asForceListedDevice(final String id, final PublicKey serverKey) { NodeId nid = new NodeId(id); Device device = newDevice(id, serverKey, Device1.DeviceStatus.DISCONNECTED); writeDevice(nid, device); } - void asUnlistedDevice(String id, PublicKey serverKey) { + void asUnlistedDevice(final String id, final PublicKey serverKey) { NodeId nid = new NodeId(id); Device device = newDevice(id, serverKey, Device1.DeviceStatus.FAILEDNOTALLOWED); writeDevice(nid, device); } - private Device newDevice(String id, PublicKey serverKey, Device1.DeviceStatus status) { + private static Device newDevice(final String id, final PublicKey serverKey, final Device1.DeviceStatus status) { String sshEncodedKey = serverKey.toString(); try { sshEncodedKey = AuthorizedKeysDecoder.encodePublicKey(serverKey); } catch (IOException e) { - e.printStackTrace(); - LOG.warn("Unable to encode public key to ssh format."); + LOG.warn("Unable to encode public key to ssh format.", e); } Device1 d1 = new Device1Builder().setDeviceStatus(Device1.DeviceStatus.FAILEDNOTALLOWED).build(); DeviceBuilder builder = new DeviceBuilder() .setUniqueId(id) - .setKey(new DeviceKey(id)) + .withKey(new DeviceKey(id)) .setSshHostKey(sshEncodedKey) .addAugmentation(Device1.class, d1); return builder.build(); } - private Device readAndGetDevice(NodeId nodeId) { - Optional opDevGet = readDevice(nodeId); - if (opDevGet != null) { - if (opDevGet.isPresent()) { - return opDevGet.get(); - } - } - - return null; + private Device readAndGetDevice(final NodeId nodeId) { + return readDevice(nodeId).orElse(null); } - private Optional readDevice(NodeId nodeId) { - ReadOnlyTransaction opTx = dataBroker.newReadOnlyTransaction(); + private Optional readDevice(final NodeId nodeId) { + ReadTransaction opTx = dataBroker.newReadOnlyTransaction(); InstanceIdentifier deviceIID = buildDeviceInstanceIdentifier(nodeId); - CheckedFuture, ReadFailedException> devFuture = - opTx.read(LogicalDatastoreType.OPERATIONAL, deviceIID); - + ListenableFuture> devFuture = opTx.read(LogicalDatastoreType.OPERATIONAL, deviceIID); try { - return devFuture.checkedGet(); - } catch (ReadFailedException e) { - return null; + return devFuture.get(); + } catch (InterruptedException | ExecutionException e) { + return Optional.empty(); } } - private void writeDevice(NodeId nodeId, Device modifiedDevice) { - ReadWriteTransaction opTx = dataBroker.newReadWriteTransaction(); + private void writeDevice(final NodeId nodeId, final Device modifiedDevice) { + WriteTransaction opTx = dataBroker.newWriteOnlyTransaction(); opTx.merge(LogicalDatastoreType.OPERATIONAL, buildDeviceInstanceIdentifier(nodeId), modifiedDevice); - opTx.submit(); + commit(opTx, modifiedDevice.key()); } - private InstanceIdentifier buildDeviceInstanceIdentifier(NodeId nodeId) { + private static InstanceIdentifier buildDeviceInstanceIdentifier(final NodeId nodeId) { return InstanceIdentifier.create(NetconfCallhomeServer.class) .child(AllowedDevices.class) .child(Device.class, new DeviceKey(nodeId.getValue())); } - private Device withConnectedStatus(Device opDev) { + 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(); + .setSshHostKey(opDev.getSshHostKey()).build(); } - private Device withFailedStatus(Device opDev) { + 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(); } - private Device withDisconnectedStatus(Device opDev) { + 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(); } - private Device withFailedAuthStatus(Device opDev) { + 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(); + .setSshHostKey(opDev.getSshHostKey()).build(); } - private void setDeviceStatus(Device device) { + private void setDeviceStatus(final Device device) { WriteTransaction tx = dataBroker.newWriteOnlyTransaction(); - InstanceIdentifier Device_IID = - InstanceIdentifier.create(NetconfCallhomeServer.class) + InstanceIdentifier deviceIId = InstanceIdentifier.create(NetconfCallhomeServer.class) .child(AllowedDevices.class) - .child(Device.class, device.getKey()); + .child(Device.class, device.key()); - tx.merge(LogicalDatastoreType.OPERATIONAL, Device_IID, device); - tx.submit(); + tx.merge(LogicalDatastoreType.OPERATIONAL, deviceIId, device); + commit(tx, device.key()); } + private static void commit(final WriteTransaction tx, final DeviceKey device) { + tx.commit().addCallback(new FutureCallback() { + @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(); - CheckedFuture, ReadFailedException> devicesFuture = + ReadTransaction rxTransaction = dataBroker.newReadOnlyTransaction(); + ListenableFuture> devicesFuture = rxTransaction.read(LogicalDatastoreType.OPERATIONAL, IetfZeroTouchCallHomeServerProvider.ALL_DEVICES); - try { - Optional opt = devicesFuture.checkedGet(); - if (opt.isPresent()) { - AllowedDevices devices = opt.get(); - return devices; - } - } catch (ReadFailedException e) { - LOG.error("Error trying to read the whitelist devices: {}", e); + return devicesFuture.get().orElse(null); + } catch (ExecutionException | InterruptedException e) { + LOG.error("Error trying to read the whitelist devices", e); + return null; } - - return null; } private List getDevicesAsList() { AllowedDevices devices = getDevices(); - return (devices == null) ? new ArrayList() : devices.getDevice(); + return devices == null ? new ArrayList<>() : devices.getDevice(); } @Override - public void reportFailedAuth(PublicKey sshKey) { + public void reportFailedAuth(final PublicKey sshKey) { AuthorizedKeysDecoder decoder = new AuthorizedKeysDecoder(); for (Device device : getDevicesAsList()) { String keyString = device.getSshHostKey(); + if (keyString == null) { + LOG.info("Whitelist device {} does not have a host key, skipping it", device.getUniqueId()); + continue; + } try { PublicKey pubKey = decoder.decodePublicKey(keyString); if (sshKey.getAlgorithm().equals(pubKey.getAlgorithm()) && sshKey.equals(pubKey)) { Device failedDevice = withFailedAuthStatus(device); - if(failedDevice==null) + if (failedDevice == null) { return; + } LOG.info("Setting auth failed status for callhome device id:{}.", failedDevice.getUniqueId()); setDeviceStatus(failedDevice); return; } - } catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchProviderException e) { - LOG.error("Failed decoding a device key with host key: {} {}", keyString, e); + } catch (GeneralSecurityException e) { + LOG.error("Failed decoding a device key with host key: {}", keyString, e); return; } } @@ -321,7 +330,7 @@ class CallhomeStatusReporter implements DataChangeListener, StatusRecorder, Auto } @Override - public void close() throws Exception { + public void close() { reg.close(); } }