From: Madhu Venugopal Date: Sat, 2 Aug 2014 07:33:30 +0000 (+0000) Subject: Merge "Added hosttracker shell for karaf (rebased)" X-Git-Tag: release/helium~364 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=5ffd4b46ca00fc8f3d801050670c890117dc0811;hp=a22e44963c31452b6ea17745b652456b8b991101 Merge "Added hosttracker shell for karaf (rebased)" --- diff --git a/.gitignore b/.gitignore index f8cf74f826..9144cda4cc 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ out/ maven-eclipse.xml .DS_STORE .metadata +opendaylight/md-sal/sal-distributed-datastore/journal + diff --git a/features/config-netty/src/main/resources/features.xml b/features/config-netty/src/main/resources/features.xml index 3121ca01a2..f1b2d1f753 100644 --- a/features/config-netty/src/main/resources/features.xml +++ b/features/config-netty/src/main/resources/features.xml @@ -12,5 +12,6 @@ mvn:org.opendaylight.controller/threadpool-config-api/${project.version} mvn:org.opendaylight.controller/threadpool-config-impl/${project.version} odl-config-startup + mvn:org.opendaylight.controller/config-netty-config/${config.version}/xml/config \ No newline at end of file diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index c21ed78064..dd7d7dfbb9 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -1241,6 +1241,26 @@ sal-rest-connector-config ${mdsal.version} + + org.opendaylight.controller + config-netty-config + ${config.version} + + + org.opendaylight.controller + md-sal-config + ${mdsal.version} + + + org.opendaylight.controller + netconf-config + ${netconf.version} + + + org.opendaylight.controller + netconf-connector-config + ${netconf.version} + org.opendaylight.controller sal-rest-docgen @@ -1497,6 +1517,11 @@ sample-toaster-provider ${mdsal.version} + + org.opendaylight.controller.samples + toaster-config + ${mdsal.version} + org.opendaylight.controller.thirdparty com.sun.jersey.jersey-servlet diff --git a/opendaylight/commons/protocol-framework/pom.xml b/opendaylight/commons/protocol-framework/pom.xml index f70698731a..774bc7c23f 100644 --- a/opendaylight/commons/protocol-framework/pom.xml +++ b/opendaylight/commons/protocol-framework/pom.xml @@ -91,6 +91,11 @@ netty-event-executor-config test + + ch.qos.logback + logback-classic + test + diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java index a62bd7da06..a05d02cd09 100644 --- a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java @@ -7,12 +7,19 @@ */ 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; @@ -28,13 +35,6 @@ import io.netty.util.concurrent.Future; 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. @@ -155,7 +155,7 @@ public abstract class AbstractDispatcher, L extends */ protected Future createClient(final InetSocketAddress address, final ReconnectStrategy strategy, final PipelineInitializer initializer) { final Bootstrap b = new Bootstrap(); - final ProtocolSessionPromise p = new ProtocolSessionPromise(executor, address, strategy, b); + final ProtocolSessionPromise p = new ProtocolSessionPromise<>(executor, address, strategy, b); b.option(ChannelOption.SO_KEEPALIVE, true).handler( new ChannelInitializer() { @Override @@ -165,18 +165,36 @@ public abstract class AbstractDispatcher, L extends }); 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 createClient(final InetSocketAddress address, final ReconnectStrategy strategy, final Bootstrap bootstrap, final PipelineInitializer initializer) { + final ProtocolSessionPromise p = new ProtocolSessionPromise<>(executor, address, strategy, bootstrap); + + bootstrap.handler( + new ChannelInitializer() { + @Override + protected void initChannel(final SocketChannel ch) { + initializer.initializeChannel(ch, p); + } + }); p.connect(); LOG.debug("Client created."); @@ -195,6 +213,9 @@ public abstract class AbstractDispatcher, L extends } /** + * + * @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 @@ -204,15 +225,47 @@ public abstract class AbstractDispatcher, L extends * @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 createReconnectingClient(final InetSocketAddress address, final ReconnectStrategyFactory connectStrategyFactory, final ReconnectStrategy reestablishStrategy, final PipelineInitializer initializer) { + return createReconnectingClient(address, connectStrategyFactory, initializer); + } - final ReconnectPromise p = new ReconnectPromise(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 createReconnectingClient(final InetSocketAddress address, final ReconnectStrategyFactory connectStrategyFactory, + final PipelineInitializer initializer) { + final Bootstrap b = new Bootstrap(); + + final ReconnectPromise 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()} */ @@ -225,5 +278,4 @@ public abstract class AbstractDispatcher, L extends this.bossGroup.shutdownGracefully(); } } - } diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolSessionPromise.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolSessionPromise.java index a78274cca0..a38db61ead 100644 --- a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolSessionPromise.java +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolSessionPromise.java @@ -7,6 +7,7 @@ */ 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; @@ -16,17 +17,12 @@ 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.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> extends DefaultPromise { private static final Logger LOG = LoggerFactory.getLogger(ProtocolSessionPromise.class); @@ -54,72 +50,12 @@ final class ProtocolSessionPromise> extends Default 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 rf = ProtocolSessionPromise.this.strategy.scheduleReconnect(cf.cause()); - rf.addListener(new FutureListener() { - @Override - public void operationComplete(final Future 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); } } @@ -140,4 +76,79 @@ final class ProtocolSessionPromise> extends Default 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 rf = ProtocolSessionPromise.this.strategy.scheduleReconnect(cf.cause()); + rf.addListener(new ReconnectingStrategyListener()); + ProtocolSessionPromise.this.pending = rf; + } + } + + private class ReconnectingStrategyListener implements FutureListener { + @Override + public void operationComplete(final Future 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()); + } + } + } + } + } + + } + } diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectPromise.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectPromise.java index 1fa6a81753..fe1012f443 100644 --- a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectPromise.java +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectPromise.java @@ -7,176 +7,100 @@ */ 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, L extends SessionListener> extends DefaultPromise { + private static final Logger LOG = LoggerFactory.getLogger(ReconnectPromise.class); + private final AbstractDispatcher dispatcher; private final InetSocketAddress address; private final ReconnectStrategyFactory strategyFactory; - private final ReconnectStrategy strategy; - private final PipelineInitializer initializer; + private final Bootstrap b; + private final AbstractDispatcher.PipelineInitializer initializer; private Future pending; - private final AtomicBoolean negotiationFinished = new AtomicBoolean(false); - public ReconnectPromise(final EventExecutor executor, final AbstractDispatcher dispatcher, final InetSocketAddress address, - final ReconnectStrategyFactory connectStrategyFactory, final ReconnectStrategy reestablishStrategy, - final PipelineInitializer initializer) { + final ReconnectStrategyFactory connectStrategyFactory, final Bootstrap b, final AbstractDispatcher.PipelineInitializer 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 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 cf = this.dispatcher.createClient(this.address, rs, new PipelineInitializer() { + // 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() { @Override public void initializeChannel(final SocketChannel channel, final Promise 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() { + @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 future) { - synchronized (lock) { - if (!future.isSuccess()) { - final Future 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() { - @Override - public void operationComplete(final Future 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 { + /** + * 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 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; - } } diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionListener.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionListener.java index 3c429fc774..a756a0da7e 100644 --- a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionListener.java +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionListener.java @@ -10,7 +10,7 @@ package org.opendaylight.protocol.framework; 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. */ diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/ServerTest.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/ServerTest.java index bead1ee49e..63026e384c 100644 --- a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/ServerTest.java +++ b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/ServerTest.java @@ -9,6 +9,14 @@ package org.opendaylight.protocol.framework; 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; @@ -16,50 +24,139 @@ import io.netty.util.concurrent.DefaultPromise; 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() { + @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() { + @Override + public SimpleSessionListener getSessionListener() { + return new SimpleSessionListener(); + } + }); + + Mockito.verify(mockReconnectStrategy, timeout(5000).atLeast(2)).scheduleReconnect(any(Throwable.class)); + + final Promise p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE); + this.dispatcher = getServerDispatcher(p); + + this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory() { + @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 p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE); - this.dispatcher = new SimpleDispatcher(new SessionNegotiatorFactory() { + this.dispatcher = getServerDispatcher(p); + this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory() { @Override - public SessionNegotiator getSessionNegotiator(final SessionListenerFactory factory, - final Channel channel, final Promise promise) { - p.setSuccess(true); - return new SimpleSessionNegotiator(promise, channel); + public SimpleSessionListener getSessionListener() { + return new SimpleSessionListener(); } - }, new DefaultPromise(GlobalEventExecutor.INSTANCE), eventLoopGroup); + }); + + this.server.get(); + + this.clientDispatcher = getClientDispatcher(); + + final ReconnectStrategy reconnectStrategy = getMockedReconnectStrategy(); + this.session = this.clientDispatcher.createClient(this.serverAddress, + reconnectStrategy, new SessionListenerFactory() { + @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 p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE); + + this.dispatcher = getServerDispatcher(p); this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory() { @Override @@ -70,13 +167,42 @@ public class ServerTest { this.server.get(); - this.clientDispatcher = new SimpleDispatcher(new SessionNegotiatorFactory() { + 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() { + @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 p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE); + + this.dispatcher = getServerDispatcher(p); + + this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory() { @Override - public SessionNegotiator getSessionNegotiator(final SessionListenerFactory factory, - final Channel channel, final Promise promise) { - return new SimpleSessionNegotiator(promise, channel); + public SimpleSessionListener getSessionListener() { + return new SimpleSessionListener(); } - }, new DefaultPromise(GlobalEventExecutor.INSTANCE), eventLoopGroup); + }); + + this.server.get(); + + this.clientDispatcher = getClientDispatcher(); this.session = this.clientDispatcher.createClient(this.serverAddress, new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000), new SessionListenerFactory() { @@ -93,15 +219,7 @@ public class ServerTest { public void testConnectionFailed() throws IOException, InterruptedException, ExecutionException, TimeoutException { final Promise p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE); - this.dispatcher = new SimpleDispatcher(new SessionNegotiatorFactory() { - - @Override - public SessionNegotiator getSessionNegotiator(final SessionListenerFactory factory, - final Channel channel, final Promise promise) { - p.setSuccess(true); - return new SimpleSessionNegotiator(promise, channel); - } - }, new DefaultPromise(GlobalEventExecutor.INSTANCE), eventLoopGroup); + this.dispatcher = getServerDispatcher(p); this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory() { @Override @@ -112,13 +230,7 @@ public class ServerTest { this.server.get(); - this.clientDispatcher = new SimpleDispatcher(new SessionNegotiatorFactory() { - @Override - public SessionNegotiator getSessionNegotiator(final SessionListenerFactory factory, - final Channel channel, final Promise promise) { - return new SimpleSessionNegotiator(promise, channel); - } - }, new DefaultPromise(GlobalEventExecutor.INSTANCE), eventLoopGroup); + this.clientDispatcher = getClientDispatcher(); this.session = this.clientDispatcher.createClient(this.serverAddress, new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000), new SessionListenerFactory() { @@ -138,14 +250,89 @@ public class ServerTest { 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 p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE); + + this.dispatcher = getServerDispatcher(p); + + this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory() { + @Override + public SimpleSessionListener getSessionListener() { + return new SimpleSessionListener(); + } + }); + + this.server.get(); + + this.clientDispatcher = new SimpleDispatcher(new SessionNegotiatorFactory() { + @Override + public SessionNegotiator getSessionNegotiator(final SessionListenerFactory factory, + final Channel channel, final Promise promise) { + + return new SimpleSessionNegotiator(promise, channel) { + @Override + protected void startNegotiation() throws Exception { + negotiationFailed(new IllegalStateException("Negotiation failed")); + } + }; + } + }, new DefaultPromise(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() { + @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() { + @Override + public SessionNegotiator getSessionNegotiator(final SessionListenerFactory factory, + final Channel channel, final Promise promise) { + return new SimpleSessionNegotiator(promise, channel); + } + }, new DefaultPromise(GlobalEventExecutor.INSTANCE), eventLoopGroup); + } + + private ReconnectStrategy getMockedReconnectStrategy() throws Exception { + final ReconnectStrategy mockReconnectStrategy = mock(ReconnectStrategy.class); + final Future 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 p) { + return new SimpleDispatcher(new SessionNegotiatorFactory() { + + @Override + public SessionNegotiator getSessionNegotiator(final SessionListenerFactory factory, + final Channel channel, final Promise promise) { + p.setSuccess(true); + return new SimpleSessionNegotiator(promise, channel); + } + }, null, serverLoopGroup); + } + } diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleDispatcher.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleDispatcher.java index 12aac9ecc5..d83738520c 100644 --- a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleDispatcher.java +++ b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleDispatcher.java @@ -54,6 +54,10 @@ public class SimpleDispatcher extends AbstractDispatcher createReconnectingClient(final InetSocketAddress address, final ReconnectStrategyFactory strategy, final SessionListenerFactory listenerFactory) { + return super.createReconnectingClient(address, strategy, new SimplePipelineInitializer(listenerFactory)); + } + public ChannelFuture createServer(final InetSocketAddress address, final SessionListenerFactory listenerFactory) { return super.createServer(address, new SimplePipelineInitializer(listenerFactory)); } diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/mapping/CodecRegistryProvider.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/mapping/CodecRegistryProvider.java index ec46219aaf..6050f7c070 100644 --- a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/mapping/CodecRegistryProvider.java +++ b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/mapping/CodecRegistryProvider.java @@ -14,7 +14,7 @@ import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy 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; /** @@ -31,7 +31,7 @@ public class CodecRegistryProvider implements AutoCloseable { 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() { diff --git a/opendaylight/config/config-netty-config/pom.xml b/opendaylight/config/config-netty-config/pom.xml new file mode 100644 index 0000000000..8dc31dcc4e --- /dev/null +++ b/opendaylight/config/config-netty-config/pom.xml @@ -0,0 +1,46 @@ + + + + + 4.0.0 + + org.opendaylight.controller + config-subsystem + 0.2.5-SNAPSHOT + + config-netty-config + Configuration files for sal-rest-connector + jar + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + + attach-artifact + + package + + + + ${project.build.directory}/classes/initial/00-netty.xml + xml + config + + + + + + + + + diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/00-netty.xml b/opendaylight/config/config-netty-config/src/main/resources/initial/00-netty.xml similarity index 100% rename from opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/00-netty.xml rename to opendaylight/config/config-netty-config/src/main/resources/initial/00-netty.xml diff --git a/opendaylight/config/pom.xml b/opendaylight/config/pom.xml index 66bb01f051..26fac47e71 100644 --- a/opendaylight/config/pom.xml +++ b/opendaylight/config/pom.xml @@ -39,6 +39,7 @@ shutdown-impl netconf-config-dispatcher config-module-archetype + config-netty-config diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index 541c1300f3..4d0770f8cb 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -745,7 +745,7 @@ generate-resources ${project.build.directory}/configuration - sal-rest-connector-config + sal-rest-connector-config,config-netty-config,md-sal-config,netconf-config,toaster-config,netconf-connector-config **\/*.xml true false @@ -1034,6 +1034,26 @@ org.opendaylight.controller sal-rest-connector-config + + org.opendaylight.controller + config-netty-config + + + org.opendaylight.controller + md-sal-config + + + org.opendaylight.controller + netconf-config + + + org.opendaylight.controller + netconf-connector-config + + + org.opendaylight.controller.samples + toaster-config + org.opendaylight.controller sal-rest-docgen @@ -1318,7 +1338,7 @@ generate-resources ${project.build.directory}/configuration - sal-rest-connector-config + sal-rest-connector-config,config-netty-config,md-sal-config,netconf-config,toaster-config,netconf-connector-config **\/*.xml true false diff --git a/opendaylight/md-sal/md-sal-config/pom.xml b/opendaylight/md-sal/md-sal-config/pom.xml new file mode 100644 index 0000000000..2e19b5a60c --- /dev/null +++ b/opendaylight/md-sal/md-sal-config/pom.xml @@ -0,0 +1,46 @@ + + + + + 4.0.0 + + org.opendaylight.controller + sal-parent + 1.1-SNAPSHOT + + md-sal-config + Configuration files for md-sal + jar + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + + attach-artifact + + package + + + + ${project.build.directory}/classes/initial/01-md-sal.xml + xml + config + + + + + + + + + diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml b/opendaylight/md-sal/md-sal-config/src/main/resources/initial/01-md-sal.xml similarity index 99% rename from opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml rename to opendaylight/md-sal/md-sal-config/src/main/resources/initial/01-md-sal.xml index 03da7f0ccd..f25b7d91bd 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml +++ b/opendaylight/md-sal/md-sal-config/src/main/resources/initial/01-md-sal.xml @@ -72,17 +72,7 @@ --> - - + prefix:inmemory-operational-datastore-provider operational-store-service @@ -157,6 +147,17 @@ + + diff --git a/opendaylight/md-sal/pom.xml b/opendaylight/md-sal/pom.xml index cadd8ca87d..02fbde8f18 100644 --- a/opendaylight/md-sal/pom.xml +++ b/opendaylight/md-sal/pom.xml @@ -32,6 +32,9 @@ sal-binding-util + + md-sal-config + samples @@ -66,11 +69,17 @@ sal-protocolbuffer-encoding - + sal-distributed-datastore + + sal-dom-xsql + sal-test-model + + + sal-remoterpc-connector diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleActor.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleActor.java index 641ec0582c..aa100df9d0 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleActor.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleActor.java @@ -11,10 +11,12 @@ package org.opendaylight.controller.cluster.example; import akka.actor.ActorRef; import akka.actor.Props; import akka.japi.Creator; +import com.google.common.base.Optional; import org.opendaylight.controller.cluster.example.messages.KeyValue; import org.opendaylight.controller.cluster.example.messages.KeyValueSaved; import org.opendaylight.controller.cluster.example.messages.PrintRole; import org.opendaylight.controller.cluster.example.messages.PrintState; +import org.opendaylight.controller.cluster.raft.ConfigParams; import org.opendaylight.controller.cluster.raft.RaftActor; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; @@ -31,15 +33,17 @@ public class ExampleActor extends RaftActor { private long persistIdentifier = 1; - public ExampleActor(String id, Map peerAddresses) { - super(id, peerAddresses); + public ExampleActor(String id, Map peerAddresses, + Optional configParams) { + super(id, peerAddresses, configParams); } - public static Props props(final String id, final Map peerAddresses){ + public static Props props(final String id, final Map peerAddresses, + final Optional configParams){ return Props.create(new Creator(){ @Override public ExampleActor create() throws Exception { - return new ExampleActor(id, peerAddresses); + return new ExampleActor(id, peerAddresses, configParams); } }); } @@ -56,10 +60,12 @@ public class ExampleActor extends RaftActor { } } else if (message instanceof PrintState) { - LOG.debug("State of the node:"+getId() + " has = "+state.size() + " entries"); + LOG.debug("State of the node:{} has entries={}, {}", + getId(), state.size(), getReplicatedLogState()); } else if (message instanceof PrintRole) { - LOG.debug(getId() + " = " + getRaftState()); + LOG.debug("{} = {}, Peers={}", getId(), getRaftState(),getPeers()); + } else { super.onReceiveCommand(message); } @@ -83,6 +89,7 @@ public class ExampleActor extends RaftActor { @Override protected void applySnapshot(Object snapshot) { state.clear(); state.putAll((HashMap) snapshot); + LOG.debug("Snapshot applied to state :" + ((HashMap) snapshot).size()); } @Override public void onReceiveRecover(Object message) { diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleConfigParamsImpl.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleConfigParamsImpl.java new file mode 100644 index 0000000000..d11377dbcb --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleConfigParamsImpl.java @@ -0,0 +1,20 @@ +/* + * 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.cluster.example; + +import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl; + +/** + * Implementation of ConfigParams for Example + */ +public class ExampleConfigParamsImpl extends DefaultConfigParamsImpl { + @Override + public long getSnapshotBatchCount() { + return 50; + } +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/Main.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/Main.java index a148ed4009..0e5d643a64 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/Main.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/Main.java @@ -11,7 +11,9 @@ package org.opendaylight.controller.cluster.example; import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.PoisonPill; +import com.google.common.base.Optional; import org.opendaylight.controller.cluster.example.messages.KeyValue; +import org.opendaylight.controller.cluster.raft.ConfigParams; import java.io.BufferedReader; import java.io.InputStreamReader; @@ -34,15 +36,15 @@ public class Main { public static void main(String[] args) throws Exception{ ActorRef example1Actor = actorSystem.actorOf(ExampleActor.props("example-1", - withoutPeer("example-1")), "example-1"); + withoutPeer("example-1"), Optional.absent()), "example-1"); ActorRef example2Actor = actorSystem.actorOf(ExampleActor.props("example-2", - withoutPeer("example-2")), "example-2"); + withoutPeer("example-2"), Optional.absent()), "example-2"); ActorRef example3Actor = actorSystem.actorOf(ExampleActor.props("example-3", - withoutPeer("example-3")), "example-3"); + withoutPeer("example-3"), Optional.absent()), "example-3"); List examples = Arrays.asList(example1Actor, example2Actor, example3Actor); @@ -74,7 +76,8 @@ public class Main { String actorName = "example-" + i; examples.add(i - 1, actorSystem.actorOf(ExampleActor.props(actorName, - withoutPeer(actorName)), actorName)); + withoutPeer(actorName), Optional.absent()), + actorName)); System.out.println("Created actor : " + actorName); continue; } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/TestDriver.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/TestDriver.java index c2d0b3a6b7..fd6e192bf0 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/TestDriver.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/TestDriver.java @@ -2,8 +2,10 @@ package org.opendaylight.controller.cluster.example; import akka.actor.ActorRef; import akka.actor.ActorSystem; +import com.google.common.base.Optional; import org.opendaylight.controller.cluster.example.messages.PrintRole; import org.opendaylight.controller.cluster.example.messages.PrintState; +import org.opendaylight.controller.cluster.raft.ConfigParams; import org.opendaylight.controller.cluster.raft.client.messages.AddRaftPeer; import org.opendaylight.controller.cluster.raft.client.messages.RemoveRaftPeer; @@ -11,14 +13,13 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; -import java.util.Random; import java.util.concurrent.ConcurrentHashMap; /** * This is a test driver for testing akka-raft implementation * Its uses ExampleActors and threads to push content(key-vals) to these actors * Each ExampleActor can have one or more ClientActors. Each ClientActor spawns - * a thread and starts push logs to the actor its assignged to. + * a thread and starts push logs to the actor its assigned to. */ public class TestDriver { @@ -26,7 +27,9 @@ public class TestDriver { private static Map allPeers = new HashMap<>(); private static Map clientActorRefs = new HashMap(); private static Map actorRefs = new HashMap(); - private static LogGenerator logGenerator = new LogGenerator();; + private static LogGenerator logGenerator = new LogGenerator(); + private int nameCounter = 0; + private static ConfigParams configParams = new ExampleConfigParamsImpl(); /** * Create nodes, add clients and start logging. @@ -35,6 +38,7 @@ public class TestDriver { * createNodes:{num} * addNodes:{num} * stopNode:{nodeName} + * reinstateNode:{nodeName} * addClients:{num} * addClientsToNode:{nodeName, num} * startLogging @@ -83,6 +87,10 @@ public class TestDriver { String[] arr = command.split(":"); td.stopNode(arr[1]); + } else if (command.startsWith("reinstateNode")) { + String[] arr = command.split(":"); + td.reinstateNode(arr[1]); + } else if (command.startsWith("startLogging")) { td.startAllLogging(); @@ -106,15 +114,19 @@ public class TestDriver { } } + public static ActorRef createExampleActor(String name) { + return actorSystem.actorOf(ExampleActor.props(name, withoutPeer(name), + Optional.of(configParams)), name); + } + public void createNodes(int num) { for (int i=0; i < num; i++) { - int rand = getUnusedRandom(num); - allPeers.put("example-"+rand, "akka://default/user/example-"+rand); + nameCounter = nameCounter + 1; + allPeers.put("example-"+nameCounter, "akka://default/user/example-"+nameCounter); } for (String s : allPeers.keySet()) { - ActorRef exampleActor = actorSystem.actorOf( - ExampleActor.props(s, withoutPeer(s)), s); + ActorRef exampleActor = createExampleActor(s); actorRefs.put(s, exampleActor); System.out.println("Created node:"+s); @@ -125,15 +137,14 @@ public class TestDriver { public void addNodes(int num) { Map newPeers = new HashMap<>(); for (int i=0; i < num; i++) { - int rand = getUnusedRandom(num); - newPeers.put("example-"+rand, "akka://default/user/example-"+rand); - allPeers.put("example-"+rand, "akka://default/user/example-"+rand); + nameCounter = nameCounter + 1; + newPeers.put("example-"+nameCounter, "akka://default/user/example-"+nameCounter); + allPeers.put("example-"+nameCounter, "akka://default/user/example-"+nameCounter); } Map newActorRefs = new HashMap(num); for (Map.Entry entry : newPeers.entrySet()) { - ActorRef exampleActor = actorSystem.actorOf( - ExampleActor.props(entry.getKey(), withoutPeer(entry.getKey())), entry.getKey()); + ActorRef exampleActor = createExampleActor(entry.getKey()); newActorRefs.put(entry.getKey(), exampleActor); //now also add these new nodes as peers from the previous nodes @@ -165,7 +176,7 @@ public class TestDriver { public void addClientsToNode(String actorName, int num) { ActorRef actorRef = actorRefs.get(actorName); for (int i=0; i < num; i++) { - String clientName = "client-" + i + "-" + actorRef; + String clientName = "client-" + i + "-" + actorName; clientActorRefs.put(clientName, actorSystem.actorOf(ClientActor.props(actorRef), clientName)); System.out.println("Added client-node:" + clientName); @@ -174,11 +185,13 @@ public class TestDriver { public void stopNode(String actorName) { ActorRef actorRef = actorRefs.get(actorName); - String clientName = "client-"+actorName; - if(clientActorRefs.containsKey(clientName)) { - actorSystem.stop(clientActorRefs.get(clientName)); - clientActorRefs.remove(clientName); + + for (Map.Entry entry : clientActorRefs.entrySet()) { + if (entry.getKey().endsWith(actorName)) { + actorSystem.stop(entry.getValue()); + } } + actorSystem.stop(actorRef); actorRefs.remove(actorName); @@ -187,7 +200,21 @@ public class TestDriver { } allPeers.remove(actorName); + } + public void reinstateNode(String actorName) { + String address = "akka://default/user/"+actorName; + allPeers.put(actorName, address); + + ActorRef exampleActor = createExampleActor(actorName); + + for (ActorRef actor : actorRefs.values()) { + actor.tell(new AddRaftPeer(actorName, address), null); + } + + actorRefs.put(actorName, exampleActor); + + addClientsToNode(actorName, 1); } public void startAllLogging() { @@ -232,14 +259,6 @@ public class TestDriver { return null; } - private int getUnusedRandom(int num) { - int rand = -1; - do { - rand = (new Random()).nextInt(num * num); - } while (allPeers.keySet().contains("example-"+rand)); - - return rand; - } private static Map withoutPeer(String peerId) { Map without = new ConcurrentHashMap<>(allPeers); diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImpl.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImpl.java new file mode 100644 index 0000000000..24bfa3de21 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImpl.java @@ -0,0 +1,153 @@ +/* + * 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.cluster.raft; + +import java.util.ArrayList; +import java.util.List; + +/** + * Abstract class handling the mapping of + * logical LogEntry Index and the physical list index. + */ +public abstract class AbstractReplicatedLogImpl implements ReplicatedLog { + + protected final List journal; + protected final Object snapshot; + protected long snapshotIndex = -1; + protected long snapshotTerm = -1; + + public AbstractReplicatedLogImpl(Object state, long snapshotIndex, + long snapshotTerm, List unAppliedEntries) { + this.snapshot = state; + this.snapshotIndex = snapshotIndex; + this.snapshotTerm = snapshotTerm; + this.journal = new ArrayList<>(unAppliedEntries); + } + + + public AbstractReplicatedLogImpl() { + this.snapshot = null; + this.journal = new ArrayList<>(); + } + + protected int adjustedIndex(long logEntryIndex) { + if(snapshotIndex < 0){ + return (int) logEntryIndex; + } + return (int) (logEntryIndex - (snapshotIndex + 1)); + } + + @Override + public ReplicatedLogEntry get(long logEntryIndex) { + int adjustedIndex = adjustedIndex(logEntryIndex); + + if (adjustedIndex < 0 || adjustedIndex >= journal.size()) { + // physical index should be less than list size and >= 0 + return null; + } + + return journal.get(adjustedIndex); + } + + @Override + public ReplicatedLogEntry last() { + if (journal.isEmpty()) { + return null; + } + // get the last entry directly from the physical index + return journal.get(journal.size() - 1); + } + + @Override + public long lastIndex() { + if (journal.isEmpty()) { + // it can happen that after snapshot, all the entries of the + // journal are trimmed till lastApplied, so lastIndex = snapshotIndex + return snapshotIndex; + } + return last().getIndex(); + } + + @Override + public long lastTerm() { + if (journal.isEmpty()) { + // it can happen that after snapshot, all the entries of the + // journal are trimmed till lastApplied, so lastTerm = snapshotTerm + return snapshotTerm; + } + return last().getTerm(); + } + + @Override + public void removeFrom(long logEntryIndex) { + int adjustedIndex = adjustedIndex(logEntryIndex); + if (adjustedIndex < 0 || adjustedIndex >= journal.size()) { + // physical index should be less than list size and >= 0 + return; + } + journal.subList(adjustedIndex , journal.size()).clear(); + } + + @Override + public void append(ReplicatedLogEntry replicatedLogEntry) { + journal.add(replicatedLogEntry); + } + + @Override + public List getFrom(long logEntryIndex) { + int adjustedIndex = adjustedIndex(logEntryIndex); + int size = journal.size(); + List entries = new ArrayList<>(100); + if (adjustedIndex >= 0 && adjustedIndex < size) { + // physical index should be less than list size and >= 0 + entries.addAll(journal.subList(adjustedIndex, size)); + } + return entries; + } + + @Override + public long size() { + return journal.size(); + } + + @Override + public boolean isPresent(long logEntryIndex) { + if (logEntryIndex > lastIndex()) { + // if the request logical index is less than the last present in the list + return false; + } + int adjustedIndex = adjustedIndex(logEntryIndex); + return (adjustedIndex >= 0); + } + + @Override + public boolean isInSnapshot(long logEntryIndex) { + return logEntryIndex <= snapshotIndex; + } + + @Override + public Object getSnapshot() { + return snapshot; + } + + @Override + public long getSnapshotIndex() { + return snapshotIndex; + } + + @Override + public long getSnapshotTerm() { + return snapshotTerm; + } + + @Override + public abstract void appendAndPersist(ReplicatedLogEntry replicatedLogEntry); + + @Override + public abstract void removeFromAndPersist(long index); +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ConfigParams.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ConfigParams.java new file mode 100644 index 0000000000..4c6434aec4 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ConfigParams.java @@ -0,0 +1,55 @@ +/* + * 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.cluster.raft; + +import scala.concurrent.duration.FiniteDuration; + +/** + * Configuration Parameter interface for configuring the Raft consensus system + *

+ * Any component using this implementation might want to provide an implementation of + * this interface to configure + * + * A default implementation will be used if none is provided. + * + * @author Kamal Rameshan + */ +public interface ConfigParams { + /** + * The minimum number of entries to be present in the in-memory Raft log + * for a snapshot to be taken + * + * @return long + */ + public long getSnapshotBatchCount(); + + /** + * The interval at which a heart beat message will be sent to the remote + * RaftActor + * + * @return FiniteDuration + */ + public FiniteDuration getHeartBeatInterval(); + + /** + * The interval in which a new election would get triggered if no leader is found + * + * Normally its set to atleast twice the heart beat interval + * + * @return FiniteDuration + */ + public FiniteDuration getElectionTimeOutInterval(); + + /** + * The maximum election time variance. The election is scheduled using both + * the Election Timeout and Variance + * + * @return int + */ + public int getElectionTimeVariance(); +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/DefaultConfigParamsImpl.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/DefaultConfigParamsImpl.java new file mode 100644 index 0000000000..c633337226 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/DefaultConfigParamsImpl.java @@ -0,0 +1,61 @@ +/* + * 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.cluster.raft; + +import scala.concurrent.duration.FiniteDuration; + +import java.util.concurrent.TimeUnit; + +/** + * Default implementation of the ConfigParams + * + * If no implementation is provided for ConfigParams, then this will be used. + */ +public class DefaultConfigParamsImpl implements ConfigParams { + + private static final int SNAPSHOT_BATCH_COUNT = 100000; + + /** + * The maximum election time variance + */ + private static final int ELECTION_TIME_MAX_VARIANCE = 100; + + + /** + * The interval at which a heart beat message will be sent to the remote + * RaftActor + *

+ * Since this is set to 100 milliseconds the Election timeout should be + * at least 200 milliseconds + */ + protected static final FiniteDuration HEART_BEAT_INTERVAL = + new FiniteDuration(100, TimeUnit.MILLISECONDS); + + + @Override + public long getSnapshotBatchCount() { + return SNAPSHOT_BATCH_COUNT; + } + + @Override + public FiniteDuration getHeartBeatInterval() { + return HEART_BEAT_INTERVAL; + } + + + @Override + public FiniteDuration getElectionTimeOutInterval() { + // returns 2 times the heart beat interval + return HEART_BEAT_INTERVAL.$times(2); + } + + @Override + public int getElectionTimeVariance() { + return ELECTION_TIME_MAX_VARIANCE; + } +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java index 15239795a8..70b85b4627 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java @@ -19,6 +19,10 @@ import akka.persistence.SaveSnapshotSuccess; import akka.persistence.SnapshotOffer; import akka.persistence.SnapshotSelectionCriteria; import akka.persistence.UntypedPersistentActor; +import org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot; +import org.opendaylight.controller.cluster.raft.base.messages.ApplyState; +import org.opendaylight.controller.cluster.raft.base.messages.Replicate; +import com.google.common.base.Optional; import org.opendaylight.controller.cluster.raft.behaviors.Candidate; import org.opendaylight.controller.cluster.raft.behaviors.Follower; import org.opendaylight.controller.cluster.raft.behaviors.Leader; @@ -26,14 +30,10 @@ import org.opendaylight.controller.cluster.raft.behaviors.RaftActorBehavior; import org.opendaylight.controller.cluster.raft.client.messages.AddRaftPeer; import org.opendaylight.controller.cluster.raft.client.messages.FindLeader; import org.opendaylight.controller.cluster.raft.client.messages.FindLeaderReply; -import org.opendaylight.controller.cluster.raft.internal.messages.ApplySnapshot; import org.opendaylight.controller.cluster.raft.client.messages.RemoveRaftPeer; -import org.opendaylight.controller.cluster.raft.internal.messages.ApplyState; -import org.opendaylight.controller.cluster.raft.internal.messages.Replicate; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; import java.io.Serializable; -import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -100,14 +100,22 @@ public abstract class RaftActor extends UntypedPersistentActor { public RaftActor(String id, Map peerAddresses) { + this(id, peerAddresses, Optional.absent()); + } + + public RaftActor(String id, Map peerAddresses, + Optional configParams) { + context = new RaftActorContextImpl(this.getSelf(), - this.getContext(), - id, new ElectionTermImpl(), - -1, -1, replicatedLog, peerAddresses, LOG); + this.getContext(), id, new ElectionTermImpl(), + -1, -1, replicatedLog, peerAddresses, + (configParams.isPresent() ? configParams.get(): new DefaultConfigParamsImpl()), + LOG); } @Override public void onReceiveRecover(Object message) { if (message instanceof SnapshotOffer) { + LOG.debug("SnapshotOffer called.."); SnapshotOffer offer = (SnapshotOffer) message; Snapshot snapshot = (Snapshot) offer.snapshot(); @@ -116,6 +124,13 @@ public abstract class RaftActor extends UntypedPersistentActor { // when we need to install it on a peer replicatedLog = new ReplicatedLogImpl(snapshot); + context.setReplicatedLog(replicatedLog); + + LOG.debug("Applied snapshot to replicatedLog. " + + "snapshotIndex={}, snapshotTerm={}, journal-size={}", + replicatedLog.snapshotIndex, replicatedLog.snapshotTerm, + replicatedLog.size()); + // Apply the snapshot to the actors state applySnapshot(snapshot.getState()); @@ -127,7 +142,11 @@ public abstract class RaftActor extends UntypedPersistentActor { context.getTermInformation().update(((UpdateElectionTerm) message).getCurrentTerm(), ((UpdateElectionTerm) message).getVotedFor()); } else if (message instanceof RecoveryCompleted) { LOG.debug( - "Last index in log : " + replicatedLog.lastIndex()); + "RecoveryCompleted - Switching actor to Follower - " + + "Last index in log:{}, snapshotIndex={}, snapshotTerm={}, " + + "journal-size={}", + replicatedLog.lastIndex(), replicatedLog.snapshotIndex, + replicatedLog.snapshotTerm, replicatedLog.size()); currentBehavior = switchBehavior(RaftState.Follower); } } @@ -191,6 +210,15 @@ public abstract class RaftActor extends UntypedPersistentActor { } } + public java.util.Set getPeers() { + return context.getPeerAddresses().keySet(); + } + + protected String getReplicatedLogState() { + return "snapshotIndex=" + context.getReplicatedLog().getSnapshotIndex() + + ", snapshotTerm=" + context.getReplicatedLog().getSnapshotTerm() + + ", im-mem journal size=" + context.getReplicatedLog().size(); + } /** @@ -250,6 +278,24 @@ public abstract class RaftActor extends UntypedPersistentActor { return currentBehavior.state(); } + /** + * setPeerAddress sets the address of a known peer at a later time. + *

+ * This is to account for situations where a we know that a peer + * exists but we do not know an address up-front. This may also be used in + * situations where a known peer starts off in a different location and we + * need to change it's address + *

+ * Note that if the peerId does not match the list of peers passed to + * this actor during construction an IllegalStateException will be thrown. + * + * @param peerId + * @param peerAddress + */ + protected void setPeerAddress(String peerId, String peerAddress){ + context.setPeerAddress(peerId, peerAddress); + } + /** @@ -325,85 +371,33 @@ public abstract class RaftActor extends UntypedPersistentActor { } private void trimPersistentData(long sequenceNumber) { - // Trim snapshots + // Trim akka snapshots // FIXME : Not sure how exactly the SnapshotSelectionCriteria is applied // For now guessing that it is ANDed. deleteSnapshots(new SnapshotSelectionCriteria( - sequenceNumber - 100000, 43200000)); + sequenceNumber - context.getConfigParams().getSnapshotBatchCount(), 43200000)); - // Trim journal + // Trim akka journal deleteMessages(sequenceNumber); } - private class ReplicatedLogImpl implements ReplicatedLog { - private final List journal; - private final Object snapshot; - private long snapshotIndex = -1; - private long snapshotTerm = -1; + private class ReplicatedLogImpl extends AbstractReplicatedLogImpl { public ReplicatedLogImpl(Snapshot snapshot) { - this.snapshot = snapshot.getState(); - this.snapshotIndex = snapshot.getLastAppliedIndex(); - this.snapshotTerm = snapshot.getLastAppliedTerm(); - - this.journal = new ArrayList<>(snapshot.getUnAppliedEntries()); + super(snapshot.getState(), + snapshot.getLastAppliedIndex(), snapshot.getLastAppliedTerm(), + snapshot.getUnAppliedEntries()); } public ReplicatedLogImpl() { - this.snapshot = null; - this.journal = new ArrayList<>(); - } - - @Override public ReplicatedLogEntry get(long index) { - int adjustedIndex = adjustedIndex(index); - - if (adjustedIndex < 0 || adjustedIndex >= journal.size()) { - return null; - } - - return journal.get(adjustedIndex); - } - - @Override public ReplicatedLogEntry last() { - if (journal.size() == 0) { - return null; - } - return get(journal.size() - 1); - } - - @Override public long lastIndex() { - if (journal.size() == 0) { - return -1; - } - - return last().getIndex(); - } - - @Override public long lastTerm() { - if (journal.size() == 0) { - return -1; - } - - return last().getTerm(); + super(); } + @Override public void removeFromAndPersist(long logEntryIndex) { + int adjustedIndex = adjustedIndex(logEntryIndex); - @Override public void removeFrom(long index) { - int adjustedIndex = adjustedIndex(index); - - if (adjustedIndex < 0 || adjustedIndex >= journal.size()) { - return; - } - - journal.subList(adjustedIndex , journal.size()).clear(); - } - - - @Override public void removeFromAndPersist(long index) { - int adjustedIndex = adjustedIndex(index); - - if (adjustedIndex < 0 || adjustedIndex >= journal.size()) { + if (adjustedIndex < 0) { return; } @@ -417,29 +411,6 @@ public abstract class RaftActor extends UntypedPersistentActor { //FIXME : Doing nothing for now } }); - - - } - - @Override public void append( - final ReplicatedLogEntry replicatedLogEntry) { - journal.add(replicatedLogEntry); - } - - @Override public List getFrom(long index) { - int adjustedIndex = adjustedIndex(index); - - List entries = new ArrayList<>(100); - if (adjustedIndex < 0 || adjustedIndex >= journal.size()) { - return entries; - } - - - for (int i = adjustedIndex; - i < journal.size(); i++) { - entries.add(journal.get(i)); - } - return entries; } @Override public void appendAndPersist( @@ -451,7 +422,7 @@ public abstract class RaftActor extends UntypedPersistentActor { final String identifier, final ReplicatedLogEntry replicatedLogEntry) { context.getLogger().debug( - "Append log entry and persist " + replicatedLogEntry); + "Append log entry and persist {} ", replicatedLogEntry); // FIXME : By adding the replicated log entry to the in-memory journal we are not truly ensuring durability of the logs journal.add(replicatedLogEntry); @@ -464,20 +435,42 @@ public abstract class RaftActor extends UntypedPersistentActor { new Procedure() { public void apply(ReplicatedLogEntry evt) throws Exception { // FIXME : Tentatively create a snapshot every hundred thousand entries. To be tuned. - if (size() > 100000) { - ReplicatedLogEntry lastAppliedEntry = - get(context.getLastApplied()); + if (journal.size() > context.getConfigParams().getSnapshotBatchCount()) { + LOG.info("Initiating Snapshot Capture.."); long lastAppliedIndex = -1; long lastAppliedTerm = -1; + + ReplicatedLogEntry lastAppliedEntry = get(context.getLastApplied()); if (lastAppliedEntry != null) { lastAppliedIndex = lastAppliedEntry.getIndex(); lastAppliedTerm = lastAppliedEntry.getTerm(); } - saveSnapshot(Snapshot.create(createSnapshot(), + LOG.debug("Snapshot Capture logSize: {}", journal.size()); + LOG.debug("Snapshot Capture lastApplied:{} ", context.getLastApplied()); + LOG.debug("Snapshot Capture lastAppliedIndex:{}", lastAppliedIndex); + LOG.debug("Snapshot Capture lastAppliedTerm:{}", lastAppliedTerm); + + // create a snapshot object from the state provided and save it + // when snapshot is saved async, SaveSnapshotSuccess is raised. + Snapshot sn = Snapshot.create(createSnapshot(), getFrom(context.getLastApplied() + 1), lastIndex(), lastTerm(), lastAppliedIndex, - lastAppliedTerm)); + lastAppliedTerm); + saveSnapshot(sn); + + LOG.info("Persisting of snapshot done:{}", sn.getLogMessage()); + + //be greedy and remove entries from in-mem journal which are in the snapshot + // and update snapshotIndex and snapshotTerm without waiting for the success, + // TODO: damage-recovery to be done on failure + journal.subList(0, (int) (lastAppliedIndex - snapshotIndex)).clear(); + snapshotIndex = lastAppliedIndex; + snapshotTerm = lastAppliedTerm; + + LOG.info("Removed in-memory snapshotted entries, " + + "adjusted snaphsotIndex:{}" + + "and term:{}", snapshotIndex, lastAppliedTerm); } // Send message for replication if (clientActor != null) { @@ -491,46 +484,8 @@ public abstract class RaftActor extends UntypedPersistentActor { ); } - @Override public long size() { - return journal.size() + snapshotIndex + 1; - } - - @Override public boolean isPresent(long index) { - int adjustedIndex = adjustedIndex(index); - - if (adjustedIndex < 0 || adjustedIndex >= journal.size()) { - return false; - } - return true; - } - - @Override public boolean isInSnapshot(long index) { - return index <= snapshotIndex; - } - - @Override public Object getSnapshot() { - return snapshot; - } - - @Override public long getSnapshotIndex() { - return snapshotIndex; - } - - @Override public long getSnapshotTerm() { - return snapshotTerm; - } - - private int adjustedIndex(long index) { - if(snapshotIndex < 0){ - return (int) index; - } - return (int) (index - snapshotIndex); - } } - - - private static class DeleteEntries implements Serializable { private final int fromIndex; @@ -591,6 +546,17 @@ public abstract class RaftActor extends UntypedPersistentActor { public long getLastAppliedTerm() { return lastAppliedTerm; } + + public String getLogMessage() { + StringBuilder sb = new StringBuilder(); + return sb.append("Snapshot={") + .append("lastTerm:" + this.getLastTerm() + ", ") + .append("LastAppliedIndex:" + this.getLastAppliedIndex() + ", ") + .append("LastAppliedTerm:" + this.getLastAppliedTerm() + ", ") + .append("UnAppliedEntries size:" + this.getUnAppliedEntries().size() + "}") + .toString(); + + } } private class ElectionTermImpl implements ElectionTerm { diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActorContext.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActorContext.java index 7150ec0e6e..0eb4b73779 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActorContext.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActorContext.java @@ -85,6 +85,12 @@ public interface RaftActorContext { */ void setLastApplied(long lastApplied); + /** + * + * @param replicatedLog + */ + public void setReplicatedLog(ReplicatedLog replicatedLog); + /** * @return A representation of the log */ @@ -96,21 +102,27 @@ public interface RaftActorContext { ActorSystem getActorSystem(); /** + * Get the logger to be used for logging messages * * @return */ LoggingAdapter getLogger(); /** - * Get a mapping of peer id's their addresses + * Get a mapping of peerId's to their addresses + * * @return + * */ Map getPeerAddresses(); /** + * Get the address of the peer as a String. This is the same format in + * which a consumer would provide the address * * @param peerId - * @return + * @return The address of the peer or null if the address has not yet been + * resolved */ String getPeerAddress(String peerId); @@ -126,4 +138,32 @@ public interface RaftActorContext { * @param name */ public void removePeer(String name); + + /** + * Given a peerId return the corresponding actor + *

+ * + * + * @param peerId + * @return The actorSelection corresponding to the peer or null if the + * address has not yet been resolved + */ + ActorSelection getPeerActorSelection(String peerId); + + /** + * Set Peer Address can be called at a later time to change the address of + * a known peer. + * + *

+ * Throws an IllegalStateException if the peer is unknown + * + * @param peerId + * @param peerAddress + */ + void setPeerAddress(String peerId, String peerAddress); + + /** + * @return ConfigParams + */ + public ConfigParams getConfigParams(); } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActorContextImpl.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActorContextImpl.java index a0f13280c2..25da37105c 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActorContextImpl.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActorContextImpl.java @@ -17,7 +17,9 @@ import akka.event.LoggingAdapter; import java.util.Map; -public class RaftActorContextImpl implements RaftActorContext{ +import static com.google.common.base.Preconditions.checkState; + +public class RaftActorContextImpl implements RaftActorContext { private final ActorRef actor; @@ -31,17 +33,20 @@ public class RaftActorContextImpl implements RaftActorContext{ private long lastApplied; - private final ReplicatedLog replicatedLog; + private ReplicatedLog replicatedLog; private final Map peerAddresses; private final LoggingAdapter LOG; + private final ConfigParams configParams; public RaftActorContextImpl(ActorRef actor, UntypedActorContext context, String id, ElectionTerm termInformation, long commitIndex, - long lastApplied, ReplicatedLog replicatedLog, Map peerAddresses, LoggingAdapter logger) { + long lastApplied, ReplicatedLog replicatedLog, + Map peerAddresses, ConfigParams configParams, + LoggingAdapter logger) { this.actor = actor; this.context = context; this.id = id; @@ -50,6 +55,7 @@ public class RaftActorContextImpl implements RaftActorContext{ this.lastApplied = lastApplied; this.replicatedLog = replicatedLog; this.peerAddresses = peerAddresses; + this.configParams = configParams; this.LOG = logger; } @@ -89,6 +95,10 @@ public class RaftActorContextImpl implements RaftActorContext{ this.lastApplied = lastApplied; } + @Override public void setReplicatedLog(ReplicatedLog replicatedLog) { + this.replicatedLog = replicatedLog; + } + @Override public ReplicatedLog getReplicatedLog() { return replicatedLog; } @@ -109,13 +119,30 @@ public class RaftActorContextImpl implements RaftActorContext{ return peerAddresses.get(peerId); } + @Override public ConfigParams getConfigParams() { + return configParams; + } + @Override public void addToPeers(String name, String address) { - LOG.debug("Kamal--> addToPeer for:"+name); peerAddresses.put(name, address); } @Override public void removePeer(String name) { - LOG.debug("Kamal--> removePeer for:"+name); peerAddresses.remove(name); } + + @Override public ActorSelection getPeerActorSelection(String peerId) { + String peerAddress = getPeerAddress(peerId); + if(peerAddress != null){ + return actorSelection(peerAddress); + } + return null; + } + + @Override public void setPeerAddress(String peerId, String peerAddress) { + LOG.info("Peer address for peer {} set to {}", peerId, peerAddress); + checkState(peerAddresses.containsKey(peerId), peerId + " is unknown"); + + peerAddresses.put(peerId, peerAddress); + } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/ApplySnapshot.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplySnapshot.java similarity index 89% rename from opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/ApplySnapshot.java rename to opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplySnapshot.java index f9c7c133ee..9739fb2f1b 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/ApplySnapshot.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplySnapshot.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.cluster.raft.internal.messages; +package org.opendaylight.controller.cluster.raft.base.messages; import java.io.Serializable; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/ApplyState.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplyState.java similarity index 94% rename from opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/ApplyState.java rename to opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplyState.java index f58d74829a..b904335be3 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/ApplyState.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ApplyState.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.cluster.raft.internal.messages; +package org.opendaylight.controller.cluster.raft.base.messages; import akka.actor.ActorRef; import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/CommitEntry.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/CommitEntry.java similarity index 86% rename from opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/CommitEntry.java rename to opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/CommitEntry.java index ba321c6c0e..07e376fca3 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/CommitEntry.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/CommitEntry.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.cluster.raft.internal.messages; +package org.opendaylight.controller.cluster.raft.base.messages; import java.io.Serializable; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/ElectionTimeout.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ElectionTimeout.java similarity index 85% rename from opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/ElectionTimeout.java rename to opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ElectionTimeout.java index 1c58f4bd31..a844849f15 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/ElectionTimeout.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/ElectionTimeout.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.cluster.raft.internal.messages; +package org.opendaylight.controller.cluster.raft.base.messages; import java.io.Serializable; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/PersistEntry.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/PersistEntry.java similarity index 87% rename from opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/PersistEntry.java rename to opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/PersistEntry.java index fe0dcc550b..6a62817e90 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/PersistEntry.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/PersistEntry.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.cluster.raft.internal.messages; +package org.opendaylight.controller.cluster.raft.base.messages; import java.io.Serializable; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/Replicate.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/Replicate.java similarity index 94% rename from opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/Replicate.java rename to opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/Replicate.java index e9cdcf0b86..9bc737a0e3 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/Replicate.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/Replicate.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.cluster.raft.internal.messages; +package org.opendaylight.controller.cluster.raft.base.messages; import akka.actor.ActorRef; import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/SaveSnapshot.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/SaveSnapshot.java similarity index 88% rename from opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/SaveSnapshot.java rename to opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/SaveSnapshot.java index ce0ad1f501..861f5ee715 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/SaveSnapshot.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/SaveSnapshot.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.cluster.raft.internal.messages; +package org.opendaylight.controller.cluster.raft.base.messages; import java.io.Serializable; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/SendHeartBeat.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/SendHeartBeat.java similarity index 88% rename from opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/SendHeartBeat.java rename to opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/SendHeartBeat.java index 1555766cdb..3c8c845f5d 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/SendHeartBeat.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/SendHeartBeat.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.cluster.raft.internal.messages; +package org.opendaylight.controller.cluster.raft.base.messages; import java.io.Serializable; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/SendInstallSnapshot.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/SendInstallSnapshot.java similarity index 85% rename from opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/SendInstallSnapshot.java rename to opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/SendInstallSnapshot.java index b85e58b4f8..6c3313f316 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/internal/messages/SendInstallSnapshot.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/SendInstallSnapshot.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.cluster.raft.internal.messages; +package org.opendaylight.controller.cluster.raft.base.messages; import java.io.Serializable; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehavior.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehavior.java index bc87370b15..0a553b40fd 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehavior.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehavior.java @@ -15,8 +15,8 @@ import org.opendaylight.controller.cluster.raft.RaftActorContext; import org.opendaylight.controller.cluster.raft.RaftState; import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; import org.opendaylight.controller.cluster.raft.SerializationUtils; -import org.opendaylight.controller.cluster.raft.internal.messages.ApplyState; -import org.opendaylight.controller.cluster.raft.internal.messages.ElectionTimeout; +import org.opendaylight.controller.cluster.raft.base.messages.ApplyState; +import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout; import org.opendaylight.controller.cluster.raft.messages.AppendEntries; import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply; import org.opendaylight.controller.cluster.raft.messages.RequestVote; @@ -43,27 +43,6 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior { */ protected final RaftActorContext context; - /** - * The maximum election time variance - */ - private static final int ELECTION_TIME_MAX_VARIANCE = 100; - - /** - * The interval at which a heart beat message will be sent to the remote - * RaftActor - *

- * Since this is set to 100 milliseconds the Election timeout should be - * at least 200 milliseconds - */ - protected static final FiniteDuration HEART_BEAT_INTERVAL = - new FiniteDuration(100, TimeUnit.MILLISECONDS); - - /** - * The interval in which a new election would get triggered if no leader is found - */ - private static final long ELECTION_TIME_INTERVAL = - HEART_BEAT_INTERVAL.toMillis() * 2; - /** * */ @@ -208,9 +187,9 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior { * @return */ protected FiniteDuration electionDuration() { - long variance = new Random().nextInt(ELECTION_TIME_MAX_VARIANCE); - return new FiniteDuration(ELECTION_TIME_INTERVAL + variance, - TimeUnit.MILLISECONDS); + long variance = new Random().nextInt(context.getConfigParams().getElectionTimeVariance()); + return context.getConfigParams().getElectionTimeOutInterval().$plus( + new FiniteDuration(variance, TimeUnit.MILLISECONDS)); } /** diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Candidate.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Candidate.java index ecd4901246..c125bd32b6 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Candidate.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Candidate.java @@ -12,16 +12,14 @@ import akka.actor.ActorRef; import akka.actor.ActorSelection; import org.opendaylight.controller.cluster.raft.RaftActorContext; import org.opendaylight.controller.cluster.raft.RaftState; -import org.opendaylight.controller.cluster.raft.internal.messages.ElectionTimeout; +import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout; import org.opendaylight.controller.cluster.raft.messages.AppendEntries; import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply; import org.opendaylight.controller.cluster.raft.messages.RaftRPC; import org.opendaylight.controller.cluster.raft.messages.RequestVote; import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.util.Set; /** * The behavior of a RaftActor when it is in the CandidateState @@ -43,24 +41,20 @@ import java.util.Map; */ public class Candidate extends AbstractRaftActorBehavior { - private final Map peerToActor = new HashMap<>(); - private int voteCount; private final int votesRequired; + private final Set peers; + public Candidate(RaftActorContext context) { super(context); - Collection peerPaths = context.getPeerAddresses().values(); + peers = context.getPeerAddresses().keySet(); - for (String peerPath : peerPaths) { - peerToActor.put(peerPath, - context.actorSelection(peerPath)); - } + context.getLogger().debug("Election:Candidate has following peers:"+ peers); - context.getLogger().debug("Election:Candidate has following peers:"+peerToActor.keySet()); - if(peerPaths.size() > 0) { + if(peers.size() > 0) { // Votes are required from a majority of the peers including self. // The votesRequired field therefore stores a calculated value // of the number of votes required for this candidate to win an @@ -73,7 +67,7 @@ public class Candidate extends AbstractRaftActorBehavior { // 0 peers = 1 votesRequired (0 + 1) / 2 + 1 = 1 // 2 peers = 2 votesRequired (2 + 1) / 2 + 1 = 2 // 4 peers = 3 votesRequired (4 + 1) / 2 + 1 = 3 - int noOfPeers = peerPaths.size(); + int noOfPeers = peers.size(); int self = 1; votesRequired = (noOfPeers + self) / 2 + 1; } else { @@ -87,6 +81,8 @@ public class Candidate extends AbstractRaftActorBehavior { @Override protected RaftState handleAppendEntries(ActorRef sender, AppendEntries appendEntries) { + context.getLogger().info("Candidate: Received {}", appendEntries.toString()); + return state(); } @@ -115,10 +111,16 @@ public class Candidate extends AbstractRaftActorBehavior { } @Override - public RaftState handleMessage(ActorRef sender, Object message) { + public RaftState handleMessage(ActorRef sender, Object originalMessage) { + + Object message = fromSerializableMessage(originalMessage); if (message instanceof RaftRPC) { + RaftRPC rpc = (RaftRPC) message; + + context.getLogger().debug("RaftRPC message received {} my term is {}", rpc.toString(), context.getTermInformation().getCurrentTerm()); + // If RPC request or response contains term T > currentTerm: // set currentTerm = T, convert to follower (§5.1) // This applies to all RPC messages and responses @@ -141,6 +143,7 @@ public class Candidate extends AbstractRaftActorBehavior { scheduleElection(electionDuration()); return state(); } + return super.handleMessage(sender, message); } @@ -153,21 +156,25 @@ public class Candidate extends AbstractRaftActorBehavior { // Increment the election term and vote for self long currentTerm = context.getTermInformation().getCurrentTerm(); - context.getTermInformation().updateAndPersist(currentTerm + 1, context.getId()); + context.getTermInformation().updateAndPersist(currentTerm + 1, + context.getId()); - context.getLogger().debug("Starting new term " + (currentTerm+1)); + context.getLogger().debug("Starting new term " + (currentTerm + 1)); // Request for a vote // TODO: Retry request for vote if replies do not arrive in a reasonable // amount of time TBD - for (ActorSelection peerActor : peerToActor.values()) { - peerActor.tell(new RequestVote( - context.getTermInformation().getCurrentTerm(), - context.getId(), - context.getReplicatedLog().lastIndex(), - context.getReplicatedLog().lastTerm()), - context.getActor() - ); + for (String peerId : peers) { + ActorSelection peerActor = context.getPeerActorSelection(peerId); + if(peerActor != null) { + peerActor.tell(new RequestVote( + context.getTermInformation().getCurrentTerm(), + context.getId(), + context.getReplicatedLog().lastIndex(), + context.getReplicatedLog().lastTerm()), + context.getActor() + ); + } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Follower.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Follower.java index 68b444b2ba..c8cd41dfa1 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Follower.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Follower.java @@ -12,8 +12,8 @@ import akka.actor.ActorRef; import org.opendaylight.controller.cluster.raft.RaftActorContext; import org.opendaylight.controller.cluster.raft.RaftState; import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; -import org.opendaylight.controller.cluster.raft.internal.messages.ApplySnapshot; -import org.opendaylight.controller.cluster.raft.internal.messages.ElectionTimeout; +import org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot; +import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout; import org.opendaylight.controller.cluster.raft.messages.AppendEntries; import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply; import org.opendaylight.controller.cluster.raft.messages.InstallSnapshot; @@ -40,6 +40,11 @@ public class Follower extends AbstractRaftActorBehavior { @Override protected RaftState handleAppendEntries(ActorRef sender, AppendEntries appendEntries) { + if(appendEntries.getEntries() != null && appendEntries.getEntries().size() > 0) { + context.getLogger() + .info("Follower: Received {}", appendEntries.toString()); + } + // TODO : Refactor this method into a bunch of smaller methods // to make it easier to read. Before refactoring ensure tests // cover the code properly @@ -121,15 +126,10 @@ public class Follower extends AbstractRaftActorBehavior { int addEntriesFrom = 0; if (context.getReplicatedLog().size() > 0) { - // Find the entry up until which the one that is not in the - // follower's log - for (int i = 0; - i < appendEntries.getEntries() - .size(); i++, addEntriesFrom++) { - ReplicatedLogEntry matchEntry = - appendEntries.getEntries().get(i); - ReplicatedLogEntry newEntry = context.getReplicatedLog() - .get(matchEntry.getIndex()); + // Find the entry up until which the one that is not in the follower's log + for (int i = 0;i < appendEntries.getEntries().size(); i++, addEntriesFrom++) { + ReplicatedLogEntry matchEntry = appendEntries.getEntries().get(i); + ReplicatedLogEntry newEntry = context.getReplicatedLog().get(matchEntry.getIndex()); if (newEntry == null) { //newEntry not found in the log @@ -162,8 +162,9 @@ public class Follower extends AbstractRaftActorBehavior { for (int i = addEntriesFrom; i < appendEntries.getEntries().size(); i++) { - context.getLogger().debug( - "Append entry to log " + appendEntries.getEntries().get(i).getData() + context.getLogger().info( + "Append entry to log " + appendEntries.getEntries().get( + i).getData() .toString() ); context.getReplicatedLog() diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Leader.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Leader.java index 26beed2f7a..2a44e8b7a5 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Leader.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Leader.java @@ -19,10 +19,10 @@ import org.opendaylight.controller.cluster.raft.FollowerLogInformationImpl; import org.opendaylight.controller.cluster.raft.RaftActorContext; import org.opendaylight.controller.cluster.raft.RaftState; import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; -import org.opendaylight.controller.cluster.raft.internal.messages.ApplyState; -import org.opendaylight.controller.cluster.raft.internal.messages.Replicate; -import org.opendaylight.controller.cluster.raft.internal.messages.SendHeartBeat; -import org.opendaylight.controller.cluster.raft.internal.messages.SendInstallSnapshot; +import org.opendaylight.controller.cluster.raft.base.messages.ApplyState; +import org.opendaylight.controller.cluster.raft.base.messages.Replicate; +import org.opendaylight.controller.cluster.raft.base.messages.SendHeartBeat; +import org.opendaylight.controller.cluster.raft.base.messages.SendInstallSnapshot; import org.opendaylight.controller.cluster.raft.messages.AppendEntries; import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply; import org.opendaylight.controller.cluster.raft.messages.InstallSnapshot; @@ -36,6 +36,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; @@ -67,7 +68,7 @@ public class Leader extends AbstractRaftActorBehavior { private final Map followerToLog = new HashMap(); - private final Map followerToActor = new HashMap<>(); + private final Set followers; private Cancellable heartbeatSchedule = null; private Cancellable appendEntriesSchedule = null; @@ -84,22 +85,21 @@ public class Leader extends AbstractRaftActorBehavior { context.setCommitIndex(lastIndex()); } - for (String followerId : context.getPeerAddresses().keySet()) { + followers = context.getPeerAddresses().keySet(); + + for (String followerId : followers) { FollowerLogInformation followerLogInformation = new FollowerLogInformationImpl(followerId, new AtomicLong(lastIndex()), new AtomicLong(-1)); - followerToActor.put(followerId, - context.actorSelection(context.getPeerAddress(followerId))); - followerToLog.put(followerId, followerLogInformation); } - context.getLogger().debug("Election:Leader has following peers:"+followerToActor.keySet()); + context.getLogger().debug("Election:Leader has following peers:"+ followers); - if (followerToActor.size() > 0) { - minReplicationCount = (followerToActor.size() + 1) / 2 + 1; + if (followers.size() > 0) { + minReplicationCount = (followers.size() + 1) / 2 + 1; } else { minReplicationCount = 0; } @@ -112,8 +112,8 @@ public class Leader extends AbstractRaftActorBehavior { scheduleHeartBeat(new FiniteDuration(0, TimeUnit.SECONDS)); scheduleInstallSnapshotCheck( - new FiniteDuration(HEART_BEAT_INTERVAL.length() * 1000, - HEART_BEAT_INTERVAL.unit()) + new FiniteDuration(context.getConfigParams().getHeartBeatInterval().length() * 1000, + context.getConfigParams().getHeartBeatInterval().unit()) ); } @@ -121,16 +121,29 @@ public class Leader extends AbstractRaftActorBehavior { @Override protected RaftState handleAppendEntries(ActorRef sender, AppendEntries appendEntries) { + context.getLogger().info("Leader: Received {}", appendEntries.toString()); + return state(); } @Override protected RaftState handleAppendEntriesReply(ActorRef sender, AppendEntriesReply appendEntriesReply) { + if(! appendEntriesReply.isSuccess()) { + context.getLogger() + .info("Leader: Received {}", appendEntriesReply.toString()); + } + // Update the FollowerLogInformation String followerId = appendEntriesReply.getFollowerId(); FollowerLogInformation followerLogInformation = followerToLog.get(followerId); + + if(followerLogInformation == null){ + context.getLogger().error("Unknown follower {}", followerId); + return state(); + } + if (appendEntriesReply.isSuccess()) { followerLogInformation .setMatchIndex(appendEntriesReply.getLogLastIndex()); @@ -228,7 +241,7 @@ public class Leader extends AbstractRaftActorBehavior { (InstallSnapshotReply) message); } } finally { - scheduleHeartBeat(HEART_BEAT_INTERVAL); + scheduleHeartBeat(context.getConfigParams().getHeartBeatInterval()); } return super.handleMessage(sender, message); @@ -251,7 +264,7 @@ public class Leader extends AbstractRaftActorBehavior { context.getLogger().debug("Replicate message " + logIndex); - if (followerToActor.size() == 0) { + if (followers.size() == 0) { context.setCommitIndex( replicate.getReplicatedLogEntry().getIndex()); @@ -277,32 +290,37 @@ public class Leader extends AbstractRaftActorBehavior { private void sendAppendEntries() { // Send an AppendEntries to all followers - for (String followerId : followerToActor.keySet()) { + for (String followerId : followers) { ActorSelection followerActor = - followerToActor.get(followerId); + context.getPeerActorSelection(followerId); - FollowerLogInformation followerLogInformation = - followerToLog.get(followerId); + if (followerActor != null) { + FollowerLogInformation followerLogInformation = + followerToLog.get(followerId); - long nextIndex = followerLogInformation.getNextIndex().get(); + long nextIndex = followerLogInformation.getNextIndex().get(); - List entries = Collections.emptyList(); + List entries = Collections.emptyList(); - if(context.getReplicatedLog().isPresent(nextIndex)){ - // TODO: Instead of sending all entries from nextIndex - // only send a fixed number of entries to each follower - // This is to avoid the situation where there are a lot of - // entries to install for a fresh follower or to a follower - // that has fallen too far behind with the log but yet is not - // eligible to receive a snapshot - entries = - context.getReplicatedLog().getFrom(nextIndex); - } + if (context.getReplicatedLog().isPresent(nextIndex)) { + // TODO: Instead of sending all entries from nextIndex + // only send a fixed number of entries to each follower + // This is to avoid the situation where there are a lot of + // entries to install for a fresh follower or to a follower + // that has fallen too far behind with the log but yet is not + // eligible to receive a snapshot + entries = + context.getReplicatedLog().getFrom(nextIndex); + } - followerActor.tell( - new AppendEntries(currentTerm(), context.getId(), prevLogIndex(nextIndex), - prevLogTerm(nextIndex), entries, context.getCommitIndex()).toSerializable(), - actor()); + followerActor.tell( + new AppendEntries(currentTerm(), context.getId(), + prevLogIndex(nextIndex), + prevLogTerm(nextIndex), entries, + context.getCommitIndex()).toSerializable(), + actor() + ); + } } } @@ -312,30 +330,33 @@ public class Leader extends AbstractRaftActorBehavior { * snapshots at every heartbeat. */ private void installSnapshotIfNeeded(){ - for (String followerId : followerToActor.keySet()) { + for (String followerId : followers) { ActorSelection followerActor = - followerToActor.get(followerId); - - FollowerLogInformation followerLogInformation = - followerToLog.get(followerId); - - long nextIndex = followerLogInformation.getNextIndex().get(); - - if(!context.getReplicatedLog().isPresent(nextIndex) && context.getReplicatedLog().isInSnapshot(nextIndex)){ - followerActor.tell( - new InstallSnapshot(currentTerm(), context.getId(), - context.getReplicatedLog().getSnapshotIndex(), - context.getReplicatedLog().getSnapshotTerm(), - context.getReplicatedLog().getSnapshot() - ), - actor() - ); + context.getPeerActorSelection(followerId); + + if(followerActor != null) { + FollowerLogInformation followerLogInformation = + followerToLog.get(followerId); + + long nextIndex = followerLogInformation.getNextIndex().get(); + + if (!context.getReplicatedLog().isPresent(nextIndex) && context + .getReplicatedLog().isInSnapshot(nextIndex)) { + followerActor.tell( + new InstallSnapshot(currentTerm(), context.getId(), + context.getReplicatedLog().getSnapshotIndex(), + context.getReplicatedLog().getSnapshotTerm(), + context.getReplicatedLog().getSnapshot() + ), + actor() + ); + } } } } private RaftState sendHeartBeat() { - if (followerToActor.size() > 0) { + if (followers.size() > 0) { sendAppendEntries(); } return state(); @@ -354,7 +375,7 @@ public class Leader extends AbstractRaftActorBehavior { } private void scheduleHeartBeat(FiniteDuration interval) { - if(followerToActor.keySet().size() == 0){ + if(followers.size() == 0){ // Optimization - do not bother scheduling a heartbeat as there are // no followers return; @@ -376,7 +397,7 @@ public class Leader extends AbstractRaftActorBehavior { private void scheduleInstallSnapshotCheck(FiniteDuration interval) { - if(followerToActor.keySet().size() == 0){ + if(followers.size() == 0){ // Optimization - do not bother scheduling a heartbeat as there are // no followers return; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntries.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntries.java index 45ac9a9ebf..94366efd5e 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntries.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntries.java @@ -14,7 +14,6 @@ import org.opendaylight.controller.cluster.raft.ReplicatedLogImplEntry; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; import org.opendaylight.controller.cluster.raft.protobuff.messages.AppendEntriesMessages; -import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -24,7 +23,7 @@ import java.util.Map; * Invoked by leader to replicate log entries (§5.3); also used as * heartbeat (§5.2). */ -public class AppendEntries extends AbstractRaftRPC implements Serializable { +public class AppendEntries extends AbstractRaftRPC { public static final Class SERIALIZABLE_CLASS = AppendEntriesMessages.AppendEntries.class; @@ -77,13 +76,16 @@ public class AppendEntries extends AbstractRaftRPC implements Serializable { } @Override public String toString() { - return "AppendEntries{" + - "leaderId='" + leaderId + '\'' + - ", prevLogIndex=" + prevLogIndex + - ", prevLogTerm=" + prevLogTerm + - ", entries=" + entries + - ", leaderCommit=" + leaderCommit + - '}'; + final StringBuilder sb = + new StringBuilder("AppendEntries{"); + sb.append("term=").append(getTerm()); + sb.append("leaderId='").append(leaderId).append('\''); + sb.append(", prevLogIndex=").append(prevLogIndex); + sb.append(", prevLogTerm=").append(prevLogTerm); + sb.append(", entries=").append(entries); + sb.append(", leaderCommit=").append(leaderCommit); + sb.append('}'); + return sb.toString(); } public Object toSerializable(){ diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntriesReply.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntriesReply.java index 7e6628abe1..b923baa716 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntriesReply.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntriesReply.java @@ -8,12 +8,10 @@ package org.opendaylight.controller.cluster.raft.messages; -import java.io.Serializable; - /** * Reply for the AppendEntriesRpc message */ -public class AppendEntriesReply extends AbstractRaftRPC implements Serializable { +public class AppendEntriesReply extends AbstractRaftRPC { // true if follower contained entry matching // prevLogIndex and prevLogTerm @@ -58,4 +56,16 @@ public class AppendEntriesReply extends AbstractRaftRPC implements Serializable public String getFollowerId() { return followerId; } + + @Override public String toString() { + final StringBuilder sb = + new StringBuilder("AppendEntriesReply{"); + sb.append("term=").append(term); + sb.append(", success=").append(success); + sb.append(", logLastIndex=").append(logLastIndex); + sb.append(", logLastTerm=").append(logLastTerm); + sb.append(", followerId='").append(followerId).append('\''); + sb.append('}'); + return sb.toString(); + } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java index 5053560b41..888854fa71 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java @@ -8,9 +8,7 @@ package org.opendaylight.controller.cluster.raft.messages; -import java.io.Serializable; - -public class InstallSnapshot extends AbstractRaftRPC implements Serializable { +public class InstallSnapshot extends AbstractRaftRPC { private final String leaderId; private final long lastIncludedIndex; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshotReply.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshotReply.java index 02a3252776..85b89b70ae 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshotReply.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshotReply.java @@ -8,9 +8,7 @@ package org.opendaylight.controller.cluster.raft.messages; -import java.io.Serializable; - -public class InstallSnapshotReply extends AbstractRaftRPC implements Serializable { +public class InstallSnapshotReply extends AbstractRaftRPC { // The followerId - this will be used to figure out which follower is // responding diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RaftRPC.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RaftRPC.java index a770e54f58..10d99988d8 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RaftRPC.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RaftRPC.java @@ -8,6 +8,8 @@ package org.opendaylight.controller.cluster.raft.messages; -public interface RaftRPC { +import java.io.Serializable; + +public interface RaftRPC extends Serializable { public long getTerm(); } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RequestVote.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RequestVote.java index 310968de95..6ef2a06285 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RequestVote.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RequestVote.java @@ -8,12 +8,10 @@ package org.opendaylight.controller.cluster.raft.messages; -import java.io.Serializable; - /** * Invoked by candidates to gather votes (§5.2). */ -public class RequestVote extends AbstractRaftRPC implements Serializable{ +public class RequestVote extends AbstractRaftRPC { // candidate requesting vote private String candidateId; @@ -63,4 +61,15 @@ public class RequestVote extends AbstractRaftRPC implements Serializable{ public void setLastLogTerm(long lastLogTerm) { this.lastLogTerm = lastLogTerm; } + + @Override public String toString() { + final StringBuilder sb = + new StringBuilder("RequestVote{"); + sb.append("term='").append(getTerm()).append('\''); + sb.append("candidateId='").append(candidateId).append('\''); + sb.append(", lastLogIndex=").append(lastLogIndex); + sb.append(", lastLogTerm=").append(lastLogTerm); + sb.append('}'); + return sb.toString(); + } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RequestVoteReply.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RequestVoteReply.java index a658ab7d81..df80b4e5e2 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RequestVoteReply.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RequestVoteReply.java @@ -8,9 +8,7 @@ package org.opendaylight.controller.cluster.raft.messages; -import java.io.Serializable; - -public class RequestVoteReply extends AbstractRaftRPC implements Serializable { +public class RequestVoteReply extends AbstractRaftRPC { // true means candidate received vot private final boolean voteGranted; diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImplTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImplTest.java new file mode 100644 index 0000000000..ae8e525233 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImplTest.java @@ -0,0 +1,153 @@ +/* + * 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.cluster.raft; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload; +import static org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockReplicatedLogEntry; +/** +* +*/ +public class AbstractReplicatedLogImplTest { + + private MockAbstractReplicatedLogImpl replicatedLogImpl; + + @Before + public void setUp() { + replicatedLogImpl = new MockAbstractReplicatedLogImpl(); + } + + @After + public void tearDown() { + replicatedLogImpl.journal.clear(); + replicatedLogImpl.setSnapshotIndex(-1); + replicatedLogImpl.setSnapshotTerm(-1); + replicatedLogImpl = null; + } + + @Test + public void testIndexOperations() { + // create a set of initial entries in the in-memory log + replicatedLogImpl.append(new MockReplicatedLogEntry(1, 0, new MockPayload("A"))); + replicatedLogImpl.append(new MockReplicatedLogEntry(1, 1, new MockPayload("B"))); + replicatedLogImpl.append(new MockReplicatedLogEntry(1, 2, new MockPayload("C"))); + replicatedLogImpl.append(new MockReplicatedLogEntry(2, 3, new MockPayload("D"))); + + // check if the values returned are correct, with snapshotIndex = -1 + assertEquals("B", replicatedLogImpl.get(1).getData().toString()); + assertEquals("D", replicatedLogImpl.last().getData().toString()); + assertEquals(3, replicatedLogImpl.lastIndex()); + assertEquals(2, replicatedLogImpl.lastTerm()); + assertEquals(2, replicatedLogImpl.getFrom(2).size()); + assertEquals(4, replicatedLogImpl.size()); + assertTrue(replicatedLogImpl.isPresent(2)); + assertFalse(replicatedLogImpl.isPresent(4)); + assertFalse(replicatedLogImpl.isInSnapshot(2)); + + // now create a snapshot of 3 entries, with 1 unapplied entry left in the log + // It removes the entries which have made it to snapshot + // and updates the snapshot index and term + Map state = takeSnapshot(3); + + // check the values after the snapshot. + // each index value passed in the test is the logical index (log entry index) + // which gets mapped to the list's physical index + assertEquals("D", replicatedLogImpl.get(3).getData().toString()); + assertEquals("D", replicatedLogImpl.last().getData().toString()); + assertNull(replicatedLogImpl.get(1)); + assertEquals(3, replicatedLogImpl.lastIndex()); + assertEquals(2, replicatedLogImpl.lastTerm()); + assertEquals(0, replicatedLogImpl.getFrom(2).size()); + assertEquals(1, replicatedLogImpl.size()); + assertFalse(replicatedLogImpl.isPresent(2)); + assertTrue(replicatedLogImpl.isPresent(3)); + assertFalse(replicatedLogImpl.isPresent(4)); + assertTrue(replicatedLogImpl.isInSnapshot(2)); + + // append few more entries + replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("E"))); + replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("F"))); + replicatedLogImpl.append(new MockReplicatedLogEntry(3, 6, new MockPayload("G"))); + replicatedLogImpl.append(new MockReplicatedLogEntry(3, 7, new MockPayload("H"))); + + // check their values as well + assertEquals(5, replicatedLogImpl.size()); + assertEquals("D", replicatedLogImpl.get(3).getData().toString()); + assertEquals("E", replicatedLogImpl.get(4).getData().toString()); + assertEquals("H", replicatedLogImpl.last().getData().toString()); + assertEquals(3, replicatedLogImpl.lastTerm()); + assertEquals(7, replicatedLogImpl.lastIndex()); + assertTrue(replicatedLogImpl.isPresent(7)); + assertFalse(replicatedLogImpl.isInSnapshot(7)); + assertEquals(1, replicatedLogImpl.getFrom(7).size()); + assertEquals(2, replicatedLogImpl.getFrom(6).size()); + + // take a second snapshot with 5 entries with 0 unapplied entries left in the log + state = takeSnapshot(5); + + assertEquals(0, replicatedLogImpl.size()); + assertNull(replicatedLogImpl.last()); + assertNull(replicatedLogImpl.get(7)); + assertNull(replicatedLogImpl.get(1)); + assertFalse(replicatedLogImpl.isPresent(7)); + assertTrue(replicatedLogImpl.isInSnapshot(7)); + assertEquals(0, replicatedLogImpl.getFrom(7).size()); + assertEquals(0, replicatedLogImpl.getFrom(6).size()); + + } + + // create a snapshot for test + public Map takeSnapshot(int numEntries) { + Map map = new HashMap(numEntries); + List entries = replicatedLogImpl.getEntriesTill(numEntries); + for (ReplicatedLogEntry entry : entries) { + map.put(entry.getIndex(), entry.getData().toString()); + } + + int term = (int) replicatedLogImpl.lastTerm(); + int lastIndex = (int) entries.get(entries.size() - 1).getIndex(); + entries.clear(); + replicatedLogImpl.setSnapshotTerm(term); + replicatedLogImpl.setSnapshotIndex(lastIndex); + + return map; + + } + class MockAbstractReplicatedLogImpl extends AbstractReplicatedLogImpl { + @Override + public void appendAndPersist(ReplicatedLogEntry replicatedLogEntry) { + } + + @Override + public void removeFromAndPersist(long index) { + } + + public void setSnapshotIndex(long snapshotIndex) { + this.snapshotIndex = snapshotIndex; + } + + public void setSnapshotTerm(long snapshotTerm) { + this.snapshotTerm = snapshotTerm; + } + + public List getEntriesTill(int index) { + return journal.subList(0, index); + } + } +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/MockRaftActorContext.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/MockRaftActorContext.java index 406c164cdb..aa50fa7442 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/MockRaftActorContext.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/MockRaftActorContext.java @@ -18,6 +18,7 @@ import com.google.protobuf.GeneratedMessage; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; import org.opendaylight.controller.cluster.raft.protobuff.messages.AppendEntriesMessages; import org.opendaylight.controller.cluster.raft.protobuff.messages.MockPayloadMessages; +import com.google.common.base.Preconditions; import java.io.Serializable; import java.util.ArrayList; @@ -159,11 +160,27 @@ public class MockRaftActorContext implements RaftActorContext { peerAddresses.remove(name); } + @Override public ActorSelection getPeerActorSelection(String peerId) { + String peerAddress = getPeerAddress(peerId); + if(peerAddress != null){ + return actorSelection(peerAddress); + } + return null; + } + + @Override public void setPeerAddress(String peerId, String peerAddress) { + Preconditions.checkState(peerAddresses.containsKey(peerId)); + peerAddresses.put(peerId, peerAddress); + } + public void setPeerAddresses(Map peerAddresses) { this.peerAddresses = peerAddresses; } - + @Override + public ConfigParams getConfigParams() { + return new DefaultConfigParamsImpl(); + } public static class SimpleReplicatedLog implements ReplicatedLog { private final List log = new ArrayList<>(); diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/CandidateTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/CandidateTest.java index 8bcee58afe..c763683705 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/CandidateTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/CandidateTest.java @@ -9,7 +9,7 @@ import org.junit.Test; import org.opendaylight.controller.cluster.raft.MockRaftActorContext; import org.opendaylight.controller.cluster.raft.RaftActorContext; import org.opendaylight.controller.cluster.raft.RaftState; -import org.opendaylight.controller.cluster.raft.internal.messages.ElectionTimeout; +import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout; import org.opendaylight.controller.cluster.raft.messages.AppendEntries; import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply; import org.opendaylight.controller.cluster.raft.messages.RequestVote; diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java index 126e7d10c2..c015d950c4 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java @@ -9,7 +9,7 @@ import org.opendaylight.controller.cluster.raft.MockRaftActorContext; import org.opendaylight.controller.cluster.raft.RaftActorContext; import org.opendaylight.controller.cluster.raft.RaftState; import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; -import org.opendaylight.controller.cluster.raft.internal.messages.ElectionTimeout; +import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout; import org.opendaylight.controller.cluster.raft.messages.AppendEntries; import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply; import org.opendaylight.controller.cluster.raft.messages.RequestVote; diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java index bc9d4a2767..d33b33925b 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java @@ -8,9 +8,9 @@ import org.junit.Test; import org.opendaylight.controller.cluster.raft.MockRaftActorContext; import org.opendaylight.controller.cluster.raft.RaftActorContext; import org.opendaylight.controller.cluster.raft.RaftState; -import org.opendaylight.controller.cluster.raft.internal.messages.ApplyState; -import org.opendaylight.controller.cluster.raft.internal.messages.Replicate; -import org.opendaylight.controller.cluster.raft.internal.messages.SendHeartBeat; +import org.opendaylight.controller.cluster.raft.base.messages.ApplyState; +import org.opendaylight.controller.cluster.raft.base.messages.Replicate; +import org.opendaylight.controller.cluster.raft.base.messages.SendHeartBeat; import org.opendaylight.controller.cluster.raft.messages.AppendEntries; import org.opendaylight.controller.cluster.raft.utils.DoNothingActor; diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java index 823a4d9f32..b0c2d742e2 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java @@ -23,7 +23,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; 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; @@ -74,7 +74,7 @@ public final class RuntimeMappingModule extends } final RuntimeGeneratedMappingServiceImpl service = new RuntimeGeneratedMappingServiceImpl(SingletonHolder.CLASS_POOL); - bundleContext.registerService(SchemaServiceListener.class, service, new Hashtable()); + bundleContext.registerService(SchemaContextListener.class, service, new Hashtable()); return service; } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java index 15e4a466cf..2d81b6022d 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java @@ -41,7 +41,6 @@ import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMapping 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; @@ -58,14 +57,14 @@ public abstract class AbstractForwardedDataBroker implements Delegator schemaListenerRegistration; + private final ListenerRegistration 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() { diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/MountPointManagerImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/MountPointManagerImpl.java index df09f78620..05651bfabe 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/MountPointManagerImpl.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/MountPointManagerImpl.java @@ -9,6 +9,7 @@ package org.opendaylight.controller.sal.binding.impl; 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; @@ -20,6 +21,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; public class MountPointManagerImpl implements MountProviderService { @@ -82,7 +84,7 @@ 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); diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1333DataChangeListenerTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1333DataChangeListenerTest.java new file mode 100644 index 0000000000..60d56db581 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1333DataChangeListenerTest.java @@ -0,0 +1,145 @@ +/* + * 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_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, 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, 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, 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, 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); + } + +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java index 79aa6b634b..e0f6f3546f 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java @@ -47,13 +47,13 @@ public class DataBrokerTestCustomizer { 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; } diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java index e82c9d385d..deb4a8aeca 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java @@ -144,8 +144,8 @@ public class BindingTestContext implements AutoCloseable { biCompatibleBroker = new BackwardsCompatibleDataBroker(newDOMDataBroker,mockSchemaService); - mockSchemaService.registerSchemaServiceListener(configStore); - mockSchemaService.registerSchemaServiceListener(operStore); + mockSchemaService.registerSchemaContextListener(configStore); + mockSchemaService.registerSchemaContextListener(operStore); biDataLegacyBroker = biCompatibleBroker; } @@ -246,7 +246,7 @@ public class BindingTestContext implements AutoCloseable { 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 moduleInfos) { diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java index c8acbcd994..63a4ffb23a 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java @@ -13,14 +13,14 @@ import org.opendaylight.yangtools.concepts.ListenerRegistration; 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 listeners = ListenerRegistry.create(); + ListenerRegistry listeners = ListenerRegistry.create(); @Override public void addModule(final Module module) { @@ -38,8 +38,8 @@ public final class MockSchemaService implements SchemaService, SchemaContextProv } @Override - public ListenerRegistration registerSchemaServiceListener( - final SchemaServiceListener listener) { + public ListenerRegistration registerSchemaContextListener( + final SchemaContextListener listener) { return listeners.register(listener); } @@ -55,8 +55,8 @@ public final class MockSchemaService implements SchemaService, SchemaContextProv public synchronized void changeSchema(final SchemaContext newContext) { schemaContext = newContext; - for (ListenerRegistration listener : listeners) { + for (ListenerRegistration listener : listeners) { listener.getInstance().onGlobalContextUpdated(schemaContext); } } -} \ No newline at end of file +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/pom.xml b/opendaylight/md-sal/sal-distributed-datastore/pom.xml index 16b97b7855..94c895dab3 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/pom.xml +++ b/opendaylight/md-sal/sal-distributed-datastore/pom.xml @@ -124,6 +124,12 @@ 1.1-SNAPSHOT + + org.opendaylight.controller + sal-akka-raft + 1.1-SNAPSHOT + + junit @@ -160,6 +166,7 @@ !*snappy;!org.jboss.*;* sal-protocolbuffer-encoding; + sal-akka-raft; !sal*; !*config-api*; !*testkit*; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/CompositeModificationPayload.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/CompositeModificationPayload.java new file mode 100644 index 0000000000..955e4bbf22 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/CompositeModificationPayload.java @@ -0,0 +1,53 @@ +/* + * 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.cluster.datastore; + +import com.google.common.base.Preconditions; +import com.google.protobuf.GeneratedMessage; +import org.opendaylight.controller.cluster.example.protobuff.messages.KeyValueMessages; +import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; +import org.opendaylight.controller.cluster.raft.protobuff.messages.AppendEntriesMessages; +import org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +public class CompositeModificationPayload extends Payload implements + Serializable { + + private final PersistentMessages.CompositeModification modification; + + public CompositeModificationPayload(){ + modification = null; + } + public CompositeModificationPayload(Object modification){ + this.modification = (PersistentMessages.CompositeModification) modification; + } + + @Override public Map encode() { + Preconditions.checkState(modification!=null); + Map map = new HashMap<>(); + map.put(org.opendaylight.controller.mdsal.CompositeModificationPayload.modification, this.modification); + return map; + } + + @Override public Payload decode( + AppendEntriesMessages.AppendEntries.ReplicatedLogEntry.Payload payload) { + PersistentMessages.CompositeModification modification = payload + .getExtension( + org.opendaylight.controller.mdsal.CompositeModificationPayload.modification); + payload.getExtension(KeyValueMessages.value); + return new CompositeModificationPayload(modification); + } + + public Object getModification(){ + return this.modification; + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Configuration.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Configuration.java index a67f58c760..34239070a3 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Configuration.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Configuration.java @@ -15,9 +15,41 @@ import java.util.List; import java.util.Map; public interface Configuration { + + /** + * Given a memberName find all the shards that belong on that member and + * return the names of those shards + * + * @param memberName + * @return + */ List getMemberShardNames(String memberName); + + /** + * Given a module namespace return the name of a module + * @param nameSpace + * @return + */ Optional getModuleNameFromNameSpace(String nameSpace); + + /** + * Get a mapping of the module names to it's corresponding ShardStrategy + * @return + */ Map getModuleNameToShardStrategyMap(); + + /** + * Given a module name find all the shardNames corresponding to it + * @param moduleName + * @return + */ List getShardNamesFromModuleName(String moduleName); + + /** + * Given a shardName find all the members on which it belongs + * + * @param shardName + * @return + */ List getMembersFromShardName(String shardName); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java index 692d1b4954..6d87271f00 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java @@ -20,7 +20,7 @@ public class DistributedDataStoreFactory { new DistributedDataStore(actorSystem, name, new ClusterWrapperImpl(actorSystem),config ); ShardStrategyFactory.setConfiguration(config); schemaService - .registerSchemaServiceListener(dataStore); + .registerSchemaContextListener(dataStore); return dataStore; } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/NoOpCohort.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/NoOpCohort.java new file mode 100644 index 0000000000..eb28159025 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/NoOpCohort.java @@ -0,0 +1,40 @@ +/* + * 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.cluster.datastore; + +import akka.actor.UntypedActor; +import org.opendaylight.controller.cluster.datastore.messages.AbortTransaction; +import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply; +import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransaction; +import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransactionReply; +import org.opendaylight.controller.cluster.datastore.messages.CommitTransaction; +import org.opendaylight.controller.cluster.datastore.messages.CommitTransactionReply; +import org.opendaylight.controller.cluster.datastore.messages.PreCommitTransaction; +import org.opendaylight.controller.cluster.datastore.messages.PreCommitTransactionReply; + +public class NoOpCohort extends UntypedActor { + + @Override public void onReceive(Object message) throws Exception { + if (message.getClass().equals(CanCommitTransaction.SERIALIZABLE_CLASS)) { + getSender().tell(new CanCommitTransactionReply(false).toSerializable(), getSelf()); + } else if (message.getClass().equals(PreCommitTransaction.SERIALIZABLE_CLASS)) { + getSender().tell( + new PreCommitTransactionReply().toSerializable(), + getSelf()); + } else if (message.getClass().equals(CommitTransaction.SERIALIZABLE_CLASS)) { + getSender().tell(new CommitTransactionReply().toSerializable(), getSelf()); + } else if (message.getClass().equals(AbortTransaction.SERIALIZABLE_CLASS)) { + getSender().tell(new AbortTransactionReply().toSerializable(), getSelf()); + } else { + throw new Exception ("Not recognized message received,message="+message); + } + + } +} + diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java index d6ad553cf3..999d0f8baf 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java @@ -14,9 +14,7 @@ import akka.actor.Props; import akka.event.Logging; import akka.event.LoggingAdapter; import akka.japi.Creator; -import akka.persistence.Persistent; -import akka.persistence.RecoveryCompleted; -import akka.persistence.UntypedProcessor; +import akka.serialization.Serialization; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; @@ -28,12 +26,13 @@ import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionC import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChainReply; import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply; import org.opendaylight.controller.cluster.datastore.messages.ForwardedCommitTransaction; -import org.opendaylight.controller.cluster.datastore.messages.NonPersistent; +import org.opendaylight.controller.cluster.datastore.messages.PeerAddressResolved; import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener; import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply; import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext; import org.opendaylight.controller.cluster.datastore.modification.Modification; import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification; +import org.opendaylight.controller.cluster.raft.RaftActor; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; @@ -54,7 +53,7 @@ import java.util.concurrent.Executors; * Our Shard uses InMemoryDataStore as it's internal representation and delegates all requests it *

*/ -public class Shard extends UntypedProcessor { +public class Shard extends RaftActor { public static final String DEFAULT_NAME = "default"; @@ -73,11 +72,16 @@ public class Shard extends UntypedProcessor { // property persistent private final boolean persistent; + private final String name; + private SchemaContext schemaContext; private final ShardStats shardMBean; - private Shard(String name) { + private Shard(String name, Map peerAddresses) { + super(name, peerAddresses); + + this.name = name; String setting = System.getProperty("shard.persistent"); @@ -91,78 +95,88 @@ public class Shard extends UntypedProcessor { } - public static Props props(final String name) { + public static Props props(final String name, final Map peerAddresses) { return Props.create(new Creator() { @Override public Shard create() throws Exception { - return new Shard(name); + return new Shard(name, peerAddresses); } }); } - @Override - public void onReceive(Object message) throws Exception { - LOG.debug("Received message " + message.getClass().toString()); - - if(!recoveryFinished()){ - // FIXME : Properly handle recovery - return; - } + @Override public void onReceiveCommand(Object message){ + LOG.debug("Received message {} from {}", message.getClass().toString(), getSender()); if (message.getClass().equals(CreateTransactionChain.SERIALIZABLE_CLASS)) { - createTransactionChain(); + if(isLeader()) { + createTransactionChain(); + } else if(getLeader() != null){ + getLeader().forward(message, getContext()); + } } else if (message.getClass().equals(RegisterChangeListener.SERIALIZABLE_CLASS)) { registerChangeListener(RegisterChangeListener.fromSerializable(getContext().system(), message)); } else if (message instanceof UpdateSchemaContext) { updateSchemaContext((UpdateSchemaContext) message); } else if (message instanceof ForwardedCommitTransaction) { handleForwardedCommit((ForwardedCommitTransaction) message); - } else if (message instanceof Persistent) { - commit(((Persistent)message).payload()); } else if (message.getClass().equals(CreateTransaction.SERIALIZABLE_CLASS)) { - createTransaction(CreateTransaction.fromSerializable(message)); - } else if(message instanceof NonPersistent){ - commit(((NonPersistent)message).payload()); - }else if (message instanceof RecoveryCompleted) { - //FIXME: PROPERLY HANDLE RECOVERY COMPLETED - - }else { - throw new Exception("Not recognized message found message=" + message); + if(isLeader()) { + createTransaction(CreateTransaction.fromSerializable(message)); + } else if(getLeader() != null){ + getLeader().forward(message, getContext()); + } + } else if (message instanceof PeerAddressResolved){ + PeerAddressResolved resolved = (PeerAddressResolved) message; + setPeerAddress(resolved.getPeerId(), resolved.getPeerAddress()); + } else { + super.onReceiveCommand(message); } } private void createTransaction(CreateTransaction createTransaction) { DOMStoreReadWriteTransaction transaction = store.newReadWriteTransaction(); + String transactionId = "shard-" + createTransaction.getTransactionId(); + LOG.info("Creating transaction : {} " , transactionId); ActorRef transactionActor = getContext().actorOf( - ShardTransaction.props(transaction, getSelf(), schemaContext), "shard-" + createTransaction.getTransactionId()); + ShardTransaction.props(transaction, getSelf(), schemaContext), transactionId); + getSender() - .tell(new CreateTransactionReply(transactionActor.path().toString(), createTransaction.getTransactionId()).toSerializable(), + .tell(new CreateTransactionReply(Serialization.serializedActorPath(transactionActor), createTransaction.getTransactionId()).toSerializable(), getSelf()); } - private void commit(Object serialized) { + private void commit(final ActorRef sender, Object serialized) { Modification modification = MutableCompositeModification.fromSerializable(serialized, schemaContext); DOMStoreThreePhaseCommitCohort cohort = modificationToCohort.remove(serialized); if (cohort == null) { LOG.error( "Could not find cohort for modification : " + modification); + LOG.info("Writing modification using a new transaction"); + modification.apply(store.newReadWriteTransaction()); return; } + final ListenableFuture future = cohort.commit(); shardMBean.incrementCommittedTransactionCount(); - final ActorRef sender = getSender(); final ActorRef self = getSelf(); future.addListener(new Runnable() { @Override public void run() { try { future.get(); - sender.tell(new CommitTransactionReply().toSerializable(), self); + + if(sender != null) { + sender + .tell(new CommitTransactionReply().toSerializable(), + self); + } else { + LOG.error("sender is null ???"); + } } catch (InterruptedException | ExecutionException e) { // FIXME : Handle this properly LOG.error(e, "An exception happened when committing"); @@ -176,12 +190,11 @@ public class Shard extends UntypedProcessor { modificationToCohort .put(serializedModification , message.getCohort()); + if(persistent) { - getSelf().forward(Persistent.create(serializedModification), - getContext()); + this.persistData(getSender(), "identifier", new CompositeModificationPayload(serializedModification)); } else { - getSelf().forward(NonPersistent.create(serializedModification), - getContext()); + this.commit(getSender(), serializedModification); } } @@ -197,7 +210,8 @@ public class Shard extends UntypedProcessor { ActorSelection dataChangeListenerPath = getContext() - .system().actorSelection(registerChangeListener.getDataChangeListenerPath()); + .system().actorSelection( + registerChangeListener.getDataChangeListenerPath()); AsyncDataChangeListener> listener = new DataChangeListenerProxy(schemaContext,dataChangeListenerPath); @@ -220,9 +234,36 @@ public class Shard extends UntypedProcessor { private void createTransactionChain() { DOMStoreTransactionChain chain = store.createTransactionChain(); ActorRef transactionChain = - getContext().actorOf(ShardTransactionChain.props(chain, schemaContext)); + getContext().actorOf( + ShardTransactionChain.props(chain, schemaContext)); getSender() - .tell(new CreateTransactionChainReply(transactionChain.path()).toSerializable(), + .tell(new CreateTransactionChainReply(transactionChain.path()) + .toSerializable(), getSelf()); } + + @Override protected void applyState(ActorRef clientActor, String identifier, + Object data) { + + if(data instanceof CompositeModificationPayload){ + Object modification = + ((CompositeModificationPayload) data).getModification(); + commit(clientActor, modification); + } else { + LOG.error("Unknown state received {}", data); + } + + } + + @Override protected Object createSnapshot() { + throw new UnsupportedOperationException("createSnapshot"); + } + + @Override protected void applySnapshot(Object snapshot) { + throw new UnsupportedOperationException("applySnapshot"); + } + + @Override public String persistenceId() { + return this.name; + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java index 0363b3ceb3..5fbce4cd98 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java @@ -19,6 +19,7 @@ import akka.japi.Creator; import akka.japi.Function; import com.google.common.base.Preconditions; import org.opendaylight.controller.cluster.datastore.messages.FindPrimary; +import org.opendaylight.controller.cluster.datastore.messages.PeerAddressResolved; import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound; import org.opendaylight.controller.cluster.datastore.messages.PrimaryNotFound; import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext; @@ -56,7 +57,7 @@ public class ShardManager extends AbstractUntypedActor { // Stores a mapping between a member name and the address of the member private final Map memberNameToAddress = new HashMap<>(); - private final Map localShards = new HashMap<>(); + private final Map localShards = new HashMap<>(); private final String type; @@ -125,14 +126,20 @@ public class ShardManager extends AbstractUntypedActor { } private void memberUp(ClusterEvent.MemberUp message) { - memberNameToAddress.put(message.member().roles().head(), message.member().address()); + String memberName = message.member().roles().head(); + + memberNameToAddress.put(memberName , message.member().address()); + + for(ShardInformation info : localShards.values()){ + String shardName = info.getShardName(); + info.updatePeerAddress(getShardActorName(memberName, shardName), + getShardActorPath(shardName, memberName)); + } } private void updateSchemaContext(Object message) { - for(ActorPath path : localShards.values()){ - getContext().system().actorSelection(path) - .forward(message, - getContext()); + for(ShardInformation info : localShards.values()){ + info.getActor().tell(message,getSelf()); } } @@ -142,35 +149,50 @@ public class ShardManager extends AbstractUntypedActor { List members = configuration.getMembersFromShardName(shardName); - for(String memberName : members) { - if (memberName.equals(cluster.getCurrentMemberName())) { - // This is a local shard - ActorPath shardPath = localShards.get(shardName); - if (shardPath == null) { - getSender() - .tell(new PrimaryNotFound(shardName).toSerializable(), getSelf()); - return; - } - getSender().tell(new PrimaryFound(shardPath.toString()).toSerializable(), - getSelf()); + // First see if the there is a local replica for the shard + ShardInformation info = localShards.get(shardName); + if(info != null) { + ActorPath shardPath = info.getActorPath(); + if (shardPath != null) { + getSender() + .tell( + new PrimaryFound(shardPath.toString()).toSerializable(), + getSelf()); return; - } else { - Address address = memberNameToAddress.get(memberName); - if(address != null){ - String path = - address.toString() + "/user/shardmanager-" + this.type + "/" + getShardActorName( - memberName, shardName); - getSender().tell(new PrimaryFound(path).toSerializable(), getSelf()); - return; - } + } + } + if(cluster.getCurrentMemberName() != null) { + members.remove(cluster.getCurrentMemberName()); + } + // There is no way for us to figure out the primary (for now) so assume + // that one of the remote nodes is a primary + for(String memberName : members) { + Address address = memberNameToAddress.get(memberName); + if(address != null){ + String path = + getShardActorPath(shardName, memberName); + getSender().tell(new PrimaryFound(path).toSerializable(), getSelf()); + return; } } - getSender().tell(new PrimaryNotFound(shardName).toSerializable(), getSelf()); } + private String + + + getShardActorPath(String shardName, String memberName) { + Address address = memberNameToAddress.get(memberName); + if(address != null) { + return address.toString() + "/user/shardmanager-" + this.type + "/" + + getShardActorName( + memberName, shardName); + } + return null; + } + private String getShardActorName(String memberName, String shardName){ return memberName + "-shard-" + shardName + "-" + this.type; } @@ -183,14 +205,35 @@ public class ShardManager extends AbstractUntypedActor { for(String shardName : memberShardNames){ String shardActorName = getShardActorName(memberName, shardName); + Map peerAddresses = getPeerAddresses(shardName); ActorRef actor = getContext() - .actorOf(Shard.props(shardActorName), shardActorName); - ActorPath path = actor.path(); - localShards.put(shardName, path); + .actorOf(Shard.props(shardActorName, peerAddresses), + shardActorName); + localShards.put(shardName, new ShardInformation(shardName, actor, peerAddresses)); } } + private Map getPeerAddresses(String shardName){ + + Map peerAddresses = new HashMap<>(); + + List members = + this.configuration.getMembersFromShardName(shardName); + + String currentMemberName = this.cluster.getCurrentMemberName(); + + for(String memberName : members){ + if(!currentMemberName.equals(memberName)){ + String shardActorName = getShardActorName(memberName, shardName); + String path = + getShardActorPath(shardName, currentMemberName); + peerAddresses.put(shardActorName, path); + } + } + return peerAddresses; + } + @Override public SupervisorStrategy supervisorStrategy() { @@ -204,4 +247,49 @@ public class ShardManager extends AbstractUntypedActor { ); } + + private class ShardInformation { + private final String shardName; + private final ActorRef actor; + private final ActorPath actorPath; + private final Map peerAddresses; + + private ShardInformation(String shardName, ActorRef actor, + Map peerAddresses) { + this.shardName = shardName; + this.actor = actor; + this.actorPath = actor.path(); + this.peerAddresses = peerAddresses; + } + + public String getShardName() { + return shardName; + } + + public ActorRef getActor(){ + return actor; + } + + public ActorPath getActorPath() { + return actorPath; + } + + public Map getPeerAddresses() { + return peerAddresses; + } + + public void updatePeerAddress(String peerId, String peerAddress){ + LOG.info("updatePeerAddress for peer {} with address {}", peerId, peerAddress); + if(peerAddresses.containsKey(peerId)){ + peerAddresses.put(peerId, peerAddress); + + LOG.info("Sending PeerAddressResolved for peer {} with address {} to {}", peerId, peerAddress, actor.path()); + + actor + .tell(new PeerAddressResolved(peerId, peerAddress), + getSelf()); + + } + } + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java index 5a0049aa6d..c85d32012f 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java @@ -9,10 +9,14 @@ package org.opendaylight.controller.cluster.datastore; import akka.actor.ActorPath; +import akka.actor.ActorRef; import akka.actor.ActorSelection; +import akka.actor.Props; import com.google.common.base.Optional; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFutureTask; +import org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException; import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction; import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction; import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply; @@ -30,6 +34,8 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCoh import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.HashMap; @@ -60,6 +66,10 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { private static final AtomicLong counter = new AtomicLong(); + private static final Logger + LOG = LoggerFactory.getLogger(TransactionProxy.class); + + private final TransactionType transactionType; private final ActorContext actorContext; private final Map remoteTransactionPaths = new HashMap<>(); @@ -72,7 +82,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { TransactionType transactionType, ExecutorService executor, SchemaContext schemaContext - ) { + ) { this.identifier = actorContext.getCurrentMemberName() + "-txn-" + counter.getAndIncrement(); this.transactionType = transactionType; @@ -88,33 +98,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { createTransactionIfMissing(actorContext, path); - final ActorSelection remoteTransaction = remoteTransactionFromIdentifier(path); - - Callable>> call = new Callable() { - - @Override public Optional> call() throws Exception { - Object response = actorContext - .executeRemoteOperation(remoteTransaction, new ReadData(path).toSerializable(), - ActorContext.ASK_DURATION); - if(response.getClass().equals(ReadDataReply.SERIALIZABLE_CLASS)){ - ReadDataReply reply = ReadDataReply.fromSerializable(schemaContext,path, response); - if(reply.getNormalizedNode() == null){ - return Optional.absent(); - } - //FIXME : A cast should not be required here ??? - return (Optional>) Optional.of(reply.getNormalizedNode()); - } - - return Optional.absent(); - } - }; - - ListenableFutureTask>> - future = ListenableFutureTask.create(call); - - executor.submit(future); - - return future; + return transactionContext(path).readData(path); } @Override @@ -122,8 +106,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { createTransactionIfMissing(actorContext, path); - final ActorSelection remoteTransaction = remoteTransactionFromIdentifier(path); - remoteTransaction.tell(new WriteData(path, data, schemaContext).toSerializable(), null); + transactionContext(path).writeData(path, data); } @Override @@ -131,8 +114,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { createTransactionIfMissing(actorContext, path); - final ActorSelection remoteTransaction = remoteTransactionFromIdentifier(path); - remoteTransaction.tell(new MergeData(path, data, schemaContext).toSerializable(), null); + transactionContext(path).mergeData(path, data); } @Override @@ -140,8 +122,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { createTransactionIfMissing(actorContext, path); - final ActorSelection remoteTransaction = remoteTransactionFromIdentifier(path); - remoteTransaction.tell(new DeleteData(path).toSerializable(), null); + transactionContext(path).deleteData(path); } @Override @@ -149,10 +130,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { List cohortPaths = new ArrayList<>(); for(TransactionContext transactionContext : remoteTransactionPaths.values()) { - Object result = actorContext.executeRemoteOperation(transactionContext.getActor(), - new ReadyTransaction().toSerializable(), - ActorContext.ASK_DURATION - ); + Object result = transactionContext.readyTransaction(); if(result.getClass().equals(ReadyTransactionReply.SERIALIZABLE_CLASS)){ ReadyTransactionReply reply = ReadyTransactionReply.fromSerializable(actorContext.getActorSystem(),result); @@ -173,14 +151,13 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { @Override public void close() { for(TransactionContext transactionContext : remoteTransactionPaths.values()) { - transactionContext.getActor().tell( - new CloseTransaction().toSerializable(), null); + transactionContext.closeTransaction(); } } - private ActorSelection remoteTransactionFromIdentifier(YangInstanceIdentifier path){ + private TransactionContext transactionContext(YangInstanceIdentifier path){ String shardName = shardNameFromIdentifier(path); - return remoteTransactionPaths.get(shardName).getActor(); + return remoteTransactionPaths.get(shardName); } private String shardNameFromIdentifier(YangInstanceIdentifier path){ @@ -198,48 +175,186 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { return; } - Object response = actorContext.executeShardOperation(shardName, new CreateTransaction(identifier).toSerializable(), ActorContext.ASK_DURATION); - if(response.getClass().equals(CreateTransactionReply.SERIALIZABLE_CLASS)){ - CreateTransactionReply reply = CreateTransactionReply.fromSerializable(response); - String transactionPath = actorContext.getRemoteActorPath(shardName, reply.getTransactionPath()); + try { + Object response = actorContext.executeShardOperation(shardName, + new CreateTransaction(identifier).toSerializable(), + ActorContext.ASK_DURATION); + if (response.getClass() + .equals(CreateTransactionReply.SERIALIZABLE_CLASS)) { + CreateTransactionReply reply = + CreateTransactionReply.fromSerializable(response); + + String transactionPath = reply.getTransactionPath(); - ActorSelection transactionActor = actorContext.actorSelection(transactionPath); - transactionContext = new TransactionContext(shardName, transactionPath, transactionActor); + LOG.info("Received transaction path = {}" , transactionPath ); - remoteTransactionPaths.put(shardName, transactionContext); + ActorSelection transactionActor = + actorContext.actorSelection(transactionPath); + transactionContext = + new TransactionContextImpl(shardName, transactionPath, + transactionActor); + + remoteTransactionPaths.put(shardName, transactionContext); + } + } catch(TimeoutException e){ + remoteTransactionPaths.put(shardName, new NoOpTransactionContext(shardName)); } } + private interface TransactionContext { + String getShardName(); + + String getResolvedCohortPath(String cohortPath); + + public void closeTransaction(); + + public Object readyTransaction(); - private class TransactionContext { + void deleteData(YangInstanceIdentifier path); + + void mergeData(YangInstanceIdentifier path, NormalizedNode data); + + ListenableFuture>> readData(final YangInstanceIdentifier path); + + void writeData(YangInstanceIdentifier path, NormalizedNode data); + } + + + private class TransactionContextImpl implements TransactionContext{ private final String shardName; private final String actorPath; private final ActorSelection actor; - private TransactionContext(String shardName, String actorPath, + private TransactionContextImpl(String shardName, String actorPath, ActorSelection actor) { this.shardName = shardName; this.actorPath = actorPath; this.actor = actor; } - - public String getShardName() { + @Override public String getShardName() { return shardName; } - public String getActorPath() { - return actorPath; - } - - public ActorSelection getActor() { + private ActorSelection getActor() { return actor; } - public String getResolvedCohortPath(String cohortPath){ + @Override public String getResolvedCohortPath(String cohortPath){ return actorContext.resolvePath(actorPath, cohortPath); } + + @Override public void closeTransaction() { + getActor().tell( + new CloseTransaction().toSerializable(), null); + } + + @Override public Object readyTransaction() { + return actorContext.executeRemoteOperation(getActor(), + new ReadyTransaction().toSerializable(), + ActorContext.ASK_DURATION + ); + + } + + @Override public void deleteData(YangInstanceIdentifier path) { + getActor().tell(new DeleteData(path).toSerializable(), null); + } + + @Override public void mergeData(YangInstanceIdentifier path, NormalizedNode data){ + getActor().tell(new MergeData(path, data, schemaContext).toSerializable(), null); + } + + @Override public ListenableFuture>> readData(final YangInstanceIdentifier path) { + + Callable>> call = new Callable() { + + @Override public Optional> call() throws Exception { + Object response = actorContext + .executeRemoteOperation(getActor(), new ReadData(path).toSerializable(), + ActorContext.ASK_DURATION); + if(response.getClass().equals(ReadDataReply.SERIALIZABLE_CLASS)){ + ReadDataReply reply = ReadDataReply.fromSerializable(schemaContext,path, response); + if(reply.getNormalizedNode() == null){ + return Optional.absent(); + } + //FIXME : A cast should not be required here ??? + return (Optional>) Optional.of(reply.getNormalizedNode()); + } + + return Optional.absent(); + } + }; + + ListenableFutureTask>> + future = ListenableFutureTask.create(call); + + executor.submit(future); + + return future; + } + + @Override public void writeData(YangInstanceIdentifier path, NormalizedNode data) { + getActor().tell(new WriteData(path, data, schemaContext).toSerializable(), null); + } + + } + + private class NoOpTransactionContext implements TransactionContext { + + private final Logger + LOG = LoggerFactory.getLogger(NoOpTransactionContext.class); + + private final String shardName; + + private ActorRef cohort; + + public NoOpTransactionContext(String shardName){ + this.shardName = shardName; + } + @Override public String getShardName() { + return shardName; + + } + + @Override public String getResolvedCohortPath(String cohortPath) { + return cohort.path().toString(); + } + + @Override public void closeTransaction() { + LOG.error("closeTransaction called"); + } + + @Override public Object readyTransaction() { + LOG.error("readyTransaction called"); + cohort = actorContext.getActorSystem().actorOf(Props.create(NoOpCohort.class)); + return new ReadyTransactionReply(cohort.path()).toSerializable(); + } + + @Override public void deleteData(YangInstanceIdentifier path) { + LOG.error("deleteData called path = {}", path); + } + + @Override public void mergeData(YangInstanceIdentifier path, + NormalizedNode data) { + LOG.error("mergeData called path = {}", path); + } + + @Override + public ListenableFuture>> readData( + YangInstanceIdentifier path) { + LOG.error("readData called path = {}", path); + return Futures.immediateFuture( + Optional.>absent()); + } + + @Override public void writeData(YangInstanceIdentifier path, + NormalizedNode data) { + LOG.error("writeData called path = {}", path); + } } + + } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/exceptions/PrimaryNotFoundException.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/exceptions/PrimaryNotFoundException.java index 5a131ade33..7b07053fd9 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/exceptions/PrimaryNotFoundException.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/exceptions/PrimaryNotFoundException.java @@ -9,4 +9,7 @@ package org.opendaylight.controller.cluster.datastore.exceptions; public class PrimaryNotFoundException extends RuntimeException { + public PrimaryNotFoundException(String message){ + super(message); + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/exceptions/TimeoutException.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/exceptions/TimeoutException.java index 4780aaccfb..472cd38734 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/exceptions/TimeoutException.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/exceptions/TimeoutException.java @@ -9,7 +9,7 @@ package org.opendaylight.controller.cluster.datastore.exceptions; public class TimeoutException extends RuntimeException { - public TimeoutException(Exception e){ - super(e); + public TimeoutException(String message, Exception e){ + super(message, e); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/PeerAddressResolved.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/PeerAddressResolved.java new file mode 100644 index 0000000000..8c2543e486 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/PeerAddressResolved.java @@ -0,0 +1,27 @@ +/* + * 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.cluster.datastore.messages; + +public class PeerAddressResolved { + private final String peerId; + private final String peerAddress; + + public PeerAddressResolved(String peerId, String peerAddress) { + this.peerId = peerId; + this.peerAddress = peerAddress; + } + + public String getPeerId() { + return peerId; + } + + public String getPeerAddress() { + return peerAddress; + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java index c7ee7d8c2c..ac0893da5a 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java @@ -14,9 +14,6 @@ import akka.actor.ActorSelection; import akka.actor.ActorSystem; import akka.actor.PoisonPill; import akka.util.Timeout; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; import org.opendaylight.controller.cluster.datastore.ClusterWrapper; import org.opendaylight.controller.cluster.datastore.Configuration; import org.opendaylight.controller.cluster.datastore.exceptions.PrimaryNotFoundException; @@ -105,7 +102,7 @@ public class ActorContext { return found.getPrimaryPath(); } - throw new PrimaryNotFoundException(); + throw new PrimaryNotFoundException("Could not find primary for shardName " + shardName); } @@ -125,7 +122,7 @@ public class ActorContext { try { return Await.result(future, AWAIT_DURATION); } catch (Exception e) { - throw new TimeoutException(e); + throw new TimeoutException("Sending message " + message.getClass().toString() + " to actor " + actor.toString() + " failed" , e); } } @@ -148,7 +145,7 @@ public class ActorContext { try { return Await.result(future, AWAIT_DURATION); } catch (Exception e) { - throw new TimeoutException(e); + throw new TimeoutException("Sending message " + message.getClass().toString() + " to actor " + actor.toString() + " failed" , e); } } @@ -178,22 +175,15 @@ public class ActorContext { actorSystem.shutdown(); } - public String getRemoteActorPath(final String shardName, - final String localPathOfRemoteActor) { - final String path = findPrimaryPath(shardName); - - LoadingCache graphs = CacheBuilder.newBuilder() - .expireAfterAccess(2, TimeUnit.SECONDS) - .build( - new CacheLoader() { - public String load(String key) { - return resolvePath(path, localPathOfRemoteActor); - } - } - ); - return graphs.getUnchecked(localPathOfRemoteActor); - } - + /** + * @deprecated Need to stop using this method. There are ways to send a + * remote ActorRef as a string which should be used instead of this hack + * + * @param primaryPath + * @param localPathOfRemoteActor + * @return + */ + @Deprecated public String resolvePath(final String primaryPath, final String localPathOfRemoteActor) { StringBuilder builder = new StringBuilder(); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractActorTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractActorTest.java index 214b3e9d3d..e23a76b0b2 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractActorTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractActorTest.java @@ -10,22 +10,38 @@ package org.opendaylight.controller.cluster.datastore; import akka.actor.ActorSystem; import akka.testkit.JavaTestKit; +import org.apache.commons.io.FileUtils; import org.junit.AfterClass; import org.junit.BeforeClass; +import java.io.File; +import java.io.IOException; + public abstract class AbstractActorTest { private static ActorSystem system; @BeforeClass - public static void setUpClass() { + public static void setUpClass() throws IOException { + File journal = new File("journal"); + + if(journal.exists()) { + FileUtils.deleteDirectory(journal); + } + System.setProperty("shard.persistent", "false"); system = ActorSystem.create("test"); } @AfterClass - public static void tearDownClass() { + public static void tearDownClass() throws IOException { JavaTestKit.shutdownActorSystem(system); system = null; + + File journal = new File("journal"); + + if(journal.exists()) { + FileUtils.deleteDirectory(journal); + } } protected ActorSystem getSystem() { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/BasicIntegrationTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/BasicIntegrationTest.java index b62a4b36d5..11ad559744 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/BasicIntegrationTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/BasicIntegrationTest.java @@ -33,6 +33,8 @@ import scala.concurrent.Await; import scala.concurrent.Future; import scala.concurrent.duration.FiniteDuration; +import java.util.Collections; + public class BasicIntegrationTest extends AbstractActorTest { @Test @@ -47,16 +49,25 @@ public class BasicIntegrationTest extends AbstractActorTest { new JavaTestKit(getSystem()) {{ - final Props props = Shard.props("config"); + final Props props = Shard.props("config", Collections.EMPTY_MAP); final ActorRef shard = getSystem().actorOf(props); new Within(duration("5 seconds")) { protected void run() { + shard.tell( new UpdateSchemaContext(TestModel.createTestContext()), getRef()); + + // Wait for Shard to become a Leader + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + // 1. Create a TransactionChain shard.tell(new CreateTransactionChain().toSerializable(), getRef()); final ActorSelection transactionChain = @@ -76,6 +87,9 @@ public class BasicIntegrationTest extends AbstractActorTest { Assert.assertNotNull(transactionChain); + System.out.println("Successfully created transaction chain"); + + // 2. Create a Transaction on the TransactionChain transactionChain.tell(new CreateTransaction("txn-1").toSerializable(), getRef()); final ActorSelection transaction = @@ -94,9 +108,9 @@ public class BasicIntegrationTest extends AbstractActorTest { Assert.assertNotNull(transaction); - // Add a watch on the transaction actor so that we are notified when it dies - final ActorRef transactionActorRef = watchActor(transaction); + System.out.println("Successfully created transaction"); + // 3. Write some data transaction.tell(new WriteData(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME), TestModel.createTestContext()).toSerializable(), getRef()); @@ -113,6 +127,10 @@ public class BasicIntegrationTest extends AbstractActorTest { Assert.assertTrue(writeDone); + System.out.println("Successfully wrote data"); + + // 4. Ready the transaction for commit + transaction.tell(new ReadyTransaction().toSerializable(), getRef()); final ActorSelection cohort = @@ -132,8 +150,9 @@ public class BasicIntegrationTest extends AbstractActorTest { Assert.assertNotNull(cohort); - // Add a watch on the transaction actor so that we are notified when it dies - final ActorRef cohorActorRef = watchActor(cohort); + System.out.println("Successfully readied the transaction"); + + // 5. PreCommit the transaction cohort.tell(new PreCommitTransaction().toSerializable(), getRef()); @@ -150,10 +169,14 @@ public class BasicIntegrationTest extends AbstractActorTest { Assert.assertTrue(preCommitDone); + System.out.println("Successfully pre-committed the transaction"); + + // 6. Commit the transaction cohort.tell(new CommitTransaction().toSerializable(), getRef()); // FIXME : Add assertions that the commit worked and that the cohort and transaction actors were terminated + System.out.println("TODO : Check Successfully committed the transaction"); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreIntegrationTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreIntegrationTest.java index 116e5e75b5..b5e3d24ef6 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreIntegrationTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreIntegrationTest.java @@ -9,7 +9,6 @@ import org.junit.Before; import org.junit.Test; import org.opendaylight.controller.cluster.datastore.shardstrategy.ShardStrategyFactory; import org.opendaylight.controller.cluster.datastore.utils.MockClusterWrapper; -import org.opendaylight.controller.cluster.datastore.utils.MockConfiguration; import org.opendaylight.controller.md.cluster.datastore.model.CarsModel; import org.opendaylight.controller.md.cluster.datastore.model.PeopleModel; import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelper; @@ -46,12 +45,15 @@ public class DistributedDataStoreIntegrationTest{ @Test public void integrationTest() throws Exception { - ShardStrategyFactory.setConfiguration(new MockConfiguration()); + Configuration configuration = new ConfigurationImpl("module-shards.conf", "modules.conf"); + ShardStrategyFactory.setConfiguration(configuration); DistributedDataStore distributedDataStore = - new DistributedDataStore(getSystem(), "config", new MockClusterWrapper(), new MockConfiguration()); + new DistributedDataStore(getSystem(), "config", new MockClusterWrapper(), configuration); distributedDataStore.onGlobalContextUpdated(TestModel.createTestContext()); + Thread.sleep(1000); + DOMStoreReadWriteTransaction transaction = distributedDataStore.newReadWriteTransaction(); @@ -95,6 +97,8 @@ public class DistributedDataStoreIntegrationTest{ distributedDataStore.onGlobalContextUpdated(SchemaContextHelper.full()); + Thread.sleep(1000); + DOMStoreReadWriteTransaction transaction = distributedDataStore.newReadWriteTransaction(); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java index f20cd8c3d7..7d57ea8284 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java @@ -7,6 +7,7 @@ import org.junit.Test; import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction; import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChain; import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChainReply; +import org.opendaylight.controller.cluster.datastore.messages.PeerAddressResolved; import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener; import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply; import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext; @@ -19,6 +20,10 @@ import org.opendaylight.controller.protobuff.messages.transaction.ShardTransacti import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -26,10 +31,18 @@ public class ShardTest extends AbstractActorTest { @Test public void testOnReceiveCreateTransactionChain() throws Exception { new JavaTestKit(getSystem()) {{ - final Props props = Shard.props("config"); + final Props props = Shard.props("config", Collections.EMPTY_MAP); final ActorRef subject = getSystem().actorOf(props, "testCreateTransactionChain"); + + // Wait for Shard to become a Leader + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + new Within(duration("1 seconds")) { protected void run() { @@ -64,7 +77,7 @@ public class ShardTest extends AbstractActorTest { @Test public void testOnReceiveRegisterListener() throws Exception { new JavaTestKit(getSystem()) {{ - final Props props = Shard.props("config"); + final Props props = Shard.props("config", Collections.EMPTY_MAP); final ActorRef subject = getSystem().actorOf(props, "testRegisterChangeListener"); @@ -107,10 +120,19 @@ public class ShardTest extends AbstractActorTest { @Test public void testCreateTransaction(){ new JavaTestKit(getSystem()) {{ - final Props props = Shard.props("config"); + final Props props = Shard.props("config", Collections.EMPTY_MAP); final ActorRef subject = getSystem().actorOf(props, "testCreateTransaction"); + + // Wait for Shard to become a Leader + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + new Within(duration("1 seconds")) { protected void run() { @@ -135,9 +157,8 @@ public class ShardTest extends AbstractActorTest { } }.get(); // this extracts the received message - assertEquals("Unexpected transaction path " + out, - "akka://test/user/testCreateTransaction/shard-txn-1", - out); + assertTrue("Unexpected transaction path " + out, + out.contains("akka://test/user/testCreateTransaction/shard-txn-1")); expectNoMsg(); } @@ -146,7 +167,29 @@ public class ShardTest extends AbstractActorTest { }}; } + @Test + public void testPeerAddressResolved(){ + new JavaTestKit(getSystem()) {{ + Map peerAddresses = new HashMap<>(); + peerAddresses.put("member-2", null); + final Props props = Shard.props("config", peerAddresses); + final ActorRef subject = + getSystem().actorOf(props, "testPeerAddressResolved"); + + new Within(duration("1 seconds")) { + protected void run() { + subject.tell( + new PeerAddressResolved("member-2", "akka://foobar"), + getRef()); + + expectNoMsg(); + } + + + }; + }}; + } private AsyncDataChangeListener> noOpDataChangeListener() { return new AsyncDataChangeListener>() { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java index 4d7c61a197..7884eeccda 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java @@ -30,6 +30,8 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import java.util.Collections; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -49,7 +51,7 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveReadData() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props("config")); + final ActorRef shard = getSystem().actorOf(Shard.props("config", Collections.EMPTY_MAP)); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, testSchemaContext); final ActorRef subject = getSystem().actorOf(props, "testReadData"); @@ -89,7 +91,7 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveReadDataWhenDataNotFound() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props("config")); + final ActorRef shard = getSystem().actorOf(Shard.props("config", Collections.EMPTY_MAP)); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, testSchemaContext); final ActorRef subject = getSystem().actorOf(props, "testReadDataWhenDataNotFound"); @@ -163,7 +165,7 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveWriteData() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props("config")); + final ActorRef shard = getSystem().actorOf(Shard.props("config", Collections.EMPTY_MAP)); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, TestModel.createTestContext()); final ActorRef subject = @@ -201,7 +203,7 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveMergeData() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props("config")); + final ActorRef shard = getSystem().actorOf(Shard.props("config", Collections.EMPTY_MAP)); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, testSchemaContext); final ActorRef subject = @@ -240,7 +242,7 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveDeleteData() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props("config")); + final ActorRef shard = getSystem().actorOf(Shard.props("config", Collections.EMPTY_MAP)); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, TestModel.createTestContext()); final ActorRef subject = @@ -277,7 +279,7 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveReadyTransaction() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props("config")); + final ActorRef shard = getSystem().actorOf(Shard.props("config", Collections.EMPTY_MAP)); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, TestModel.createTestContext()); final ActorRef subject = @@ -313,7 +315,7 @@ public class ShardTransactionTest extends AbstractActorTest { @Test public void testOnReceiveCloseTransaction() throws Exception { new JavaTestKit(getSystem()) {{ - final ActorRef shard = getSystem().actorOf(Shard.props("config")); + final ActorRef shard = getSystem().actorOf(Shard.props("config", Collections.EMPTY_MAP)); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, TestModel.createTestContext()); final ActorRef subject = diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CarsModel.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CarsModel.java index 57df20172d..6860872b75 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CarsModel.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CarsModel.java @@ -8,6 +8,7 @@ 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; @@ -42,14 +43,14 @@ public class CarsModel { 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); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/PeopleModel.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/PeopleModel.java index 1b4020af43..e637920e78 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/PeopleModel.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/PeopleModel.java @@ -42,14 +42,14 @@ public class PeopleModel { 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); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SampleModelsTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SampleModelsTest.java index d8fefcd986..be8713c702 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SampleModelsTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SampleModelsTest.java @@ -18,45 +18,45 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; 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); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/module-shards.conf b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/module-shards.conf index 9f35f2b9f2..b3de998479 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/module-shards.conf +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/module-shards.conf @@ -18,9 +18,7 @@ module-shards = [ { name="people-1" replicas = [ - "member-1", - "member-2", - "member-3" + "member-1" ] } ] @@ -31,9 +29,18 @@ module-shards = [ { name="cars-1" replicas = [ - "member-4", - "member-1", - "member-5" + "member-1" + ] + } + ] + }, + { + name = "test" + shards = [ + { + name="test-1" + replicas = [ + "member-1" ] } ] diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/modules.conf b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/modules.conf index 52f249a7ff..22854cb11a 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/modules.conf +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/modules.conf @@ -8,6 +8,11 @@ modules = [ name = "cars" namespace = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test:cars" shard-strategy = "module" + }, + { + name = "test" + namespace = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test" + shard-strategy = "module" } ] diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/model/SchemaService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/model/SchemaService.java index 34e5b1b803..c3e979c536 100644 --- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/model/SchemaService.java +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/model/SchemaService.java @@ -11,7 +11,7 @@ import org.opendaylight.controller.sal.core.api.BrokerService; 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 { @@ -42,5 +42,11 @@ public interface SchemaService extends BrokerService { */ SchemaContext getGlobalContext(); - ListenerRegistration registerSchemaServiceListener(SchemaServiceListener listener); + /** + * Register a listener for changes in schema context. + * + * @param listener Listener which should be registered + * @return Listener registration handle + */ + ListenerRegistration registerSchemaContextListener(SchemaContextListener listener); } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java index 17b78f4ebd..f1f16cd635 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java @@ -91,7 +91,7 @@ public final class DomBrokerImplModule extends org.opendaylight.controller.confi wrappedStore.changeDelegate(legacyStore); wrappedStore.setValidationEnabled(false); - schemaService.registerSchemaServiceListener(wrappedStore); + schemaService.registerSchemaContextListener(wrappedStore); dataService.registerConfigurationReader(rootPath, wrappedStore); dataService.registerCommitHandler(rootPath, wrappedStore); diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java index 69b17ee3c4..667c0fc282 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java @@ -50,7 +50,7 @@ public final class DomInmemoryDataBrokerModule extends //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(); @@ -58,7 +58,7 @@ public final class DomInmemoryDataBrokerModule extends //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 datastores = ImmutableMap . builder().put(LogicalDatastoreType.OPERATIONAL, operStore) diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/SchemaServiceImplSingletonModule.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/SchemaServiceImplSingletonModule.java index fbc418dc2a..62b026430a 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/SchemaServiceImplSingletonModule.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/SchemaServiceImplSingletonModule.java @@ -13,7 +13,7 @@ import org.opendaylight.yangtools.concepts.Delegator; 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; @@ -90,8 +90,8 @@ org.opendaylight.controller.config.yang.md.sal.dom.impl.AbstractSchemaServiceImp } @Override - public ListenerRegistration registerSchemaServiceListener(final SchemaServiceListener arg0) { - return delegate.registerSchemaServiceListener(arg0); + public ListenerRegistration registerSchemaContextListener(final SchemaContextListener arg0) { + return delegate.registerSchemaContextListener(arg0); } @Override diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleDataBroker.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleDataBroker.java index e8f8da53c9..dc122cfdc2 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleDataBroker.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleDataBroker.java @@ -1,3 +1,10 @@ +/* + * 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; @@ -21,18 +28,17 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode; 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 schemaReg; + private final ListenerRegistration schemaReg; public BackwardsCompatibleDataBroker(final DOMDataBroker newBiDataImpl, final SchemaService schemaService) { backingBroker = newBiDataImpl; - schemaReg = schemaService.registerSchemaServiceListener(new SchemaListener()); + schemaReg = schemaService.registerSchemaContextListener(new SchemaListener()); } @Override diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BackwardsCompatibleMountPoint.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BackwardsCompatibleMountPoint.java index d837d75ddc..61ea47e39b 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BackwardsCompatibleMountPoint.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BackwardsCompatibleMountPoint.java @@ -72,7 +72,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum 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 { @@ -83,7 +83,7 @@ public class BackwardsCompatibleMountPoint implements MountProvisionInstance, Sc private final NotificationPublishService notificationPublishService; private final RpcProvisionRegistry rpcs; - private final ListenerRegistry schemaListenerRegistry = new ListenerRegistry<>(); + private final ListenerRegistry schemaListenerRegistry = new ListenerRegistry<>(); private SchemaContext schemaContext; @@ -154,7 +154,7 @@ public class BackwardsCompatibleMountPoint implements MountProvisionInstance, Sc } @Override - public ListenerRegistration registerSchemaServiceListener(final SchemaServiceListener listener) { + public ListenerRegistration registerSchemaContextListener(final SchemaContextListener listener) { return schemaListenerRegistry.register(listener); } @@ -275,7 +275,7 @@ public class BackwardsCompatibleMountPoint implements MountProvisionInstance, Sc @Override public void setSchemaContext(final SchemaContext schemaContext) { this.schemaContext = schemaContext; - for (ListenerRegistration schemaServiceListenerListenerRegistration : schemaListenerRegistry.getListeners()) { + for (ListenerRegistration schemaServiceListenerListenerRegistration : schemaListenerRegistry.getListeners()) { schemaServiceListenerListenerRegistration.getInstance().onGlobalContextUpdated(schemaContext); } } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java index d8174c312a..82637327f6 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java @@ -23,7 +23,7 @@ import org.opendaylight.yangtools.concepts.Registration; 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; @@ -40,15 +40,15 @@ import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; -public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer, AutoCloseable { +public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(GlobalBundleScanningSchemaServiceImpl.class); - private final ListenerRegistry listeners = new ListenerRegistry<>(); + private final ListenerRegistry listeners = new ListenerRegistry<>(); private final URLSchemaContextResolver contextResolver = new URLSchemaContextResolver(); private final BundleScanner scanner = new BundleScanner(); private final BundleContext context; - private ServiceTracker listenerTracker; + private ServiceTracker listenerTracker; private BundleTracker> bundleTracker; private boolean starting = true; private static GlobalBundleScanningSchemaServiceImpl instance; @@ -81,7 +81,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi 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(); @@ -115,7 +115,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi } @Override - public synchronized ListenerRegistration registerSchemaServiceListener(final SchemaServiceListener listener) { + public synchronized ListenerRegistration registerSchemaContextListener(final SchemaContextListener listener) { Optional potentialCtx = contextResolver.getSchemaContext(); if(potentialCtx.isPresent()) { listener.onGlobalContextUpdated(potentialCtx.get()); @@ -137,7 +137,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi private synchronized void updateContext(final SchemaContext snapshot) { Object[] services = listenerTracker.getServices(); - for (ListenerRegistration listener : listeners) { + for (ListenerRegistration listener : listeners) { try { listener.getInstance().onGlobalContextUpdated(snapshot); } catch (Exception e) { @@ -146,7 +146,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi } if (services != null) { for (Object rawListener : services) { - SchemaServiceListener listener = (SchemaServiceListener) rawListener; + final SchemaContextListener listener = (SchemaContextListener) rawListener; try { listener.onGlobalContextUpdated(snapshot); } catch (Exception e) { @@ -213,9 +213,9 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi } @Override - public synchronized SchemaServiceListener addingService(final ServiceReference reference) { + public synchronized SchemaContextListener addingService(final ServiceReference reference) { - SchemaServiceListener listener = context.getService(reference); + SchemaContextListener listener = context.getService(reference); SchemaContext _ctxContext = getGlobalContext(); if (getContext() != null && _ctxContext != null) { listener.onGlobalContextUpdated(_ctxContext); @@ -234,12 +234,12 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi } @Override - public void modifiedService(final ServiceReference reference, final SchemaServiceListener service) { + public void modifiedService(final ServiceReference reference, final SchemaContextListener service) { // NOOP } @Override - public void removedService(final ServiceReference reference, final SchemaServiceListener service) { + public void removedService(final ServiceReference reference, final SchemaContextListener service) { context.ungetService(reference); } } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceProxy.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceProxy.java index 1d864eec5b..d8d2346a8c 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceProxy.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceProxy.java @@ -8,10 +8,10 @@ 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 implements SchemaService { @@ -41,12 +41,9 @@ public class SchemaServiceProxy extends AbstractBrokerServiceProxy registerSchemaServiceListener(SchemaServiceListener listener) { - ListenerRegistration registration = getDelegate().registerSchemaServiceListener(listener); + public ListenerRegistration registerSchemaContextListener(SchemaContextListener listener) { + ListenerRegistration registration = getDelegate().registerSchemaContextListener(listener); addRegistration(registration); return registration; } - - - } diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/yang/gen/v1/http/netconfcentral/org/ns/xsql/rev140626/XSQLModule.java b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/yang/gen/v1/http/netconfcentral/org/ns/xsql/rev140626/XSQLModule.java index 399f49bd6b..59cdc76e7a 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/yang/gen/v1/http/netconfcentral/org/ns/xsql/rev140626/XSQLModule.java +++ b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/yang/gen/v1/http/netconfcentral/org/ns/xsql/rev140626/XSQLModule.java @@ -20,7 +20,7 @@ public class XSQLModule extends org.opendaylight.yang.gen.v1.http.netconfcentral @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()); diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java index 01a5989dcd..805608d479 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java @@ -23,7 +23,7 @@ public class InMemoryConfigDataStoreProviderModule extends org.opendaylight.cont @Override public java.lang.AutoCloseable createInstance() { InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-CFG", MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor())); - getSchemaServiceDependency().registerSchemaServiceListener(ids); + getSchemaServiceDependency().registerSchemaContextListener(ids); return ids; } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java index b39c9bbbd8..f4795588ab 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java @@ -23,7 +23,7 @@ public class InMemoryOperationalDataStoreProviderModule extends org.opendaylight @Override public java.lang.AutoCloseable createInstance() { InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-OPER", MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor())); - getOperationalSchemaServiceDependency().registerSchemaServiceListener(ids); + getOperationalSchemaServiceDependency().registerSchemaContextListener(ids); return ids; } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java index 037bfb4a82..b75df80f4e 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java @@ -13,8 +13,10 @@ import static org.opendaylight.controller.config.api.JmxAttributeValidationExcep 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; @@ -24,6 +26,7 @@ import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; 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; @@ -40,6 +43,8 @@ import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Optional; + /** * */ @@ -49,6 +54,7 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co private static AbstractCachingSchemaSourceProvider GLOBAL_NETCONF_SOURCE_PROVIDER = null; private BundleContext bundleContext; + private Optional userCapabilities; public NetconfConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); @@ -82,9 +88,11 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co 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); } @@ -98,10 +106,14 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co final Broker domBroker = getDomRegistryDependency(); final BindingAwareBroker bindingBroker = getBindingRegistryDependency(); - final RemoteDeviceHandler salFacade = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor); + final RemoteDeviceHandler 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(); @@ -116,6 +128,26 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co }; } + private Optional getUserCapabilities() { + if(getYangModuleCapabilities() == null) { + return Optional.absent(); + } + + final List 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 getGlobalNetconfSchemaProvider() { if(GLOBAL_NETCONF_SOURCE_PROVIDER == null) { final String storageFile = "cache/schema"; @@ -175,8 +207,8 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co 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()); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java index de4ac7ac18..07d3c08774 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java @@ -118,6 +118,7 @@ public final class NetconfDevice implements RemoteDevice { private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceCommunicator.class); private final RemoteDevice remoteDevice; + private final Optional overrideNetconfCapabilities; private final RemoteDeviceId id; private final Lock sessionLock = new ReentrantLock(); + private final Queue requests = new ArrayDeque<>(); + private NetconfClientSession session; + + public NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice remoteDevice, + final NetconfSessionCapabilities netconfSessionCapabilities) { + this(id, remoteDevice, Optional.of(netconfSessionCapabilities)); + } + public NetconfDeviceCommunicator(final RemoteDeviceId id, - final RemoteDevice remoteDevice) { + final RemoteDevice remoteDevice) { + this(id, remoteDevice, Optional.absent()); + } + + private NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice remoteDevice, + final Optional overrideNetconfCapabilities) { this.id = id; this.remoteDevice = remoteDevice; + this.overrideNetconfCapabilities = overrideNetconfCapabilities; } - private final Queue requests = new ArrayDeque<>(); - private NetconfClientSession session; - @Override public void onSessionUp(final NetconfClientSession session) { sessionLock.lock(); @@ -68,10 +78,15 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, 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 { @@ -223,7 +238,7 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, return; } - request.future.set( RpcResultBuilder.success( message ).build() ); + request.future.set( RpcResultBuilder.success( message ).build() ); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java index 8964a80228..1a7d90e9c0 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java @@ -8,6 +8,7 @@ import com.google.common.base.Splitter; 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; @@ -19,6 +20,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class NetconfSessionCapabilities { + private static final class ParameterMatcher { private final Predicate predicate; private final int skipLength; @@ -57,10 +59,10 @@ public final class NetconfSessionCapabilities { }; private final Set moduleBasedCaps; - private final Set capabilities; + private final Set nonModuleCaps; - private NetconfSessionCapabilities(final Set capabilities, final Set moduleBasedCaps) { - this.capabilities = Preconditions.checkNotNull(capabilities); + private NetconfSessionCapabilities(final Set nonModuleCaps, final Set moduleBasedCaps) { + this.nonModuleCaps = Preconditions.checkNotNull(nonModuleCaps); this.moduleBasedCaps = Preconditions.checkNotNull(moduleBasedCaps); } @@ -68,30 +70,49 @@ public final class NetconfSessionCapabilities { return moduleBasedCaps; } - public boolean containsCapability(final String capability) { - return capabilities.contains(capability); + public Set 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 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) { @@ -100,6 +121,7 @@ public final class NetconfSessionCapabilities { public static NetconfSessionCapabilities fromStrings(final Collection capabilities) { final Set moduleBasedCaps = new HashSet<>(); + final Set nonModuleCaps = Sets.newHashSet(capabilities); for (final String capability : capabilities) { final int qmark = capability.indexOf('?'); @@ -117,6 +139,7 @@ public final class NetconfSessionCapabilities { String revision = REVISION_PARAM.from(queryParams); if (revision != null) { moduleBasedCaps.add(QName.create(namespace, revision, moduleName)); + nonModuleCaps.remove(capability); continue; } @@ -136,8 +159,9 @@ public final class NetconfSessionCapabilities { // 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)); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java index ee0c8b7217..f3a9acd630 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java @@ -42,7 +42,7 @@ final class NetconfDeviceDataBroker implements DOMDataBroker { @Override public DOMDataReadOnlyTransaction newReadOnlyTransaction() { - return new NetconfDeviceReadOnlyTx(rpc, normalizer); + return new NetconfDeviceReadOnlyTx(rpc, normalizer, id); } @Override @@ -52,8 +52,7 @@ final class NetconfDeviceDataBroker implements DOMDataBroker { @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 diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java index 3248453baf..9ef44f6584 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java @@ -15,6 +15,7 @@ import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessag 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; @@ -22,6 +23,7 @@ import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizat 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; @@ -32,16 +34,19 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; 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>> readConfigurationData(final YangInstanceIdentifier path) { @@ -51,6 +56,8 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction return Futures.transform(future, new Function, Optional>>() { @Override public Optional> apply(final RpcResult result) { + checkReadSuccess(result, path); + final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME); final CompositeNode node = (CompositeNode) findNode(data, path); @@ -61,6 +68,11 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction }); } + private void checkReadSuccess(final RpcResult 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> transform(final YangInstanceIdentifier path, final CompositeNode node) { if(node == null) { return Optional.absent(); @@ -68,7 +80,7 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction try { return Optional.>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; } } @@ -79,6 +91,8 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction return Futures.transform(future, new Function, Optional>>() { @Override public Optional> apply(final RpcResult result) { + checkReadSuccess(result, path); + final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME); final CompositeNode node = (CompositeNode) findNode(data, path); @@ -123,7 +137,7 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction @Override public ListenableFuture>> read(final LogicalDatastoreType store, final YangInstanceIdentifier path) { - final YangInstanceIdentifier legacyPath = toLegacyPath(normalizer, path); + final YangInstanceIdentifier legacyPath = toLegacyPath(normalizer, path, id); switch (store) { case CONFIGURATION : { @@ -134,14 +148,14 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction } } - 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); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTx.java index c8d9028210..87f5477d35 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTx.java @@ -8,10 +8,11 @@ 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; @@ -26,13 +27,14 @@ import com.google.common.collect.ImmutableList; 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; @@ -57,97 +59,114 @@ import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction { +public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction, FutureCallback> { 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. absent(), Optional.fromNullable(legacyData)), Optional. 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.absent()), Optional.of(ModifyAction.NONE)); + sendEditRpc( + createEditConfigStructure(NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id), Optional.of(ModifyAction.DELETE), Optional.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 submit() { final ListenableFuture commmitFutureAsVoid = Futures.transform(commit(), new Function, Void>() { - @Nullable @Override - public Void apply(@Nullable final RpcResult input) { + public Void apply(final RpcResult input) { return null; } }); @@ -162,25 +181,46 @@ public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction { @Override public ListenableFuture> commit() { - // FIXME do not allow commit if closed or failed + checkNotFinished(); + finished.set(true); - final ListenableFuture> rpcResult = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, getCommitRequest()); - return Futures.transform(rpcResult, new Function, RpcResult>() { - @Override - public RpcResult apply(@Nullable final RpcResult input) { - if(input.isSuccessful()) { - return RpcResultBuilder.success(TransactionStatus.COMMITED).build(); - } else { - final RpcResultBuilder 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 = rpc.invokeRpc( + NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, NetconfMessageTransformUtil.COMMIT_RPC_CONTENT); + + final ListenableFuture> transformed = Futures.transform(rpcResult, + new Function, RpcResult>() { + @Override + public RpcResult apply(final RpcResult input) { + if (input.isSuccessful()) { + return RpcResultBuilder.success(TransactionStatus.COMMITED).build(); + } else { + final RpcResultBuilder 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 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 defaultOperation) throws ExecutionException { @@ -200,6 +240,22 @@ public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction { } } + private void sendDiscardChanges() { + final ListenableFuture> discardFuture = rpc.invokeRpc(NETCONF_DISCARD_CHANGES_QNAME, DISCARD_CHANGES_RPC_CONTENT); + Futures.addCallback(discardFuture, new FutureCallback>() { + @Override + public void onSuccess(final RpcResult 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 operation, final Optional lastChildOverride) { Preconditions.checkArgument(Iterables.isEmpty(dataPath.getPathArguments()) == false, "Instance identifier with empty path %s", dataPath); @@ -298,13 +354,6 @@ public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction { } } - private ImmutableCompositeNode getCommitRequest() { - final CompositeNodeBuilder commitInput = ImmutableCompositeNode.builder(); - commitInput.setQName(NETCONF_COMMIT_QNAME); - return commitInput.toInstance(); - } - - @Override public Object getIdentifier() { return this; diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java index a6924d9d37..d3faddd471 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java @@ -35,6 +35,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; 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; @@ -45,8 +46,7 @@ import org.w3c.dom.Element; 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"); @@ -66,14 +66,27 @@ public class NetconfMessageTransformUtil { 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.>emptyList()); + + // Commit changes message + public static final CompositeNode COMMIT_RPC_CONTENT = + NodeFactory.createImmutableCompositeNode(NETCONF_COMMIT_QNAME, null, Collections.>emptyList()); + public static Node toFilterStructure(final YangInstanceIdentifier identifier) { Node previous = null; if (Iterables.isEmpty(identifier.getPathArguments())) { @@ -269,5 +282,4 @@ public class NetconfMessageTransformUtil { return it.toInstance(); } } - } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang b/opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang index 6bad4798c2..e13398b1df 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang @@ -58,6 +58,14 @@ module odl-sal-netconf-connector-cfg { 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 { diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java index 391bf9c6a4..001b9a8d3a 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java @@ -8,35 +8,35 @@ 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; @@ -56,10 +56,6 @@ import org.opendaylight.yangtools.yang.common.RpcResult; 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 @@ -129,9 +125,9 @@ public class NetconfDeviceCommunicatorTest { 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() ); diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilitiesTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilitiesTest.java new file mode 100644 index 0000000000..87947b57fa --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilitiesTest.java @@ -0,0 +1,50 @@ +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 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 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()); + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java new file mode 100644 index 0000000000..a65e426d59 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java @@ -0,0 +1,79 @@ +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.>immediateFailedFuture(new IllegalStateException("Failed tx"))) + .doReturn(Futures.immediateFuture(RpcResultBuilder.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 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.success().build())) + .doReturn(Futures.>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); + } + +} diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/pom.xml b/opendaylight/md-sal/sal-protocolbuffer-encoding/pom.xml index e3fac63a83..28e629a92c 100644 --- a/opendaylight/md-sal/sal-protocolbuffer-encoding/pom.xml +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/pom.xml @@ -114,6 +114,13 @@ org.opendaylight.yangtools yang-binding
+ + + org.opendaylight.controller + sal-akka-raft + 1.1-SNAPSHOT + + com.google.guava guava diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/mdsal/CompositeModificationPayload.java b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/mdsal/CompositeModificationPayload.java new file mode 100644 index 0000000000..87b246bd7e --- /dev/null +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/mdsal/CompositeModificationPayload.java @@ -0,0 +1,60 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: CompositeModificationPayload.proto + +package org.opendaylight.controller.mdsal; + +public final class CompositeModificationPayload { + private CompositeModificationPayload() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registry.add(org.opendaylight.controller.mdsal.CompositeModificationPayload.modification); + } + public static final int MODIFICATION_FIELD_NUMBER = 2; + /** + * extend .org.opendaylight.controller.cluster.raft.AppendEntries.ReplicatedLogEntry.Payload { ... } + */ + public static final + com.google.protobuf.GeneratedMessage.GeneratedExtension< + org.opendaylight.controller.cluster.raft.protobuff.messages.AppendEntriesMessages.AppendEntries.ReplicatedLogEntry.Payload, + org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.CompositeModification> modification = com.google.protobuf.GeneratedMessage + .newFileScopedGeneratedExtension( + org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.CompositeModification.class, + org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.CompositeModification.getDefaultInstance()); + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\"CompositeModificationPayload.proto\022!or" + + "g.opendaylight.controller.mdsal\032\033AppendE" + + "ntriesMessages.proto\032\014Common.proto\032\020Pers" + + "istent.proto:\242\001\n\014modification\022R.org.open" + + "daylight.controller.cluster.raft.AppendE" + + "ntries.ReplicatedLogEntry.Payload\030\002 \001(\0132" + + "8.org.opendaylight.controller.mdsal.Comp" + + "ositeModification" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + modification.internalInit(descriptor.getExtensions().get(0)); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + org.opendaylight.controller.cluster.raft.protobuff.messages.AppendEntriesMessages.getDescriptor(), + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.getDescriptor(), + org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.getDescriptor(), + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/cohort3pc/ThreePhaseCommitCohortMessages.java b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/cohort3pc/ThreePhaseCommitCohortMessages.java index 22a93c0e10..ac0701a6d8 100644 --- a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/cohort3pc/ThreePhaseCommitCohortMessages.java +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/cohort3pc/ThreePhaseCommitCohortMessages.java @@ -652,7 +652,7 @@ public final class ThreePhaseCommitCohortMessages { public final boolean isInitialized() { if (!hasCanCommit()) { - + return false; } return true; diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/common/NormalizedNodeMessages.java b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/common/NormalizedNodeMessages.java index 35c2940be3..81e5b462cc 100644 --- a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/common/NormalizedNodeMessages.java +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/common/NormalizedNodeMessages.java @@ -179,7 +179,7 @@ public final class NormalizedNodeMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -195,7 +195,7 @@ public final class NormalizedNodeMessages { getNameBytes() { java.lang.Object ref = name_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); name_ = b; @@ -222,7 +222,7 @@ public final class NormalizedNodeMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -238,7 +238,7 @@ public final class NormalizedNodeMessages { getValueBytes() { java.lang.Object ref = value_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); value_ = b; @@ -265,7 +265,7 @@ public final class NormalizedNodeMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -281,7 +281,7 @@ public final class NormalizedNodeMessages { getTypeBytes() { java.lang.Object ref = type_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); type_ = b; @@ -541,7 +541,7 @@ public final class NormalizedNodeMessages { public final boolean isInitialized() { if (!hasName()) { - + return false; } return true; @@ -595,7 +595,7 @@ public final class NormalizedNodeMessages { getNameBytes() { java.lang.Object ref = name_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); name_ = b; @@ -669,7 +669,7 @@ public final class NormalizedNodeMessages { getValueBytes() { java.lang.Object ref = value_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); value_ = b; @@ -743,7 +743,7 @@ public final class NormalizedNodeMessages { getTypeBytes() { java.lang.Object ref = type_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); type_ = b; @@ -930,7 +930,7 @@ public final class NormalizedNodeMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -946,7 +946,7 @@ public final class NormalizedNodeMessages { getValueBytes() { java.lang.Object ref = value_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); value_ = b; @@ -1168,7 +1168,7 @@ public final class NormalizedNodeMessages { public final boolean isInitialized() { if (!hasValue()) { - + return false; } return true; @@ -1222,7 +1222,7 @@ public final class NormalizedNodeMessages { getValueBytes() { java.lang.Object ref = value_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); value_ = b; @@ -1341,7 +1341,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 4; */ - java.util.List + java.util.List getAttributesList(); /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 4; @@ -1354,7 +1354,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 4; */ - java.util.List + java.util.List getAttributesOrBuilderList(); /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 4; @@ -1504,7 +1504,7 @@ public final class NormalizedNodeMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -1520,7 +1520,7 @@ public final class NormalizedNodeMessages { getValueBytes() { java.lang.Object ref = value_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); value_ = b; @@ -1555,7 +1555,7 @@ public final class NormalizedNodeMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -1575,7 +1575,7 @@ public final class NormalizedNodeMessages { getTypeBytes() { java.lang.Object ref = type_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); type_ = b; @@ -1619,7 +1619,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 4; */ - public java.util.List + public java.util.List getAttributesOrBuilderList() { return attributes_; } @@ -1948,7 +1948,7 @@ public final class NormalizedNodeMessages { attributesBuilder_ = null; attributes_ = other.attributes_; bitField0_ = (bitField0_ & ~0x00000008); - attributesBuilder_ = + attributesBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? getAttributesFieldBuilder() : null; } else { @@ -1962,18 +1962,18 @@ public final class NormalizedNodeMessages { public final boolean isInitialized() { if (!hasValue()) { - + return false; } if (hasNodeType()) { if (!getNodeType().isInitialized()) { - + return false; } } for (int i = 0; i < getAttributesCount(); i++) { if (!getAttributes(i).isInitialized()) { - + return false; } } @@ -2028,7 +2028,7 @@ public final class NormalizedNodeMessages { getValueBytes() { java.lang.Object ref = value_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); value_ = b; @@ -2114,7 +2114,7 @@ public final class NormalizedNodeMessages { getTypeBytes() { java.lang.Object ref = type_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); type_ = b; @@ -2275,7 +2275,7 @@ public final class NormalizedNodeMessages { * optional .org.opendaylight.controller.mdsal.QName nodeType = 3; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.QName, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.QName.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.QNameOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.QName, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.QName.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.QNameOrBuilder> getNodeTypeFieldBuilder() { if (nodeTypeBuilder_ == null) { nodeTypeBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -2483,7 +2483,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 4; */ - public java.util.List + public java.util.List getAttributesOrBuilderList() { if (attributesBuilder_ != null) { return attributesBuilder_.getMessageOrBuilderList(); @@ -2509,12 +2509,12 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 4; */ - public java.util.List + public java.util.List getAttributesBuilderList() { return getAttributesFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Attribute, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Attribute.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.AttributeOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Attribute, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Attribute.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.AttributeOrBuilder> getAttributesFieldBuilder() { if (attributesBuilder_ == null) { attributesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< @@ -2546,7 +2546,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.PathArgument arguments = 1; */ - java.util.List + java.util.List getArgumentsList(); /** * repeated .org.opendaylight.controller.mdsal.PathArgument arguments = 1; @@ -2559,7 +2559,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.PathArgument arguments = 1; */ - java.util.List + java.util.List getArgumentsOrBuilderList(); /** * repeated .org.opendaylight.controller.mdsal.PathArgument arguments = 1; @@ -2680,7 +2680,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.PathArgument arguments = 1; */ - public java.util.List + public java.util.List getArgumentsOrBuilderList() { return arguments_; } @@ -2933,7 +2933,7 @@ public final class NormalizedNodeMessages { argumentsBuilder_ = null; arguments_ = other.arguments_; bitField0_ = (bitField0_ & ~0x00000001); - argumentsBuilder_ = + argumentsBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? getArgumentsFieldBuilder() : null; } else { @@ -2948,7 +2948,7 @@ public final class NormalizedNodeMessages { public final boolean isInitialized() { for (int i = 0; i < getArgumentsCount(); i++) { if (!getArguments(i).isInitialized()) { - + return false; } } @@ -3169,7 +3169,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.PathArgument arguments = 1; */ - public java.util.List + public java.util.List getArgumentsOrBuilderList() { if (argumentsBuilder_ != null) { return argumentsBuilder_.getMessageOrBuilderList(); @@ -3195,12 +3195,12 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.PathArgument arguments = 1; */ - public java.util.List + public java.util.List getArgumentsBuilderList() { return getArgumentsFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.PathArgument, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.PathArgument.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.PathArgumentOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.PathArgument, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.PathArgument.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.PathArgumentOrBuilder> getArgumentsFieldBuilder() { if (argumentsBuilder_ == null) { argumentsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< @@ -3262,7 +3262,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 3; */ - java.util.List + java.util.List getAttributesList(); /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 3; @@ -3275,7 +3275,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 3; */ - java.util.List + java.util.List getAttributesOrBuilderList(); /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 3; @@ -3287,7 +3287,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Node child = 4; */ - java.util.List + java.util.List getChildList(); /** * repeated .org.opendaylight.controller.mdsal.Node child = 4; @@ -3300,7 +3300,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Node child = 4; */ - java.util.List + java.util.List getChildOrBuilderList(); /** * repeated .org.opendaylight.controller.mdsal.Node child = 4; @@ -3546,7 +3546,7 @@ public final class NormalizedNodeMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -3562,7 +3562,7 @@ public final class NormalizedNodeMessages { getPathBytes() { java.lang.Object ref = path_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); path_ = b; @@ -3589,7 +3589,7 @@ public final class NormalizedNodeMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -3605,7 +3605,7 @@ public final class NormalizedNodeMessages { getTypeBytes() { java.lang.Object ref = type_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); type_ = b; @@ -3627,7 +3627,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 3; */ - public java.util.List + public java.util.List getAttributesOrBuilderList() { return attributes_; } @@ -3663,7 +3663,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Node child = 4; */ - public java.util.List + public java.util.List getChildOrBuilderList() { return child_; } @@ -3704,7 +3704,7 @@ public final class NormalizedNodeMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -3720,7 +3720,7 @@ public final class NormalizedNodeMessages { getValueBytes() { java.lang.Object ref = value_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); value_ = b; @@ -3747,7 +3747,7 @@ public final class NormalizedNodeMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -3763,7 +3763,7 @@ public final class NormalizedNodeMessages { getValueTypeBytes() { java.lang.Object ref = valueType_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); valueType_ = b; @@ -4202,7 +4202,7 @@ public final class NormalizedNodeMessages { attributesBuilder_ = null; attributes_ = other.attributes_; bitField0_ = (bitField0_ & ~0x00000004); - attributesBuilder_ = + attributesBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? getAttributesFieldBuilder() : null; } else { @@ -4228,7 +4228,7 @@ public final class NormalizedNodeMessages { childBuilder_ = null; child_ = other.child_; bitField0_ = (bitField0_ & ~0x00000008); - childBuilder_ = + childBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? getChildFieldBuilder() : null; } else { @@ -4266,19 +4266,19 @@ public final class NormalizedNodeMessages { public final boolean isInitialized() { for (int i = 0; i < getAttributesCount(); i++) { if (!getAttributes(i).isInitialized()) { - + return false; } } for (int i = 0; i < getChildCount(); i++) { if (!getChild(i).isInitialized()) { - + return false; } } if (hasInstanceIdentifierValue()) { if (!getInstanceIdentifierValue().isInitialized()) { - + return false; } } @@ -4333,7 +4333,7 @@ public final class NormalizedNodeMessages { getPathBytes() { java.lang.Object ref = path_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); path_ = b; @@ -4407,7 +4407,7 @@ public final class NormalizedNodeMessages { getTypeBytes() { java.lang.Object ref = type_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); type_ = b; @@ -4647,7 +4647,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 3; */ - public java.util.List + public java.util.List getAttributesOrBuilderList() { if (attributesBuilder_ != null) { return attributesBuilder_.getMessageOrBuilderList(); @@ -4673,12 +4673,12 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Attribute attributes = 3; */ - public java.util.List + public java.util.List getAttributesBuilderList() { return getAttributesFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Attribute, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Attribute.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.AttributeOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Attribute, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Attribute.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.AttributeOrBuilder> getAttributesFieldBuilder() { if (attributesBuilder_ == null) { attributesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< @@ -4887,7 +4887,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Node child = 4; */ - public java.util.List + public java.util.List getChildOrBuilderList() { if (childBuilder_ != null) { return childBuilder_.getMessageOrBuilderList(); @@ -4913,12 +4913,12 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.Node child = 4; */ - public java.util.List + public java.util.List getChildBuilderList() { return getChildFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> getChildFieldBuilder() { if (childBuilder_ == null) { childBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< @@ -4961,7 +4961,7 @@ public final class NormalizedNodeMessages { getValueBytes() { java.lang.Object ref = value_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); value_ = b; @@ -5035,7 +5035,7 @@ public final class NormalizedNodeMessages { getValueTypeBytes() { java.lang.Object ref = valueType_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); valueType_ = b; @@ -5277,7 +5277,7 @@ public final class NormalizedNodeMessages { * optional .org.opendaylight.controller.mdsal.InstanceIdentifier instanceIdentifierValue = 8; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> getInstanceIdentifierValueFieldBuilder() { if (instanceIdentifierValueBuilder_ == null) { instanceIdentifierValueBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -5459,7 +5459,7 @@ public final class NormalizedNodeMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -5475,7 +5475,7 @@ public final class NormalizedNodeMessages { getParentPathBytes() { java.lang.Object ref = parentPath_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); parentPath_ = b; @@ -5751,12 +5751,12 @@ public final class NormalizedNodeMessages { public final boolean isInitialized() { if (!hasParentPath()) { - + return false; } if (hasNormalizedNode()) { if (!getNormalizedNode().isInitialized()) { - + return false; } } @@ -5811,7 +5811,7 @@ public final class NormalizedNodeMessages { getParentPathBytes() { java.lang.Object ref = parentPath_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); parentPath_ = b; @@ -5960,7 +5960,7 @@ public final class NormalizedNodeMessages { * optional .org.opendaylight.controller.mdsal.Node normalizedNode = 2; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> getNormalizedNodeFieldBuilder() { if (normalizedNodeBuilder_ == null) { normalizedNodeBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -6431,16 +6431,16 @@ public final class NormalizedNodeMessages { public final boolean isInitialized() { if (!hasInstanceIdentifierPath()) { - + return false; } if (!getInstanceIdentifierPath().isInitialized()) { - + return false; } if (hasNormalizedNode()) { if (!getNormalizedNode().isInitialized()) { - + return false; } } @@ -6570,7 +6570,7 @@ public final class NormalizedNodeMessages { * required .org.opendaylight.controller.mdsal.InstanceIdentifier instanceIdentifierPath = 1; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> getInstanceIdentifierPathFieldBuilder() { if (instanceIdentifierPathBuilder_ == null) { instanceIdentifierPathBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -6687,7 +6687,7 @@ public final class NormalizedNodeMessages { * optional .org.opendaylight.controller.mdsal.Node normalizedNode = 2; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> getNormalizedNodeFieldBuilder() { if (normalizedNodeBuilder_ == null) { normalizedNodeBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -6718,7 +6718,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.NodeMapEntry mapEntries = 1; */ - java.util.List + java.util.List getMapEntriesList(); /** * repeated .org.opendaylight.controller.mdsal.NodeMapEntry mapEntries = 1; @@ -6731,7 +6731,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.NodeMapEntry mapEntries = 1; */ - java.util.List + java.util.List getMapEntriesOrBuilderList(); /** * repeated .org.opendaylight.controller.mdsal.NodeMapEntry mapEntries = 1; @@ -6852,7 +6852,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.NodeMapEntry mapEntries = 1; */ - public java.util.List + public java.util.List getMapEntriesOrBuilderList() { return mapEntries_; } @@ -7105,7 +7105,7 @@ public final class NormalizedNodeMessages { mapEntriesBuilder_ = null; mapEntries_ = other.mapEntries_; bitField0_ = (bitField0_ & ~0x00000001); - mapEntriesBuilder_ = + mapEntriesBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? getMapEntriesFieldBuilder() : null; } else { @@ -7120,7 +7120,7 @@ public final class NormalizedNodeMessages { public final boolean isInitialized() { for (int i = 0; i < getMapEntriesCount(); i++) { if (!getMapEntries(i).isInitialized()) { - + return false; } } @@ -7341,7 +7341,7 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.NodeMapEntry mapEntries = 1; */ - public java.util.List + public java.util.List getMapEntriesOrBuilderList() { if (mapEntriesBuilder_ != null) { return mapEntriesBuilder_.getMessageOrBuilderList(); @@ -7367,12 +7367,12 @@ public final class NormalizedNodeMessages { /** * repeated .org.opendaylight.controller.mdsal.NodeMapEntry mapEntries = 1; */ - public java.util.List + public java.util.List getMapEntriesBuilderList() { return getMapEntriesFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMapEntry, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMapEntry.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMapEntryOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMapEntry, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMapEntry.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMapEntryOrBuilder> getMapEntriesFieldBuilder() { if (mapEntriesBuilder_ == null) { mapEntriesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/common/SimpleNormalizedNodeMessage.java b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/common/SimpleNormalizedNodeMessage.java index 67ab472d0c..29e54571d3 100644 --- a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/common/SimpleNormalizedNodeMessage.java +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/common/SimpleNormalizedNodeMessage.java @@ -159,7 +159,7 @@ public final class SimpleNormalizedNodeMessage { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -175,7 +175,7 @@ public final class SimpleNormalizedNodeMessage { getNodeIdentifierBytes() { java.lang.Object ref = nodeIdentifier_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); nodeIdentifier_ = b; @@ -202,7 +202,7 @@ public final class SimpleNormalizedNodeMessage { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -218,7 +218,7 @@ public final class SimpleNormalizedNodeMessage { getXmlStringBytes() { java.lang.Object ref = xmlString_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); xmlString_ = b; @@ -463,11 +463,11 @@ public final class SimpleNormalizedNodeMessage { public final boolean isInitialized() { if (!hasNodeIdentifier()) { - + return false; } if (!hasXmlString()) { - + return false; } return true; @@ -521,7 +521,7 @@ public final class SimpleNormalizedNodeMessage { getNodeIdentifierBytes() { java.lang.Object ref = nodeIdentifier_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); nodeIdentifier_ = b; @@ -595,7 +595,7 @@ public final class SimpleNormalizedNodeMessage { getXmlStringBytes() { java.lang.Object ref = xmlString_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); xmlString_ = b; diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/datachange/notification/DataChangeListenerMessages.java b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/datachange/notification/DataChangeListenerMessages.java index 384b389f92..2018834768 100644 --- a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/datachange/notification/DataChangeListenerMessages.java +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/datachange/notification/DataChangeListenerMessages.java @@ -85,7 +85,7 @@ public final class DataChangeListenerMessages { /** * repeated .org.opendaylight.controller.mdsal.InstanceIdentifier removedPaths = 6; */ - java.util.List + java.util.List getRemovedPathsList(); /** * repeated .org.opendaylight.controller.mdsal.InstanceIdentifier removedPaths = 6; @@ -98,7 +98,7 @@ public final class DataChangeListenerMessages { /** * repeated .org.opendaylight.controller.mdsal.InstanceIdentifier removedPaths = 6; */ - java.util.List + java.util.List getRemovedPathsOrBuilderList(); /** * repeated .org.opendaylight.controller.mdsal.InstanceIdentifier removedPaths = 6; @@ -395,7 +395,7 @@ public final class DataChangeListenerMessages { /** * repeated .org.opendaylight.controller.mdsal.InstanceIdentifier removedPaths = 6; */ - public java.util.List + public java.util.List getRemovedPathsOrBuilderList() { return removedPaths_; } @@ -810,7 +810,7 @@ public final class DataChangeListenerMessages { removedPathsBuilder_ = null; removedPaths_ = other.removedPaths_; bitField0_ = (bitField0_ & ~0x00000020); - removedPathsBuilder_ = + removedPathsBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? getRemovedPathsFieldBuilder() : null; } else { @@ -825,37 +825,37 @@ public final class DataChangeListenerMessages { public final boolean isInitialized() { if (hasOriginalSubTree()) { if (!getOriginalSubTree().isInitialized()) { - + return false; } } if (hasUpdatedSubTree()) { if (!getUpdatedSubTree().isInitialized()) { - + return false; } } if (hasOriginalData()) { if (!getOriginalData().isInitialized()) { - + return false; } } if (hasUpdatedData()) { if (!getUpdatedData().isInitialized()) { - + return false; } } if (hasCreatedData()) { if (!getCreatedData().isInitialized()) { - + return false; } } for (int i = 0; i < getRemovedPathsCount(); i++) { if (!getRemovedPaths(i).isInitialized()) { - + return false; } } @@ -985,7 +985,7 @@ public final class DataChangeListenerMessages { * optional .org.opendaylight.controller.mdsal.Node originalSubTree = 1; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> getOriginalSubTreeFieldBuilder() { if (originalSubTreeBuilder_ == null) { originalSubTreeBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -1102,7 +1102,7 @@ public final class DataChangeListenerMessages { * optional .org.opendaylight.controller.mdsal.Node updatedSubTree = 2; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> getUpdatedSubTreeFieldBuilder() { if (updatedSubTreeBuilder_ == null) { updatedSubTreeBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -1219,7 +1219,7 @@ public final class DataChangeListenerMessages { * optional .org.opendaylight.controller.mdsal.NodeMap originalData = 3; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMap, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMap.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMapOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMap, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMap.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMapOrBuilder> getOriginalDataFieldBuilder() { if (originalDataBuilder_ == null) { originalDataBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -1336,7 +1336,7 @@ public final class DataChangeListenerMessages { * optional .org.opendaylight.controller.mdsal.NodeMap updatedData = 4; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMap, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMap.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMapOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMap, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMap.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMapOrBuilder> getUpdatedDataFieldBuilder() { if (updatedDataBuilder_ == null) { updatedDataBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -1453,7 +1453,7 @@ public final class DataChangeListenerMessages { * optional .org.opendaylight.controller.mdsal.NodeMap createdData = 5; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMap, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMap.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMapOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMap, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMap.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeMapOrBuilder> getCreatedDataFieldBuilder() { if (createdDataBuilder_ == null) { createdDataBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -1661,7 +1661,7 @@ public final class DataChangeListenerMessages { /** * repeated .org.opendaylight.controller.mdsal.InstanceIdentifier removedPaths = 6; */ - public java.util.List + public java.util.List getRemovedPathsOrBuilderList() { if (removedPathsBuilder_ != null) { return removedPathsBuilder_.getMessageOrBuilderList(); @@ -1687,12 +1687,12 @@ public final class DataChangeListenerMessages { /** * repeated .org.opendaylight.controller.mdsal.InstanceIdentifier removedPaths = 6; */ - public java.util.List + public java.util.List getRemovedPathsBuilderList() { return getRemovedPathsFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> getRemovedPathsFieldBuilder() { if (removedPathsBuilder_ == null) { removedPathsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/persistent/PersistentMessages.java b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/persistent/PersistentMessages.java index 6c1e2722f6..eaa90012db 100644 --- a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/persistent/PersistentMessages.java +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/persistent/PersistentMessages.java @@ -193,7 +193,7 @@ public final class PersistentMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -209,7 +209,7 @@ public final class PersistentMessages { getTypeBytes() { java.lang.Object ref = type_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); type_ = b; @@ -541,20 +541,20 @@ public final class PersistentMessages { public final boolean isInitialized() { if (!hasType()) { - + return false; } if (!hasPath()) { - + return false; } if (!getPath().isInitialized()) { - + return false; } if (hasData()) { if (!getData().isInitialized()) { - + return false; } } @@ -609,7 +609,7 @@ public final class PersistentMessages { getTypeBytes() { java.lang.Object ref = type_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); type_ = b; @@ -758,7 +758,7 @@ public final class PersistentMessages { * required .org.opendaylight.controller.mdsal.InstanceIdentifier path = 2; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> getPathFieldBuilder() { if (pathBuilder_ == null) { pathBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -875,7 +875,7 @@ public final class PersistentMessages { * optional .org.opendaylight.controller.mdsal.Node data = 3; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> getDataFieldBuilder() { if (dataBuilder_ == null) { dataBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -906,7 +906,7 @@ public final class PersistentMessages { /** * repeated .org.opendaylight.controller.mdsal.Modification modification = 1; */ - java.util.List + java.util.List getModificationList(); /** * repeated .org.opendaylight.controller.mdsal.Modification modification = 1; @@ -919,7 +919,7 @@ public final class PersistentMessages { /** * repeated .org.opendaylight.controller.mdsal.Modification modification = 1; */ - java.util.List + java.util.List getModificationOrBuilderList(); /** * repeated .org.opendaylight.controller.mdsal.Modification modification = 1; @@ -1040,7 +1040,7 @@ public final class PersistentMessages { /** * repeated .org.opendaylight.controller.mdsal.Modification modification = 1; */ - public java.util.List + public java.util.List getModificationOrBuilderList() { return modification_; } @@ -1293,7 +1293,7 @@ public final class PersistentMessages { modificationBuilder_ = null; modification_ = other.modification_; bitField0_ = (bitField0_ & ~0x00000001); - modificationBuilder_ = + modificationBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? getModificationFieldBuilder() : null; } else { @@ -1308,7 +1308,7 @@ public final class PersistentMessages { public final boolean isInitialized() { for (int i = 0; i < getModificationCount(); i++) { if (!getModification(i).isInitialized()) { - + return false; } } @@ -1529,7 +1529,7 @@ public final class PersistentMessages { /** * repeated .org.opendaylight.controller.mdsal.Modification modification = 1; */ - public java.util.List + public java.util.List getModificationOrBuilderList() { if (modificationBuilder_ != null) { return modificationBuilder_.getMessageOrBuilderList(); @@ -1555,12 +1555,12 @@ public final class PersistentMessages { /** * repeated .org.opendaylight.controller.mdsal.Modification modification = 1; */ - public java.util.List + public java.util.List getModificationBuilderList() { return getModificationFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilder< - org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.Modification, org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.Modification.Builder, org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.ModificationOrBuilder> + org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.Modification, org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.Modification.Builder, org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages.ModificationOrBuilder> getModificationFieldBuilder() { if (modificationBuilder_ == null) { modificationBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< @@ -1605,15 +1605,16 @@ public final class PersistentMessages { static { java.lang.String[] descriptorData = { "\n\020Persistent.proto\022!org.opendaylight.con" + - "troller.mdsal\032\014Common.proto\"\230\001\n\014Modifica" + - "tion\022\014\n\004type\030\001 \002(\t\022C\n\004path\030\002 \002(\01325.org.o" + - "pendaylight.controller.mdsal.InstanceIde" + - "ntifier\0225\n\004data\030\003 \001(\0132\'.org.opendaylight" + - ".controller.mdsal.Node\"^\n\025CompositeModif" + - "ication\022E\n\014modification\030\001 \003(\0132/.org.open" + - "daylight.controller.mdsal.ModificationBO" + - "\n9org.opendaylight.controller.protobuff." + - "messages.persistentB\022PersistentMessages" + "troller.mdsal\032\014Common.proto\032\033AppendEntri" + + "esMessages.proto\"\230\001\n\014Modification\022\014\n\004typ" + + "e\030\001 \002(\t\022C\n\004path\030\002 \002(\01325.org.opendaylight" + + ".controller.mdsal.InstanceIdentifier\0225\n\004" + + "data\030\003 \001(\0132\'.org.opendaylight.controller" + + ".mdsal.Node\"^\n\025CompositeModification\022E\n\014" + + "modification\030\001 \003(\0132/.org.opendaylight.co" + + "ntroller.mdsal.ModificationBO\n9org.opend" + + "aylight.controller.protobuff.messages.pe", + "rsistentB\022PersistentMessages" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -1639,6 +1640,7 @@ public final class PersistentMessages { .internalBuildGeneratedFileFrom(descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] { org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.getDescriptor(), + org.opendaylight.controller.cluster.raft.protobuff.messages.AppendEntriesMessages.getDescriptor(), }, assigner); } diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/registration/ListenerRegistrationMessages.java b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/registration/ListenerRegistrationMessages.java index 77cbd4da46..e06dd0d429 100644 --- a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/registration/ListenerRegistrationMessages.java +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/registration/ListenerRegistrationMessages.java @@ -837,7 +837,7 @@ public final class ListenerRegistrationMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -853,7 +853,7 @@ public final class ListenerRegistrationMessages { getDataChangeListenerActorPathBytes() { java.lang.Object ref = dataChangeListenerActorPath_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); dataChangeListenerActorPath_ = b; @@ -1146,19 +1146,19 @@ public final class ListenerRegistrationMessages { public final boolean isInitialized() { if (!hasInstanceIdentifierPath()) { - + return false; } if (!hasDataChangeListenerActorPath()) { - + return false; } if (!hasDataChangeScope()) { - + return false; } if (!getInstanceIdentifierPath().isInitialized()) { - + return false; } return true; @@ -1287,7 +1287,7 @@ public final class ListenerRegistrationMessages { * required .org.opendaylight.controller.mdsal.InstanceIdentifier instanceIdentifierPath = 1; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> getInstanceIdentifierPathFieldBuilder() { if (instanceIdentifierPathBuilder_ == null) { instanceIdentifierPathBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -1329,7 +1329,7 @@ public final class ListenerRegistrationMessages { getDataChangeListenerActorPathBytes() { java.lang.Object ref = dataChangeListenerActorPath_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); dataChangeListenerActorPath_ = b; @@ -1556,7 +1556,7 @@ public final class ListenerRegistrationMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -1572,7 +1572,7 @@ public final class ListenerRegistrationMessages { getListenerRegistrationPathBytes() { java.lang.Object ref = listenerRegistrationPath_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); listenerRegistrationPath_ = b; @@ -1801,7 +1801,7 @@ public final class ListenerRegistrationMessages { public final boolean isInitialized() { if (!hasListenerRegistrationPath()) { - + return false; } return true; @@ -1855,7 +1855,7 @@ public final class ListenerRegistrationMessages { getListenerRegistrationPathBytes() { java.lang.Object ref = listenerRegistrationPath_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); listenerRegistrationPath_ = b; diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/shard/ShardManagerMessages.java b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/shard/ShardManagerMessages.java index 2324dfc2a2..7c2a47e1b0 100644 --- a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/shard/ShardManagerMessages.java +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/shard/ShardManagerMessages.java @@ -139,7 +139,7 @@ public final class ShardManagerMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -155,7 +155,7 @@ public final class ShardManagerMessages { getShardNameBytes() { java.lang.Object ref = shardName_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); shardName_ = b; @@ -377,7 +377,7 @@ public final class ShardManagerMessages { public final boolean isInitialized() { if (!hasShardName()) { - + return false; } return true; @@ -431,7 +431,7 @@ public final class ShardManagerMessages { getShardNameBytes() { java.lang.Object ref = shardName_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); shardName_ = b; @@ -618,7 +618,7 @@ public final class ShardManagerMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -634,7 +634,7 @@ public final class ShardManagerMessages { getPrimaryPathBytes() { java.lang.Object ref = primaryPath_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); primaryPath_ = b; @@ -856,7 +856,7 @@ public final class ShardManagerMessages { public final boolean isInitialized() { if (!hasPrimaryPath()) { - + return false; } return true; @@ -910,7 +910,7 @@ public final class ShardManagerMessages { getPrimaryPathBytes() { java.lang.Object ref = primaryPath_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); primaryPath_ = b; @@ -1097,7 +1097,7 @@ public final class ShardManagerMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -1113,7 +1113,7 @@ public final class ShardManagerMessages { getShardNameBytes() { java.lang.Object ref = shardName_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); shardName_ = b; @@ -1335,7 +1335,7 @@ public final class ShardManagerMessages { public final boolean isInitialized() { if (!hasShardName()) { - + return false; } return true; @@ -1389,7 +1389,7 @@ public final class ShardManagerMessages { getShardNameBytes() { java.lang.Object ref = shardName_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); shardName_ = b; diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/transaction/ShardTransactionChainMessages.java b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/transaction/ShardTransactionChainMessages.java index 63dd5e7081..3f354ba40e 100644 --- a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/transaction/ShardTransactionChainMessages.java +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/transaction/ShardTransactionChainMessages.java @@ -1066,7 +1066,7 @@ public final class ShardTransactionChainMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -1082,7 +1082,7 @@ public final class ShardTransactionChainMessages { getTransactionChainPathBytes() { java.lang.Object ref = transactionChainPath_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); transactionChainPath_ = b; @@ -1304,7 +1304,7 @@ public final class ShardTransactionChainMessages { public final boolean isInitialized() { if (!hasTransactionChainPath()) { - + return false; } return true; @@ -1358,7 +1358,7 @@ public final class ShardTransactionChainMessages { getTransactionChainPathBytes() { java.lang.Object ref = transactionChainPath_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); transactionChainPath_ = b; diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/transaction/ShardTransactionMessages.java b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/transaction/ShardTransactionMessages.java index 7ce3b586b4..ee2c70423e 100644 --- a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/transaction/ShardTransactionMessages.java +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/java/org/opendaylight/controller/protobuff/messages/transaction/ShardTransactionMessages.java @@ -757,7 +757,7 @@ public final class ShardTransactionMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -773,7 +773,7 @@ public final class ShardTransactionMessages { getTransactionIdBytes() { java.lang.Object ref = transactionId_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); transactionId_ = b; @@ -995,7 +995,7 @@ public final class ShardTransactionMessages { public final boolean isInitialized() { if (!hasTransactionId()) { - + return false; } return true; @@ -1049,7 +1049,7 @@ public final class ShardTransactionMessages { getTransactionIdBytes() { java.lang.Object ref = transactionId_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); transactionId_ = b; @@ -1256,7 +1256,7 @@ public final class ShardTransactionMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -1272,7 +1272,7 @@ public final class ShardTransactionMessages { getTransactionActorPathBytes() { java.lang.Object ref = transactionActorPath_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); transactionActorPath_ = b; @@ -1299,7 +1299,7 @@ public final class ShardTransactionMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -1315,7 +1315,7 @@ public final class ShardTransactionMessages { getTransactionIdBytes() { java.lang.Object ref = transactionId_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); transactionId_ = b; @@ -1560,11 +1560,11 @@ public final class ShardTransactionMessages { public final boolean isInitialized() { if (!hasTransactionActorPath()) { - + return false; } if (!hasTransactionId()) { - + return false; } return true; @@ -1618,7 +1618,7 @@ public final class ShardTransactionMessages { getTransactionActorPathBytes() { java.lang.Object ref = transactionActorPath_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); transactionActorPath_ = b; @@ -1692,7 +1692,7 @@ public final class ShardTransactionMessages { getTransactionIdBytes() { java.lang.Object ref = transactionId_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); transactionId_ = b; @@ -2188,7 +2188,7 @@ public final class ShardTransactionMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -2204,7 +2204,7 @@ public final class ShardTransactionMessages { getActorPathBytes() { java.lang.Object ref = actorPath_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); actorPath_ = b; @@ -2426,7 +2426,7 @@ public final class ShardTransactionMessages { public final boolean isInitialized() { if (!hasActorPath()) { - + return false; } return true; @@ -2480,7 +2480,7 @@ public final class ShardTransactionMessages { getActorPathBytes() { java.lang.Object ref = actorPath_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); actorPath_ = b; @@ -2902,11 +2902,11 @@ public final class ShardTransactionMessages { public final boolean isInitialized() { if (!hasInstanceIdentifierPathArguments()) { - + return false; } if (!getInstanceIdentifierPathArguments().isInitialized()) { - + return false; } return true; @@ -3035,7 +3035,7 @@ public final class ShardTransactionMessages { * required .org.opendaylight.controller.mdsal.InstanceIdentifier instanceIdentifierPathArguments = 1; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> getInstanceIdentifierPathArgumentsFieldBuilder() { if (instanceIdentifierPathArgumentsBuilder_ == null) { instanceIdentifierPathArgumentsBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -3734,11 +3734,11 @@ public final class ShardTransactionMessages { public final boolean isInitialized() { if (!hasInstanceIdentifierPathArguments()) { - + return false; } if (!getInstanceIdentifierPathArguments().isInitialized()) { - + return false; } return true; @@ -3867,7 +3867,7 @@ public final class ShardTransactionMessages { * required .org.opendaylight.controller.mdsal.InstanceIdentifier instanceIdentifierPathArguments = 1; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> getInstanceIdentifierPathArgumentsFieldBuilder() { if (instanceIdentifierPathArgumentsBuilder_ == null) { instanceIdentifierPathArgumentsBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -4256,7 +4256,7 @@ public final class ShardTransactionMessages { public final boolean isInitialized() { if (hasNormalizedNode()) { if (!getNormalizedNode().isInitialized()) { - + return false; } } @@ -4386,7 +4386,7 @@ public final class ShardTransactionMessages { * optional .org.opendaylight.controller.mdsal.Node normalizedNode = 1; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> getNormalizedNodeFieldBuilder() { if (normalizedNodeBuilder_ == null) { normalizedNodeBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -4859,19 +4859,19 @@ public final class ShardTransactionMessages { public final boolean isInitialized() { if (!hasInstanceIdentifierPathArguments()) { - + return false; } if (!hasNormalizedNode()) { - + return false; } if (!getInstanceIdentifierPathArguments().isInitialized()) { - + return false; } if (!getNormalizedNode().isInitialized()) { - + return false; } return true; @@ -5000,7 +5000,7 @@ public final class ShardTransactionMessages { * required .org.opendaylight.controller.mdsal.InstanceIdentifier instanceIdentifierPathArguments = 1; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> getInstanceIdentifierPathArgumentsFieldBuilder() { if (instanceIdentifierPathArgumentsBuilder_ == null) { instanceIdentifierPathArgumentsBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -5117,7 +5117,7 @@ public final class ShardTransactionMessages { * required .org.opendaylight.controller.mdsal.Node normalizedNode = 2; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> getNormalizedNodeFieldBuilder() { if (normalizedNodeBuilder_ == null) { normalizedNodeBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -5899,19 +5899,19 @@ public final class ShardTransactionMessages { public final boolean isInitialized() { if (!hasInstanceIdentifierPathArguments()) { - + return false; } if (!hasNormalizedNode()) { - + return false; } if (!getInstanceIdentifierPathArguments().isInitialized()) { - + return false; } if (!getNormalizedNode().isInitialized()) { - + return false; } return true; @@ -6040,7 +6040,7 @@ public final class ShardTransactionMessages { * required .org.opendaylight.controller.mdsal.InstanceIdentifier instanceIdentifierPathArguments = 1; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifier.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.InstanceIdentifierOrBuilder> getInstanceIdentifierPathArgumentsFieldBuilder() { if (instanceIdentifierPathArgumentsBuilder_ == null) { instanceIdentifierPathArgumentsBuilder_ = new com.google.protobuf.SingleFieldBuilder< @@ -6157,7 +6157,7 @@ public final class ShardTransactionMessages { * required .org.opendaylight.controller.mdsal.Node normalizedNode = 2; */ private com.google.protobuf.SingleFieldBuilder< - org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> + org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node.Builder, org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.NodeOrBuilder> getNormalizedNodeFieldBuilder() { if (normalizedNodeBuilder_ == null) { normalizedNodeBuilder_ = new com.google.protobuf.SingleFieldBuilder< diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/resources/CompositeModificationPayload.proto b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/resources/CompositeModificationPayload.proto new file mode 100644 index 0000000000..b571cd25c5 --- /dev/null +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/resources/CompositeModificationPayload.proto @@ -0,0 +1,11 @@ +package org.opendaylight.controller.mdsal; + +import "AppendEntriesMessages.proto"; +import "Common.proto"; +import "Persistent.proto"; + +extend org.opendaylight.controller.cluster.raft.AppendEntries.ReplicatedLogEntry.Payload { + optional CompositeModification modification=2; +} + + diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/resources/Persistent.proto b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/resources/Persistent.proto index b3f28197ed..8e834494cb 100644 --- a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/resources/Persistent.proto +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/main/resources/Persistent.proto @@ -1,6 +1,7 @@ package org.opendaylight.controller.mdsal; import "Common.proto"; +import "AppendEntriesMessages.proto"; option java_package = "org.opendaylight.controller.protobuff.messages.persistent"; option java_outer_classname = "PersistentMessages"; @@ -16,3 +17,4 @@ message Modification { message CompositeModification { repeated Modification modification=1; } + diff --git a/opendaylight/md-sal/sal-remoterpc-connector/pom.xml b/opendaylight/md-sal/sal-remoterpc-connector/pom.xml index 6ee301d827..ce3bfe9a4c 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/pom.xml +++ b/opendaylight/md-sal/sal-remoterpc-connector/pom.xml @@ -55,22 +55,37 @@ org.opendaylight.controller netconf-util - + + org.opendaylight.controller + sal-core-spi + + + org.opendaylight.controller + sal-common-impl + org.opendaylight.yangtools yang-data-api + + + org.opendaylight.yangtools + yang-model-api + + org.opendaylight.yangtools yang-data-impl + org.opendaylight.yangtools yang-common + @@ -107,6 +122,17 @@ ${slf4j.version} test + + org.opendaylight.yangtools + yang-parser-impl + test + + + com.google.collections + google-collections + 1.0 + test +
diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModule.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModule.java index 8315bbeeb3..9824889b80 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModule.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModule.java @@ -2,10 +2,8 @@ package org.opendaylight.controller.config.yang.config.remote_rpc_connector; import org.opendaylight.controller.remote.rpc.RemoteRpcProviderFactory; import org.opendaylight.controller.sal.core.api.Broker; -import org.osgi.framework.BundleContext; public class RemoteRPCBrokerModule extends org.opendaylight.controller.config.yang.config.remote_rpc_connector.AbstractRemoteRPCBrokerModule { - private BundleContext bundleContext; public RemoteRPCBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); } @@ -22,10 +20,6 @@ public class RemoteRPCBrokerModule extends org.opendaylight.controller.config.ya @Override public java.lang.AutoCloseable createInstance() { Broker broker = getDomBrokerDependency(); - return RemoteRpcProviderFactory.createInstance(broker, bundleContext); - } - - public void setBundleContext(final BundleContext bundleContext) { - this.bundleContext = bundleContext; + return RemoteRpcProviderFactory.createInstance(broker); } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModuleFactory.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModuleFactory.java index e1ba46ae15..330845b14f 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModuleFactory.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/config/yang/config/remote_rpc_connector/RemoteRPCBrokerModuleFactory.java @@ -9,27 +9,7 @@ */ package org.opendaylight.controller.config.yang.config.remote_rpc_connector; -import org.opendaylight.controller.config.api.DependencyResolver; -import org.opendaylight.controller.config.api.DynamicMBeanWithInstance; -import org.opendaylight.controller.config.spi.Module; -import org.osgi.framework.BundleContext; public class RemoteRPCBrokerModuleFactory extends org.opendaylight.controller.config.yang.config.remote_rpc_connector.AbstractRemoteRPCBrokerModuleFactory { - @Override - public Module createModule(String instanceName, DependencyResolver dependencyResolver, BundleContext bundleContext) { - RemoteRPCBrokerModule module = (RemoteRPCBrokerModule)super.createModule(instanceName,dependencyResolver,bundleContext); - module.setBundleContext(bundleContext); - return module; - } - - @Override - public Module createModule(String instanceName, DependencyResolver dependencyResolver, - DynamicMBeanWithInstance old, BundleContext bundleContext) throws Exception { - RemoteRPCBrokerModule module = (RemoteRPCBrokerModule)super.createModule(instanceName, dependencyResolver, - old, bundleContext); - module.setBundleContext(bundleContext); - return module; - } - } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorConstants.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorConstants.java new file mode 100644 index 0000000000..1f1a0f5cc6 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorConstants.java @@ -0,0 +1,19 @@ +/* + * 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"; +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java index 43aa5b7e85..d384144f4f 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java @@ -7,21 +7,19 @@ import org.opendaylight.controller.remote.rpc.messages.ErrorResponse; import org.opendaylight.controller.remote.rpc.messages.InvokeRoutedRpc; import org.opendaylight.controller.remote.rpc.messages.InvokeRpc; import org.opendaylight.controller.remote.rpc.messages.RpcResponse; -import org.opendaylight.controller.sal.common.util.RpcErrors; -import org.opendaylight.controller.sal.common.util.Rpcs; +import org.opendaylight.controller.remote.rpc.utils.ActorUtil; +import org.opendaylight.controller.remote.rpc.utils.XmlUtils; import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation; import org.opendaylight.controller.sal.core.api.RpcImplementation; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.RpcError; 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.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Set; @@ -37,7 +35,7 @@ public class RemoteRpcImplementation implements RpcImplementation, } @Override - public ListenableFuture> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) { + public ListenableFuture> invokeRpc(QName rpc, YangInstanceIdentifier identifier, CompositeNode input) { InvokeRoutedRpc rpcMsg = new InvokeRoutedRpc(rpc, identifier, input); return executeMsg(rpcMsg); @@ -56,22 +54,33 @@ public class RemoteRpcImplementation implements RpcImplementation, } private ListenableFuture> executeMsg(Object rpcMsg) { - CompositeNode result = null; - Collection errors = errors = new ArrayList<>(); + ListenableFuture> listenableFuture = null; + try { Object response = ActorUtil.executeLocalOperation(rpcBroker, rpcMsg, ActorUtil.ASK_DURATION, ActorUtil.AWAIT_DURATION); if(response instanceof RpcResponse) { + RpcResponse rpcResponse = (RpcResponse) response; - result = XmlUtils.xmlToCompositeNode(rpcResponse.getResultCompositeNode()); + CompositeNode result = XmlUtils.xmlToCompositeNode(rpcResponse.getResultCompositeNode()); + listenableFuture = Futures.immediateFuture(RpcResultBuilder.success(result).build()); + } else if(response instanceof ErrorResponse) { + ErrorResponse errorResponse = (ErrorResponse) response; Exception e = errorResponse.getException(); - errors.add(RpcErrors.getRpcError(null, null, null, null, e.getMessage(), null, e.getCause())); + final RpcResultBuilder failed = RpcResultBuilder.failed(); + failed.withError(null, null, e.getMessage(), null, null, e.getCause()); + listenableFuture = Futures.immediateFuture(failed.build()); + } } catch (Exception e) { LOG.error("Error occurred while invoking RPC actor {}", e.toString()); - errors.add(RpcErrors.getRpcError(null, null, null, null, e.getMessage(), null, e.getCause())); + + final RpcResultBuilder failed = RpcResultBuilder.failed(); + failed.withError(null, null, e.getMessage(), null, null, e.getCause()); + listenableFuture = Futures.immediateFuture(failed.build()); } - return Futures.immediateFuture(Rpcs.getRpcResult(true, result, errors)); + + return listenableFuture; } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProvider.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProvider.java index 1bb7ea4514..ac50b8fe5b 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProvider.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProvider.java @@ -11,37 +11,35 @@ package org.opendaylight.controller.remote.rpc; import akka.actor.ActorRef; import akka.actor.ActorSystem; +import org.opendaylight.controller.remote.rpc.messages.UpdateSchemaContext; import org.opendaylight.controller.remote.rpc.registry.ClusterWrapper; import org.opendaylight.controller.remote.rpc.registry.ClusterWrapperImpl; -import org.opendaylight.controller.remote.rpc.registry.RpcRegistry; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.controller.sal.core.api.Provider; import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; import org.opendaylight.controller.sal.core.api.model.SchemaService; -import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; -import java.util.Set; /** * This is the base class which initialize all the actors, listeners and * default RPc implementation so remote invocation of rpcs. */ -public class RemoteRpcProvider implements AutoCloseable, Provider{ +public class RemoteRpcProvider implements AutoCloseable, Provider, SchemaContextListener { private static final Logger LOG = LoggerFactory.getLogger(RemoteRpcProvider.class); private final ActorSystem actorSystem; - private ActorRef rpcBroker; - private ActorRef rpcRegistry; private final RpcProvisionRegistry rpcProvisionRegistry; private Broker.ProviderSession brokerSession; - private RpcListener rpcListener; - private RoutedRpcListener routeChangeListener; - private RemoteRpcImplementation rpcImplementation; + private SchemaContext schemaContext; + private ActorRef rpcManager; + + public RemoteRpcProvider(ActorSystem actorSystem, RpcProvisionRegistry rpcProvisionRegistry) { this.actorSystem = actorSystem; this.rpcProvisionRegistry = rpcProvisionRegistry; @@ -50,8 +48,6 @@ public class RemoteRpcProvider implements AutoCloseable, Provider{ @Override public void close() throws Exception { this.actorSystem.shutdown(); - unregisterSupportedRpcs(); - unregisterSupportedRoutedRpcs(); } @Override @@ -66,64 +62,22 @@ public class RemoteRpcProvider implements AutoCloseable, Provider{ } private void start() { - LOG.debug("Starting all rpc listeners."); + LOG.info("Starting all rpc listeners and actors."); // Create actor to handle and sync routing table in cluster ClusterWrapper clusterWrapper = new ClusterWrapperImpl(actorSystem); - rpcRegistry = actorSystem.actorOf(RpcRegistry.props(clusterWrapper), "rpc-registry"); - - // Create actor to invoke and execute rpc SchemaService schemaService = brokerSession.getService(SchemaService.class); - SchemaContext schemaContext = schemaService.getGlobalContext(); - rpcBroker = actorSystem.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "rpc-broker"); - String rpcBrokerPath = clusterWrapper.getAddress().toString() + "/user/rpc-broker"; - rpcListener = new RpcListener(rpcRegistry, rpcBrokerPath); - routeChangeListener = new RoutedRpcListener(rpcRegistry, rpcBrokerPath); - rpcImplementation = new RemoteRpcImplementation(rpcBroker, schemaContext); - brokerSession.addRpcRegistrationListener(rpcListener); - rpcProvisionRegistry.registerRouteChangeListener(routeChangeListener); - rpcProvisionRegistry.setRoutedRpcDefaultDelegate(rpcImplementation); - announceSupportedRpcs(); - announceSupportedRoutedRpcs(); + schemaContext = schemaService.getGlobalContext(); - } + rpcManager = actorSystem.actorOf(RpcManager.props(clusterWrapper, schemaContext, brokerSession, rpcProvisionRegistry), ActorConstants.RPC_MANAGER); - /** - * Add all the locally registered RPCs in the clustered routing table - */ - private void announceSupportedRpcs(){ - LOG.debug("Adding all supported rpcs to routing table"); - Set currentlySupported = brokerSession.getSupportedRpcs(); - for (QName rpc : currentlySupported) { - rpcListener.onRpcImplementationAdded(rpc); - } + LOG.debug("Rpc actors are created."); } - /** - * Add all the locally registered Routed RPCs in the clustered routing table - */ - private void announceSupportedRoutedRpcs(){ - - //TODO: announce all routed RPCs as well - } - - /** - * Un-Register all the supported RPCs from clustered routing table - */ - private void unregisterSupportedRpcs(){ - LOG.debug("removing all supported rpcs to routing table"); - Set currentlySupported = brokerSession.getSupportedRpcs(); - for (QName rpc : currentlySupported) { - rpcListener.onRpcImplementationRemoved(rpc); - } - } - - /** - * Un-Register all the locally supported Routed RPCs from clustered routing table - */ - private void unregisterSupportedRoutedRpcs(){ - - //TODO: remove all routed RPCs as well + @Override + public void onGlobalContextUpdated(SchemaContext schemaContext) { + this.schemaContext = schemaContext; + rpcManager.tell(new UpdateSchemaContext(schemaContext), null); } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderFactory.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderFactory.java index 61dc8183df..4c40ca1777 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderFactory.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProviderFactory.java @@ -11,13 +11,12 @@ package org.opendaylight.controller.remote.rpc; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; -import org.osgi.framework.BundleContext; public class RemoteRpcProviderFactory { - public static RemoteRpcProvider createInstance(final Broker broker, final BundleContext bundleContext){ + public static RemoteRpcProvider createInstance(final Broker broker){ RemoteRpcProvider rpcProvider = new RemoteRpcProvider(ActorSystemFactory.getInstance(), (RpcProvisionRegistry) broker); - broker.registerProvider(rpcProvider, bundleContext); + broker.registerProvider(rpcProvider); return rpcProvider; } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RouteIdentifierImpl.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RouteIdentifierImpl.java index ea72238fbc..85d21381b6 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RouteIdentifierImpl.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RouteIdentifierImpl.java @@ -7,20 +7,23 @@ */ package org.opendaylight.controller.remote.rpc; +import com.google.common.base.Preconditions; import org.opendaylight.controller.sal.connector.api.RpcRouter; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + import java.io.Serializable; -public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier,Serializable { +public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier,Serializable { private static final long serialVersionUID = 1L; - private QName context; - private QName type; - private InstanceIdentifier route; + private final QName context; + private final QName type; + private final YangInstanceIdentifier route; - public RouteIdentifierImpl(QName context, QName type, InstanceIdentifier route) { + public RouteIdentifierImpl(final QName context, final QName type, final YangInstanceIdentifier route) { + Preconditions.checkNotNull(type, "Rpc type should not be null"); this.context = context; this.type = type; this.route = route; @@ -37,7 +40,7 @@ public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier{ +public class RoutedRpcListener implements RouteChangeListener{ private static final Logger LOG = LoggerFactory.getLogger(RoutedRpcListener.class); private final ActorRef rpcRegistry; private final String actorPath; public RoutedRpcListener(ActorRef rpcRegistry, String actorPath) { + Preconditions.checkNotNull(rpcRegistry, "rpc registry actor should not be null"); + Preconditions.checkNotNull(actorPath, "actor path of rpc broker on current node should not be null"); + this.rpcRegistry = rpcRegistry; this.actorPath = actorPath; } @Override - public void onRouteChange(RouteChange routeChange) { - Map> announcements = routeChange.getAnnouncements(); + public void onRouteChange(RouteChange routeChange) { + Map> announcements = routeChange.getAnnouncements(); announce(getRouteIdentifiers(announcements)); - Map> removals = routeChange.getRemovals(); + Map> removals = routeChange.getRemovals(); remove(getRouteIdentifiers(removals)); } @@ -78,12 +83,12 @@ public class RoutedRpcListener implements RouteChangeListener> getRouteIdentifiers(Map> changes) { + private Set> getRouteIdentifiers(Map> changes) { RouteIdentifierImpl routeId = null; Set> routeIdSet = new HashSet<>(); for (RpcRoutingContext context : changes.keySet()){ - for (InstanceIdentifier instanceId : changes.get(context)){ + for (YangInstanceIdentifier instanceId : changes.get(context)){ routeId = new RouteIdentifierImpl(null, context.getRpc(), instanceId); routeIdSet.add(routeId); } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java index 3354fc3da2..26e8e960e3 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java @@ -20,6 +20,8 @@ import org.opendaylight.controller.remote.rpc.messages.GetRpcReply; import org.opendaylight.controller.remote.rpc.messages.InvokeRoutedRpc; import org.opendaylight.controller.remote.rpc.messages.InvokeRpc; import org.opendaylight.controller.remote.rpc.messages.RpcResponse; +import org.opendaylight.controller.remote.rpc.utils.ActorUtil; +import org.opendaylight.controller.remote.rpc.utils.XmlUtils; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; @@ -38,7 +40,7 @@ public class RpcBroker extends AbstractUntypedActor { private static final Logger LOG = LoggerFactory.getLogger(RpcBroker.class); private final Broker.ProviderSession brokerSession; private final ActorRef rpcRegistry; - private final SchemaContext schemaContext; + private SchemaContext schemaContext; private RpcBroker(Broker.ProviderSession brokerSession, ActorRef rpcRegistry, SchemaContext schemaContext){ this.brokerSession = brokerSession; @@ -72,7 +74,7 @@ public class RpcBroker extends AbstractUntypedActor { try { RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, msg.getRpc(), msg.getIdentifier()); GetRoutedRpc routedRpcMsg = new GetRoutedRpc(routeId); - GetRoutedRpcReply rpcReply = (GetRoutedRpcReply)ActorUtil.executeLocalOperation(rpcRegistry, routedRpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION); + GetRoutedRpcReply rpcReply = (GetRoutedRpcReply) ActorUtil.executeLocalOperation(rpcRegistry, routedRpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION); String remoteActorPath = rpcReply.getRoutePath(); if(remoteActorPath == null) { @@ -105,11 +107,12 @@ public class RpcBroker extends AbstractUntypedActor { String remoteActorPath = rpcReply.getRoutePath(); if(remoteActorPath == null) { - LOG.debug("No remote actor found for rpc execution."); + LOG.debug("No remote actor found for rpc {{}}.", msg.getRpc()); getSender().tell(new ErrorResponse( - new IllegalStateException("No remote actor found for rpc execution.")), self()); + new IllegalStateException("No remote actor found for rpc execution of : " + msg.getRpc())), self()); } else { + ExecuteRpc executeMsg = new ExecuteRpc(XmlUtils.inputCompositeNodeToXml(msg.getInput(), schemaContext), msg.getRpc()); Object operationRes = ActorUtil.executeRemoteOperation(this.context().actorSelection(remoteActorPath), executeMsg, ActorUtil.REMOTE_ASK_DURATION, ActorUtil.REMOTE_AWAIT_DURATION); @@ -127,7 +130,6 @@ public class RpcBroker extends AbstractUntypedActor { try { Future> rpc = brokerSession.rpc(msg.getRpc(), XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(), schemaContext)); RpcResult rpcResult = rpc != null ? rpc.get():null; - CompositeNode result = rpcResult != null ? rpcResult.getResult() : null; getSender().tell(new RpcResponse(XmlUtils.outputCompositeNodeToXml(result, schemaContext)), self()); } catch (Exception e) { diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcListener.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcListener.java index ae760fadc4..f614990669 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcListener.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcListener.java @@ -12,6 +12,7 @@ package org.opendaylight.controller.remote.rpc; import akka.actor.ActorRef; import org.opendaylight.controller.remote.rpc.messages.AddRpc; import org.opendaylight.controller.remote.rpc.messages.RemoveRpc; +import org.opendaylight.controller.remote.rpc.utils.ActorUtil; import org.opendaylight.controller.sal.core.api.RpcRegistrationListener; import org.opendaylight.yangtools.yang.common.QName; import org.slf4j.Logger; diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java new file mode 100644 index 0000000000..5c56455bd0 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java @@ -0,0 +1,128 @@ +/* + * 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; + + +import akka.actor.ActorRef; +import akka.actor.OneForOneStrategy; +import akka.actor.Props; +import akka.actor.SupervisorStrategy; +import akka.japi.Creator; +import akka.japi.Function; +import org.opendaylight.controller.remote.rpc.messages.UpdateSchemaContext; +import org.opendaylight.controller.remote.rpc.registry.ClusterWrapper; +import org.opendaylight.controller.remote.rpc.registry.RpcRegistry; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import scala.concurrent.duration.Duration; +import java.util.Set; + +/** + * This class acts as a supervisor, creates all the actors, resumes them, if an exception is thrown. + * + * It also starts the rpc listeners + */ + +public class RpcManager extends AbstractUntypedActor { + + private static final Logger LOG = LoggerFactory.getLogger(RpcManager.class); + + private SchemaContext schemaContext; + private final ClusterWrapper clusterWrapper; + private ActorRef rpcBroker; + private ActorRef rpcRegistry; + private final Broker.ProviderSession brokerSession; + private RpcListener rpcListener; + private RoutedRpcListener routeChangeListener; + private RemoteRpcImplementation rpcImplementation; + private final RpcProvisionRegistry rpcProvisionRegistry; + + private RpcManager(ClusterWrapper clusterWrapper, SchemaContext schemaContext, + Broker.ProviderSession brokerSession, RpcProvisionRegistry rpcProvisionRegistry) { + this.clusterWrapper = clusterWrapper; + this.schemaContext = schemaContext; + this.brokerSession = brokerSession; + this.rpcProvisionRegistry = rpcProvisionRegistry; + + createRpcActors(); + startListeners(); + } + + + public static Props props(final ClusterWrapper clusterWrapper, final SchemaContext schemaContext, + final Broker.ProviderSession brokerSession, final RpcProvisionRegistry rpcProvisionRegistry) { + return Props.create(new Creator() { + @Override + public RpcManager create() throws Exception { + return new RpcManager(clusterWrapper, schemaContext, brokerSession, rpcProvisionRegistry); + } + }); + } + + private void createRpcActors() { + LOG.debug("Create rpc registry and broker actors"); + + 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() + ActorConstants.RPC_BROKER_PATH; + rpcListener = new RpcListener(rpcRegistry, rpcBrokerPath); + routeChangeListener = new RoutedRpcListener(rpcRegistry, rpcBrokerPath); + rpcImplementation = new RemoteRpcImplementation(rpcBroker, schemaContext); + + brokerSession.addRpcRegistrationListener(rpcListener); + rpcProvisionRegistry.registerRouteChangeListener(routeChangeListener); + rpcProvisionRegistry.setRoutedRpcDefaultDelegate(rpcImplementation); + announceSupportedRpcs(); + } + + /** + * Add all the locally registered RPCs in the clustered routing table + */ + private void announceSupportedRpcs(){ + LOG.debug("Adding all supported rpcs to routing table"); + Set currentlySupported = brokerSession.getSupportedRpcs(); + for (QName rpc : currentlySupported) { + rpcListener.onRpcImplementationAdded(rpc); + } + } + + + @Override + protected void handleReceive(Object message) throws Exception { + if(message instanceof UpdateSchemaContext) { + updateSchemaContext((UpdateSchemaContext) message); + } + + } + + private void updateSchemaContext(UpdateSchemaContext message) { + this.schemaContext = message.getSchemaContext(); + } + + @Override + public SupervisorStrategy supervisorStrategy() { + return new OneForOneStrategy(10, Duration.create("1 minute"), + new Function() { + @Override + public SupervisorStrategy.Directive apply(Throwable t) { + return SupervisorStrategy.resume(); + } + } + ); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/XmlUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/XmlUtils.java deleted file mode 100644 index 5fb2bb8772..0000000000 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/XmlUtils.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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; - -import com.google.common.base.Optional; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.Node; -import org.opendaylight.yangtools.yang.data.api.SimpleNode; -import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder; -import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.RpcDefinition; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.controller.netconf.util.xml.XmlUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.w3c.dom.Document; -import org.xml.sax.SAXException; - -import javax.activation.UnsupportedDataTypeException; -import javax.xml.stream.XMLStreamException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.StringWriter; -import java.util.Set; - -public class XmlUtils { - - private static final Logger LOG = LoggerFactory.getLogger(XmlUtils.class); - - public static String inputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){ - if (cNode == null) return new String(); - - //Document domTree = NodeUtils.buildShadowDomTree(cNode); - Document domTree = null; - try { - Set rpcs = schemaContext.getOperations(); - for(RpcDefinition rpc : rpcs) { - if(rpc.getQName().equals(cNode.getNodeType())){ - domTree = XmlDocumentUtils.toDocument(cNode, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider()); - break; - } - } - - } catch (UnsupportedDataTypeException e) { - LOG.error("Error during translation of CompositeNode to Document", e); - } - return domTransformer(domTree); - } - - public static String outputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){ - if (cNode == null) return new String(); - - //Document domTree = NodeUtils.buildShadowDomTree(cNode); - Document domTree = null; - try { - Set rpcs = schemaContext.getOperations(); - for(RpcDefinition rpc : rpcs) { - if(rpc.getQName().equals(cNode.getNodeType())){ - domTree = XmlDocumentUtils.toDocument(cNode, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider()); - break; - } - } - - } catch (UnsupportedDataTypeException e) { - LOG.error("Error during translation of CompositeNode to Document", e); - } - return domTransformer(domTree); - } - - private static String domTransformer(Document domTree) { - StringWriter writer = new StringWriter(); - try { - TransformerFactory tf = TransformerFactory.newInstance(); - Transformer transformer = tf.newTransformer(); - transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - transformer.transform(new DOMSource(domTree), new StreamResult(writer)); - } catch (TransformerException e) { - - LOG.error("Error during translation of Document to OutputStream", e); - } - LOG.debug("compositeNodeToXml " + writer.toString()); - - return writer.toString(); - } - - public static CompositeNode xmlToCompositeNode(String xml){ - if (xml==null || xml.length()==0) return null; - - Node dataTree; - try { - dataTree = XmlTreeBuilder.buildDataTree(new ByteArrayInputStream(xml.getBytes())); - } catch (XMLStreamException e) { - LOG.error("Error during building data tree from XML", e); - return null; - } - if (dataTree == null) { - LOG.error("data tree is null"); - return null; - } - if (dataTree instanceof SimpleNode) { - LOG.error("RPC XML was resolved as SimpleNode"); - return null; - } - return (CompositeNode) dataTree; - } - - public static CompositeNode inputXmlToCompositeNode(QName rpc, String xml, SchemaContext schemaContext){ - if (xml==null || xml.length()==0) return null; - - Node dataTree = null; - try { - - Document doc = XmlUtil.readXmlToDocument(xml); - LOG.debug("xmlToCompositeNode Document is " + xml ); - Set rpcs = schemaContext.getOperations(); - for(RpcDefinition rpcDef : rpcs) { - if(rpcDef.getQName().equals(rpc)){ - dataTree = XmlDocumentUtils.toDomNode(doc.getDocumentElement(), Optional.of(rpcDef.getInput()), Optional.of(XmlDocumentUtils.defaultValueCodecProvider())); - break; - } - } - } catch (SAXException e) { - LOG.error("Error during building data tree from XML", e); - } catch (IOException e) { - LOG.error("Error during building data tree from XML", e); - } - - LOG.debug("xmlToCompositeNode " + dataTree.toString()); - return (CompositeNode) dataTree; - } -} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRoutedRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRoutedRpc.java index 25773bb8a6..fd1af2b33c 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRoutedRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRoutedRpc.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.sal.connector.api.RpcRouter; import java.io.Serializable; @@ -15,10 +16,13 @@ import java.util.Set; public class AddRoutedRpc implements Serializable { - Set> announcements; - String actorPath; + private final Set> announcements; + private final String actorPath; + + public AddRoutedRpc(final Set> announcements, final String actorPath) { + Preconditions.checkNotNull(announcements, "Route identifier should not be null"); + Preconditions.checkNotNull(actorPath, "Actor path should not be null"); - public AddRoutedRpc(Set> announcements, String actorPath) { this.announcements = announcements; this.actorPath = actorPath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRpc.java index eac973137e..7eaa8f0618 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/AddRpc.java @@ -8,16 +8,20 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; import java.io.Serializable; public class AddRpc implements Serializable { - RouteIdentifierImpl routeId; - String actorPath; + private final RouteIdentifierImpl routeId; + private final String actorPath; + + public AddRpc(final RouteIdentifierImpl routeId, final String actorPath) { + Preconditions.checkNotNull(routeId, "Route identifier should not be null"); + Preconditions.checkNotNull(actorPath, "Actor path should not be null"); - public AddRpc(RouteIdentifierImpl routeId, String actorPath) { this.routeId = routeId; this.actorPath = actorPath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java index ef3f528112..2c26243fe9 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ErrorResponse.java @@ -7,13 +7,16 @@ */ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; + import java.io.Serializable; public class ErrorResponse implements Serializable { - Exception exception; + private final Exception exception; - public ErrorResponse(Exception e) { + public ErrorResponse(final Exception e) { + Preconditions.checkNotNull(e, "Exception should be present for error message"); this.exception = e; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ExecuteRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ExecuteRpc.java index 030d81ac7e..522dd44f5b 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ExecuteRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/ExecuteRpc.java @@ -8,16 +8,20 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.yangtools.yang.common.QName; import java.io.Serializable; public class ExecuteRpc implements Serializable { - private String inputCompositeNode; - private QName rpc; + private final String inputCompositeNode; + private final QName rpc; + + public ExecuteRpc(final String inputCompositeNode, final QName rpc) { + Preconditions.checkNotNull(inputCompositeNode, "Composite Node input string should be present"); + Preconditions.checkNotNull(rpc, "rpc Qname should not be null"); - public ExecuteRpc(String inputCompositeNode, QName rpc) { this.inputCompositeNode = inputCompositeNode; this.rpc = rpc; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpc.java index b1fa410e8d..e8d2262182 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpc.java @@ -9,15 +9,17 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; import java.io.Serializable; public class GetRoutedRpc implements Serializable { - RouteIdentifierImpl routeId; + private final RouteIdentifierImpl routeId; - public GetRoutedRpc(RouteIdentifierImpl routeId) { + public GetRoutedRpc(final RouteIdentifierImpl routeId) { + Preconditions.checkNotNull(routeId, "route id should not be null"); this.routeId = routeId; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpcReply.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpcReply.java index 0e1563340b..d426662192 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpcReply.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRoutedRpcReply.java @@ -12,9 +12,9 @@ import java.io.Serializable; public class GetRoutedRpcReply implements Serializable { - private String routePath; + private final String routePath; - public GetRoutedRpcReply(String routePath) { + public GetRoutedRpcReply(final String routePath) { this.routePath = routePath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpc.java index c55627961a..c1d4240dca 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpc.java @@ -7,15 +7,17 @@ */ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; import java.io.Serializable; public class GetRpc implements Serializable { - RouteIdentifierImpl routeId; + private final RouteIdentifierImpl routeId; - public GetRpc(RouteIdentifierImpl routeId) { + public GetRpc(final RouteIdentifierImpl routeId) { + Preconditions.checkNotNull(routeId, "Route Id should not be null"); this.routeId = routeId; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpcReply.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpcReply.java index 3309b989c5..aaf089d16f 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpcReply.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/GetRpcReply.java @@ -12,9 +12,9 @@ import java.io.Serializable; public class GetRpcReply implements Serializable { - private String routePath; + private final String routePath; - public GetRpcReply(String routePath) { + public GetRpcReply(final String routePath) { this.routePath = routePath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRoutedRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRoutedRpc.java index 00ef980bb1..fd73144e1d 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRoutedRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRoutedRpc.java @@ -7,26 +7,26 @@ */ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import java.io.Serializable; public class InvokeRoutedRpc implements Serializable { - private QName rpc; - private InstanceIdentifier identifier; - private CompositeNode input; + private final QName rpc; + private final YangInstanceIdentifier identifier; + private final CompositeNode input; - public InvokeRoutedRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) { - this.rpc = rpc; - this.identifier = identifier; - this.input = input; - } + public InvokeRoutedRpc(final QName rpc, final YangInstanceIdentifier identifier, final CompositeNode input) { + Preconditions.checkNotNull(rpc, "rpc qname should not be null"); + Preconditions.checkNotNull(identifier, "instance identifier of routed rpc should not be null"); + Preconditions.checkNotNull(input, "rpc input should not be null"); - public InvokeRoutedRpc(QName rpc, CompositeNode input) { this.rpc = rpc; + this.identifier = identifier; this.input = input; } @@ -34,7 +34,7 @@ public class InvokeRoutedRpc implements Serializable { return rpc; } - public InstanceIdentifier getIdentifier() { + public YangInstanceIdentifier getIdentifier() { return identifier; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRpc.java index 1f4eab0971..94b7fe4024 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/InvokeRpc.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; @@ -14,10 +15,13 @@ import java.io.Serializable; public class InvokeRpc implements Serializable { - private QName rpc; - private CompositeNode input; + private final QName rpc; + private final CompositeNode input; + + public InvokeRpc(final QName rpc, final CompositeNode input) { + Preconditions.checkNotNull(rpc, "rpc qname should not be null"); + Preconditions.checkNotNull(input, "rpc input should not be null"); - public InvokeRpc(QName rpc, CompositeNode input) { this.rpc = rpc; this.input = input; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRoutedRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRoutedRpc.java index a3aa9d12fa..b560b8c8c0 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRoutedRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRoutedRpc.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.sal.connector.api.RpcRouter; import java.io.Serializable; @@ -15,10 +16,13 @@ import java.util.Set; public class RemoveRoutedRpc implements Serializable { - Set> announcements; - String actorPath; + private final Set> announcements; + private final String actorPath; + + public RemoveRoutedRpc(final Set> announcements, final String actorPath) { + Preconditions.checkNotNull(announcements, "Route identifier should not be null"); + Preconditions.checkNotNull(actorPath, "Actor path should not be null"); - public RemoveRoutedRpc(Set> announcements, String actorPath) { this.announcements = announcements; this.actorPath = actorPath; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRpc.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRpc.java index 0bfd78aaae..289334fccc 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRpc.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RemoveRpc.java @@ -8,15 +8,18 @@ package org.opendaylight.controller.remote.rpc.messages; +import com.google.common.base.Preconditions; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; import java.io.Serializable; public class RemoveRpc implements Serializable { - RouteIdentifierImpl routeId; + private final RouteIdentifierImpl routeId; + + public RemoveRpc(final RouteIdentifierImpl routeId) { + Preconditions.checkNotNull(routeId, "Route Id should not be null"); - public RemoveRpc(RouteIdentifierImpl routeId) { this.routeId = routeId; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RoutingTableData.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RoutingTableData.java index 132fdba054..c57a258426 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RoutingTableData.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RoutingTableData.java @@ -15,11 +15,11 @@ import java.util.LinkedHashSet; import java.util.Map; public class RoutingTableData implements Serializable { - private Map, String> rpcMap; - private Map, LinkedHashSet> routedRpcMap; + private final Map, String> rpcMap; + private final Map, LinkedHashSet> routedRpcMap; - public RoutingTableData(Map, String> rpcMap, - Map, LinkedHashSet> routedRpcMap) { + public RoutingTableData(final Map, String> rpcMap, + final Map, LinkedHashSet> routedRpcMap) { this.rpcMap = rpcMap; this.routedRpcMap = routedRpcMap; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RpcResponse.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RpcResponse.java index cbfecb1918..17766f15b9 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RpcResponse.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/RpcResponse.java @@ -12,9 +12,9 @@ package org.opendaylight.controller.remote.rpc.messages; import java.io.Serializable; public class RpcResponse implements Serializable { - private String resultCompositeNode; + private final String resultCompositeNode; - public RpcResponse(String resultCompositeNode) { + public RpcResponse(final String resultCompositeNode) { this.resultCompositeNode = resultCompositeNode; } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/UpdateSchemaContext.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/UpdateSchemaContext.java new file mode 100644 index 0000000000..83fc7723b3 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/messages/UpdateSchemaContext.java @@ -0,0 +1,24 @@ +/* + * 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.messages; + +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +public class UpdateSchemaContext { + + private final SchemaContext schemaContext; + + public UpdateSchemaContext(final SchemaContext schemaContext) { + this.schemaContext = schemaContext; + } + + public SchemaContext getSchemaContext() { + return schemaContext; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java index 7cb505aa98..e36060cc13 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java @@ -14,6 +14,7 @@ import akka.cluster.ClusterEvent; 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; @@ -171,7 +172,7 @@ public class RpcRegistry extends AbstractUntypedActor { } 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++; @@ -180,7 +181,7 @@ public class RpcRegistry extends AbstractUntypedActor { 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; diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorUtil.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/ActorUtil.java similarity index 97% rename from opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorUtil.java rename to opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/ActorUtil.java index 13324f996e..8f60eabf5f 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorUtil.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/ActorUtil.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html * */ -package org.opendaylight.controller.remote.rpc; +package org.opendaylight.controller.remote.rpc.utils; import akka.actor.ActorRef; import akka.actor.ActorSelection; diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java new file mode 100644 index 0000000000..92a7fbae79 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java @@ -0,0 +1,219 @@ +/* + * 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.utils; + +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class InstanceIdentifierForXmlCodec { + private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]"); + private static final Splitter SLASH_SPLITTER = Splitter.on('/'); + private static final Splitter COLON_SPLITTER = Splitter.on(':'); + private static final Splitter AT_SPLITTER = Splitter.on('@'); + private static final Logger logger = LoggerFactory.getLogger(InstanceIdentifierForXmlCodec.class); + + private InstanceIdentifierForXmlCodec() { + throw new UnsupportedOperationException("Utility class"); + } + + public static YangInstanceIdentifier deserialize(final Element element, final SchemaContext schemaContext) { + Preconditions.checkNotNull(element, "Value of element for deserialization can't be null"); + Preconditions.checkNotNull(schemaContext, + "Schema context for deserialization of instance identifier type can't be null"); + + final String valueTrimmed = element.getTextContent().trim(); + logger.debug("Instance identifier derserialize: splitting the text {} with Slash to find path arguments", valueTrimmed); + final Iterator xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator(); + + // must be at least "/pr:node" + if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) { + logger.debug("Instance identifier derserialize: No path argument found for element."); + return null; + } + + List result = new ArrayList<>(); + while (xPathParts.hasNext()) { + String xPathPartTrimmed = xPathParts.next().trim(); + + PathArgument pathArgument = toPathArgument(xPathPartTrimmed, element, schemaContext); + if (pathArgument != null) { + result.add(pathArgument); + } + } + return YangInstanceIdentifier.create(result); + } + + public static Element serialize(final YangInstanceIdentifier id, final Element element) { + Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null"); + Preconditions.checkNotNull(element, "DOM element can't be null"); + + final RandomPrefix prefixes = new RandomPrefix(); + final String str = XmlUtils.encodeIdentifier(prefixes, id); + + for (Entry e: prefixes.getPrefixes()) { + element.setAttribute("xmlns:" + e.getValue(), e.getKey().toString()); + } + element.setTextContent(str); + return element; + } + + private static String getIdAndPrefixAsStr(final String pathPart) { + int predicateStartIndex = pathPart.indexOf('['); + return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex); + } + + private static PathArgument toPathArgument(final String xPathArgument, final Element element, final SchemaContext schemaContext) { + final QName mainQName = toIdentity(xPathArgument, element, schemaContext); + + // predicates + final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument); + final Map predicates = new HashMap<>(); + QName currentQName = mainQName; + + while (matcher.find()) { + final String predicateStr = matcher.group(1).trim(); + final int indexOfEqualityMark = predicateStr.indexOf('='); + if (indexOfEqualityMark != -1) { + final Object predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1)); + if (predicateValue == null) { + return null; + } + + if (predicateStr.charAt(0) != '.') { + // target is not a leaf-list + currentQName = toIdentity(predicateStr.substring(0, indexOfEqualityMark), element, schemaContext); + if (currentQName == null) { + return null; + } + } + logger.debug("Instance identifier derserialize: finding predicates of node {}", predicateValue); + predicates.put(currentQName, predicateValue); + } + } + + if (predicates.isEmpty()) { + return new YangInstanceIdentifier.NodeIdentifier(mainQName); + } else { + return new YangInstanceIdentifier.NodeIdentifierWithPredicates(mainQName, predicates); + } + + } + + public static QName toIdentity(final String xPathArgument, final Element element, final SchemaContext schemaContext) { + final String xPathPartTrimmed = getIdAndPrefixAsStr(xPathArgument).trim(); + final Iterator it = COLON_SPLITTER.split(xPathPartTrimmed).iterator(); + + // Empty string + if (!it.hasNext()) { + return null; + } + + final String prefix = it.next().trim(); + if (prefix.isEmpty()) { + return null; + } + + // it is not "prefix:value" + if (!it.hasNext()) { + return null; + } + + final String identifier = it.next().trim(); + if (identifier.isEmpty()) { + return null; + } + + URI namespace = null; + String namespaceStr = null; + try { + namespaceStr = element.getAttribute("xmlns:"+prefix); + namespace = new URI(namespaceStr); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("It wasn't possible to convert " + namespaceStr + " to URI object."); + } catch (NullPointerException e) { + throw new IllegalArgumentException("I wasn't possible to get namespace for prefix " + prefix); + } + + Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null); + return QName.create(module.getQNameModule(), identifier); + } + + private static String trimIfEndIs(final String str, final char end) { + final int l = str.length() - 1; + if (str.charAt(l) != end) { + return null; + } + + return str.substring(1, l); + } + + private static Object toPredicateValue(final String predicatedValue) { + logger.debug("Instance identifier derserialize: converting the predicate vstring to object {}", predicatedValue); + final String predicatedValueTrimmed = predicatedValue.trim(); + if (predicatedValue.isEmpty()) { + return null; + } + String updatedValue = null; + switch (predicatedValueTrimmed.charAt(0)) { + case '"': + updatedValue = trimIfEndIs(predicatedValueTrimmed, '"'); + break; + case '\'': + updatedValue = trimIfEndIs(predicatedValueTrimmed, '\''); + break; + default: + updatedValue = predicatedValueTrimmed; + } + Iterator it = AT_SPLITTER.split(updatedValue).iterator(); + // Empty string + if (!it.hasNext()) { + return null; + } + + final String value = it.next().trim(); + if (value.isEmpty()) { + return null; + } + + if (!it.hasNext()) { + return value; + } + + final String type = it.next().trim(); + if (type.isEmpty()) { + return value; + } + Object predicateObject = null; + try { + logger.debug("Instance identifier derserialize: converting the predicate value {{}}to correct object type {{}}", value, type); + predicateObject = Class.forName(type).getConstructor(String.class).newInstance(value); + } catch (Exception e) { + logger.error("Could not convert to valid type of value", e); + } + return predicateObject; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java new file mode 100644 index 0000000000..55cc8192a8 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java @@ -0,0 +1,50 @@ +/* + * 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.utils; + +import org.opendaylight.yangtools.yang.common.QName; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Picked from Yang tools because it was not public class + */ + +final class RandomPrefix { + final Map prefixes = new HashMap<>(); + + Iterable> getPrefixes() { + return prefixes.entrySet(); + } + + String encodeQName(final QName qname) { + String prefix = prefixes.get(qname.getNamespace()); + if (prefix == null) { + prefix = qname.getPrefix(); + if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) { + final ThreadLocalRandom random = ThreadLocalRandom.current(); + do { + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 4; i++) { + sb.append((char)('a' + random.nextInt(25))); + } + + prefix = sb.toString(); + } while (prefixes.containsValue(prefix)); + } + + prefixes.put(qname.getNamespace(), prefix); + } + + return prefix + ':' + qname.getLocalName(); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java new file mode 100644 index 0000000000..127aa0732b --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2013 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.utils; + +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.ModifyAction; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; +import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider; +import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; +import org.opendaylight.yangtools.yang.model.api.ChoiceNode; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import javax.activation.UnsupportedDataTypeException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.dom.DOMResult; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static com.google.common.base.Preconditions.checkState; + +public class XmlDocumentUtils { + private static class ElementWithSchemaContext { + Element element; + SchemaContext schemaContext; + + ElementWithSchemaContext(final Element element,final SchemaContext schemaContext) { + this.schemaContext = schemaContext; + this.element = element; + } + + Element getElement() { + return element; + } + + SchemaContext getSchemaContext() { + return schemaContext; + } + } + + public static final QName OPERATION_ATTRIBUTE_QNAME = QName.create(URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"), null, "operation"); + private static final Logger logger = LoggerFactory.getLogger(XmlDocumentUtils.class); + private static final XMLOutputFactory FACTORY = XMLOutputFactory.newFactory(); + + /** + * Converts Data DOM structure to XML Document for specified XML Codec Provider and corresponding + * Data Node Container schema. The CompositeNode data parameter enters as root of Data DOM tree and will + * be transformed to root in XML Document. Each element of Data DOM tree is compared against specified Data + * Node Container Schema and transformed accordingly. + * + * @param data Data DOM root element + * @param schema Data Node Container Schema + * @param codecProvider XML Codec Provider + * @return new instance of XML Document + * @throws javax.activation.UnsupportedDataTypeException + */ + public static Document toDocument(final CompositeNode data, final DataNodeContainer schema, final XmlCodecProvider codecProvider) + throws UnsupportedDataTypeException { + Preconditions.checkNotNull(data); + Preconditions.checkNotNull(schema); + + if (!(schema instanceof ContainerSchemaNode || schema instanceof ListSchemaNode)) { + throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet."); + } + + final DOMResult result = new DOMResult(getDocument()); + try { + final XMLStreamWriter writer = FACTORY.createXMLStreamWriter(result); + XmlStreamUtils.create(codecProvider).writeDocument(writer, data, (SchemaNode)schema); + writer.close(); + return (Document)result.getNode(); + } catch (XMLStreamException e) { + logger.error("Failed to serialize data {}", data, e); + return null; + } + } + + public static Document getDocument() { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + Document doc = null; + try { + DocumentBuilder bob = dbf.newDocumentBuilder(); + doc = bob.newDocument(); + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } + return doc; + } + + + public static QName qNameFromElement(final Element xmlElement) { + String namespace = xmlElement.getNamespaceURI(); + String localName = xmlElement.getLocalName(); + return QName.create(namespace != null ? URI.create(namespace) : null, null, localName); + } + + private static Node toNodeWithSchema(final Element xmlElement, final DataSchemaNode schema, final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) { + checkQName(xmlElement, schema.getQName()); + if (schema instanceof DataNodeContainer) { + return toCompositeNodeWithSchema(xmlElement, schema.getQName(), (DataNodeContainer) schema, schemaCtx); + } else if (schema instanceof LeafSchemaNode) { + return toSimpleNodeWithType(xmlElement, (LeafSchemaNode) schema, codecProvider,schemaCtx); + } else if (schema instanceof LeafListSchemaNode) { + return toSimpleNodeWithType(xmlElement, (LeafListSchemaNode) schema, codecProvider,schemaCtx); + } + return null; + } + + + + private static Node toSimpleNodeWithType(final Element xmlElement, final LeafSchemaNode schema, + final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) { + TypeDefinitionAwareCodec> codec = codecProvider.codecFor(schema.getType()); + String text = xmlElement.getTextContent(); + Object value = null; + if (codec != null) { + logger.debug("toSimpleNodeWithType: found codec, deserializing text {}", text); + value = codec.deserialize(text); + } + + final TypeDefinition baseType = XmlUtils.resolveBaseTypeFrom(schema.getType()); + if (baseType instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) { + logger.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement); + value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx); + + } else if(baseType instanceof IdentityrefTypeDefinition){ + logger.debug("toSimpleNodeWithType: base type of node is IdentityrefTypeDefinition, deserializing element", xmlElement); + value = InstanceIdentifierForXmlCodec.toIdentity(xmlElement.getTextContent(), xmlElement, schemaCtx); + + } + + if (value == null) { + logger.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement); + value = xmlElement.getTextContent(); + } + + Optional modifyAction = getModifyOperationFromAttributes(xmlElement); + return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull()); + } + + private static Node toSimpleNodeWithType(final Element xmlElement, final LeafListSchemaNode schema, + final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) { + TypeDefinitionAwareCodec> codec = codecProvider.codecFor(schema.getType()); + String text = xmlElement.getTextContent(); + Object value = null; + if (codec != null) { + logger.debug("toSimpleNodeWithType: found codec, deserializing text {}", text); + value = codec.deserialize(text); + } + + if (schema.getType() instanceof org.opendaylight.yangtools.yang.model.util.InstanceIdentifier) { + logger.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement); + value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx); + } + + if (value == null) { + logger.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement); + value = xmlElement.getTextContent(); + } + + Optional modifyAction = getModifyOperationFromAttributes(xmlElement); + return new SimpleNodeTOImpl<>(schema.getQName(), null, value, modifyAction.orNull()); + } + + private static Node toCompositeNodeWithSchema(final Element xmlElement, final QName qName, final DataNodeContainer schema, + final SchemaContext schemaCtx) { + List> values = toDomNodes(xmlElement, Optional.fromNullable(schema.getChildNodes()),schemaCtx); + Optional modifyAction = getModifyOperationFromAttributes(xmlElement); + return ImmutableCompositeNode.create(qName, values, modifyAction.orNull()); + } + + private static Optional getModifyOperationFromAttributes(final Element xmlElement) { + Attr attributeNodeNS = xmlElement.getAttributeNodeNS(OPERATION_ATTRIBUTE_QNAME.getNamespace().toString(), OPERATION_ATTRIBUTE_QNAME.getLocalName()); + if(attributeNodeNS == null) { + return Optional.absent(); + } + + ModifyAction action = ModifyAction.fromXmlValue(attributeNodeNS.getValue()); + Preconditions.checkArgument(action.isOnElementPermitted(), "Unexpected operation %s on %s", action, xmlElement); + + return Optional.of(action); + } + + private static void checkQName(final Element xmlElement, final QName qName) { + checkState(Objects.equal(xmlElement.getNamespaceURI(), qName.getNamespace().toString())); + checkState(qName.getLocalName().equals(xmlElement.getLocalName())); + } + + public static final Optional findFirstSchema(final QName qname, final Collection dataSchemaNode) { + if (dataSchemaNode != null && !dataSchemaNode.isEmpty() && qname != null) { + for (DataSchemaNode dsn : dataSchemaNode) { + if (qname.isEqualWithoutRevision(dsn.getQName())) { + return Optional. of(dsn); + } else if (dsn instanceof ChoiceNode) { + for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) { + Optional foundDsn = findFirstSchema(qname, choiceCase.getChildNodes()); + if (foundDsn != null && foundDsn.isPresent()) { + return foundDsn; + } + } + } + } + } + return Optional.absent(); + } + + private static Node toDomNode(Element element) { + QName qname = qNameFromElement(element); + + ImmutableList.Builder> values = ImmutableList.> builder(); + NodeList nodes = element.getChildNodes(); + boolean isSimpleObject = true; + String value = null; + for (int i = 0; i < nodes.getLength(); i++) { + org.w3c.dom.Node child = nodes.item(i); + if (child instanceof Element) { + isSimpleObject = false; + values.add(toDomNode((Element) child)); + } + if (isSimpleObject && child instanceof org.w3c.dom.Text) { + value = element.getTextContent(); + if (!Strings.isNullOrEmpty(value)) { + isSimpleObject = true; + } + } + } + if (isSimpleObject) { + return new SimpleNodeTOImpl<>(qname, null, value); + } + return ImmutableCompositeNode.create(qname, values.build()); + } + + public static List> toDomNodes(final Element element, final Optional> context,SchemaContext schemaCtx) { + return forEachChild(element.getChildNodes(),schemaCtx, new Function>>() { + + @Override + public Optional> apply(ElementWithSchemaContext input) { + if (context.isPresent()) { + QName partialQName = qNameFromElement(input.getElement()); + Optional schemaNode = XmlDocumentUtils.findFirstSchema(partialQName, context.get()); + if (schemaNode.isPresent()) { + return Optional.> fromNullable(// + toNodeWithSchema(input.getElement(), schemaNode.get(), XmlDocumentUtils.defaultValueCodecProvider(),input.getSchemaContext())); + } + } + return Optional.> fromNullable(toDomNode(input.getElement())); + } + + }); + + } + + private static final List forEachChild(final NodeList nodes, final SchemaContext schemaContext, final Function> forBody) { + final int l = nodes.getLength(); + if (l == 0) { + return ImmutableList.of(); + } + + final List list = new ArrayList<>(l); + for (int i = 0; i < l; i++) { + org.w3c.dom.Node child = nodes.item(i); + if (child instanceof Element) { + Optional result = forBody.apply(new ElementWithSchemaContext((Element) child,schemaContext)); + if (result.isPresent()) { + list.add(result.get()); + } + } + } + return ImmutableList.copyOf(list); + } + + public static final XmlCodecProvider defaultValueCodecProvider() { + return XmlUtils.DEFAULT_XML_CODEC_PROVIDER; + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java new file mode 100644 index 0000000000..e4576c445b --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java @@ -0,0 +1,249 @@ +/* + * 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.utils; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.AttributesContainer; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import java.net.URI; +import java.util.Map.Entry; + +/** + * Utility class for bridging JAXP Stream and YANG Data APIs. Note that the definition of this class + * by no means final and subject to change as more functionality is centralized here. + */ +@Beta +public class XmlStreamUtils { + private static final Logger LOG = LoggerFactory.getLogger(XmlStreamUtils.class); + private final XmlCodecProvider codecProvider; + + protected XmlStreamUtils(final XmlCodecProvider codecProvider) { + this.codecProvider = Preconditions.checkNotNull(codecProvider); + } + + /** + * Create a new instance encapsulating a particular codec provider. + * + * @param codecProvider XML codec provider + * @return A new instance + */ + public static XmlStreamUtils create(final XmlCodecProvider codecProvider) { + return new XmlStreamUtils(codecProvider); + } + + /** + * Check if a particular data element can be emitted as an empty element, bypassing value encoding. This + * functionality is optional, as valid XML stream is produced even if start/end element is produced unconditionally. + * + * @param data Data node + * @return True if the data node will result in empty element body. + */ + public static boolean isEmptyElement(final Node data) { + if (data == null) { + return true; + } + + if (data instanceof CompositeNode) { + return ((CompositeNode) data).getValue().isEmpty(); + } + if (data instanceof SimpleNode) { + return data.getValue() == null; + } + + // Safe default + return false; + } + + /** + * Write an InstanceIdentifier into the output stream. Calling corresponding {@link javax.xml.stream.XMLStreamWriter#writeStartElement(String)} + * and {@link javax.xml.stream.XMLStreamWriter#writeEndElement()} is the responsibility of the caller. + * + * @param writer XML Stream writer + * @param id InstanceIdentifier + * @throws javax.xml.stream.XMLStreamException + */ + public static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull YangInstanceIdentifier id) throws XMLStreamException { + Preconditions.checkNotNull(writer, "Writer may not be null"); + Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null"); + LOG.debug("Writing Instance identifier with Random prefix"); + final RandomPrefix prefixes = new RandomPrefix(); + final String str = XmlUtils.encodeIdentifier(prefixes, id); + + for (Entry e: prefixes.getPrefixes()) { + writer.writeNamespace(e.getValue(), e.getKey().toString()); + } + LOG.debug("Instance identifier with Random prefix is now {}", str); + writer.writeCharacters(str); + } + + /** + * Write a full XML document corresponding to a CompositeNode into an XML stream writer. + * + * @param writer XML Stream writer + * @param data data node + * @param schema corresponding schema node, may be null + * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs + */ + public void writeDocument(final @Nonnull XMLStreamWriter writer, final @Nonnull CompositeNode data, final @Nullable SchemaNode schema) throws XMLStreamException { + // final Boolean repairing = (Boolean) writer.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES); + // Preconditions.checkArgument(repairing == true, "XML Stream Writer has to be repairing namespaces"); + + writer.writeStartDocument(); + writeElement(writer, data, schema); + writer.writeEndDocument(); + writer.flush(); + } + + + /** + * Write an element into a XML stream writer. This includes the element start/end tags and + * the value of the element. + * + * @param writer XML Stream writer + * @param data data node + * @param schema Schema node + * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs + */ + public void writeElement(final XMLStreamWriter writer, final @Nonnull Node data, final SchemaNode schema) throws XMLStreamException { + final QName qname = data.getNodeType(); + final String pfx = qname.getPrefix() != null ? qname.getPrefix() : ""; + final String ns = qname.getNamespace() != null ? qname.getNamespace().toString() : ""; + + if (isEmptyElement(data)) { + writer.writeEmptyElement(pfx, qname.getLocalName(), ns); + return; + } + + writer.writeStartElement(pfx, qname.getLocalName(), ns); + if (data instanceof AttributesContainer && ((AttributesContainer) data).getAttributes() != null) { + for (Entry attribute : ((AttributesContainer) data).getAttributes().entrySet()) { + writer.writeAttribute(attribute.getKey().getNamespace().toString(), attribute.getKey().getLocalName(), attribute.getValue()); + } + } + + if (data instanceof SimpleNode) { + LOG.debug("writeElement : node is of type SimpleNode"); + // Simple node + if (schema instanceof LeafListSchemaNode) { + writeValue(writer, ((LeafListSchemaNode) schema).getType(), data.getValue()); + } else if (schema instanceof LeafSchemaNode) { + writeValue(writer, ((LeafSchemaNode) schema).getType(), data.getValue()); + } else { + Object value = data.getValue(); + if (value != null) { + writer.writeCharacters(String.valueOf(value)); + } + } + } else { + LOG.debug("writeElement : node is of type CompositeNode"); + // CompositeNode + for (Node child : ((CompositeNode) data).getValue()) { + DataSchemaNode childSchema = null; + if (schema instanceof DataNodeContainer) { + childSchema = SchemaUtils.findFirstSchema(child.getNodeType(), ((DataNodeContainer) schema).getChildNodes()).orNull(); + if (childSchema == null) { + LOG.debug("Probably the data node \"{}\" does not conform to schema", child == null ? "" : child.getNodeType().getLocalName()); + } + } + + writeElement(writer, child, childSchema); + } + } + + writer.writeEndElement(); + } + + /** + * Write a value into a XML stream writer. This method assumes the start and end of element is + * emitted by the caller. + * + * @param writer XML Stream writer + * @param type type definitions + * @param value object value + * @throws javax.xml.stream.XMLStreamException if an encoding problem occurs + */ + public void writeValue(final @Nonnull XMLStreamWriter writer, final @Nonnull TypeDefinition type, final Object value) throws XMLStreamException { + if (value == null) { + LOG.debug("Value of {}:{} is null, not encoding it", type.getQName().getNamespace(), type.getQName().getLocalName()); + return; + } + + final TypeDefinition baseType = XmlUtils.resolveBaseTypeFrom(type); + if (baseType instanceof IdentityrefTypeDefinition) { + write(writer, (IdentityrefTypeDefinition) baseType, value); + } else if (baseType instanceof InstanceIdentifierTypeDefinition) { + write(writer, (InstanceIdentifierTypeDefinition) baseType, value); + } else { + final TypeDefinitionAwareCodec codec = codecProvider.codecFor(baseType); + String text; + if (codec != null) { + try { + text = codec.serialize(value); + } catch (ClassCastException e) { + LOG.error("Provided node value {} did not have type {} required by mapping. Using stream instead.", value, baseType, e); + text = String.valueOf(value); + } + } else { + LOG.error("Failed to find codec for {}, falling back to using stream", baseType); + text = String.valueOf(value); + } + writer.writeCharacters(text); + } + } + + private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull IdentityrefTypeDefinition type, final @Nonnull Object value) throws XMLStreamException { + if (value instanceof QName) { + final QName qname = (QName) value; + final String prefix; + if (qname.getPrefix() != null && !qname.getPrefix().isEmpty()) { + prefix = qname.getPrefix(); + } else { + prefix = "x"; + } + + writer.writeNamespace(prefix, qname.getNamespace().toString()); + writer.writeCharacters(prefix + ':' + qname.getLocalName()); + } else { + LOG.debug("Value of {}:{} is not a QName but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass()); + writer.writeCharacters(String.valueOf(value)); + } + } + + private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull InstanceIdentifierTypeDefinition type, final @Nonnull Object value) throws XMLStreamException { + if (value instanceof YangInstanceIdentifier) { + LOG.debug("Writing InstanceIdentifier object {}", value); + write(writer, (YangInstanceIdentifier)value); + } else { + LOG.debug("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass()); + writer.writeCharacters(String.valueOf(value)); + } + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java new file mode 100644 index 0000000000..e07401a3e0 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java @@ -0,0 +1,281 @@ +/* + * 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.utils; + +import com.google.common.base.Optional; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder; +import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider; +import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.activation.UnsupportedDataTypeException; +import javax.annotation.Nonnull; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Common XML-related utility methods, which are not specific to a particular + * JAXP API. + */ +public class XmlUtils { + + public static final XmlCodecProvider DEFAULT_XML_CODEC_PROVIDER = new XmlCodecProvider() { + @Override + public TypeDefinitionAwareCodec> codecFor(final TypeDefinition baseType) { + return TypeDefinitionAwareCodec.from(baseType); + } + }; + + private XmlUtils() { + } + + private static final String BLANK = ""; + private static final Logger LOG = LoggerFactory.getLogger(XmlUtils.class); + + /** + * Converts the composite node to xml using rpc input schema node + * @param cNode + * @param schemaContext + * @return xml String + */ + public static String inputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){ + LOG.debug("Converting input composite node to xml {}", cNode); + if (cNode == null) return BLANK; + + if(schemaContext == null) return BLANK; + + Document domTree = null; + try { + Set rpcs = schemaContext.getOperations(); + for(RpcDefinition rpc : rpcs) { + if(rpc.getQName().equals(cNode.getNodeType())){ + LOG.debug("Found the rpc definition from schema context matching with input composite node {}", rpc.getQName()); + + CompositeNode inputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "input")); + domTree = XmlDocumentUtils.toDocument(inputContainer, rpc.getInput(), XmlDocumentUtils.defaultValueCodecProvider()); + + LOG.debug("input composite node to document conversion complete, document is {}", domTree); + break; + } + } + + } catch (UnsupportedDataTypeException e) { + LOG.error("Error during translation of CompositeNode to Document", e); + } + return domTransformer(domTree); + } + + /** + * Converts the composite node to xml String using rpc output schema node + * @param cNode + * @param schemaContext + * @return xml string + */ + public static String outputCompositeNodeToXml(CompositeNode cNode, SchemaContext schemaContext){ + LOG.debug("Converting output composite node to xml {}", cNode); + if (cNode == null) return BLANK; + + if(schemaContext == null) return BLANK; + + Document domTree = null; + try { + Set rpcs = schemaContext.getOperations(); + for(RpcDefinition rpc : rpcs) { + if(rpc.getQName().equals(cNode.getNodeType())){ + LOG.debug("Found the rpc definition from schema context matching with output composite node {}", rpc.getQName()); + + CompositeNode outputContainer = cNode.getFirstCompositeByName(QName.create(cNode.getNodeType(), "output")); + domTree = XmlDocumentUtils.toDocument(outputContainer, rpc.getOutput(), XmlDocumentUtils.defaultValueCodecProvider()); + + LOG.debug("output composite node to document conversion complete, document is {}", domTree); + break; + } + } + + } catch (UnsupportedDataTypeException e) { + LOG.error("Error during translation of CompositeNode to Document", e); + } + return domTransformer(domTree); + } + + private static String domTransformer(Document domTree) { + StringWriter writer = new StringWriter(); + try { + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.transform(new DOMSource(domTree), new StreamResult(writer)); + } catch (TransformerException e) { + + LOG.error("Error during translation of Document to OutputStream", e); + } + LOG.debug("Document to string conversion complete, xml string is {} ", writer.toString()); + + return writer.toString(); + } + + public static CompositeNode xmlToCompositeNode(String xml){ + if (xml==null || xml.length()==0) return null; + + Node dataTree; + try { + dataTree = XmlTreeBuilder.buildDataTree(new ByteArrayInputStream(xml.getBytes())); + } catch (XMLStreamException e) { + LOG.error("Error during building data tree from XML", e); + return null; + } + if (dataTree == null) { + LOG.error("data tree is null"); + return null; + } + if (dataTree instanceof SimpleNode) { + LOG.error("RPC XML was resolved as SimpleNode"); + return null; + } + return (CompositeNode) dataTree; + } + + /** + * Converts the xml to composite node using rpc input schema node + * @param rpc + * @param xml + * @param schemaContext + * @return CompositeNode object based on the input, if any of the input parameter is null, a null object is returned + */ + public static CompositeNode inputXmlToCompositeNode(QName rpc, String xml, SchemaContext schemaContext){ + LOG.debug("Converting input xml to composite node {}", xml); + if (xml==null || xml.length()==0) return null; + + if(rpc == null) return null; + + if(schemaContext == null) return null; + + CompositeNode compositeNode = null; + try { + + Document doc = XmlUtil.readXmlToDocument(xml); + Set rpcs = schemaContext.getOperations(); + for(RpcDefinition rpcDef : rpcs) { + if(rpcDef.getQName().equals(rpc)){ + LOG.debug("found the rpc definition from schema context matching rpc {}", rpc); + + if(rpcDef.getInput() == null) { + LOG.warn("found rpc definition's input is null"); + return null; + } + + QName input = rpcDef.getInput().getQName(); + NodeList nodeList = doc.getElementsByTagNameNS(input.getNamespace().toString(), "input"); + if(nodeList == null || nodeList.getLength() < 1) { + LOG.warn("xml does not have input entry. {}", xml); + return null; + } + Element xmlData = (Element)nodeList.item(0); + + List> dataNodes = XmlDocumentUtils.toDomNodes(xmlData, + Optional.of(rpcDef.getInput().getChildNodes()), schemaContext); + + LOG.debug("Converted xml input to list of nodes {}", dataNodes); + + final CompositeNodeBuilder it = ImmutableCompositeNode.builder(); + it.setQName(input); + it.add(ImmutableCompositeNode.create(input, dataNodes)); + compositeNode = it.toInstance(); + break; + } + } + } catch (SAXException e) { + LOG.error("Error during building data tree from XML", e); + } catch (IOException e) { + LOG.error("Error during building data tree from XML", e); + } + + LOG.debug("Xml to composite node conversion complete {} ", compositeNode); + return compositeNode; + } + + public static TypeDefinition resolveBaseTypeFrom(final @Nonnull TypeDefinition type) { + TypeDefinition superType = type; + while (superType.getBaseType() != null) { + superType = superType.getBaseType(); + } + return superType; + } + + /** + * This code is picked from yangtools and modified to add type of instance identifier + * output of instance identifier something like below for a flow ref composite node of type instance identifier, + * which has path arguments with predicates, whose value is of type java.lang.short + * + * /jdlk:nodes/jdlk:node[jdlk:id='openflow:205558455098190@java.lang.String'] + * /bgkj:table[bgkj:id='3@java.lang.Short'] + * /bgkj:flow[bgkj:id='156@java.lang.String'] + * + * + */ + + public static String encodeIdentifier(final RandomPrefix prefixes, final YangInstanceIdentifier id) { + StringBuilder textContent = new StringBuilder(); + for (PathArgument pathArgument : id.getPathArguments()) { + textContent.append('/'); + textContent.append(prefixes.encodeQName(pathArgument.getNodeType())); + if (pathArgument instanceof NodeIdentifierWithPredicates) { + Map predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues(); + + for (QName keyValue : predicates.keySet()) { + Object value = predicates.get(keyValue); + String type = value.getClass().getName(); + String predicateValue = String.valueOf(value); + textContent.append('['); + textContent.append(prefixes.encodeQName(keyValue)); + textContent.append("='"); + textContent.append(predicateValue); + textContent.append("@"); + textContent.append(type); + textContent.append("']"); + } + } else if (pathArgument instanceof NodeWithValue) { + textContent.append("[.='"); + textContent.append(((NodeWithValue) pathArgument).getValue()); + textContent.append("']"); + } + } + + return textContent.toString(); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf b/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf index 9585e9ffb6..6088dd0e0e 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/resources/application.conf @@ -7,7 +7,7 @@ odl-cluster{ remote { log-remote-lifecycle-events = off netty.tcp { - hostname = "192.168.141.142" + hostname = "192.168.141.141" port = 2551 } } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java index 595d8331eb..392c1e637d 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/RpcBrokerTest.java @@ -35,6 +35,7 @@ import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.ModifyAction; import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -72,15 +73,16 @@ public class RpcBrokerTest { Broker.ProviderSession brokerSession = Mockito.mock(Broker.ProviderSession.class); SchemaContext schemaContext = mock(SchemaContext.class); ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext)); - QName rpc = new QName(new URI("actor1"), "actor1"); - InvokeRpc invokeMsg = new InvokeRpc(rpc, null); + QName rpc = new QName(new URI("noactor1"), "noactor1"); + CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "no child"), new ArrayList>(), ModifyAction.REPLACE); + InvokeRpc invokeMsg = new InvokeRpc(rpc, input); rpcBroker.tell(invokeMsg, getRef()); Boolean getMsg = new ExpectMsg("ErrorResponse") { protected Boolean match(Object in) { if (in instanceof ErrorResponse) { ErrorResponse reply = (ErrorResponse)in; - return "No remote actor found for rpc execution.".equals(reply.getException().getMessage()); + return reply.getException().getMessage().contains("No remote actor found for rpc execution of :"); } else { throw noMatch(); } @@ -144,7 +146,8 @@ public class RpcBrokerTest { SchemaContext schemaContext = mock(SchemaContext.class); ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext)); QName rpc = new QName(new URI("actor1"), "actor1"); - InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, null); + CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "child1"), new ArrayList>(), ModifyAction.REPLACE); + InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(rpc)), input); rpcBroker.tell(invokeMsg, getRef()); Boolean getMsg = new ExpectMsg("ErrorResponse") { @@ -176,7 +179,8 @@ public class RpcBrokerTest { ActorRef rpcBrokerRemote = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "actor2"); // Add Routed RPC in table QName rpc = new QName(new URI("actor2"), "actor2"); - RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, null); + YangInstanceIdentifier identifier = YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(rpc)); + RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, identifier); final String route = rpcBrokerRemote.path().toString(); Set> routeIds = new HashSet<>(); routeIds.add(routeId); @@ -192,7 +196,7 @@ public class RpcBrokerTest { RpcResult result = Rpcs.getRpcResult(true, invokeRpcResult, errors); Future> rpcResult = Futures.immediateFuture(result); when(brokerSession.rpc(rpc, input)).thenReturn(rpcResult); - InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, input); + InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, identifier, input); rpcBroker.tell(invokeMsg, getRef()); //verify response msg diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RoutingTableTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RoutingTableTest.java index a57402a793..129a5a56e8 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RoutingTableTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RoutingTableTest.java @@ -39,7 +39,8 @@ public class RoutingTableTest { @Test public void addGlobalRouteNullRouteTest() { try { - RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, null, null); + QName type = new QName(new URI("actor1"), "actor1"); + RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null); routingTable.addGlobalRoute(routeId, null); Assert.fail("Null pointer exception was not thrown."); @@ -109,7 +110,8 @@ public class RoutingTableTest { @Test public void addRoutedRpcNullRouteTest() { try { - RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, null, null); + QName type = new QName(new URI("actor1"), "actor1"); + RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null); routingTable.addRoutedRpc(routeId, null); diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java new file mode 100644 index 0000000000..a408e1d55a --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java @@ -0,0 +1,133 @@ +/* + * 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.utils; + + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.io.ByteSource; +import junit.framework.Assert; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.ModifyAction; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; + +import javax.xml.parsers.DocumentBuilderFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + + +public class XmlUtilsTest { + + private static final DocumentBuilderFactory BUILDERFACTORY; + + static { + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setCoalescing(true); + factory.setIgnoringElementContentWhitespace(true); + factory.setIgnoringComments(true); + BUILDERFACTORY = factory; + } + + private SchemaContext schema; + private RpcDefinition testRpc; + + public static final String XML_CONTENT = "" + + "flowid" + + "/ltha:node/ltha:node1[ltha:id='3@java.lang.Short']" + + ""; + + @Before + public void setUp() throws Exception { + final ByteSource byteSource = new ByteSource() { + @Override + public InputStream openStream() throws IOException { + return XmlUtilsTest.this.getClass().getResourceAsStream("rpcTest.yang"); + } + }; + schema = new YangParserImpl().parseSources(Lists.newArrayList(byteSource)); + final Module rpcTestModule = schema.getModules().iterator().next(); + testRpc = rpcTestModule.getRpcs().iterator().next(); + } + + @Test + public void testNullInputXmlToComposite() { + CompositeNode node = XmlUtils.inputXmlToCompositeNode(testRpc.getQName(), null, schema); + Assert.assertNull(node); + } + + @Test + public void testNullRpcXmlToComposite() { + CompositeNode node = XmlUtils.inputXmlToCompositeNode(null, XML_CONTENT, schema); + Assert.assertNull(node); + } + + @Test + public void testInputXmlToCompositeNode() { + CompositeNode node = XmlUtils.inputXmlToCompositeNode(testRpc.getQName(), XML_CONTENT, schema); + ImmutableList input = (ImmutableList)node.getValue().get(0).getValue(); + SimpleNode firstNode = input.get(0); + + Assert.assertEquals("id", firstNode.getNodeType().getLocalName()); + Assert.assertEquals("flowid", firstNode.getValue()); + + SimpleNode secondNode = input.get(1); + Assert.assertEquals("flow", secondNode.getNodeType().getLocalName()); + + YangInstanceIdentifier instance = (YangInstanceIdentifier) secondNode.getValue(); + Iterable iterable = instance.getPathArguments(); + Iterator it = iterable.iterator(); + YangInstanceIdentifier.NodeIdentifier firstPath = (YangInstanceIdentifier.NodeIdentifier) it.next(); + Assert.assertEquals("node", firstPath.getNodeType().getLocalName()); + YangInstanceIdentifier.NodeIdentifierWithPredicates secondPath = (YangInstanceIdentifier.NodeIdentifierWithPredicates)it.next(); + Short value = (Short)secondPath.getKeyValues().values().iterator().next(); + Short expected = 3; + Assert.assertEquals(expected, value); + } + + @Test + public void testInputCompositeNodeToXML() { + CompositeNode input = XmlUtils.inputXmlToCompositeNode(testRpc.getQName(), XML_CONTENT, schema); + List> childNodes = new ArrayList(); + childNodes.add(input); + QName rpcQName = schema.getOperations().iterator().next().getQName(); + CompositeNode node = new ImmutableCompositeNode(rpcQName, input.getValue(), ModifyAction.REPLACE); + String xml = XmlUtils.inputCompositeNodeToXml(node, schema); + Assert.assertNotNull(xml); + Assert.assertTrue(xml.contains("3@java.lang.Short")); + } + + @Test + public void testNullCompositeNodeToXml(){ + String xml = XmlUtils.inputCompositeNodeToXml(null, schema); + Assert.assertTrue(xml.isEmpty()); + } + + @Test + public void testNullSchemaCompositeNodeToXml(){ + String xml = XmlUtils.inputCompositeNodeToXml(new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "child1"), new ArrayList>(), ModifyAction.REPLACE), null); + Assert.assertTrue(xml.isEmpty()); + } + + +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang new file mode 100644 index 0000000000..5fc564fec3 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang @@ -0,0 +1,32 @@ +/* + * 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 + */ +module rpc-test { + yang-version 1; + namespace "urn:opendaylight:controller:rpc:test"; + prefix "rpct"; + + revision 2014-07-29 { + description "rpc test"; + } + + typedef flow-ref { + type instance-identifier; + } + + rpc add-flow { + input { + leaf id { + type string; + } + + leaf flow { + type flow-ref; + } + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfProviderImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfProviderImpl.java index adb176a65d..559be5aa6f 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfProviderImpl.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfProviderImpl.java @@ -21,13 +21,13 @@ import org.opendaylight.controller.sal.restconf.impl.ControllerContext; 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 listenerRegistration; + private ListenerRegistration listenerRegistration; private PortNumber port; public void setWebsocketPort(PortNumber port) { this.port = port; @@ -43,7 +43,7 @@ public class RestconfProviderImpl implements Provider, AutoCloseable, RestConnec 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)); diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java index dad7a2cda2..f8bcbe3c61 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java @@ -508,9 +508,9 @@ public class ControllerContext implements SchemaContextListener { 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; } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/streams/listeners/Notificator.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/streams/listeners/Notificator.java index cf1bcd6a30..99bd8c5aaf 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/streams/listeners/Notificator.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/streams/listeners/Notificator.java @@ -114,7 +114,7 @@ public class Notificator { result = result.substring(1); } if (result.endsWith("/")) { - result = result.substring(0, result.length()); + result = result.substring(0, result.length()-1); } return result; } diff --git a/opendaylight/md-sal/samples/pom.xml b/opendaylight/md-sal/samples/pom.xml index c601647a2e..ae7d323480 100644 --- a/opendaylight/md-sal/samples/pom.xml +++ b/opendaylight/md-sal/samples/pom.xml @@ -15,6 +15,7 @@ toaster toaster-consumer toaster-provider + toaster-config l2switch diff --git a/opendaylight/md-sal/samples/toaster-config/pom.xml b/opendaylight/md-sal/samples/toaster-config/pom.xml new file mode 100644 index 0000000000..b30c4ba12f --- /dev/null +++ b/opendaylight/md-sal/samples/toaster-config/pom.xml @@ -0,0 +1,46 @@ + + + + + 4.0.0 + + org.opendaylight.controller.samples + sal-samples + 1.1-SNAPSHOT + + toaster-config + Configuration files for toaster + jar + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + + attach-artifact + + package + + + + ${project.build.directory}/classes/initial/03-toaster-sample.xml + xml + config + + + + + + + + + diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/03-toaster-sample.xml b/opendaylight/md-sal/samples/toaster-config/src/main/resources/initial/03-toaster-sample.xml similarity index 99% rename from opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/03-toaster-sample.xml rename to opendaylight/md-sal/samples/toaster-config/src/main/resources/initial/03-toaster-sample.xml index 3958e18560..2e8c7d5ce6 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/03-toaster-sample.xml +++ b/opendaylight/md-sal/samples/toaster-config/src/main/resources/initial/03-toaster-sample.xml @@ -26,7 +26,7 @@ binding:binding-async-data-broker binding-data-broker - + binding:binding-notification-service @@ -54,7 +54,7 @@ - + diff --git a/opendaylight/netconf/netconf-config/pom.xml b/opendaylight/netconf/netconf-config/pom.xml new file mode 100644 index 0000000000..db5d14d75a --- /dev/null +++ b/opendaylight/netconf/netconf-config/pom.xml @@ -0,0 +1,46 @@ + + + + + 4.0.0 + + org.opendaylight.controller + netconf-subsystem + 0.2.5-SNAPSHOT + + netconf-config + Configuration files for netconf + jar + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + + attach-artifact + + package + + + + ${project.build.directory}/classes/initial/01-netconf.xml + xml + config + + + + + + + + + diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-netconf.xml b/opendaylight/netconf/netconf-config/src/main/resources/initial/01-netconf.xml similarity index 100% rename from opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-netconf.xml rename to opendaylight/netconf/netconf-config/src/main/resources/initial/01-netconf.xml diff --git a/opendaylight/netconf/netconf-connector-config/pom.xml b/opendaylight/netconf/netconf-connector-config/pom.xml new file mode 100644 index 0000000000..d9cc5eab43 --- /dev/null +++ b/opendaylight/netconf/netconf-connector-config/pom.xml @@ -0,0 +1,46 @@ + + + + + 4.0.0 + + org.opendaylight.controller + netconf-subsystem + 0.2.5-SNAPSHOT + + netconf-connector-config + Configuration files for netconf-connector + jar + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + + attach-artifact + + package + + + + ${project.build.directory}/classes/initial/99-netconf-connector.xml + xml + config + + + + + + + + + diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/99-netconf-connector.xml b/opendaylight/netconf/netconf-connector-config/src/main/resources/initial/99-netconf-connector.xml similarity index 100% rename from opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/99-netconf-connector.xml rename to opendaylight/netconf/netconf-connector-config/src/main/resources/initial/99-netconf-connector.xml diff --git a/opendaylight/netconf/pom.xml b/opendaylight/netconf/pom.xml index 937949a17e..c72705d50e 100644 --- a/opendaylight/netconf/pom.xml +++ b/opendaylight/netconf/pom.xml @@ -20,6 +20,7 @@ netconf-api netconf-cli + netconf-config netconf-impl config-netconf-connector netconf-util @@ -32,6 +33,7 @@ netconf-monitoring ietf-netconf-monitoring ietf-netconf-monitoring-extension + netconf-connector-config diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronSubnet.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronSubnet.java index 840029006b..1f10b39513 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronSubnet.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronSubnet.java @@ -75,7 +75,7 @@ public class NeutronSubnet extends ConfigurationObject implements Serializable, */ List myPorts; - boolean gatewayIPAssigned; + Boolean gatewayIPAssigned; public NeutronSubnet() { myPorts = new ArrayList(); @@ -299,7 +299,7 @@ public class NeutronSubnet extends ConfigurationObject implements Serializable, 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) { @@ -460,6 +460,10 @@ public class NeutronSubnet extends ConfigurationObject implements Serializable, gatewayIPAssigned = false; } + public Boolean getGatewayIPAllocated() { + return gatewayIPAssigned; + } + @Override public String toString() { return "NeutronSubnet [subnetUUID=" + subnetUUID + ", networkUUID=" + networkUUID + ", name=" + name diff --git a/opendaylight/northbound/commons/pom.xml b/opendaylight/northbound/commons/pom.xml index a2d2dac112..cbc1f0c328 100644 --- a/opendaylight/northbound/commons/pom.xml +++ b/opendaylight/northbound/commons/pom.xml @@ -91,7 +91,6 @@ javax.ws.rs.core, javax.xml.bind, javax.xml.bind.annotation, - org.objectweb.asm, org.opendaylight.controller.sal.utils, org.opendaylight.controller.sal.core, org.opendaylight.controller.sal.authorization, diff --git a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronRoutersNorthbound.java b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronRoutersNorthbound.java index 17b2fcfcf9..806e853b36 100644 --- a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronRoutersNorthbound.java +++ b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronRoutersNorthbound.java @@ -422,7 +422,9 @@ public class NeutronRoutersNorthbound { 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(); } } @@ -498,7 +500,9 @@ public class NeutronRoutersNorthbound { 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(); } }