--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * 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.sal.connect.netconf.sal;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.FluentIterable;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+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.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.inventory.rev140108.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.inventory.rev140108.NetconfNodeBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Asynchronous (Binding-aware) adapter over datastore subtree for netconf device.
+ *
+ * All data changes are submitted to an ExecutorService to avoid Thread blocking while sal is waiting for schema.
+ *
+ * @deprecated Data is pushed into Topology instead if Inventory model
+ */
+@Deprecated
+final class NetconfDeviceDatastoreAdapter implements AutoCloseable {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceDatastoreAdapter.class);
+
+ private final RemoteDeviceId id;
+ private final BindingTransactionChain txChain;
+
+ NetconfDeviceDatastoreAdapter(final RemoteDeviceId deviceId, final DataBroker dataService) {
+ this.id = Preconditions.checkNotNull(deviceId);
+ this.txChain = Preconditions.checkNotNull(dataService).createTransactionChain(new TransactionChainListener() {
+ @Override
+ public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction, Throwable cause) {
+ logger.error("{}: TransactionChain({}) {} FAILED!", id, chain, transaction.getIdentifier(), cause);
+ throw new IllegalStateException(id + " TransactionChain(" + chain + ") not committed correctly", cause);
+ }
+
+ @Override
+ public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {
+ logger.trace("{}: TransactionChain({}) {} SUCCESSFUL", id, chain);
+ }
+ });
+
+ initDeviceData();
+ }
+
+ public void updateDeviceState(final boolean up, final Set<QName> capabilities) {
+ final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node data = buildDataForDeviceState(
+ up, capabilities, id);
+
+ final ReadWriteTransaction transaction = txChain.newReadWriteTransaction();
+ logger.trace("{}: Update device state transaction {} merging operational data started.", id, transaction.getIdentifier());
+ transaction.put(LogicalDatastoreType.OPERATIONAL, id.getBindingPath(), data);
+ logger.trace("{}: Update device state transaction {} merging operational data ended.", id, transaction.getIdentifier());
+
+ commitTransaction(transaction, "update");
+ }
+
+ private void removeDeviceConfigAndState() {
+ final WriteTransaction transaction = txChain.newWriteOnlyTransaction();
+ logger.trace("{}: Close device state transaction {} removing all data started.", id, transaction.getIdentifier());
+ transaction.delete(LogicalDatastoreType.CONFIGURATION, id.getBindingPath());
+ transaction.delete(LogicalDatastoreType.OPERATIONAL, id.getBindingPath());
+ logger.trace("{}: Close device state transaction {} removing all data ended.", id, transaction.getIdentifier());
+
+ commitTransaction(transaction, "close");
+ }
+
+ private void initDeviceData() {
+ final WriteTransaction transaction = txChain.newWriteOnlyTransaction();
+
+ createNodesListIfNotPresent(transaction);
+
+ final InstanceIdentifier<Node> path = id.getBindingPath();
+ final Node nodeWithId = getNodeWithId(id);
+
+ logger.trace("{}: Init device state transaction {} putting if absent operational data started.", id, transaction.getIdentifier());
+ transaction.put(LogicalDatastoreType.OPERATIONAL, path, nodeWithId);
+ logger.trace("{}: Init device state transaction {} putting operational data ended.", id, transaction.getIdentifier());
+
+ logger.trace("{}: Init device state transaction {} putting if absent config data started.", id, transaction.getIdentifier());
+ transaction.put(LogicalDatastoreType.CONFIGURATION, path, nodeWithId);
+ logger.trace("{}: Init device state transaction {} putting config data ended.", id, transaction.getIdentifier());
+
+ commitTransaction(transaction, "init");
+ }
+
+ private void createNodesListIfNotPresent(final WriteTransaction writeTx) {
+ final Nodes nodes = new NodesBuilder().build();
+ final InstanceIdentifier<Nodes> path = InstanceIdentifier.builder(Nodes.class).build();
+ logger.trace("{}: Merging {} container to ensure its presence", id, Nodes.QNAME, writeTx.getIdentifier());
+ writeTx.merge(LogicalDatastoreType.CONFIGURATION, path, nodes);
+ writeTx.merge(LogicalDatastoreType.OPERATIONAL, path, nodes);
+ }
+
+ private void commitTransaction(final WriteTransaction transaction, final String txType) {
+ logger.trace("{}: Committing Transaction {}:{}", id, txType, transaction.getIdentifier());
+ final CheckedFuture<Void, TransactionCommitFailedException> result = transaction.submit();
+
+ Futures.addCallback(result, new FutureCallback<Void>() {
+ @Override
+ public void onSuccess(final Void result) {
+ logger.trace("{}: Transaction({}) {} SUCCESSFUL", id, txType, transaction.getIdentifier());
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ logger.error("{}: Transaction({}) {} FAILED!", id, txType, transaction.getIdentifier(), t);
+ throw new IllegalStateException(id + " Transaction(" + txType + ") not committed correctly", t);
+ }
+ });
+
+ }
+
+ @Override
+ public void close() throws Exception {
+ removeDeviceConfigAndState();
+ txChain.close();
+ }
+
+ public static org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node buildDataForDeviceState(
+ final boolean up, final Set<QName> capabilities, final RemoteDeviceId id) {
+
+ final NodeBuilder nodeBuilder = getNodeWithIdBuilder(id);
+ final NetconfNodeBuilder netconfNodeBuilder = new NetconfNodeBuilder();
+ netconfNodeBuilder.setConnected(up);
+ netconfNodeBuilder.setInitialCapability(FluentIterable.from(capabilities)
+ .transform(new Function<QName, String>() {
+ @Override
+ public String apply(final QName input) {
+ return input.toString();
+ }
+ }).toList());
+ nodeBuilder.addAugmentation(NetconfNode.class, netconfNodeBuilder.build());
+
+ return nodeBuilder.build();
+ }
+
+ private static ListenableFuture<Optional<Node>> readNodeData(
+ final LogicalDatastoreType store,
+ final ReadWriteTransaction transaction,
+ final InstanceIdentifier<Node> path) {
+ return transaction.read(store, path);
+ }
+
+ private static Node getNodeWithId(final RemoteDeviceId id) {
+ final NodeBuilder nodeBuilder = getNodeWithIdBuilder(id);
+ return nodeBuilder.build();
+ }
+
+ private static NodeBuilder getNodeWithIdBuilder(final RemoteDeviceId id) {
+ final NodeBuilder nodeBuilder = new NodeBuilder();
+ nodeBuilder.setKey(id.getBindingKey());
+ nodeBuilder.setId(id.getBindingKey().getId());
+ return nodeBuilder;
+ }
+}
package org.opendaylight.netconf.sal.connect.netconf.sal;
import com.google.common.collect.Lists;
+import java.util.Collections;
import java.util.List;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final NetconfDeviceNotificationService notificationService = new NetconfDeviceNotificationService();
+ salProvider.getMountInstance().onDeviceConnected(schemaContext, domBroker, deviceRpc, notificationService);
+ salProvider.getDatastoreAdapter().updateDeviceState(true, netconfSessionPreferences.getModuleBasedCaps());
salProvider.getMountInstance().onTopologyDeviceConnected(schemaContext, domBroker, deviceRpc, notificationService);
salProvider.getTopologyDatastoreAdapter().updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities());
}
@Override
public synchronized void onDeviceDisconnected() {
- salProvider.getTopologyDatastoreAdapter().updateDeviceData(false,
- new NetconfDeviceCapabilities());
+ salProvider.getDatastoreAdapter().updateDeviceState(false, Collections.<QName>emptySet());
+ salProvider.getTopologyDatastoreAdapter().updateDeviceData(false, new NetconfDeviceCapabilities());
+ salProvider.getMountInstance().onDeviceDisconnected();
salProvider.getMountInstance().onTopologyDeviceDisconnected();
}
@Override
public synchronized void onDeviceFailed(final Throwable throwable) {
salProvider.getTopologyDatastoreAdapter().setDeviceAsFailed(throwable);
+ salProvider.getMountInstance().onDeviceDisconnected();
salProvider.getMountInstance().onTopologyDeviceDisconnected();
}
try {
resource.close();
} catch (final Exception e) {
- LOG.warn("{}: Ignoring exception while closing {}", id,
- resource, e);
+ LOG.warn("{}: Ignoring exception while closing {}", id, resource, e);
}
}
}
final class NetconfDeviceSalProvider implements AutoCloseable, Provider, BindingAwareProvider {
- private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceSalProvider.class);
+ private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceSalProvider.class);
private final RemoteDeviceId id;
+ private volatile NetconfDeviceDatastoreAdapter datastoreAdapter;
private MountInstance mountInstance;
private volatile NetconfDeviceTopologyAdapter topologyDatastoreAdapter;
return mountInstance;
}
+ public NetconfDeviceDatastoreAdapter getDatastoreAdapter() {
+ Preconditions.checkState(datastoreAdapter != null,
+ "%s: Sal provider %s was not initialized by sal. Cannot get datastore adapter", id);
+ return datastoreAdapter;
+ }
+
public NetconfDeviceTopologyAdapter getTopologyDatastoreAdapter() {
Preconditions.checkState(topologyDatastoreAdapter != null,
"%s: Sal provider %s was not initialized by sal. Cannot get topology datastore adapter", id);
@Override
public void onSessionInitiated(final Broker.ProviderSession session) {
- LOG.debug("{}: (BI)Session with sal established {}", id, session);
+ logger.debug("{}: (BI)Session with sal established {}", id, session);
final DOMMountPointService mountService = session.getService(DOMMountPointService.class);
if (mountService != null) {
@Override
public void onSessionInitiated(final BindingAwareBroker.ProviderContext session) {
- LOG.debug("{}: Session with sal established {}", id, session);
+ logger.debug("{}: Session with sal established {}", id, session);
final DataBroker dataBroker = session.getSALService(DataBroker.class);
+ datastoreAdapter = new NetconfDeviceDatastoreAdapter(id, dataBroker);
topologyDatastoreAdapter = new NetconfDeviceTopologyAdapter(id, dataBroker);
}
public void close() throws Exception {
mountInstance.close();
+ datastoreAdapter.close();
+ datastoreAdapter = null;
topologyDatastoreAdapter.close();
topologyDatastoreAdapter = null;
}
private DOMMountPointService mountService;
private final RemoteDeviceId id;
+ private ObjectRegistration<DOMMountPoint> registration;
private NetconfDeviceNotificationService notificationService;
private ObjectRegistration<DOMMountPoint> topologyRegistration;
this.id = Preconditions.checkNotNull(id);
}
+ @Deprecated
+ synchronized void onDeviceConnected(final SchemaContext initialCtx,
+ final DOMDataBroker broker, final DOMRpcService rpc,
+ final NetconfDeviceNotificationService notificationService) {
+
+ Preconditions.checkNotNull(mountService, "Closed");
+ Preconditions.checkState(registration == null, "Already initialized");
+
+ final DOMMountPointService.DOMMountPointBuilder mountBuilder = mountService.createMountPoint(id.getPath());
+ mountBuilder.addInitialSchemaContext(initialCtx);
+
+ mountBuilder.addService(DOMDataBroker.class, broker);
+ mountBuilder.addService(DOMRpcService.class, rpc);
+ mountBuilder.addService(DOMNotificationService.class, notificationService);
+ this.notificationService = notificationService;
+
+ registration = mountBuilder.register();
+ logger.debug("{}: Mountpoint exposed into MD-SAL {}", id, registration);
+ }
+
+ @Deprecated
+ synchronized void onDeviceDisconnected() {
+ if(registration == null) {
+ logger.trace("{}: Not removing mountpoint from MD-SAL, mountpoint was not registered yet", id);
+ return;
+ }
+
+ try {
+ registration.close();
+ } catch (final Exception e) {
+ // Only log and ignore
+ logger.warn("Unable to unregister mount instance for {}. Ignoring exception", id.getPath(), e);
+ } finally {
+ logger.debug("{}: Mountpoint removed from MD-SAL {}", id, registration);
+ registration = null;
+ }
+ }
+
synchronized void onTopologyDeviceConnected(final SchemaContext initialCtx,
final DOMDataBroker broker, final DOMRpcService rpc,
final NetconfDeviceNotificationService notificationService) {
mountBuilder.addService(DOMDataBroker.class, broker);
mountBuilder.addService(DOMRpcService.class, rpc);
mountBuilder.addService(DOMNotificationService.class, notificationService);
- this.notificationService = notificationService;
topologyRegistration = mountBuilder.register();
- LOG.debug("{}: TOPOLOGY Mountpoint exposed into MD-SAL {}", id,
- topologyRegistration);
+ logger.debug("{}: TOPOLOGY Mountpoint exposed into MD-SAL {}", id, registration);
}
synchronized void onTopologyDeviceDisconnected() {
if(topologyRegistration == null) {
- LOG.trace(
- "{}: Not removing TOPOLOGY mountpoint from MD-SAL, mountpoint was not registered yet",
- id);
+ logger.trace("{}: Not removing TOPOLOGY mountpoint from MD-SAL, mountpoint was not registered yet", id);
return;
}
topologyRegistration.close();
} catch (final Exception e) {
// Only log and ignore
- LOG.warn(
- "Unable to unregister mount instance for {}. Ignoring exception",
- id.getTopologyPath(), e);
+ logger.warn("Unable to unregister mount instance for {}. Ignoring exception", id.getTopologyPath(), e);
} finally {
- LOG.debug("{}: TOPOLOGY Mountpoint removed from MD-SAL {}",
- id, topologyRegistration);
+ logger.debug("{}: TOPOLOGY Mountpoint removed from MD-SAL {}", id, registration);
topologyRegistration = null;
}
}
@Override
synchronized public void close() throws Exception {
+ onDeviceDisconnected();
onTopologyDeviceDisconnected();
mountService = null;
}