<bundle>mvn:org.opendaylight.controller/threadpool-config-api/${project.version}</bundle>
<bundle>mvn:org.opendaylight.controller/threadpool-config-impl/${project.version}</bundle>
<feature version='${project.version}'>odl-config-startup</feature>
+ <configfile finalname="configuration/initial/00-netty.xml">mvn:org.opendaylight.controller/config-netty-config/${config.version}/xml/config</configfile>
</feature>
</features>
\ No newline at end of file
<artifactId>sal-rest-connector-config</artifactId>
<version>${mdsal.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-netty-config</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>md-sal-config</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-config</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-connector-config</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-rest-docgen</artifactId>
<artifactId>sample-toaster-provider</artifactId>
<version>${mdsal.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>toaster-config</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller.thirdparty</groupId>
<artifactId>com.sun.jersey.jersey-servlet</artifactId>
<artifactId>netty-event-executor-config</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
*/
package org.opendaylight.protocol.framework;
+import java.io.Closeable;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.google.common.base.Preconditions;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
-import io.netty.channel.Channel;
import io.netty.buffer.PooledByteBufAllocator;
+import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.Promise;
-import java.io.Closeable;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
/**
* Dispatcher class for creating servers and clients. The idea is to first create servers and clients and the run the
* start method that will handle sockets in different thread.
*/
protected Future<S> createClient(final InetSocketAddress address, final ReconnectStrategy strategy, final PipelineInitializer<S> initializer) {
final Bootstrap b = new Bootstrap();
- final ProtocolSessionPromise<S> p = new ProtocolSessionPromise<S>(executor, address, strategy, b);
+ final ProtocolSessionPromise<S> p = new ProtocolSessionPromise<>(executor, address, strategy, b);
b.option(ChannelOption.SO_KEEPALIVE, true).handler(
new ChannelInitializer<SocketChannel>() {
@Override
});
customizeBootstrap(b);
+ setWorkerGroup(b);
+ setChannelFactory(b);
+
+ p.connect();
+ LOG.debug("Client created.");
+ return p;
+ }
+ private void setWorkerGroup(final Bootstrap b) {
if (b.group() == null) {
b.group(workerGroup);
}
+ }
- // There is no way to detect if this was already set by
- // customizeBootstrap()
- try {
- b.channel(NioSocketChannel.class);
- } catch (IllegalStateException e) {
- LOG.trace("Not overriding channelFactory on bootstrap {}", b, e);
- }
+ /**
+ * Create a client but use a pre-configured bootstrap.
+ * This method however replaces the ChannelInitializer in the bootstrap. All other configuration is preserved.
+ *
+ * @param address remote address
+ */
+ protected Future<S> createClient(final InetSocketAddress address, final ReconnectStrategy strategy, final Bootstrap bootstrap, final PipelineInitializer<S> initializer) {
+ final ProtocolSessionPromise<S> p = new ProtocolSessionPromise<>(executor, address, strategy, bootstrap);
+
+ bootstrap.handler(
+ new ChannelInitializer<SocketChannel>() {
+ @Override
+ protected void initChannel(final SocketChannel ch) {
+ initializer.initializeChannel(ch, p);
+ }
+ });
p.connect();
LOG.debug("Client created.");
}
/**
+ *
+ * @deprecated use {@link org.opendaylight.protocol.framework.AbstractDispatcher#createReconnectingClient(java.net.InetSocketAddress, ReconnectStrategyFactory, org.opendaylight.protocol.framework.AbstractDispatcher.PipelineInitializer)} with only one reconnectStrategyFactory instead.
+ *
* Creates a client.
*
* @param address remote address
* @return Future representing the reconnection task. It will report completion based on reestablishStrategy, e.g.
* success if it indicates no further attempts should be made and failure if it reports an error
*/
+ @Deprecated
protected Future<Void> createReconnectingClient(final InetSocketAddress address, final ReconnectStrategyFactory connectStrategyFactory,
final ReconnectStrategy reestablishStrategy, final PipelineInitializer<S> initializer) {
+ return createReconnectingClient(address, connectStrategyFactory, initializer);
+ }
- final ReconnectPromise<S, L> p = new ReconnectPromise<S, L>(GlobalEventExecutor.INSTANCE, this, address, connectStrategyFactory, reestablishStrategy, initializer);
- p.connect();
+ /**
+ * Creates a reconnecting client.
+ *
+ * @param address remote address
+ * @param connectStrategyFactory Factory for creating reconnection strategy for every reconnect attempt
+ *
+ * @return Future representing the reconnection task. It will report completion based on reestablishStrategy, e.g.
+ * success if it indicates no further attempts should be made and failure if it reports an error
+ */
+ protected Future<Void> createReconnectingClient(final InetSocketAddress address, final ReconnectStrategyFactory connectStrategyFactory,
+ final PipelineInitializer<S> initializer) {
+ final Bootstrap b = new Bootstrap();
+
+ final ReconnectPromise<S, L> p = new ReconnectPromise<>(GlobalEventExecutor.INSTANCE, this, address, connectStrategyFactory, b, initializer);
+
+ b.option(ChannelOption.SO_KEEPALIVE, true);
+ customizeBootstrap(b);
+ setWorkerGroup(b);
+ setChannelFactory(b);
+
+ p.connect();
return p;
}
+ private void setChannelFactory(final Bootstrap b) {
+ // There is no way to detect if this was already set by
+ // customizeBootstrap()
+ try {
+ b.channel(NioSocketChannel.class);
+ } catch (final IllegalStateException e) {
+ LOG.trace("Not overriding channelFactory on bootstrap {}", b, e);
+ }
+ }
+
/**
* @deprecated Should only be used with {@link AbstractDispatcher#AbstractDispatcher()}
*/
this.bossGroup.shutdownGracefully();
}
}
-
}
*/
package org.opendaylight.protocol.framework;
+import com.google.common.base.Preconditions;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
-
import java.net.InetSocketAddress;
-
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Preconditions;
-
@ThreadSafe
final class ProtocolSessionPromise<S extends ProtocolSession<?>> extends DefaultPromise<S> {
private static final Logger LOG = LoggerFactory.getLogger(ProtocolSessionPromise.class);
LOG.debug("Promise {} attempting connect for {}ms", lock, timeout);
this.b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout);
- this.pending = this.b.connect(this.address).addListener(new ChannelFutureListener() {
- @Override
- public void operationComplete(final ChannelFuture cf) throws Exception {
- synchronized (lock) {
-
- LOG.debug("Promise {} connection resolved", lock);
-
- // Triggered when a connection attempt is resolved.
- Preconditions.checkState(ProtocolSessionPromise.this.pending.equals(cf));
-
- /*
- * The promise we gave out could have been cancelled,
- * which cascades to the connect getting cancelled,
- * but there is a slight race window, where the connect
- * is already resolved, but the listener has not yet
- * been notified -- cancellation at that point won't
- * stop the notification arriving, so we have to close
- * the race here.
- */
- if (isCancelled()) {
- if (cf.isSuccess()) {
- LOG.debug("Closing channel for cancelled promise {}", lock);
- cf.channel().close();
- }
- return;
- }
-
- if (!cf.isSuccess()) {
- LOG.debug("Attempt to connect to {} failed", ProtocolSessionPromise.this.address, cf.cause());
-
- final Future<Void> rf = ProtocolSessionPromise.this.strategy.scheduleReconnect(cf.cause());
- rf.addListener(new FutureListener<Void>() {
- @Override
- public void operationComplete(final Future<Void> sf) {
- synchronized (lock) {
- // Triggered when a connection attempt is to be made.
- Preconditions.checkState(ProtocolSessionPromise.this.pending.equals(sf));
-
- /*
- * The promise we gave out could have been cancelled,
- * which cascades to the reconnect attempt getting
- * cancelled, but there is a slight race window, where
- * the reconnect attempt is already enqueued, but the
- * listener has not yet been notified -- if cancellation
- * happens at that point, we need to catch it here.
- */
- if (!isCancelled()) {
- if (sf.isSuccess()) {
- connect();
- } else {
- setFailure(sf.cause());
- }
- }
- }
- }
- });
-
- ProtocolSessionPromise.this.pending = rf;
- } else {
- LOG.debug("Promise {} connection successful", lock);
- }
- }
- }
- });
+ final ChannelFuture connectFuture = this.b.connect(this.address);
+ // Add listener that attempts reconnect by invoking this method again.
+ connectFuture.addListener(new BootstrapConnectListener(lock));
+ this.pending = connectFuture;
} catch (final Exception e) {
- LOG.info("Failed to connect to {}", e);
+ LOG.info("Failed to connect to {}", address, e);
setFailure(e);
}
}
this.strategy.reconnectSuccessful();
return super.setSuccess(result);
}
+
+ private class BootstrapConnectListener implements ChannelFutureListener {
+ private final Object lock;
+
+ public BootstrapConnectListener(final Object lock) {
+ this.lock = lock;
+ }
+
+ @Override
+ public void operationComplete(final ChannelFuture cf) throws Exception {
+ synchronized (lock) {
+
+ LOG.debug("Promise {} connection resolved", lock);
+
+ // Triggered when a connection attempt is resolved.
+ Preconditions.checkState(ProtocolSessionPromise.this.pending.equals(cf));
+
+ /*
+ * The promise we gave out could have been cancelled,
+ * which cascades to the connect getting cancelled,
+ * but there is a slight race window, where the connect
+ * is already resolved, but the listener has not yet
+ * been notified -- cancellation at that point won't
+ * stop the notification arriving, so we have to close
+ * the race here.
+ */
+ if (isCancelled()) {
+ if (cf.isSuccess()) {
+ LOG.debug("Closing channel for cancelled promise {}", lock);
+ cf.channel().close();
+ }
+ return;
+ }
+
+ if(cf.isSuccess()) {
+ LOG.debug("Promise {} connection successful", lock);
+ return;
+ }
+
+ LOG.debug("Attempt to connect to {} failed", ProtocolSessionPromise.this.address, cf.cause());
+
+ final Future<Void> rf = ProtocolSessionPromise.this.strategy.scheduleReconnect(cf.cause());
+ rf.addListener(new ReconnectingStrategyListener());
+ ProtocolSessionPromise.this.pending = rf;
+ }
+ }
+
+ private class ReconnectingStrategyListener implements FutureListener<Void> {
+ @Override
+ public void operationComplete(final Future<Void> sf) {
+ synchronized (lock) {
+ // Triggered when a connection attempt is to be made.
+ Preconditions.checkState(ProtocolSessionPromise.this.pending.equals(sf));
+
+ /*
+ * The promise we gave out could have been cancelled,
+ * which cascades to the reconnect attempt getting
+ * cancelled, but there is a slight race window, where
+ * the reconnect attempt is already enqueued, but the
+ * listener has not yet been notified -- if cancellation
+ * happens at that point, we need to catch it here.
+ */
+ if (!isCancelled()) {
+ if (sf.isSuccess()) {
+ connect();
+ } else {
+ setFailure(sf.cause());
+ }
+ }
+ }
+ }
+ }
+
+ }
+
}
*/
package org.opendaylight.protocol.framework;
-import io.netty.channel.ChannelFuture;
+import com.google.common.base.Preconditions;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
-
-import java.io.Closeable;
import java.net.InetSocketAddress;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.opendaylight.protocol.framework.AbstractDispatcher.PipelineInitializer;
-
-import com.google.common.base.Preconditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
final class ReconnectPromise<S extends ProtocolSession<?>, L extends SessionListener<?, ?, ?>> extends DefaultPromise<Void> {
+ private static final Logger LOG = LoggerFactory.getLogger(ReconnectPromise.class);
+
private final AbstractDispatcher<S, L> dispatcher;
private final InetSocketAddress address;
private final ReconnectStrategyFactory strategyFactory;
- private final ReconnectStrategy strategy;
- private final PipelineInitializer<S> initializer;
+ private final Bootstrap b;
+ private final AbstractDispatcher.PipelineInitializer<S> initializer;
private Future<?> pending;
- private final AtomicBoolean negotiationFinished = new AtomicBoolean(false);
-
public ReconnectPromise(final EventExecutor executor, final AbstractDispatcher<S, L> dispatcher, final InetSocketAddress address,
- final ReconnectStrategyFactory connectStrategyFactory, final ReconnectStrategy reestablishStrategy,
- final PipelineInitializer<S> initializer) {
+ final ReconnectStrategyFactory connectStrategyFactory, final Bootstrap b, final AbstractDispatcher.PipelineInitializer<S> initializer) {
super(executor);
+ this.b = b;
+ this.initializer = Preconditions.checkNotNull(initializer);
this.dispatcher = Preconditions.checkNotNull(dispatcher);
this.address = Preconditions.checkNotNull(address);
this.strategyFactory = Preconditions.checkNotNull(connectStrategyFactory);
- this.strategy = Preconditions.checkNotNull(reestablishStrategy);
- this.initializer = Preconditions.checkNotNull(initializer);
}
- // FIXME: BUG-190: refactor
-
synchronized void connect() {
- negotiationFinished.set(false);
-
final ReconnectStrategy cs = this.strategyFactory.createReconnectStrategy();
- final ReconnectStrategy rs = new ReconnectStrategy() {
- @Override
- public Future<Void> scheduleReconnect(final Throwable cause) {
- return cs.scheduleReconnect(cause);
- }
- @Override
- public void reconnectSuccessful() {
- cs.reconnectSuccessful();
- }
-
- @Override
- public int getConnectTimeout() throws Exception {
- final int cst = cs.getConnectTimeout();
- final int rst = ReconnectPromise.this.strategy.getConnectTimeout();
-
- if (cst == 0) {
- return rst;
- }
- if (rst == 0) {
- return cst;
- }
- return Math.min(cst, rst);
- }
- };
-
- final Future<S> cf = this.dispatcher.createClient(this.address, rs, new PipelineInitializer<S>() {
+ // Set up a client with pre-configured bootstrap, but add a closed channel handler into the pipeline to support reconnect attempts
+ pending = this.dispatcher.createClient(this.address, cs, b, new AbstractDispatcher.PipelineInitializer<S>() {
@Override
public void initializeChannel(final SocketChannel channel, final Promise<S> promise) {
- addChannelClosedListener(channel.closeFuture());
initializer.initializeChannel(channel, promise);
+
+ // add closed channel handler
+ channel.pipeline().addFirst(new ClosedChannelHandler(ReconnectPromise.this));
}
});
+ }
- final Object lock = this;
- this.pending = cf;
+ /**
+ *
+ * @return true if initial connection was established successfully, false if initial connection failed due to e.g. Connection refused, Negotiation failed
+ */
+ private boolean isInitialConnectFinished() {
+ Preconditions.checkNotNull(pending);
+ return pending.isDone() && pending.isSuccess();
+ }
- cf.addListener(new FutureListener<S>() {
+ @Override
+ public synchronized boolean cancel(final boolean mayInterruptIfRunning) {
+ if (super.cancel(mayInterruptIfRunning)) {
+ Preconditions.checkNotNull(pending);
+ this.pending.cancel(mayInterruptIfRunning);
+ return true;
+ }
- @Override
- public void operationComplete(final Future<S> future) {
- synchronized (lock) {
- if (!future.isSuccess()) {
- final Future<Void> rf = ReconnectPromise.this.strategy.scheduleReconnect(cf.cause());
-
- if(rf == null) {
- // This should reflect: no more reconnecting strategies, enough
- // Currently all reconnect strategies fail with exception, should return null
- return;
- }
-
- ReconnectPromise.this.pending = rf;
-
- rf.addListener(new FutureListener<Void>() {
- @Override
- public void operationComplete(final Future<Void> sf) {
- synchronized (lock) {
- /*
- * The promise we gave out could have been cancelled,
- * which cascades to the reconnect attempt getting
- * cancelled, but there is a slight race window, where
- * the reconnect attempt is already enqueued, but the
- * listener has not yet been notified -- if cancellation
- * happens at that point, we need to catch it here.
- */
- if (!isCancelled()) {
- if (sf.isSuccess()) {
- connect();
- } else {
- setFailure(sf.cause());
- }
- }
- }
- }
- });
- } else {
- /*
- * FIXME: BUG-190: we have a slight race window with cancellation
- * here. Analyze and define its semantics.
- */
- ReconnectPromise.this.strategy.reconnectSuccessful();
- negotiationFinished.set(true);
- }
- }
- }
- });
+ return false;
}
- private final ClosedChannelListener closedChannelListener = new ClosedChannelListener();
-
- class ClosedChannelListener implements Closeable, FutureListener<Void> {
+ /**
+ * Channel handler that responds to channelInactive event and reconnects the session.
+ * Only if the initial connection was successfully established and promise was not canceled.
+ */
+ private static final class ClosedChannelHandler extends ChannelInboundHandlerAdapter {
+ private final ReconnectPromise<?, ?> promise;
- private final AtomicBoolean stop = new AtomicBoolean(false);
+ public ClosedChannelHandler(final ReconnectPromise<?, ?> promise) {
+ this.promise = promise;
+ }
@Override
- public void operationComplete(final Future<Void> future) throws Exception {
- if (stop.get()) {
+ public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
+ if (promise.isCancelled()) {
return;
}
- // Start reconnecting crashed session after negotiation was successful
- if (!negotiationFinished.get()) {
+ // Check if initial connection was fully finished. If the session was dropped during negotiation, reconnect will not happen.
+ // Session can be dropped during negotiation on purpose by the client side and would make no sense to initiate reconnect
+ if (promise.isInitialConnectFinished() == false) {
return;
}
- connect();
- }
-
- @Override
- public void close() {
- this.stop.set(true);
+ LOG.debug("Reconnecting after connection to {} was dropped", promise.address);
+ promise.connect();
}
}
- private void addChannelClosedListener(final ChannelFuture channelFuture) {
- channelFuture.addListener(closedChannelListener);
- }
-
- @Override
- public synchronized boolean cancel(final boolean mayInterruptIfRunning) {
- closedChannelListener.close();
-
- if (super.cancel(mayInterruptIfRunning)) {
- this.pending.cancel(mayInterruptIfRunning);
- return true;
- }
-
- return false;
- }
}
import java.util.EventListener;
/**
- * Listener that receives session state informations. This interface should be
+ * Listener that receives session state information. This interface should be
* implemented by a protocol specific abstract class, that is extended by
* a final class that implements the methods.
*/
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.Promise;
-
+import io.netty.util.concurrent.SucceededFuture;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mockito;
public class ServerTest {
SimpleDispatcher clientDispatcher, dispatcher;
- final SimpleSessionListener pce = new SimpleSessionListener();
-
SimpleSession session = null;
ChannelFuture server = null;
InetSocketAddress serverAddress;
private NioEventLoopGroup eventLoopGroup;
-
+ // Dedicated loop group for server, needed for testing reconnection client
+ // With dedicated server group we can simulate session drop by shutting only the server group down
+ private NioEventLoopGroup serverLoopGroup;
@Before
public void setUp() {
final int port = 10000 + (int)(10000 * Math.random());
serverAddress = new InetSocketAddress("127.0.0.1", port);
eventLoopGroup = new NioEventLoopGroup();
+ serverLoopGroup = new NioEventLoopGroup();
+ }
+
+ @After
+ public void tearDown() throws IOException, InterruptedException, ExecutionException {
+ if(server != null) {
+ this.server.channel().close();
+ }
+ this.eventLoopGroup.shutdownGracefully().get();
+ this.serverLoopGroup.shutdownGracefully().get();
+ try {
+ Thread.sleep(500);
+ } catch (final InterruptedException e) {
+ throw new RuntimeException(e);
+ }
}
@Test
- public void testConnectionEstablished() throws Exception {
+ public void testConnectionRefused() throws Exception {
+ this.clientDispatcher = getClientDispatcher();
+
+ final ReconnectStrategy mockReconnectStrategy = getMockedReconnectStrategy();
+
+ this.clientDispatcher.createClient(this.serverAddress,
+ mockReconnectStrategy, new SessionListenerFactory<SimpleSessionListener>() {
+ @Override
+ public SimpleSessionListener getSessionListener() {
+ return new SimpleSessionListener();
+ }
+ });
+
+ Mockito.verify(mockReconnectStrategy, timeout(5000).atLeast(2)).scheduleReconnect(any(Throwable.class));
+ }
+
+ @Test
+ public void testConnectionReestablishInitial() throws Exception {
+ this.clientDispatcher = getClientDispatcher();
+
+ final ReconnectStrategy mockReconnectStrategy = getMockedReconnectStrategy();
+
+ this.clientDispatcher.createClient(this.serverAddress,
+ mockReconnectStrategy, new SessionListenerFactory<SimpleSessionListener>() {
+ @Override
+ public SimpleSessionListener getSessionListener() {
+ return new SimpleSessionListener();
+ }
+ });
+
+ Mockito.verify(mockReconnectStrategy, timeout(5000).atLeast(2)).scheduleReconnect(any(Throwable.class));
+
+ final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
+ this.dispatcher = getServerDispatcher(p);
+
+ this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory<SimpleSessionListener>() {
+ @Override
+ public SimpleSessionListener getSessionListener() {
+ return new SimpleSessionListener();
+ }
+ });
+
+ this.server.get();
+
+ assertEquals(true, p.get(3, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testConnectionDrop() throws Exception {
final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
- this.dispatcher = new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
+ this.dispatcher = getServerDispatcher(p);
+ this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory<SimpleSessionListener>() {
@Override
- public SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
- final Channel channel, final Promise<SimpleSession> promise) {
- p.setSuccess(true);
- return new SimpleSessionNegotiator(promise, channel);
+ public SimpleSessionListener getSessionListener() {
+ return new SimpleSessionListener();
}
- }, new DefaultPromise<SimpleSession>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
+ });
+
+ this.server.get();
+
+ this.clientDispatcher = getClientDispatcher();
+
+ final ReconnectStrategy reconnectStrategy = getMockedReconnectStrategy();
+ this.session = this.clientDispatcher.createClient(this.serverAddress,
+ reconnectStrategy, new SessionListenerFactory<SimpleSessionListener>() {
+ @Override
+ public SimpleSessionListener getSessionListener() {
+ return new SimpleSessionListener();
+ }
+ }).get(6, TimeUnit.SECONDS);
+
+ assertEquals(true, p.get(3, TimeUnit.SECONDS));
+
+ shutdownServer();
+
+ // No reconnect should be scheduled after server drops connection with not-reconnecting client
+ verify(reconnectStrategy, times(0)).scheduleReconnect(any(Throwable.class));
+ }
+
+ @Test
+ public void testConnectionReestablishAfterDrop() throws Exception {
+ final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
+
+ this.dispatcher = getServerDispatcher(p);
this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory<SimpleSessionListener>() {
@Override
this.server.get();
- this.clientDispatcher = new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
+ this.clientDispatcher = getClientDispatcher();
+
+ final ReconnectStrategyFactory reconnectStrategyFactory = mock(ReconnectStrategyFactory.class);
+ final ReconnectStrategy reconnectStrategy = getMockedReconnectStrategy();
+ doReturn(reconnectStrategy).when(reconnectStrategyFactory).createReconnectStrategy();
+
+ this.clientDispatcher.createReconnectingClient(this.serverAddress,
+ reconnectStrategyFactory, new SessionListenerFactory<SimpleSessionListener>() {
+ @Override
+ public SimpleSessionListener getSessionListener() {
+ return new SimpleSessionListener();
+ }
+ });
+
+ assertEquals(true, p.get(3, TimeUnit.SECONDS));
+ shutdownServer();
+
+ verify(reconnectStrategyFactory, timeout(20000).atLeast(2)).createReconnectStrategy();
+ }
+
+ @Test
+ public void testConnectionEstablished() throws Exception {
+ final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
+
+ this.dispatcher = getServerDispatcher(p);
+
+ this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory<SimpleSessionListener>() {
@Override
- public SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
- final Channel channel, final Promise<SimpleSession> promise) {
- return new SimpleSessionNegotiator(promise, channel);
+ public SimpleSessionListener getSessionListener() {
+ return new SimpleSessionListener();
}
- }, new DefaultPromise<SimpleSession>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
+ });
+
+ this.server.get();
+
+ this.clientDispatcher = getClientDispatcher();
this.session = this.clientDispatcher.createClient(this.serverAddress,
new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000), new SessionListenerFactory<SimpleSessionListener>() {
public void testConnectionFailed() throws IOException, InterruptedException, ExecutionException, TimeoutException {
final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
- this.dispatcher = new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
-
- @Override
- public SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
- final Channel channel, final Promise<SimpleSession> promise) {
- p.setSuccess(true);
- return new SimpleSessionNegotiator(promise, channel);
- }
- }, new DefaultPromise<SimpleSession>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
+ this.dispatcher = getServerDispatcher(p);
this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory<SimpleSessionListener>() {
@Override
this.server.get();
- this.clientDispatcher = new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
- @Override
- public SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
- final Channel channel, final Promise<SimpleSession> promise) {
- return new SimpleSessionNegotiator(promise, channel);
- }
- }, new DefaultPromise<SimpleSession>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
+ this.clientDispatcher = getClientDispatcher();
this.session = this.clientDispatcher.createClient(this.serverAddress,
new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000), new SessionListenerFactory<SimpleSessionListener>() {
assertFalse(session.isSuccess());
}
- @After
- public void tearDown() throws IOException, InterruptedException {
- this.server.channel().close();
- this.eventLoopGroup.shutdownGracefully();
- try {
- Thread.sleep(500);
- } catch (final InterruptedException e) {
- throw new RuntimeException(e);
- }
+ @Test
+ public void testNegotiationFailedNoReconnect() throws Exception {
+ final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
+
+ this.dispatcher = getServerDispatcher(p);
+
+ this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory<SimpleSessionListener>() {
+ @Override
+ public SimpleSessionListener getSessionListener() {
+ return new SimpleSessionListener();
+ }
+ });
+
+ this.server.get();
+
+ this.clientDispatcher = new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
+ @Override
+ public SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
+ final Channel channel, final Promise<SimpleSession> promise) {
+
+ return new SimpleSessionNegotiator(promise, channel) {
+ @Override
+ protected void startNegotiation() throws Exception {
+ negotiationFailed(new IllegalStateException("Negotiation failed"));
+ }
+ };
+ }
+ }, new DefaultPromise<SimpleSession>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
+
+ final ReconnectStrategyFactory reconnectStrategyFactory = mock(ReconnectStrategyFactory.class);
+ final ReconnectStrategy reconnectStrategy = getMockedReconnectStrategy();
+ doReturn(reconnectStrategy).when(reconnectStrategyFactory).createReconnectStrategy();
+
+ this.clientDispatcher.createReconnectingClient(this.serverAddress,
+ reconnectStrategyFactory, new SessionListenerFactory<SimpleSessionListener>() {
+ @Override
+ public SimpleSessionListener getSessionListener() {
+ return new SimpleSessionListener();
+ }
+ });
+
+
+ // Only one strategy should be created for initial connect, no more = no reconnects
+ verify(reconnectStrategyFactory, times(1)).createReconnectStrategy();
}
+
+ private SimpleDispatcher getClientDispatcher() {
+ return new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
+ @Override
+ public SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
+ final Channel channel, final Promise<SimpleSession> promise) {
+ return new SimpleSessionNegotiator(promise, channel);
+ }
+ }, new DefaultPromise<SimpleSession>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
+ }
+
+ private ReconnectStrategy getMockedReconnectStrategy() throws Exception {
+ final ReconnectStrategy mockReconnectStrategy = mock(ReconnectStrategy.class);
+ final Future<Void> future = new SucceededFuture<>(GlobalEventExecutor.INSTANCE, null);
+ doReturn(future).when(mockReconnectStrategy).scheduleReconnect(any(Throwable.class));
+ doReturn(5000).when(mockReconnectStrategy).getConnectTimeout();
+ doNothing().when(mockReconnectStrategy).reconnectSuccessful();
+ return mockReconnectStrategy;
+ }
+
+
+ private void shutdownServer() throws InterruptedException, ExecutionException {
+ // Shutdown server
+ server.channel().close().get();
+ // Closing server channel does not close established connections, eventLoop has to be closed as well to simulate dropped session
+ serverLoopGroup.shutdownGracefully().get();
+ }
+
+ private SimpleDispatcher getServerDispatcher(final Promise<Boolean> p) {
+ return new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
+
+ @Override
+ public SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
+ final Channel channel, final Promise<SimpleSession> promise) {
+ p.setSuccess(true);
+ return new SimpleSessionNegotiator(promise, channel);
+ }
+ }, null, serverLoopGroup);
+ }
+
}
return super.createClient(address, strategy, new SimplePipelineInitializer(listenerFactory));
}
+ public Future<Void> createReconnectingClient(final InetSocketAddress address, final ReconnectStrategyFactory strategy, final SessionListenerFactory<SimpleSessionListener> listenerFactory) {
+ return super.createReconnectingClient(address, strategy, new SimplePipelineInitializer(listenerFactory));
+ }
+
public ChannelFuture createServer(final InetSocketAddress address, final SessionListenerFactory<SimpleSessionListener> listenerFactory) {
return super.createServer(address, new SimplePipelineInitializer(listenerFactory));
}
import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl;
import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
import org.osgi.framework.BundleContext;
/**
public CodecRegistryProvider(final ClassLoadingStrategy classLoadingStrategy, final BundleContext context) {
service = new RuntimeGeneratedMappingServiceImpl(CLASS_POOL, classLoadingStrategy);
registration = OsgiRegistrationUtil.registerService(context, service,
- SchemaServiceListener.class, BindingIndependentMappingService.class);
+ SchemaContextListener.class, BindingIndependentMappingService.class);
}
public CodecRegistry getCodecRegistry() {
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2014 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-subsystem</artifactId>
+ <version>0.2.5-SNAPSHOT</version>
+ </parent>
+ <artifactId>config-netty-config</artifactId>
+ <description>Configuration files for sal-rest-connector</description>
+ <packaging>jar</packaging>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/initial/00-netty.xml</file>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
<module>shutdown-impl</module>
<module>netconf-config-dispatcher</module>
<module>config-module-archetype</module>
+ <module>config-netty-config</module>
</modules>
<dependencies>
<phase>generate-resources</phase>
<configuration>
<outputDirectory>${project.build.directory}/configuration</outputDirectory>
- <includeArtifactIds>sal-rest-connector-config</includeArtifactIds>
+ <includeArtifactIds>sal-rest-connector-config,config-netty-config,md-sal-config,netconf-config,toaster-config,netconf-connector-config</includeArtifactIds>
<includes>**\/*.xml</includes>
<excludeTransitive>true</excludeTransitive>
<ignorePermissions>false</ignorePermissions>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-rest-connector-config</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-netty-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>md-sal-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-connector-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>toaster-config</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-rest-docgen</artifactId>
<phase>generate-resources</phase>
<configuration>
<outputDirectory>${project.build.directory}/configuration</outputDirectory>
- <includeArtifactIds>sal-rest-connector-config</includeArtifactIds>
+ <includeArtifactIds>sal-rest-connector-config,config-netty-config,md-sal-config,netconf-config,toaster-config,netconf-connector-config</includeArtifactIds>
<includes>**\/*.xml</includes>
<excludeTransitive>true</excludeTransitive>
<ignorePermissions>false</ignorePermissions>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2014 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>md-sal-config</artifactId>
+ <description>Configuration files for md-sal</description>
+ <packaging>jar</packaging>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/initial/01-md-sal.xml</file>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
<module>sal-binding-util</module>
+ <!-- Configuration File -->
+ <module>md-sal-config</module>
+
<!-- Samples -->
<module>samples</module>
<!-- sal-distributed-datastore -->
<module>sal-distributed-datastore</module>
+ <!-- XSQL -->
+ <module>sal-dom-xsql</module>
+
<!-- Yang Test Models for MD-SAL -->
<module>sal-test-model</module>
import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
}
final RuntimeGeneratedMappingServiceImpl service = new RuntimeGeneratedMappingServiceImpl(SingletonHolder.CLASS_POOL);
- bundleContext.registerService(SchemaServiceListener.class, service, new Hashtable<String,String>());
+ bundleContext.registerService(SchemaContextListener.class, service, new Hashtable<String,String>());
return service;
}
import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final BindingToNormalizedNodeCodec codec;
private BindingIndependentConnector connector;
private ProviderSession context;
- private final ListenerRegistration<SchemaServiceListener> schemaListenerRegistration;
+ private final ListenerRegistration<SchemaContextListener> schemaListenerRegistration;
protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker,
final BindingIndependentMappingService mappingService,final SchemaService schemaService) {
this.domDataBroker = domDataBroker;
this.mappingService = mappingService;
this.codec = new BindingToNormalizedNodeCodec(mappingService);
- this.schemaListenerRegistration = schemaService.registerSchemaServiceListener(this);
+ this.schemaListenerRegistration = schemaService.registerSchemaContextListener(this);
}
protected BindingToNormalizedNodeCodec getCodec() {
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
import org.opendaylight.controller.md.sal.binding.util.AbstractBindingSalProviderInstance;
import org.opendaylight.controller.sal.binding.api.mount.MountProviderInstance;
import org.slf4j.LoggerFactory;
import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
public class MountPointManagerImpl implements MountProviderService {
RpcProviderRegistryImpl rpcRegistry = new RpcProviderRegistryImpl("mount");
NotificationBrokerImpl notificationBroker = new NotificationBrokerImpl(getNotificationExecutor());
DataBrokerImpl dataBroker = new DataBrokerImpl();
- dataBroker.setExecutor(getDataCommitExecutor());
+ dataBroker.setExecutor(MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()));
BindingMountPointImpl mountInstance = new BindingMountPointImpl(path, rpcRegistry, notificationBroker,
dataBroker);
mountPoints.putIfAbsent(path, mountInstance);
--- /dev/null
+/*
+ * Copyright (c) 2014 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.controller.md.sal.binding.impl.test;
+
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertContains;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertEmpty;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertNotContains;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.USES_ONE_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.USES_TWO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.complexUsesAugment;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.path;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataChangeListenerTest;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ * This testsuite tries to replicate bug 1333 and tests regresion of it
+ * using test-model with similar construction as one reported.
+ *
+ *
+ * See https://bugs.opendaylight.org/show_bug.cgi?id=1333 for Bug Description
+ *
+ */
+public class Bug1333DataChangeListenerTest extends AbstractDataChangeListenerTest{
+
+ private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+
+ private static final InstanceIdentifier<?> AUGMENT_WILDCARD = TOP_PATH.child(TopLevelList.class).augmentation(
+ TreeComplexUsesAugment.class);
+
+ public void writeTopWithListItem(final LogicalDatastoreType store) {
+ ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
+ Top topItem = top(topLevelList(TOP_FOO_KEY, complexUsesAugment(USES_ONE_KEY, USES_TWO_KEY)));
+ tx.put(store, TOP_PATH, topItem);
+ assertCommit(tx.submit());
+ }
+
+ public void deleteItem(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+ ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
+ tx.delete(store, path);
+ assertCommit(tx.submit());
+ }
+
+ @Test
+ public void writeTopWithListItemAugmentedListenTopSubtree() {
+ TestListener listener = createListener(CONFIGURATION,TOP_PATH, DataChangeScope.SUBTREE);
+ listener.startCapture();
+
+ writeTopWithListItem(CONFIGURATION);
+
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+ assertContains(event.getCreatedData(), TOP_PATH);
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY));
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_ONE_KEY));
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_TWO_KEY));
+
+ assertEmpty(event.getUpdatedData());
+ assertEmpty(event.getRemovedPaths());
+ }
+
+ @Test
+ public void writeTopWithListItemAugmentedListenAugmentSubtreeWildcarded() {
+ TestListener listener = createListener(CONFIGURATION,AUGMENT_WILDCARD, DataChangeScope.SUBTREE);
+ listener.startCapture();
+ writeTopWithListItem(CONFIGURATION);
+
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+ /*
+ * Event should not contain parent nodes
+ */
+ assertNotContains(event.getCreatedData(), TOP_PATH, path(TOP_FOO_KEY));
+
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_ONE_KEY));
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_TWO_KEY));
+
+ assertEmpty(event.getUpdatedData());
+ assertEmpty(event.getRemovedPaths());
+ }
+
+ @Test
+ public void deleteAugmentChildListenTopSubtree() {
+ writeTopWithListItem(CONFIGURATION);
+ TestListener listener = createListener(CONFIGURATION, TOP_PATH, DataChangeScope.SUBTREE);
+ InstanceIdentifier<?> deletePath = path(TOP_FOO_KEY,USES_ONE_KEY);
+ deleteItem(CONFIGURATION,deletePath);
+
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+
+ assertEmpty(event.getCreatedData());
+
+ assertContains(event.getRemovedPaths(), deletePath);
+
+ assertContains(event.getUpdatedData(), TOP_PATH);
+ assertContains(event.getUpdatedData(), path(TOP_FOO_KEY));
+ assertContains(event.getUpdatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+
+ assertNotContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_TWO_KEY));
+ }
+
+ @Test
+ public void deleteAugmentChildListenAugmentSubtreeWildcarded() {
+ writeTopWithListItem(CONFIGURATION);
+
+ TestListener listener = createListener(CONFIGURATION, AUGMENT_WILDCARD, DataChangeScope.SUBTREE);
+ InstanceIdentifier<?> deletePath = path(TOP_FOO_KEY,USES_ONE_KEY);
+ deleteItem(CONFIGURATION,deletePath);
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+ assertEmpty(event.getCreatedData());
+
+ assertContains(event.getUpdatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+
+ /*
+ * Event should not contain parent nodes
+ */
+ assertNotContains(event.getUpdatedData(), TOP_PATH, path(TOP_FOO_KEY));
+
+ assertContains(event.getRemovedPaths(), deletePath);
+ }
+
+}
public DOMStore createConfigurationDatastore() {
InMemoryDOMDataStore store = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
- schemaService.registerSchemaServiceListener(store);
+ schemaService.registerSchemaContextListener(store);
return store;
}
public DOMStore createOperationalDatastore() {
InMemoryDOMDataStore store = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
- schemaService.registerSchemaServiceListener(store);
+ schemaService.registerSchemaContextListener(store);
return store;
}
biCompatibleBroker = new BackwardsCompatibleDataBroker(newDOMDataBroker,mockSchemaService);
- mockSchemaService.registerSchemaServiceListener(configStore);
- mockSchemaService.registerSchemaServiceListener(operStore);
+ mockSchemaService.registerSchemaContextListener(configStore);
+ mockSchemaService.registerSchemaContextListener(operStore);
biDataLegacyBroker = biCompatibleBroker;
}
public void startBindingToDomMappingService() {
checkState(classPool != null, "ClassPool needs to be present");
mappingServiceImpl = new RuntimeGeneratedMappingServiceImpl(classPool);
- mockSchemaService.registerSchemaServiceListener(mappingServiceImpl);
+ mockSchemaService.registerSchemaContextListener(mappingServiceImpl);
}
private void updateYangSchema(final ImmutableSet<YangModuleInfo> moduleInfos) {
import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
@SuppressWarnings("deprecation")
public final class MockSchemaService implements SchemaService, SchemaContextProvider {
private SchemaContext schemaContext;
- ListenerRegistry<SchemaServiceListener> listeners = ListenerRegistry.create();
+ ListenerRegistry<SchemaContextListener> listeners = ListenerRegistry.create();
@Override
public void addModule(final Module module) {
}
@Override
- public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(
- final SchemaServiceListener listener) {
+ public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(
+ final SchemaContextListener listener) {
return listeners.register(listener);
}
public synchronized void changeSchema(final SchemaContext newContext) {
schemaContext = newContext;
- for (ListenerRegistration<SchemaServiceListener> listener : listeners) {
+ for (ListenerRegistration<SchemaContextListener> listener : listeners) {
listener.getInstance().onGlobalContextUpdated(schemaContext);
}
}
-}
\ No newline at end of file
+}
new DistributedDataStore(actorSystem, name, new ClusterWrapperImpl(actorSystem),config );
ShardStrategyFactory.setConfiguration(config);
schemaService
- .registerSchemaServiceListener(dataStore);
+ .registerSchemaContextListener(dataStore);
return dataStore;
}
package org.opendaylight.controller.md.cluster.datastore.model;
+import java.math.BigInteger;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
MapEntryNode altima =
ImmutableNodes.mapEntryBuilder(CAR_QNAME, CAR_NAME_QNAME, "altima")
.withChild(ImmutableNodes.leafNode(CAR_NAME_QNAME, "altima"))
- .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, 1000))
+ .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, new BigInteger("1000")))
.build();
// Create an entry for the car accord
MapEntryNode honda =
ImmutableNodes.mapEntryBuilder(CAR_QNAME, CAR_NAME_QNAME, "accord")
.withChild(ImmutableNodes.leafNode(CAR_NAME_QNAME, "accord"))
- .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, 2000))
+ .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, new BigInteger("2000")))
.build();
cars.withChild(altima);
MapEntryNode jack =
ImmutableNodes.mapEntryBuilder(PERSON_QNAME, PERSON_NAME_QNAME, "jack")
.withChild(ImmutableNodes.leafNode(PERSON_NAME_QNAME, "jack"))
- .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 100))
+ .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 100L))
.build();
// Create an entry for the person jill
MapEntryNode jill =
ImmutableNodes.mapEntryBuilder(PERSON_QNAME, PERSON_NAME_QNAME, "jill")
.withChild(ImmutableNodes.leafNode(PERSON_NAME_QNAME, "jill"))
- .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 200))
+ .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 200L))
.build();
cars.withChild(jack);
public class SampleModelsTest {
@Test
public void testPeopleModel(){
- NormalizedNode<?, ?> expected = PeopleModel.create();
+ final NormalizedNode<?, ?> expected = PeopleModel.create();
- NormalizedNodeMessages.Container node =
+ final NormalizedNodeMessages.Container node =
new NormalizedNodeToNodeCodec(SchemaContextHelper.full())
.encode(YangInstanceIdentifier.of(PeopleModel.BASE_QNAME),
expected);
- NormalizedNodeMessages.Node normalizedNode =
+ final NormalizedNodeMessages.Node normalizedNode =
node.getNormalizedNode();
- NormalizedNode<?,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(YangInstanceIdentifier.of(PeopleModel.BASE_QNAME),
+ final NormalizedNode<?,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(YangInstanceIdentifier.of(PeopleModel.BASE_QNAME),
normalizedNode);
- Assert.assertEquals(expected.toString(), actual.toString());
+ Assert.assertEquals(expected, actual);
}
@Test
public void testCarsModel(){
- NormalizedNode<?, ?> expected = CarsModel.create();
+ final NormalizedNode<?, ?> expected = CarsModel.create();
- NormalizedNodeMessages.Container node =
+ final NormalizedNodeMessages.Container node =
new NormalizedNodeToNodeCodec(SchemaContextHelper.full())
.encode(YangInstanceIdentifier.of(CarsModel.BASE_QNAME),
expected);
- NormalizedNodeMessages.Node normalizedNode =
+ final NormalizedNodeMessages.Node normalizedNode =
node.getNormalizedNode();
- NormalizedNode<?,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(
+ final NormalizedNode<?,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(
YangInstanceIdentifier.of(CarsModel.BASE_QNAME),
normalizedNode);
- Assert.assertEquals(expected.toString(), actual.toString());
+ Assert.assertEquals(expected, actual);
}
}
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
public interface SchemaService extends BrokerService {
*/
SchemaContext getGlobalContext();
- ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(SchemaServiceListener listener);
+ /**
+ * Register a listener for changes in schema context.
+ *
+ * @param listener Listener which should be registered
+ * @return Listener registration handle
+ */
+ ListenerRegistration<SchemaContextListener> registerSchemaContextListener(SchemaContextListener listener);
}
wrappedStore.changeDelegate(legacyStore);
wrappedStore.setValidationEnabled(false);
- schemaService.registerSchemaServiceListener(wrappedStore);
+ schemaService.registerSchemaContextListener(wrappedStore);
dataService.registerConfigurationReader(rootPath, wrappedStore);
dataService.registerCommitHandler(rootPath, wrappedStore);
//we will default to InMemoryDOMDataStore creation
operStore = new InMemoryDOMDataStore("DOM-OPER", storeExecutor);
//here we will register the SchemaContext listener
- getSchemaServiceDependency().registerSchemaServiceListener((InMemoryDOMDataStore)operStore);
+ getSchemaServiceDependency().registerSchemaContextListener((InMemoryDOMDataStore)operStore);
}
DOMStore configStore = getConfigDataStoreDependency();
//we will default to InMemoryDOMDataStore creation
configStore = new InMemoryDOMDataStore("DOM-CFG", storeExecutor);
//here we will register the SchemaContext listener
- getSchemaServiceDependency().registerSchemaServiceListener((InMemoryDOMDataStore)configStore);
+ getSchemaServiceDependency().registerSchemaContextListener((InMemoryDOMDataStore)configStore);
}
ImmutableMap<LogicalDatastoreType, DOMStore> datastores = ImmutableMap
.<LogicalDatastoreType, DOMStore> builder().put(LogicalDatastoreType.OPERATIONAL, operStore)
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
}
@Override
- public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(final SchemaServiceListener arg0) {
- return delegate.registerSchemaServiceListener(arg0);
+ public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(final SchemaContextListener arg0) {
+ return delegate.registerSchemaContextListener(arg0);
}
@Override
+/*
+ * Copyright (c) 2014 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.controller.md.sal.dom.broker.impl.compat;
import javax.annotation.concurrent.ThreadSafe;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
@ThreadSafe
public class BackwardsCompatibleDataBroker implements DataProviderService {
private final DOMDataBroker backingBroker;
private volatile DataNormalizer normalizer;
- private final ListenerRegistration<SchemaServiceListener> schemaReg;
+ private final ListenerRegistration<SchemaContextListener> schemaReg;
public BackwardsCompatibleDataBroker(final DOMDataBroker newBiDataImpl, final SchemaService schemaService) {
backingBroker = newBiDataImpl;
- schemaReg = schemaService.registerSchemaServiceListener(new SchemaListener());
+ schemaReg = schemaService.registerSchemaContextListener(new SchemaListener());
}
@Override
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
public class BackwardsCompatibleMountPoint implements MountProvisionInstance, SchemaContextProvider, SchemaService {
private final NotificationPublishService notificationPublishService;
private final RpcProvisionRegistry rpcs;
- private final ListenerRegistry<SchemaServiceListener> schemaListenerRegistry = new ListenerRegistry<>();
+ private final ListenerRegistry<SchemaContextListener> schemaListenerRegistry = new ListenerRegistry<>();
private SchemaContext schemaContext;
}
@Override
- public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(final SchemaServiceListener listener) {
+ public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(final SchemaContextListener listener) {
return schemaListenerRegistry.register(listener);
}
@Override
public void setSchemaContext(final SchemaContext schemaContext) {
this.schemaContext = schemaContext;
- for (ListenerRegistration<SchemaServiceListener> schemaServiceListenerListenerRegistration : schemaListenerRegistry.getListeners()) {
+ for (ListenerRegistration<SchemaContextListener> schemaServiceListenerListenerRegistration : schemaListenerRegistry.getListeners()) {
schemaServiceListenerListenerRegistration.getInstance().onGlobalContextUpdated(schemaContext);
}
}
import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
import org.opendaylight.yangtools.yang.parser.impl.util.URLSchemaContextResolver;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
-public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer<SchemaServiceListener, SchemaServiceListener>, AutoCloseable {
+public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer<SchemaContextListener, SchemaContextListener>, AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(GlobalBundleScanningSchemaServiceImpl.class);
- private final ListenerRegistry<SchemaServiceListener> listeners = new ListenerRegistry<>();
+ private final ListenerRegistry<SchemaContextListener> listeners = new ListenerRegistry<>();
private final URLSchemaContextResolver contextResolver = new URLSchemaContextResolver();
private final BundleScanner scanner = new BundleScanner();
private final BundleContext context;
- private ServiceTracker<SchemaServiceListener, SchemaServiceListener> listenerTracker;
+ private ServiceTracker<SchemaContextListener, SchemaContextListener> listenerTracker;
private BundleTracker<Iterable<Registration>> bundleTracker;
private boolean starting = true;
private static GlobalBundleScanningSchemaServiceImpl instance;
public void start() {
checkState(context != null);
- listenerTracker = new ServiceTracker<>(context, SchemaServiceListener.class, GlobalBundleScanningSchemaServiceImpl.this);
+ listenerTracker = new ServiceTracker<>(context, SchemaContextListener.class, GlobalBundleScanningSchemaServiceImpl.this);
bundleTracker = new BundleTracker<>(context, BundleEvent.RESOLVED | BundleEvent.UNRESOLVED, scanner);
bundleTracker.open();
listenerTracker.open();
}
@Override
- public synchronized ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(final SchemaServiceListener listener) {
+ public synchronized ListenerRegistration<SchemaContextListener> registerSchemaContextListener(final SchemaContextListener listener) {
Optional<SchemaContext> potentialCtx = contextResolver.getSchemaContext();
if(potentialCtx.isPresent()) {
listener.onGlobalContextUpdated(potentialCtx.get());
private synchronized void updateContext(final SchemaContext snapshot) {
Object[] services = listenerTracker.getServices();
- for (ListenerRegistration<SchemaServiceListener> listener : listeners) {
+ for (ListenerRegistration<SchemaContextListener> listener : listeners) {
try {
listener.getInstance().onGlobalContextUpdated(snapshot);
} catch (Exception e) {
}
if (services != null) {
for (Object rawListener : services) {
- SchemaServiceListener listener = (SchemaServiceListener) rawListener;
+ final SchemaContextListener listener = (SchemaContextListener) rawListener;
try {
listener.onGlobalContextUpdated(snapshot);
} catch (Exception e) {
}
@Override
- public synchronized SchemaServiceListener addingService(final ServiceReference<SchemaServiceListener> reference) {
+ public synchronized SchemaContextListener addingService(final ServiceReference<SchemaContextListener> reference) {
- SchemaServiceListener listener = context.getService(reference);
+ SchemaContextListener listener = context.getService(reference);
SchemaContext _ctxContext = getGlobalContext();
if (getContext() != null && _ctxContext != null) {
listener.onGlobalContextUpdated(_ctxContext);
}
@Override
- public void modifiedService(final ServiceReference<SchemaServiceListener> reference, final SchemaServiceListener service) {
+ public void modifiedService(final ServiceReference<SchemaContextListener> reference, final SchemaContextListener service) {
// NOOP
}
@Override
- public void removedService(final ServiceReference<SchemaServiceListener> reference, final SchemaServiceListener service) {
+ public void removedService(final ServiceReference<SchemaContextListener> reference, final SchemaContextListener service) {
context.ungetService(reference);
}
}
package org.opendaylight.controller.sal.dom.broker.osgi;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
import org.osgi.framework.ServiceReference;
public class SchemaServiceProxy extends AbstractBrokerServiceProxy<SchemaService> implements SchemaService {
}
@Override
- public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(SchemaServiceListener listener) {
- ListenerRegistration<SchemaServiceListener> registration = getDelegate().registerSchemaServiceListener(listener);
+ public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(SchemaContextListener listener) {
+ ListenerRegistration<SchemaContextListener> registration = getDelegate().registerSchemaContextListener(listener);
addRegistration(registration);
return registration;
}
-
-
-
}
@Override
public java.lang.AutoCloseable createInstance() {
XSQLAdapter xsqlAdapter = XSQLAdapter.getInstance();
- getSchemaServiceDependency().registerSchemaServiceListener(xsqlAdapter);
+ getSchemaServiceDependency().registerSchemaContextListener(xsqlAdapter);
xsqlAdapter.setDataBroker(getAsyncDataBrokerDependency());
XSQLProvider p = new XSQLProvider();
p.buildXSQL(getDataBrokerDependency());
@Override
public java.lang.AutoCloseable createInstance() {
InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-CFG", MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()));
- getSchemaServiceDependency().registerSchemaServiceListener(ids);
+ getSchemaServiceDependency().registerSchemaContextListener(ids);
return ids;
}
@Override
public java.lang.AutoCloseable createInstance() {
InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-OPER", MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()));
- getOperationalSchemaServiceDependency().registerSchemaServiceListener(ids);
+ getOperationalSchemaServiceDependency().registerSchemaContextListener(ids);
return ids;
}
import java.io.File;
import java.io.InputStream;
import java.net.InetSocketAddress;
+import java.util.List;
import java.util.concurrent.ExecutorService;
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration;
import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator;
+import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities;
import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceSalFacade;
import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
import org.opendaylight.controller.sal.core.api.Broker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Optional;
+
/**
*
*/
private static AbstractCachingSchemaSourceProvider<String, InputStream> GLOBAL_NETCONF_SOURCE_PROVIDER = null;
private BundleContext bundleContext;
+ private Optional<NetconfSessionCapabilities> userCapabilities;
public NetconfConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
checkNotNull(getPassword(), passwordJmxAttribute);
}
+ userCapabilities = getUserCapabilities();
+
}
- private boolean isHostAddressPresent(Host address) {
+ private boolean isHostAddressPresent(final Host address) {
return address.getDomainName() != null ||
address.getIpAddress() != null && (address.getIpAddress().getIpv4Address() != null || address.getIpAddress().getIpv6Address() != null);
}
final Broker domBroker = getDomRegistryDependency();
final BindingAwareBroker bindingBroker = getBindingRegistryDependency();
- final RemoteDeviceHandler salFacade = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor);
+ final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade
+ = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor);
final NetconfDevice device =
NetconfDevice.createNetconfDevice(id, getGlobalNetconfSchemaProvider(), globalProcessingExecutor, salFacade);
- final NetconfDeviceCommunicator listener = new NetconfDeviceCommunicator(id, device);
+
+ final NetconfDeviceCommunicator listener = userCapabilities.isPresent() ?
+ new NetconfDeviceCommunicator(id, device, userCapabilities.get()) : new NetconfDeviceCommunicator(id, device);
+
final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(listener);
final NetconfClientDispatcher dispatcher = getClientDispatcherDependency();
};
}
+ private Optional<NetconfSessionCapabilities> getUserCapabilities() {
+ if(getYangModuleCapabilities() == null) {
+ return Optional.absent();
+ }
+
+ final List<String> capabilities = getYangModuleCapabilities().getCapability();
+ if(capabilities == null || capabilities.isEmpty()) {
+ return Optional.absent();
+ }
+
+ final NetconfSessionCapabilities parsedOverrideCapabilities = NetconfSessionCapabilities.fromStrings(capabilities);
+ JmxAttributeValidationException.checkCondition(
+ parsedOverrideCapabilities.getNonModuleCaps().isEmpty(),
+ "Capabilities to override can only contain module based capabilities, non-module capabilities will be retrieved from the device," +
+ " configured non-module capabilities: " + parsedOverrideCapabilities.getNonModuleCaps(),
+ yangModuleCapabilitiesJmxAttribute);
+
+ return Optional.of(parsedOverrideCapabilities);
+ }
+
private synchronized AbstractCachingSchemaSourceProvider<String, InputStream> getGlobalNetconfSchemaProvider() {
if(GLOBAL_NETCONF_SOURCE_PROVIDER == null) {
final String storageFile = "cache/schema";
if(getAddress().getDomainName() != null) {
return new InetSocketAddress(getAddress().getDomainName().getValue(), getPort().getValue());
} else {
- IpAddress ipAddress = getAddress().getIpAddress();
- String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
+ final IpAddress ipAddress = getAddress().getIpAddress();
+ final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
return new InetSocketAddress(ip, getPort().getValue());
}
}
// Unable to initialize device, set as disconnected
logger.error("{}: Initialization failed", id, t);
salFacade.onDeviceDisconnected();
+ // TODO ssh connection is still open if sal initialization fails
}
});
}
*/
package org.opendaylight.controller.sal.connect.netconf.listener;
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.FutureListener;
import java.util.ArrayDeque;
import java.util.Iterator;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
-
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-
-import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.FutureListener;
-
public class NetconfDeviceCommunicator implements NetconfClientSessionListener, RemoteDeviceCommunicator<NetconfMessage> {
private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceCommunicator.class);
private final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice;
+ private final Optional<NetconfSessionCapabilities> overrideNetconfCapabilities;
private final RemoteDeviceId id;
private final Lock sessionLock = new ReentrantLock();
+ private final Queue<Request> requests = new ArrayDeque<>();
+ private NetconfClientSession session;
+
+ public NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice,
+ final NetconfSessionCapabilities netconfSessionCapabilities) {
+ this(id, remoteDevice, Optional.of(netconfSessionCapabilities));
+ }
+
public NetconfDeviceCommunicator(final RemoteDeviceId id,
- final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice) {
+ final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice) {
+ this(id, remoteDevice, Optional.<NetconfSessionCapabilities>absent());
+ }
+
+ private NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice,
+ final Optional<NetconfSessionCapabilities> overrideNetconfCapabilities) {
this.id = id;
this.remoteDevice = remoteDevice;
+ this.overrideNetconfCapabilities = overrideNetconfCapabilities;
}
- private final Queue<Request> requests = new ArrayDeque<>();
- private NetconfClientSession session;
-
@Override
public void onSessionUp(final NetconfClientSession session) {
sessionLock.lock();
logger.debug("{}: Session established", id);
this.session = session;
- final NetconfSessionCapabilities netconfSessionCapabilities =
+ NetconfSessionCapabilities netconfSessionCapabilities =
NetconfSessionCapabilities.fromNetconfSession(session);
logger.trace("{}: Session advertised capabilities: {}", id, netconfSessionCapabilities);
+ if(overrideNetconfCapabilities.isPresent()) {
+ netconfSessionCapabilities = netconfSessionCapabilities.replaceModuleCaps(overrideNetconfCapabilities.get());
+ logger.debug("{}: Session capabilities overridden, capabilities that will be used: {}", id, netconfSessionCapabilities);
+ }
+
remoteDevice.onRemoteSessionUp(netconfSessionCapabilities, this);
}
finally {
return;
}
- request.future.set( RpcResultBuilder.<NetconfMessage>success( message ).build() );
+ request.future.set( RpcResultBuilder.success( message ).build() );
}
}
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.LoggerFactory;
public final class NetconfSessionCapabilities {
+
private static final class ParameterMatcher {
private final Predicate<String> predicate;
private final int skipLength;
};
private final Set<QName> moduleBasedCaps;
- private final Set<String> capabilities;
+ private final Set<String> nonModuleCaps;
- private NetconfSessionCapabilities(final Set<String> capabilities, final Set<QName> moduleBasedCaps) {
- this.capabilities = Preconditions.checkNotNull(capabilities);
+ private NetconfSessionCapabilities(final Set<String> nonModuleCaps, final Set<QName> moduleBasedCaps) {
+ this.nonModuleCaps = Preconditions.checkNotNull(nonModuleCaps);
this.moduleBasedCaps = Preconditions.checkNotNull(moduleBasedCaps);
}
return moduleBasedCaps;
}
- public boolean containsCapability(final String capability) {
- return capabilities.contains(capability);
+ public Set<String> getNonModuleCaps() {
+ return nonModuleCaps;
+ }
+
+ public boolean containsNonModuleCapability(final String capability) {
+ return nonModuleCaps.contains(capability);
}
- public boolean containsCapability(final QName capability) {
+ public boolean containsModuleCapability(final QName capability) {
return moduleBasedCaps.contains(capability);
}
@Override
public String toString() {
return Objects.toStringHelper(this)
- .add("capabilities", capabilities)
+ .add("capabilities", nonModuleCaps)
+ .add("moduleBasedCapabilities", moduleBasedCaps)
.add("rollback", isRollbackSupported())
.add("monitoring", isMonitoringSupported())
.toString();
}
public boolean isRollbackSupported() {
- return containsCapability(NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString());
+ return containsNonModuleCapability(NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString());
+ }
+
+ public boolean isCandidateSupported() {
+ return containsNonModuleCapability(NetconfMessageTransformUtil.NETCONF_CANDIDATE_URI.toString());
}
public boolean isMonitoringSupported() {
- return containsCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)
- || containsCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
+ return containsModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)
+ || containsNonModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
+ }
+
+ public NetconfSessionCapabilities replaceModuleCaps(final NetconfSessionCapabilities netconfSessionModuleCapabilities) {
+ final Set<QName> moduleBasedCaps = Sets.newHashSet(netconfSessionModuleCapabilities.getModuleBasedCaps());
+
+ // Preserve monitoring module, since it indicates support for ietf-netconf-monitoring
+ if(containsModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)) {
+ moduleBasedCaps.add(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING);
+ }
+ return new NetconfSessionCapabilities(getNonModuleCaps(), moduleBasedCaps);
}
public static NetconfSessionCapabilities fromNetconfSession(final NetconfClientSession session) {
public static NetconfSessionCapabilities fromStrings(final Collection<String> capabilities) {
final Set<QName> moduleBasedCaps = new HashSet<>();
+ final Set<String> nonModuleCaps = Sets.newHashSet(capabilities);
for (final String capability : capabilities) {
final int qmark = capability.indexOf('?');
String revision = REVISION_PARAM.from(queryParams);
if (revision != null) {
moduleBasedCaps.add(QName.create(namespace, revision, moduleName));
+ nonModuleCaps.remove(capability);
continue;
}
// FIXME: do we really want to continue here?
moduleBasedCaps.add(QName.create(namespace, revision, moduleName));
+ nonModuleCaps.remove(capability);
}
- return new NetconfSessionCapabilities(ImmutableSet.copyOf(capabilities), ImmutableSet.copyOf(moduleBasedCaps));
+ return new NetconfSessionCapabilities(ImmutableSet.copyOf(nonModuleCaps), ImmutableSet.copyOf(moduleBasedCaps));
}
}
@Override
public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
- return new NetconfDeviceReadOnlyTx(rpc, normalizer);
+ return new NetconfDeviceReadOnlyTx(rpc, normalizer, id);
}
@Override
@Override
public DOMDataWriteTransaction newWriteOnlyTransaction() {
- // FIXME detect if candidate is supported, true is hardcoded
- return new NetconfDeviceWriteOnlyTx(id, rpc, normalizer, true, netconfSessionPreferences.isRollbackSupported());
+ return new NetconfDeviceWriteOnlyTx(id, rpc, normalizer, netconfSessionPreferences.isCandidateSupported(), netconfSessionPreferences.isRollbackSupported());
}
@Override
import com.google.common.base.Function;
import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+
public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction {
private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceReadOnlyTx.class);
private final RpcImplementation rpc;
private final DataNormalizer normalizer;
+ private final RemoteDeviceId id;
- public NetconfDeviceReadOnlyTx(final RpcImplementation rpc, final DataNormalizer normalizer) {
+ public NetconfDeviceReadOnlyTx(final RpcImplementation rpc, final DataNormalizer normalizer, final RemoteDeviceId id) {
this.rpc = rpc;
this.normalizer = normalizer;
+ this.id = id;
}
public ListenableFuture<Optional<NormalizedNode<?, ?>>> readConfigurationData(final YangInstanceIdentifier path) {
return Futures.transform(future, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
@Override
public Optional<NormalizedNode<?, ?>> apply(final RpcResult<CompositeNode> result) {
+ checkReadSuccess(result, path);
+
final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
final CompositeNode node = (CompositeNode) findNode(data, path);
});
}
+ private void checkReadSuccess(final RpcResult<CompositeNode> result, final YangInstanceIdentifier path) {
+ LOG.warn("{}: Unable to read data: {}, errors: {}", id, path, result.getErrors());
+ Preconditions.checkArgument(result.isSuccessful(), "%s: Unable to read data: %s, errors: %s", id, path, result.getErrors());
+ }
+
private Optional<NormalizedNode<?, ?>> transform(final YangInstanceIdentifier path, final CompositeNode node) {
if(node == null) {
return Optional.absent();
try {
return Optional.<NormalizedNode<?, ?>>of(normalizer.toNormalized(path, node).getValue());
} catch (final Exception e) {
- LOG.error("Unable to normalize data for {}, data: {}", path, node, e);
+ LOG.error("{}: Unable to normalize data for {}, data: {}", id, path, node, e);
throw e;
}
}
return Futures.transform(future, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
@Override
public Optional<NormalizedNode<?, ?>> apply(final RpcResult<CompositeNode> result) {
+ checkReadSuccess(result, path);
+
final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
final CompositeNode node = (CompositeNode) findNode(data, path);
@Override
public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
- final YangInstanceIdentifier legacyPath = toLegacyPath(normalizer, path);
+ final YangInstanceIdentifier legacyPath = toLegacyPath(normalizer, path, id);
switch (store) {
case CONFIGURATION : {
}
}
- throw new IllegalArgumentException(String.format("Cannot read data %s for %s datastore, unknown datastore type", path, store));
+ throw new IllegalArgumentException(String.format("%s, Cannot read data %s for %s datastore, unknown datastore type", id, path, store));
}
- static YangInstanceIdentifier toLegacyPath(final DataNormalizer normalizer, final YangInstanceIdentifier path) {
+ static YangInstanceIdentifier toLegacyPath(final DataNormalizer normalizer, final YangInstanceIdentifier path, final RemoteDeviceId id) {
try {
return normalizer.toLegacy(path);
} catch (final DataNormalizationException e) {
- throw new IllegalArgumentException("Cannot normalize path " + path, e);
+ throw new IllegalArgumentException(id + ": Cannot normalize path " + path, e);
}
}
package org.opendaylight.controller.sal.connect.netconf.sal.tx;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CONFIG_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DEFAULT_OPERATION_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_ERROR_OPTION_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_OPERATION_QNAME;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
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.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
-import javax.annotation.Nullable;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction {
+public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction, FutureCallback<RpcResult<TransactionStatus>> {
private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceWriteOnlyTx.class);
private final RemoteDeviceId id;
private final RpcImplementation rpc;
private final DataNormalizer normalizer;
+
private final boolean rollbackSupported;
+ private final boolean candidateSupported;
private final CompositeNode targetNode;
+ // Allow commit to be called only once
+ private final AtomicBoolean finished = new AtomicBoolean(false);
+
public NetconfDeviceWriteOnlyTx(final RemoteDeviceId id, final RpcImplementation rpc, final DataNormalizer normalizer, final boolean candidateSupported, final boolean rollbackOnErrorSupported) {
this.id = id;
this.rpc = rpc;
this.normalizer = normalizer;
- this.targetNode = getTargetNode(candidateSupported);
+
+ this.candidateSupported = candidateSupported;
+ this.targetNode = getTargetNode(this.candidateSupported);
this.rollbackSupported = rollbackOnErrorSupported;
}
- // FIXME add logging
-
@Override
public boolean cancel() {
- if(isCommitted()) {
+ if(isFinished()) {
return false;
}
return discardChanges();
}
- private boolean isCommitted() {
- // TODO 732
- return true;
+ private boolean isFinished() {
+ return finished.get();
}
private boolean discardChanges() {
- // TODO 732
+ finished.set(true);
+
+ if(candidateSupported) {
+ sendDiscardChanges();
+ }
return true;
}
// TODO should the edit operations be blocking ?
+ // TODO should the discard-changes operations be blocking ?
@Override
public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ checkNotFinished();
Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can merge only configuration, not %s", store);
try {
- final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path);
+ final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id);
final CompositeNode legacyData = normalizer.toLegacy(path, data);
- sendEditRpc(createEditConfigStructure(legacyPath, Optional.of(ModifyAction.REPLACE), Optional.fromNullable(legacyData)), Optional.of(ModifyAction.NONE));
+ sendEditRpc(
+ createEditConfigStructure(legacyPath, Optional.of(ModifyAction.REPLACE), Optional.fromNullable(legacyData)), Optional.of(ModifyAction.NONE));
} catch (final ExecutionException e) {
- LOG.warn("Error putting data to {}, data: {}, discarding changes", path, data, e);
+ LOG.warn("{}: Error putting data to {}, data: {}, discarding changes", id, path, data, e);
discardChanges();
- throw new RuntimeException("Error while replacing " + path, e);
+ throw new RuntimeException(id + ": Error while replacing " + path, e);
}
}
+ private void checkNotFinished() {
+ Preconditions.checkState(isFinished() == false, "%s: Transaction %s already finished", id, getIdentifier());
+ }
+
@Override
public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
- Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can merge only configuration, not %s", store);
+ checkNotFinished();
+ Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "%s: Can merge only configuration, not %s", id, store);
try {
- final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path);
+ final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id);
final CompositeNode legacyData = normalizer.toLegacy(path, data);
sendEditRpc(
createEditConfigStructure(legacyPath, Optional.<ModifyAction> absent(), Optional.fromNullable(legacyData)), Optional.<ModifyAction> absent());
} catch (final ExecutionException e) {
- LOG.warn("Error merging data to {}, data: {}, discarding changes", path, data, e);
+ LOG.warn("{}: Error merging data to {}, data: {}, discarding changes", id, path, data, e);
discardChanges();
- throw new RuntimeException("Error while merging " + path, e);
+ throw new RuntimeException(id + ": Error while merging " + path, e);
}
}
@Override
public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
- Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can merge only configuration, not %s", store);
+ checkNotFinished();
+ Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "%s: Can merge only configuration, not %s", id, store);
try {
- sendEditRpc(createEditConfigStructure(NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path), Optional.of(ModifyAction.DELETE), Optional.<CompositeNode>absent()), Optional.of(ModifyAction.NONE));
+ sendEditRpc(
+ createEditConfigStructure(NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id), Optional.of(ModifyAction.DELETE), Optional.<CompositeNode>absent()), Optional.of(ModifyAction.NONE));
} catch (final ExecutionException e) {
- LOG.warn("Error deleting data {}, discarding changes", path, e);
+ LOG.warn("{}: Error deleting data {}, discarding changes", id, path, e);
discardChanges();
- throw new RuntimeException("Error while deleting " + path, e);
+ throw new RuntimeException(id + ": Error while deleting " + path, e);
}
}
@Override
public CheckedFuture<Void, TransactionCommitFailedException> submit() {
final ListenableFuture<Void> commmitFutureAsVoid = Futures.transform(commit(), new Function<RpcResult<TransactionStatus>, Void>() {
- @Nullable
@Override
- public Void apply(@Nullable final RpcResult<TransactionStatus> input) {
+ public Void apply(final RpcResult<TransactionStatus> input) {
return null;
}
});
@Override
public ListenableFuture<RpcResult<TransactionStatus>> commit() {
- // FIXME do not allow commit if closed or failed
+ checkNotFinished();
+ finished.set(true);
- final ListenableFuture<RpcResult<CompositeNode>> rpcResult = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, getCommitRequest());
- return Futures.transform(rpcResult, new Function<RpcResult<CompositeNode>, RpcResult<TransactionStatus>>() {
- @Override
- public RpcResult<TransactionStatus> apply(@Nullable final RpcResult<CompositeNode> input) {
- if(input.isSuccessful()) {
- return RpcResultBuilder.success(TransactionStatus.COMMITED).build();
- } else {
- final RpcResultBuilder<TransactionStatus> failed = RpcResultBuilder.failed();
- for (final RpcError rpcError : input.getErrors()) {
- failed.withError(rpcError.getErrorType(), rpcError.getTag(), rpcError.getMessage(), rpcError.getApplicationTag(), rpcError.getInfo(), rpcError.getCause());
+ if(candidateSupported == false) {
+ return Futures.immediateFuture(RpcResultBuilder.success(TransactionStatus.COMMITED).build());
+ }
+
+ final ListenableFuture<RpcResult<CompositeNode>> rpcResult = rpc.invokeRpc(
+ NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, NetconfMessageTransformUtil.COMMIT_RPC_CONTENT);
+
+ final ListenableFuture<RpcResult<TransactionStatus>> transformed = Futures.transform(rpcResult,
+ new Function<RpcResult<CompositeNode>, RpcResult<TransactionStatus>>() {
+ @Override
+ public RpcResult<TransactionStatus> apply(final RpcResult<CompositeNode> input) {
+ if (input.isSuccessful()) {
+ return RpcResultBuilder.success(TransactionStatus.COMMITED).build();
+ } else {
+ final RpcResultBuilder<TransactionStatus> failed = RpcResultBuilder.failed();
+ for (final RpcError rpcError : input.getErrors()) {
+ failed.withError(rpcError.getErrorType(), rpcError.getTag(), rpcError.getMessage(),
+ rpcError.getApplicationTag(), rpcError.getInfo(), rpcError.getCause());
+ }
+ return failed.build();
+ }
}
- return failed.build();
- }
- }
- });
+ });
- // FIXME 732 detect commit failure
+ Futures.addCallback(transformed, this);
+ return transformed;
+ }
+
+ @Override
+ public void onSuccess(final RpcResult<TransactionStatus> result) {
+ LOG.debug("{}: Write successful, transaction: {}", id, getIdentifier());
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ LOG.warn("{}: Write failed, transaction {}, discarding changes", id, getIdentifier(), t);
+ discardChanges();
}
private void sendEditRpc(final CompositeNode editStructure, final Optional<ModifyAction> defaultOperation) throws ExecutionException {
}
}
+ private void sendDiscardChanges() {
+ final ListenableFuture<RpcResult<CompositeNode>> discardFuture = rpc.invokeRpc(NETCONF_DISCARD_CHANGES_QNAME, DISCARD_CHANGES_RPC_CONTENT);
+ Futures.addCallback(discardFuture, new FutureCallback<RpcResult<CompositeNode>>() {
+ @Override
+ public void onSuccess(final RpcResult<CompositeNode> result) {
+ LOG.debug("{}: Discarding transaction: {}", id, getIdentifier());
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ LOG.error("{}: Discarding changes failed, transaction: {}. Device configuration might be corrupted", id, getIdentifier(), t);
+ throw new RuntimeException(id + ": Discarding changes failed, transaction " + getIdentifier(), t);
+ }
+ });
+ }
+
private CompositeNode createEditConfigStructure(final YangInstanceIdentifier dataPath, final Optional<ModifyAction> operation,
final Optional<CompositeNode> lastChildOverride) {
Preconditions.checkArgument(Iterables.isEmpty(dataPath.getPathArguments()) == false, "Instance identifier with empty path %s", dataPath);
}
}
- private ImmutableCompositeNode getCommitRequest() {
- final CompositeNodeBuilder<ImmutableCompositeNode> commitInput = ImmutableCompositeNode.builder();
- commitInput.setQName(NETCONF_COMMIT_QNAME);
- return commitInput.toInstance();
- }
-
-
@Override
public Object getIdentifier() {
return this;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
public class NetconfMessageTransformUtil {
- private NetconfMessageTransformUtil() {
- }
+ private NetconfMessageTransformUtil() {}
public static final QName IETF_NETCONF_MONITORING = QName.create("urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", "2010-10-04", "ietf-netconf-monitoring");
public static URI NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
public static QName NETCONF_DEFAULT_OPERATION_QNAME = QName.create(NETCONF_OPERATION_QNAME, "default-operation");
public static QName NETCONF_EDIT_CONFIG_QNAME = QName.create(NETCONF_QNAME, "edit-config");
public static QName NETCONF_GET_CONFIG_QNAME = QName.create(NETCONF_QNAME, "get-config");
+ public static QName NETCONF_DISCARD_CHANGES_QNAME = QName.create(NETCONF_QNAME, "discard-changes");
public static QName NETCONF_TYPE_QNAME = QName.create(NETCONF_QNAME, "type");
public static QName NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter");
public static QName NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get");
public static QName NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc");
+
public static URI NETCONF_ROLLBACK_ON_ERROR_URI = URI
.create("urn:ietf:params:netconf:capability:rollback-on-error:1.0");
public static String ROLLBACK_ON_ERROR_OPTION = "rollback-on-error";
+ public static URI NETCONF_CANDIDATE_URI = URI
+ .create("urn:ietf:params:netconf:capability:candidate:1.0");
+
+ // Discard changes message
+ public static final CompositeNode DISCARD_CHANGES_RPC_CONTENT =
+ NodeFactory.createImmutableCompositeNode(NETCONF_DISCARD_CHANGES_QNAME, null, Collections.<Node<?>>emptyList());
+
+ // Commit changes message
+ public static final CompositeNode COMMIT_RPC_CONTENT =
+ NodeFactory.createImmutableCompositeNode(NETCONF_COMMIT_QNAME, null, Collections.<Node<?>>emptyList());
+
public static Node<?> toFilterStructure(final YangInstanceIdentifier identifier) {
Node<?> previous = null;
if (Iterables.isEmpty(identifier.getPathArguments())) {
return it.toInstance();
}
}
-
}
type string;
}
+ container yang-module-capabilities {
+ leaf-list capability {
+ type string;
+ description "Set a list of capabilities to override capabilities provided in device's hello message.
+ Can be used for devices that do not report any yang modules in their hello message";
+ }
+ }
+
container dom-registry {
uses config:service-ref {
refine type {
package org.opendaylight.controller.sal.connect.netconf.listener;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.RPC_REPLY_KEY;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ListenableFuture;
import io.netty.channel.ChannelFuture;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
-
import java.io.ByteArrayInputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
-
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.same;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.RPC_REPLY_KEY;
-import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0;
-
import org.apache.commons.lang3.StringUtils;
import org.junit.Before;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-import com.google.common.base.Strings;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.ListenableFuture;
-
public class NetconfDeviceCommunicatorTest {
@Mock
verify( mockDevice ).onRemoteSessionUp( netconfSessionCapabilities.capture(), eq( communicator ) );
NetconfSessionCapabilities actualCapabilites = netconfSessionCapabilities.getValue();
- assertEquals( "containsCapability", true, actualCapabilites.containsCapability(
- NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString() ) );
- assertEquals( "containsCapability", true, actualCapabilites.containsCapability( testCapability ) );
+ assertEquals( "containsModuleCapability", true, actualCapabilites.containsNonModuleCapability(
+ NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString()) );
+ assertEquals( "containsModuleCapability", false, actualCapabilites.containsNonModuleCapability(testCapability) );
assertEquals( "getModuleBasedCaps", Sets.newHashSet(
QName.create( "urn:opendaylight:params:xml:ns:test", "2014-06-02", "test-module" )),
actualCapabilites.getModuleBasedCaps() );
--- /dev/null
+package org.opendaylight.controller.sal.connect.netconf.listener;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.google.common.collect.Lists;
+import java.util.List;
+import org.junit.Test;
+import org.junit.matchers.JUnitMatchers;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class NetconfSessionCapabilitiesTest {
+
+ @Test
+ public void testMerge() throws Exception {
+ final List<String> caps1 = Lists.newArrayList(
+ "namespace:1?module=module1&revision=2012-12-12",
+ "namespace:2?module=module2&revision=2012-12-12",
+ "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04",
+ "urn:ietf:params:netconf:base:1.0",
+ "urn:ietf:params:netconf:capability:rollback-on-error:1.0"
+ );
+ final NetconfSessionCapabilities sessionCaps1 = NetconfSessionCapabilities.fromStrings(caps1);
+ assertCaps(sessionCaps1, 2, 3);
+
+ final List<String> caps2 = Lists.newArrayList(
+ "namespace:3?module=module3&revision=2012-12-12",
+ "namespace:4?module=module4&revision=2012-12-12",
+ "randomNonModuleCap"
+ );
+ final NetconfSessionCapabilities sessionCaps2 = NetconfSessionCapabilities.fromStrings(caps2);
+ assertCaps(sessionCaps2, 1, 2);
+
+ final NetconfSessionCapabilities merged = sessionCaps1.replaceModuleCaps(sessionCaps2);
+ assertCaps(merged, 2, 2 + 1 /*Preserved monitoring*/);
+ for (final QName qName : sessionCaps2.getModuleBasedCaps()) {
+ assertThat(merged.getModuleBasedCaps(), JUnitMatchers.hasItem(qName));
+ }
+ assertThat(merged.getModuleBasedCaps(), JUnitMatchers.hasItem(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING));
+
+ assertThat(merged.getNonModuleCaps(), JUnitMatchers.hasItem("urn:ietf:params:netconf:base:1.0"));
+ assertThat(merged.getNonModuleCaps(), JUnitMatchers.hasItem("urn:ietf:params:netconf:capability:rollback-on-error:1.0"));
+ }
+
+ private void assertCaps(final NetconfSessionCapabilities sessionCaps1, final int nonModuleCaps, final int moduleCaps) {
+ assertEquals(nonModuleCaps, sessionCaps1.getNonModuleCaps().size());
+ assertEquals(moduleCaps, sessionCaps1.getModuleBasedCaps().size());
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.connect.netconf.sal.tx;
+
+import static junit.framework.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+public class NetconfDeviceWriteOnlyTxTest {
+
+ private final RemoteDeviceId id = new RemoteDeviceId("test-mount");
+
+ @Mock
+ private RpcImplementation rpc;
+ @Mock
+ private DataNormalizer normalizer;
+ private YangInstanceIdentifier yangIId;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ doReturn(Futures.<RpcResult<CompositeNode>>immediateFailedFuture(new IllegalStateException("Failed tx")))
+ .doReturn(Futures.immediateFuture(RpcResultBuilder.<CompositeNode>success().build()))
+ .when(rpc).invokeRpc(any(QName.class), any(CompositeNode.class));
+
+ yangIId = YangInstanceIdentifier.builder().node(QName.create("namespace", "2012-12-12", "name")).build();
+ doReturn(yangIId).when(normalizer).toLegacy(yangIId);
+ }
+
+ @Test
+ public void testDiscardCahnges() {
+ final NetconfDeviceWriteOnlyTx tx = new NetconfDeviceWriteOnlyTx(id, rpc, normalizer, true, true);
+ final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = tx.submit();
+ try {
+ submitFuture.checkedGet();
+ } catch (final TransactionCommitFailedException e) {
+ // verify discard changes was sent
+ verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME, DISCARD_CHANGES_RPC_CONTENT);
+ return;
+ }
+
+ fail("Submit should fail");
+ }
+
+
+ @Test
+ public void testDiscardCahngesNotSentWithoutCandidate() {
+ doReturn(Futures.immediateFuture(RpcResultBuilder.<CompositeNode>success().build()))
+ .doReturn(Futures.<RpcResult<CompositeNode>>immediateFailedFuture(new IllegalStateException("Failed tx")))
+ .when(rpc).invokeRpc(any(QName.class), any(CompositeNode.class));
+
+ final NetconfDeviceWriteOnlyTx tx = new NetconfDeviceWriteOnlyTx(id, rpc, normalizer, false, true);
+ tx.delete(LogicalDatastoreType.CONFIGURATION, yangIId);
+ verify(rpc).invokeRpc(eq(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME), any(CompositeNode.class));
+ verifyNoMoreInteractions(rpc);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.controller.remote.rpc;
+
+
+public class ActorConstants {
+ public static final String RPC_BROKER = "rpc-broker";
+ public static final String RPC_REGISTRY = "rpc-registry";
+ public static final String RPC_MANAGER = "rpc";
+
+ public static final String RPC_BROKER_PATH= "/user/rpc/rpc-broker";
+ public static final String RPC_REGISTRY_PATH = "/user/rpc/rpc-registry";
+}
SchemaService schemaService = brokerSession.getService(SchemaService.class);
schemaContext = schemaService.getGlobalContext();
- rpcManager = actorSystem.actorOf(RpcManager.props(clusterWrapper, schemaContext, brokerSession, rpcProvisionRegistry), "rpc");
+ rpcManager = actorSystem.actorOf(RpcManager.props(clusterWrapper, schemaContext, brokerSession, rpcProvisionRegistry), ActorConstants.RPC_MANAGER);
LOG.debug("Rpc actors are created.");
}
private void createRpcActors() {
LOG.debug("Create rpc registry and broker actors");
- rpcRegistry = getContext().actorOf(RpcRegistry.props(clusterWrapper), "rpc-registry");
- rpcBroker = getContext().actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "rpc-broker");
+ rpcRegistry = getContext().actorOf(RpcRegistry.props(clusterWrapper), ActorConstants.RPC_REGISTRY);
+ rpcBroker = getContext().actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), ActorConstants.RPC_BROKER);
}
private void startListeners() {
LOG.debug("Registers rpc listeners");
- String rpcBrokerPath = clusterWrapper.getAddress().toString() + "/user/rpc/rpc-broker";
+ String rpcBrokerPath = clusterWrapper.getAddress().toString() + ActorConstants.RPC_BROKER_PATH;
rpcListener = new RpcListener(rpcRegistry, rpcBrokerPath);
routeChangeListener = new RoutedRpcListener(rpcRegistry, rpcBrokerPath);
rpcImplementation = new RemoteRpcImplementation(rpcBroker, schemaContext);
import akka.cluster.Member;
import akka.japi.Creator;
import org.opendaylight.controller.remote.rpc.AbstractUntypedActor;
+import org.opendaylight.controller.remote.rpc.ActorConstants;
import org.opendaylight.controller.remote.rpc.messages.AddRoutedRpc;
import org.opendaylight.controller.remote.rpc.messages.AddRpc;
import org.opendaylight.controller.remote.rpc.messages.GetRoutedRpc;
}
if(i == index) {
if(!currentNodeAddress.equals(member.address())) {
- actor = this.context().actorSelection(member.address() + "/user/rpc-registry");
+ actor = this.context().actorSelection(member.address() + ActorConstants.RPC_REGISTRY_PATH);
break;
} else if(index < memberSize-1){ // pick the next element in the set
index++;
i++;
}
if(actor == null && previousMember != null) {
- actor = this.context().actorSelection(previousMember.address() + "/user/rpc-registry");
+ actor = this.context().actorSelection(previousMember.address() + ActorConstants.RPC_REGISTRY_PATH);
}
}
return actor;
import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
public class RestconfProviderImpl implements Provider, AutoCloseable, RestConnector {
public final static String NOT_INITALIZED_MSG = "Restconf is not initialized yet. Please try again later";
- private ListenerRegistration<SchemaServiceListener> listenerRegistration;
+ private ListenerRegistration<SchemaContextListener> listenerRegistration;
private PortNumber port;
public void setWebsocketPort(PortNumber port) {
this.port = port;
BrokerFacade.getInstance().setDataService(dataService);
SchemaService schemaService = session.getService(SchemaService.class);
- listenerRegistration = schemaService.registerSchemaServiceListener(ControllerContext.getInstance());
+ listenerRegistration = schemaService.registerSchemaContextListener(ControllerContext.getInstance());
ControllerContext.getInstance().setSchemas(schemaService.getGlobalContext());
ControllerContext.getInstance().setMountService(session.getService(MountService.class));
DataSchemaNode ret = container.getDataChildByName(name);
if (ret == null) {
for (final DataSchemaNode node : container.getChildNodes()) {
- if ((node instanceof ChoiceCaseNode)) {
- final ChoiceCaseNode caseNode = ((ChoiceCaseNode) node);
- DataSchemaNode childByQName = ControllerContext.childByQName(caseNode, name);
+ if ((node instanceof ChoiceNode)) {
+ final ChoiceNode choiceNode = ((ChoiceNode) node);
+ DataSchemaNode childByQName = ControllerContext.childByQName(choiceNode, name);
if (childByQName != null) {
return childByQName;
}
result = result.substring(1);
}
if (result.endsWith("/")) {
- result = result.substring(0, result.length());
+ result = result.substring(0, result.length()-1);
}
return result;
}
<module>toaster</module>
<module>toaster-consumer</module>
<module>toaster-provider</module>
+ <module>toaster-config</module>
<module>l2switch</module>
</modules>
<scm>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2014 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sal-samples</artifactId>
+ <version>1.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>toaster-config</artifactId>
+ <description>Configuration files for toaster</description>
+ <packaging>jar</packaging>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/initial/03-toaster-sample.xml</file>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
<type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
<name>binding-data-broker</name>
</data-broker>
-
+
<notification-service>
<type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
binding:binding-notification-service
</notification-service>
</module>
</modules>
-
+
<services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
<service>
<type xmlns:kitchen="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2014 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-subsystem</artifactId>
+ <version>0.2.5-SNAPSHOT</version>
+ </parent>
+ <artifactId>netconf-config</artifactId>
+ <description>Configuration files for netconf</description>
+ <packaging>jar</packaging>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/initial/01-netconf.xml</file>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2014 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-subsystem</artifactId>
+ <version>0.2.5-SNAPSHOT</version>
+ </parent>
+ <artifactId>netconf-connector-config</artifactId>
+ <description>Configuration files for netconf-connector</description>
+ <packaging>jar</packaging>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/initial/99-netconf-connector.xml</file>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
<modules>
<module>netconf-api</module>
<module>netconf-cli</module>
+ <module>netconf-config</module>
<module>netconf-impl</module>
<module>config-netconf-connector</module>
<module>netconf-util</module>
<module>netconf-monitoring</module>
<module>ietf-netconf-monitoring</module>
<module>ietf-netconf-monitoring-extension</module>
+ <module>netconf-connector-config</module>
</modules>
<dependencies>
*/
List<NeutronPort> myPorts;
- boolean gatewayIPAssigned;
+ Boolean gatewayIPAssigned;
public NeutronSubnet() {
myPorts = new ArrayList<NeutronPort>();
try {
SubnetUtils util = new SubnetUtils(cidr);
SubnetInfo info = util.getInfo();
- if (gatewayIP == null) {
+ if (gatewayIP == null || ("").equals(gatewayIP)) {
gatewayIP = info.getLowAddress();
}
if (allocationPools.size() < 1) {
gatewayIPAssigned = false;
}
+ public Boolean getGatewayIPAllocated() {
+ return gatewayIPAssigned;
+ }
+
@Override
public String toString() {
return "NeutronSubnet [subnetUUID=" + subnetUUID + ", networkUUID=" + networkUUID + ", name=" + name
if (instances != null) {
for (Object instance : instances) {
INeutronRouterAware service = (INeutronRouterAware) instance;
- service.canAttachInterface(target, input);
+ int status = service.canAttachInterface(target, input);
+ if (status < 200 || status > 299)
+ return Response.status(status).build();
}
}
if (instances != null) {
for (Object instance : instances) {
INeutronRouterAware service = (INeutronRouterAware) instance;
- service.canDetachInterface(target, input);
+ int status = service.canDetachInterface(target, input);
+ if (status < 200 || status > 299)
+ return Response.status(status).build();
}
}