* 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;
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<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())));
private static final Logger LOG = LoggerFactory.getLogger(CallhomeStatusReporter.class);
private final DataBroker dataBroker;
- private final ListenerRegistration<DataChangeListener> reg;
+ private final ListenerRegistration<CallhomeStatusReporter> 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<InstanceIdentifier<?>, 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<DataTreeModification<Node>> changes) {
+ for (DataTreeModification<Node> change: changes) {
+ final DataObjectModification<Node> rootNode = change.getRootNode();
+ final InstanceIdentifier<Node> 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<InstanceIdentifier<?>, 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) {
}
}
- 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());
}
}
- private void handleDisconnectedNetconfNode(NodeId nodeId) {
+ private void handleDisconnectedNetconfNode(final NodeId nodeId) {
LOG.debug("NETCONF Node: {} disconnected", nodeId.getValue());
Device opDev = readAndGetDevice(nodeId);
}
}
- 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.
}
}
- 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);
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<Device> 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<Device> readDevice(NodeId nodeId) {
- ReadOnlyTransaction opTx = dataBroker.newReadOnlyTransaction();
+ private Optional<Device> readDevice(final NodeId nodeId) {
+ ReadTransaction opTx = dataBroker.newReadOnlyTransaction();
InstanceIdentifier<Device> deviceIID = buildDeviceInstanceIdentifier(nodeId);
- CheckedFuture<Optional<Device>, ReadFailedException> devFuture =
- opTx.read(LogicalDatastoreType.OPERATIONAL, deviceIID);
-
+ ListenableFuture<Optional<Device>> 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<Device> buildDeviceInstanceIdentifier(NodeId nodeId) {
+ private static InstanceIdentifier<Device> 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();
}
- 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();
}
- private void setDeviceStatus(Device device) {
+ 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.getKey());
+ .child(Device.class, device.key());
tx.merge(LogicalDatastoreType.OPERATIONAL, deviceIId, device);
- tx.submit();
+ 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();
- CheckedFuture<Optional<AllowedDevices>, ReadFailedException> devicesFuture =
+ ReadTransaction rxTransaction = dataBroker.newReadOnlyTransaction();
+ ListenableFuture<Optional<AllowedDevices>> devicesFuture =
rxTransaction.read(LogicalDatastoreType.OPERATIONAL, IetfZeroTouchCallHomeServerProvider.ALL_DEVICES);
-
try {
- Optional<AllowedDevices> 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<Device> getDevicesAsList() {
AllowedDevices devices = getDevices();
- return (devices == null) ? new ArrayList<Device>() : 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()) {
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;
}
}
}
@Override
- public void close() throws Exception {
+ public void close() {
reg.close();
}
}