<classifier>features</classifier>
<type>xml</type>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>features-netconf</artifactId>
+ <classifier>features</classifier>
+ <type>xml</type>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>features-config-persister</artifactId>
<repository>mvn:org.opendaylight.controller/features-config/${config.version}/xml/features</repository>
<repository>mvn:org.opendaylight.controller/features-config-persister/${config.version}/xml/features</repository>
<repository>mvn:org.opendaylight.controller/features-config-netty/${config.version}/xml/features</repository>
+ <repository>mvn:org.opendaylight.controller/features-netconf/${netconf.version}/xml/features</repository>
<repository>mvn:org.opendaylight.controller/features-akka/${commons.opendaylight.version}/xml/features</repository>
<feature name='odl-mdsal-all' version='${project.version}' description="OpenDaylight :: MDSAL :: All">
<feature version='${project.version}'>odl-mdsal-broker</feature>
<bundle>mvn:org.opendaylight.controller/sal-common-impl/${mdsal.version}</bundle>
<bundle>mvn:org.opendaylight.controller/sal-common-util/${mdsal.version}</bundle>
</feature>
+
+ <!-- TODO move to netconf features, however there are some weird dependencies on features-config-persister all over that cause cyclic dependencies-->
+ <feature name='odl-netconf-mdsal' version='${project.version}' description="OpenDaylight :: Netconf :: All">
+ <feature version='${config.version}'>odl-config-all</feature>
+ <feature version='${netconf.version}'>odl-netconf-all</feature>
+ <bundle>mvn:org.opendaylight.controller/netconf-ssh/${netconf.version}</bundle>
+ <feature version='${mdsal.version}'>odl-mdsal-broker</feature>
+ <bundle>mvn:org.opendaylight.controller/mdsal-netconf-connector/${netconf.version}</bundle>
+ <!-- TODO 01-netconf.xml file requires netconf-config-dispatcher to be present and its part of netconf-connector features. Clean Up-->
+ <bundle>mvn:org.opendaylight.controller/netconf-config-dispatcher/${config.version}</bundle>
+ <configfile finalname='${config.configfile.directory}/${config.netconf.client.configfile}'>mvn:org.opendaylight.controller/netconf-config/${netconf.version}/xml/config</configfile>
+ <configfile finalname='${config.configfile.directory}/${config.netconf.mdsal.configfile}'>mvn:org.opendaylight.controller/netconf-mdsal-config/${netconf.version}/xml/config</configfile>
+ </feature>
+
<feature name='odl-mdsal-broker' version='${project.version}' description="OpenDaylight :: MDSAL :: Broker">
<feature version='${yangtools.version}'>odl-yangtools-common</feature>
<feature version='${yangtools.version}'>odl-yangtools-binding</feature>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-config-dispatcher</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>mdsal-netconf-connector</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-tcp</artifactId>
-->
<feature version='${project.version}'>odl-netconf-connector</feature>
<feature version='${project.version}'>odl-netconf-connector-ssh</feature>
+
+
</feature>
<!--
Necessary TODO: Define your features. It is useful to list then in order of dependency. So if A depends on B, list A first.
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-mdsal-config</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-auth</artifactId>
<config.xsql.configfile>04-xsql.xml</config.xsql.configfile>
<config.netconf.client.configfile>01-netconf.xml</config.netconf.client.configfile>
<config.toaster.configfile>03-toaster-sample.xml</config.toaster.configfile>
+ <config.netconf.mdsal.configfile>08-mdsal-netconf.xml</config.netconf.mdsal.configfile>
<config.restconf.configfile>10-rest-connector.xml</config.restconf.configfile>
<config.netconf.connector.configfile>99-netconf-connector.xml</config.netconf.connector.configfile>
<configuration.implementation.version>0.5.0-SNAPSHOT</configuration.implementation.version>
/**
*
*/
+@Deprecated
public final class NeverReconnectStrategyFactoryModule extends org.opendaylight.controller.config.yang.protocol.framework.AbstractNeverReconnectStrategyFactoryModule
{
/**
*
*/
+@Deprecated
public class NeverReconnectStrategyFactoryModuleFactory extends org.opendaylight.controller.config.yang.protocol.framework.AbstractNeverReconnectStrategyFactoryModuleFactory
{
/**
*
*/
+@Deprecated
public final class ReconnectImmediatelyStrategyFactoryModule extends org.opendaylight.controller.config.yang.protocol.framework.AbstractReconnectImmediatelyStrategyFactoryModule
{
/**
*
*/
+@Deprecated
public class ReconnectImmediatelyStrategyFactoryModuleFactory extends org.opendaylight.controller.config.yang.protocol.framework.AbstractReconnectImmediatelyStrategyFactoryModuleFactory
{
/**
*
*/
+@Deprecated
public final class TimedReconnectStrategyFactoryModule extends org.opendaylight.controller.config.yang.protocol.framework.AbstractTimedReconnectStrategyFactoryModule
{
/**
*
*/
+@Deprecated
public class TimedReconnectStrategyFactoryModuleFactory extends org.opendaylight.controller.config.yang.protocol.framework.AbstractTimedReconnectStrategyFactoryModuleFactory
{
* 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.
*/
+@Deprecated
public abstract class AbstractDispatcher<S extends ProtocolSession<?>, L extends SessionListener<?, ?, ?>> implements Closeable {
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+@Deprecated
public abstract class AbstractProtocolSession<M> extends SimpleChannelInboundHandler<Object> implements ProtocolSession<M> {
private static final Logger LOG = LoggerFactory.getLogger(AbstractProtocolSession.class);
* @param <M> Protocol message type
* @param <S> Protocol session type, has to extend ProtocolSession<M>
*/
+@Deprecated
public abstract class AbstractSessionNegotiator<M, S extends AbstractProtocolSession<?>> extends ChannelInboundHandlerAdapter implements SessionNegotiator<S> {
private final Logger LOG = LoggerFactory.getLogger(AbstractSessionNegotiator.class);
private final Promise<S> promise;
* Utility ReconnectStrategy singleton, which will cause the reconnect process
* to always fail.
*/
+@Deprecated
@ThreadSafe
public final class NeverReconnectStrategy implements ReconnectStrategy {
private final EventExecutor executor;
*
* This interface should be implemented by a final class representing a protocol specific session.
*/
+@Deprecated
public interface ProtocolSession<T> extends Closeable {
@Override
void close();
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+@Deprecated
@ThreadSafe
final class ProtocolSessionPromise<S extends ProtocolSession<?>> extends DefaultPromise<S> {
private static final Logger LOG = LoggerFactory.getLogger(ProtocolSessionPromise.class);
* Utility ReconnectStrategy singleton, which will cause the reconnect process
* to immediately schedule a reconnection attempt.
*/
+@Deprecated
@ThreadSafe
public final class ReconnectImmediatelyStrategy implements ReconnectStrategy {
private static final Logger LOG = LoggerFactory.getLogger(ReconnectImmediatelyStrategy.class);
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+@Deprecated
final class ReconnectPromise<S extends ProtocolSession<?>, L extends SessionListener<?, ?, ?>> extends DefaultPromise<Void> {
private static final Logger LOG = LoggerFactory.getLogger(ReconnectPromise.class);
* not attempt any more connection attempts and should abort the reconnection
* process.
*/
+@Deprecated
public interface ReconnectStrategy {
/**
* Query the strategy for the connect timeout.
* primarily useful for allowing injection of a specific type of strategy for
* on-demand use, pretty much like you would use a ThreadFactory.
*/
+@Deprecated
public interface ReconnectStrategyFactory {
/**
* Create a new ReconnectStrategy.
* implemented by a protocol specific abstract class, that is extended by
* a final class that implements the methods.
*/
+@Deprecated
public interface SessionListener<M, S extends ProtocolSession<?>, T extends TerminationReason> extends EventListener {
/**
* Fired when the session was established successfully.
* implemented by a protocol specific abstract class, that is extended by
* a final class that implements the methods.
*/
+@Deprecated
public interface SessionListenerFactory<T extends SessionListener<?, ?, ?>> {
/**
* Returns one session listener
*
* @param <T> Protocol session type.
*/
+@Deprecated
public interface SessionNegotiator<T extends ProtocolSession<?>> extends ChannelInboundHandler {
}
*
* @param <S> session type
*/
+@Deprecated
public interface SessionNegotiatorFactory<M, S extends ProtocolSession<?>, L extends SessionListener<?, ?, ?>> {
/**
* Create a new negotiator attached to a channel, which will notify
/**
* Marker interface for grouping session termination cause.
*/
+@Deprecated
public interface TerminationReason {
/**
*
* Both these caps can be combined, with the strategy giving up as soon as the first one is reached.
*/
+@Deprecated
@ThreadSafe
public final class TimedReconnectStrategy implements ReconnectStrategy {
private static final Logger LOG = LoggerFactory.getLogger(TimedReconnectStrategy.class);
<type>xml</type>
</dependency>
+ <!-- MessageBus -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>message-bus-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>message-bus-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
</dependencies>
</dependencyManagement>
</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+
+This program and the accompanying materials are made available under the
+terms of the Eclipse Public License v1.0 which accompanies this distribution,
+and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>message-bus-api</artifactId>
+ <name>${project.artifactId}</name>
+
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-inventory</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>yang-ext</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-topology</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${project.build.directory}/generated-sources/sal</outputBaseDir>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${project.build.directory}/generated-sources/config</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>target/site/models</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.8</version>
+ <executions>
+ <execution>
+ <id>add-source</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${project.build.directory}/generated-sources/config</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Export-Package>org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.*</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+module event-aggregator {
+ // FIXME: this module needs to be split up to concepts and API
+ // as the concepts are shared with the other model in this
+ // package.
+ yang-version 1;
+ namespace "urn:cisco:params:xml:ns:yang:messagebus:eventaggregator";
+ prefix "eventaggregator";
+
+ organization "Cisco Systems, Inc.";
+ contact "Robert Gallas";
+
+ description
+ "Module implementing message but RPC.
+
+ Copyright (c)2014 Cisco Systems, Inc. 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";
+
+ revision "2014-12-02" {
+ description "Initial revision";
+ }
+
+ typedef pattern {
+ type string {
+ length 1..max;
+ }
+
+ // FIXME: make this a regular expression
+ description "A match pattern. Specifically this is a wildcard pattern.";
+ }
+
+ typedef notification-pattern {
+ type pattern;
+ description
+ "Pattern for matching candidate notification types. This pattern is to be
+ applied against the concatenation of the namespace of the module which
+ defines that particular notification, followed by a single colon, and
+ then followed by notification identifier, as supplied in the argument to
+ the notification statement.";
+ }
+
+ typedef topic-id {
+ type string {
+ length 1..max;
+ }
+ description
+ "A topic identifier. It uniquely defines a topic as seen by the the user
+ of this model's RPCs";
+ }
+
+ // FIXME: we would really like to share instances here, but that requires some sort
+ // of sane reference counting. The reason for sharing is the data path part
+ // of notification delivery -- multiple creators of topics can still share
+ // a single data path.
+ rpc create-topic {
+ description
+ "Create a new topic. A topic is an aggregation of several notification
+ types from a set of nodes. Each successful invocation results in a unique
+ topic being created. The caller is responsible for removing the topic
+ once it is no longer needed.";
+
+ input {
+ leaf notification-pattern {
+ type notification-pattern;
+ mandatory true;
+ description
+ "Pattern matching notification which should be forwarded into this
+ topic.";
+ }
+
+ leaf node-id-pattern {
+ type pattern;
+ mandatory true;
+ description
+ "Pattern for matching candidate event source nodes when looking
+ for contributors to the topic. The pattern will be applied against
+ /network-topology/topology/node/node-id";
+ }
+ }
+
+ output {
+ leaf topic-id {
+ type topic-id;
+ mandatory true;
+ }
+ }
+ }
+
+ rpc destroy-topic {
+ description
+ "Destroy a topic. No further messages will be delivered to it.";
+
+ input {
+ leaf topic-id {
+ type topic-id;
+ mandatory true;
+ }
+ }
+ }
+
+ notification topic-notification {
+ description
+ "Notification of an event occuring on a particular node. This notification
+ acts as an encapsulation for the event being delivered.";
+
+ leaf topic-id {
+ type topic-id;
+ mandatory true;
+ description
+ "Topic to which this event is being delivered.";
+ }
+
+ leaf node-id {
+ // FIXME: should be topology node ID
+ type string;
+ mandatory true;
+ description
+ "Node ID of the node which generated the event.";
+ }
+
+ anyxml payload {
+ mandatory true;
+ description
+ "Encapsulated notification. The format is the XML representation of
+ a notification according to RFC6020 section 7.14.2.";
+ }
+ }
+}
--- /dev/null
+module event-source {
+ yang-version 1;
+ namespace "urn:cisco:params:xml:ns:yang:messagebus:eventsource";
+ prefix "eventsource";
+
+ import event-aggregator { prefix aggr; }
+ import network-topology { prefix nt; revision-date "2013-10-21"; }
+ import opendaylight-inventory {prefix inv; revision-date "2013-08-19"; }
+ import yang-ext {prefix ext; revision-date "2013-07-09"; }
+
+ organization "Cisco Systems, Inc.";
+ contact "Robert Gallas";
+
+ description
+ "Base model for a topology where individual nodes can produce events.
+
+ Module implementing event source topology and encapped notification.
+
+ Copyright (c)2014 Cisco Systems, Inc. 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";
+
+ revision "2014-12-02" {
+ description "first revision";
+ }
+
+ // FIXME: expand this
+ typedef join-topic-status {
+ type enumeration {
+ enum up;
+ enum down;
+ }
+ description "Object status";
+ }
+
+ // FIXME: migrate to topology
+ typedef node-ref {
+ type leafref {
+ path "/inv:nodes/inv:node/inv:id";
+ }
+ }
+
+ grouping topology-event-source-type {
+ container topology-event-source {
+ presence "indicates an event source-aware topology";
+ }
+ }
+
+ rpc join-topic {
+ input {
+ leaf node {
+ ext:context-reference "inv:node-context";
+ type "instance-identifier";
+ }
+ leaf topic-id {
+ type aggr:topic-id;
+ description "in current implementation notification-pattern is defined by topic-id.
+ By persisting topic definition we could omit notification-pattern";
+ }
+ leaf notification-pattern {
+ type aggr:notification-pattern;
+ }
+ }
+
+ output {
+ leaf status {
+ type join-topic-status;
+ }
+ }
+ }
+
+ augment "/nt:network-topology/nt:topology/nt:topology-types" {
+ uses topology-event-source-type;
+ }
+
+ augment "/nt:network-topology/nt:topology/nt:node" {
+ when "../../nt:topology-types/topology-event-source";
+ leaf event-source-node {
+ type node-ref;
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<project xmlns="http://maven.apache.org/POM/4.0.0"\r
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">\r
+ <modelVersion>4.0.0</modelVersion>\r
+\r
+ <parent>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>sal-parent</artifactId>\r
+ <version>1.2.0-SNAPSHOT</version>\r
+ </parent>\r
+\r
+ <artifactId>message-bus-impl</artifactId>\r
+ <name>${project.artifactId}</name>\r
+\r
+ <packaging>bundle</packaging>\r
+\r
+ <dependencies>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>ietf-netconf-notifications</artifactId>\r
+ <version>0.3.0-SNAPSHOT</version>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>sal-binding-api</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>sal-core-api</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>sal-common-util</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.yangtools</groupId>\r
+ <artifactId>yang-data-impl</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>config-api</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>message-bus-api</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>sal-binding-config</artifactId>\r
+ </dependency>\r
+ </dependencies>\r
+\r
+ <build>\r
+ <plugins>\r
+ <plugin>\r
+ <groupId>org.opendaylight.yangtools</groupId>\r
+ <artifactId>yang-maven-plugin</artifactId>\r
+ <executions>\r
+ <execution>\r
+ <goals>\r
+ <goal>generate-sources</goal>\r
+ </goals>\r
+ <configuration>\r
+ <codeGenerators>\r
+ <generator>\r
+ <codeGeneratorClass>\r
+ org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl\r
+ </codeGeneratorClass>\r
+ <outputBaseDir>\r
+ ${project.build.directory}/generated-sources/sal\r
+ </outputBaseDir>\r
+ </generator>\r
+ <generator>\r
+ <codeGeneratorClass>\r
+ org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator\r
+ </codeGeneratorClass>\r
+ <outputBaseDir>${project.build.directory}/generated-sources/config</outputBaseDir>\r
+ <additionalConfiguration>\r
+ <namespaceToPackage1>\r
+ urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang\r
+ </namespaceToPackage1>\r
+ </additionalConfiguration>\r
+ </generator>\r
+ <generator>\r
+ <codeGeneratorClass>org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl</codeGeneratorClass>\r
+ <outputBaseDir>target/site/models</outputBaseDir>\r
+ </generator>\r
+ </codeGenerators>\r
+ <inspectDependencies>true</inspectDependencies>\r
+ </configuration>\r
+ </execution>\r
+ </executions>\r
+ </plugin>\r
+ <plugin>\r
+ <groupId>org.codehaus.mojo</groupId>\r
+ <artifactId>build-helper-maven-plugin</artifactId>\r
+ <version>1.8</version>\r
+ <executions>\r
+ <execution>\r
+ <id>add-source</id>\r
+ <phase>generate-sources</phase>\r
+ <goals>\r
+ <goal>add-source</goal>\r
+ </goals>\r
+ <configuration>\r
+ <sources>\r
+ <source>${project.build.directory}/generated-sources/config</source>\r
+ </sources>\r
+ </configuration>\r
+ </execution>\r
+ </executions>\r
+ </plugin>\r
+ </plugins>\r
+ </build>\r
+</project>\r
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yang.messagebus.app.impl;
+
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.mdsal.InitializationContext;
+import org.opendaylight.controller.mdsal.Providers;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class MessageBusAppImplModule extends org.opendaylight.controller.config.yang.messagebus.app.impl.AbstractMessageBusAppImplModule {
+ private static final Logger LOGGER = LoggerFactory.getLogger(MessageBusAppImplModule.class);
+
+ private BundleContext bundleContext;
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public MessageBusAppImplModule( ModuleIdentifier identifier, DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public MessageBusAppImplModule( ModuleIdentifier identifier,
+ DependencyResolver dependencyResolver,
+ MessageBusAppImplModule oldModule,
+ java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ protected void customValidation() {}
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ List<NamespaceToStream> namespaceMapping = getNamespaceToStream();
+ InitializationContext ic = new InitializationContext(namespaceMapping);
+
+ final Providers.BindingAware bap = new Providers.BindingAware(ic);
+ final Providers.BindingIndependent bip = new Providers.BindingIndependent(ic);
+
+ getBindingBrokerDependency().registerProvider(bap, getBundleContext());
+ getDomBrokerDependency().registerProvider(bip);
+
+ AutoCloseable closer = new AutoCloseable() {
+ @Override public void close() {
+ closeProvider(bap);
+ closeProvider(bip);
+ }
+ };
+
+ return closer;
+ }
+
+ private void closeProvider(AutoCloseable closable) {
+ try {
+ closable.close();
+ } catch (Exception e) {
+ LOGGER.error("Exception while closing: {}\n Exception: {}", closable, e);
+ }
+ }
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: message-bus-app-impl yang module local name: messagebus-app-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Tue Feb 03 09:03:11 CET 2015
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.messagebus.app.impl;
+
+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 MessageBusAppImplModuleFactory extends org.opendaylight.controller.config.yang.messagebus.app.impl.AbstractMessageBusAppImplModuleFactory {
+ @Override
+ public Module createModule(String instanceName,
+ DependencyResolver dependencyResolver,
+ BundleContext bundleContext) {
+
+ MessageBusAppImplModule module =
+ (MessageBusAppImplModule) super.createModule(instanceName,
+ dependencyResolver,
+ bundleContext);
+
+ module.setBundleContext(bundleContext);
+
+ return module;
+ }
+
+ @Override
+ public Module createModule(String instanceName,
+ DependencyResolver dependencyResolver,
+ DynamicMBeanWithInstance old,
+ BundleContext bundleContext)
+ throws Exception {
+
+ MessageBusAppImplModule module =
+ (MessageBusAppImplModule) super.createModule(instanceName,
+ dependencyResolver,
+ old,
+ bundleContext);
+
+ module.setBundleContext(bundleContext);
+
+ return module;
+ }
+
+}
--- /dev/null
+/*
+ * 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.mdsal;
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class DataStore {
+ private static final FutureCallback<Void> DEFAULT_CALLBACK =
+ new FutureCallback<Void>() {
+ public void onSuccess(Void result) {
+ // TODO: Implement default behaviour
+ }
+
+ public void onFailure(Throwable t) {
+ // TODO: Implement default behaviour
+ };
+ };
+
+ private final MdSAL mdSAL;
+
+ public DataStore(MdSAL mdSAL) {
+ this.mdSAL = mdSAL;
+ }
+
+ public ListenerRegistration<DataChangeListener> registerDataChangeListener(LogicalDatastoreType store,
+ InstanceIdentifier<?> path,
+ DataChangeListener listener,
+ AsyncDataBroker.DataChangeScope triggeringScope) {
+ return mdSAL.getDataBroker().registerDataChangeListener(store, path, listener, triggeringScope);
+ }
+
+ public <T extends DataObject> void asyncPUT(LogicalDatastoreType datastoreType,
+ InstanceIdentifier<T> path,
+ T data) {
+ asyncPUT(datastoreType, path, data, DEFAULT_CALLBACK);
+ }
+
+ public <T extends DataObject> void asyncPUT(LogicalDatastoreType datastoreType,
+ InstanceIdentifier<T> path,
+ T data,
+ FutureCallback<Void> callback) {
+ WriteTransaction tx = mdSAL.getDataBroker().newWriteOnlyTransaction();
+ tx.put(datastoreType, path, data, true);
+ execPut(tx, callback);
+ }
+
+ public <T extends DataObject> T read(LogicalDatastoreType datastoreType,
+ InstanceIdentifier<T> path) {
+
+ ReadOnlyTransaction tx = mdSAL.getDataBroker().newReadOnlyTransaction();
+ T result = null;
+
+ try {
+ result = tx.read(datastoreType, path).get().get();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ return result;
+ }
+
+ private static void execPut(WriteTransaction tx, FutureCallback<Void> callback) {
+ Futures.addCallback(tx.submit(), callback);
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.mdsal;
+
+import org.opendaylight.controller.config.yang.messagebus.app.impl.NamespaceToStream;
+import org.opendaylight.controller.messagebus.app.impl.EventAggregator;
+import org.opendaylight.controller.messagebus.app.impl.EventSourceManager;
+import org.opendaylight.controller.messagebus.app.impl.EventSourceTopology;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class InitializationContext {
+ private static final Logger LOGGER = LoggerFactory.getLogger(InitializationContext.class);
+
+ private final MdSAL mdSal;
+ private final DataStore dataStore;
+ private final EventSourceTopology eventSourceTopology;
+ private final EventSourceManager eventSourceManager;
+ private final EventAggregator eventAggregator;
+
+ public InitializationContext(List<NamespaceToStream> namespaceMapping) {
+ this.mdSal = new MdSAL();
+ this.dataStore = new DataStore(mdSal);
+ this.eventSourceTopology = new EventSourceTopology(dataStore);
+ this.eventSourceManager = new EventSourceManager(dataStore, mdSal, eventSourceTopology, namespaceMapping);
+ this.eventAggregator = new EventAggregator(mdSal, eventSourceTopology);
+ }
+
+ public synchronized void set(BindingAwareBroker.ProviderContext session) {
+ mdSal.setBindingAwareContext(session);
+
+ if (mdSal.isReady()) {
+ initialize();
+ }
+ }
+
+ public synchronized void set(Broker.ProviderSession session) {
+ mdSal.setBindingIndependentContext(session);
+
+ if (mdSal.isReady()) {
+ initialize();
+ }
+ }
+
+ private void initialize() {
+ eventSourceTopology.mdsalReady();
+ eventSourceManager.mdsalReady();
+ eventAggregator.mdsalReady();
+
+ LOGGER.info("InitializationContext started.");
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.mdsal;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.controller.sal.binding.api.mount.MountInstance;
+import org.opendaylight.controller.sal.binding.api.mount.MountProviderService;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
+import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
+import org.opendaylight.controller.sal.core.api.notify.NotificationService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.QName;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MdSAL {
+ private static final Logger LOGGER = LoggerFactory.getLogger(MdSAL.class);
+
+ private BindingAwareBroker.ProviderContext bindingAwareContext;
+ private Broker.ProviderSession bindingIndependentContext;
+
+ // -----------------------------
+ // ----- FRAMEWORK METHODS -----
+ // -----------------------------
+ public void setBindingAwareContext(BindingAwareBroker.ProviderContext bindingAwareContext) {
+ this.bindingAwareContext = bindingAwareContext;
+ }
+
+ public void setBindingIndependentContext(Broker.ProviderSession bindingIndependentContext) {
+ this.bindingIndependentContext = bindingIndependentContext;
+ }
+
+ //TODO: We should hide brokers and expose functionalities instead
+ public DataBroker getDataBroker() {
+ return getBaSalService(DataBroker.class);
+ }
+
+ public synchronized boolean isReady() {
+ return (bindingAwareContext != null && bindingIndependentContext != null);
+ }
+
+ // -----------------------
+ // ----- API METHODS -----
+ // -----------------------
+ // TODO: Factor out API methods to interface
+ // method does not return registration object. Rather will hold references internally and manipulate using node id and API
+ public <T extends RpcService> void addRpcImplementation(Class<T> serviceInterface,
+ T implementation)
+ throws IllegalStateException {
+ bindingAwareContext.addRpcImplementation(serviceInterface, implementation);
+ }
+
+ // method does not return registration object. Rather will hold references internally and manipulate using node id and API
+ public <T extends RpcService> void addRpcImplementation(Node node,
+ Class<T> serviceInterface,
+ T implementation)
+ throws IllegalStateException {
+ BindingAwareBroker.RoutedRpcRegistration<T> registration
+ = addRoutedRpcImplementation(serviceInterface, implementation);
+
+ NodeRef nodeRef = createNodeRef(node.getId());
+ registration.registerPath(NodeContext.class, nodeRef.getValue());
+ }
+
+ public ListenerRegistration<NotificationListener> addNotificationListener(String nodeId,
+ QName notification,
+ NotificationListener listener) {
+ YangInstanceIdentifier yii = inventoryNodeBIIdentifier(nodeId);
+
+ NotificationService notificationService =
+ getBiSalService(DOMMountPointService.class)
+ .getMountPoint(yii)
+ .get()
+ .getService(NotificationPublishService.class)
+ .get();
+
+ ListenerRegistration<NotificationListener> registration =
+ notificationService.addNotificationListener(notification, listener);
+
+ LOGGER.info("Notification listener registered for {}, at node {}", notification, nodeId);
+
+ return registration;
+ }
+
+ public ListenerRegistration<NotificationListener> addNotificationListener(QName notification,
+ NotificationListener listener) {
+ NotificationService notificationService =
+ getBiSalService(NotificationPublishService.class);
+
+ ListenerRegistration<NotificationListener> registration =
+ notificationService.addNotificationListener(notification, listener);
+
+ LOGGER.info("Notification listener registered for {}.", notification);
+
+ return registration;
+ }
+
+ public <T extends RpcService> T getRpcService(Class<T> serviceInterface) {
+ return bindingAwareContext.getRpcService(serviceInterface);
+ }
+
+ public <T extends RpcService> T getRpcService(String nodeId, Class<T> serviceInterface) {
+ MountProviderService mountProviderService = getBaSalService(MountProviderService.class);
+
+ InstanceIdentifier<Node> key = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class,
+ new NodeKey(new NodeId(nodeId)));
+
+ MountInstance mountPoint = mountProviderService.getMountPoint(key);
+ return mountPoint.getRpcService(serviceInterface);
+ }
+
+ public void publishNotification(CompositeNode notification) {
+ getBiSalService(NotificationPublishService.class).publish(notification);
+ }
+
+ public SchemaContext getSchemaContext(String nodeId) {
+ YangInstanceIdentifier yii = inventoryNodeBIIdentifier(nodeId);
+
+ SchemaContext schemaContext =
+ getBiSalService(DOMMountPointService.class)
+ .getMountPoint(yii)
+ .get().getSchemaContext();
+
+ return schemaContext;
+ }
+
+ // ---------------------------
+ // ----- UTILITY METHODS -----
+ // ---------------------------
+ private <T extends BindingAwareService> T getBaSalService(Class<T> service) {
+ return bindingAwareContext.getSALService(service);
+ }
+
+ private <T extends BrokerService> T getBiSalService(Class<T> service) {
+ return bindingIndependentContext.getService(service);
+ }
+
+ private static final String NODE_ID_NAME = "id";
+
+ public static YangInstanceIdentifier inventoryNodeBIIdentifier(String nodeId) {
+ return YangInstanceIdentifier.builder()
+ .node(Nodes.QNAME)
+ .nodeWithKey(Node.QNAME,
+ QName.create(Node.QNAME.getNamespace(),
+ Node.QNAME.getRevision(),
+ NODE_ID_NAME),
+ nodeId)
+ .build();
+ }
+
+ private <T extends RpcService> BindingAwareBroker.RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> serviceInterface,
+ T implementation)
+ throws IllegalStateException {
+ return bindingAwareContext.addRoutedRpcImplementation(serviceInterface, implementation);
+ }
+
+ public static NodeRef createNodeRef(NodeId nodeId) {
+ NodeKey nodeKey = new NodeKey(nodeId);
+ InstanceIdentifier<Node> path = InstanceIdentifier
+ .builder(Nodes.class)
+ .child(Node.class, nodeKey)
+ .build();
+ return new NodeRef(path);
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.mdsal;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.core.api.AbstractProvider;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Providers {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Providers.class);
+
+ public static class BindingAware implements BindingAwareProvider, AutoCloseable {
+ private final InitializationContext initializationContext;
+
+ public BindingAware(InitializationContext ic) {
+ this.initializationContext = ic;
+ }
+
+ @Override
+ public void onSessionInitiated(BindingAwareBroker.ProviderContext session) {
+ initializationContext.set(session);
+
+ LOGGER.info("BindingAwareBroker.ProviderContext initialized");
+ }
+
+ @Override
+ public void close() throws Exception {}
+ }
+
+ public static class BindingIndependent extends AbstractProvider implements AutoCloseable {
+ private final InitializationContext initializationContext;
+
+ public BindingIndependent(InitializationContext ic) {
+ this.initializationContext = ic;
+ }
+
+ @Override
+ public void onSessionInitiated(Broker.ProviderSession session) {
+ initializationContext.set(session);
+
+ LOGGER.info("Broker.ProviderSession initialized");
+ }
+
+ @Override
+ public void close() throws Exception {}
+ }
+
+}
--- /dev/null
+/*
+ * 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.messagebus.app.impl;
+
+import java.util.List;
+import java.util.concurrent.Future;
+import org.opendaylight.controller.mdsal.MdSAL;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventaggregator.rev141202.CreateTopicInput;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventaggregator.rev141202.CreateTopicOutput;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventaggregator.rev141202.CreateTopicOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventaggregator.rev141202.DestroyTopicInput;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventaggregator.rev141202.EventAggregatorService;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventaggregator.rev141202.NotificationPattern;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.Node1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// TODO: implement topic created notification
+public class EventAggregator implements EventAggregatorService {
+ private static final Logger LOGGER = LoggerFactory.getLogger(EventAggregator.class);
+
+ private final MdSAL mdSAL;
+ private final EventSourceTopology eventSourceTopology;
+
+ public EventAggregator(final MdSAL mdSAL, final EventSourceTopology eventSourceTopology) {
+ this.mdSAL = mdSAL;
+ this.eventSourceTopology = eventSourceTopology;
+ }
+
+ public void mdsalReady() {
+ mdSAL.addRpcImplementation(EventAggregatorService.class, this);
+ }
+
+ @Override
+ public Future<RpcResult<CreateTopicOutput>> createTopic(final CreateTopicInput input) {
+ LOGGER.info("Received Topic creation request: NotificationPattern -> {}, NodeIdPattern -> {}",
+ input.getNotificationPattern(),
+ input.getNodeIdPattern());
+
+ Topic topic = new Topic(new NotificationPattern(input.getNotificationPattern()), input.getNodeIdPattern().getValue(), mdSAL);
+
+ //# Make sure we capture all nodes from now on
+ eventSourceTopology.registerDataChangeListener(topic);
+
+ //# Notify existing nodes
+ //# Code reader note: Context of Node type is NetworkTopology
+ List<Node> nodes = eventSourceTopology.snapshot();
+ for (Node node : nodes) {
+ NodeId nodeIdToNotify = node.getAugmentation(Node1.class).getEventSourceNode();
+ topic.notifyNode(nodeIdToNotify);
+ }
+
+ CreateTopicOutput cto = new CreateTopicOutputBuilder()
+ .setTopicId(topic.getTopicId())
+ .build();
+
+ return Util.resultFor(cto);
+ }
+
+ @Override
+ public Future<RpcResult<Void>> destroyTopic(final DestroyTopicInput input) {
+ // 1. UNREGISTER DATA CHANGE LISTENER -> ?
+ // 2. CLOSE TOPIC
+ return null;
+ }
+}
--- /dev/null
+/*
+ * 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.messagebus.app.impl;
+
+import org.opendaylight.controller.config.yang.messagebus.app.impl.NamespaceToStream;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.mdsal.DataStore;
+import org.opendaylight.controller.mdsal.MdSAL;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.EventSourceService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.inventory.rev140108.NetconfNode;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public final class EventSourceManager implements DataChangeListener {
+ private static final Logger LOGGER = LoggerFactory.getLogger(EventSourceManager.class);
+ private static final InstanceIdentifier<Node> INVENTORY_PATH = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class);
+ private final DataStore dataStore;
+ private final MdSAL mdSal;
+ private final EventSourceTopology eventSourceTopology;
+ private final Map<String, String> streamMap;
+
+ public EventSourceManager(DataStore dataStore,
+ MdSAL mdSal,
+ EventSourceTopology eventSourceTopology,
+ List<NamespaceToStream> namespaceMapping) {
+ this.dataStore = dataStore;
+ this.mdSal = mdSal;
+ this.eventSourceTopology = eventSourceTopology;
+ this.streamMap = namespaceToStreamMapping(namespaceMapping);
+ }
+
+ private Map namespaceToStreamMapping(List<NamespaceToStream> namespaceMapping) {
+ Map<String, String> streamMap = new HashMap<>(namespaceMapping.size());
+
+ for (NamespaceToStream nToS : namespaceMapping) {
+ streamMap.put(nToS.getUrnPrefix(), nToS.getStreamName());
+ }
+
+ return streamMap;
+ }
+
+ public void mdsalReady() {
+ dataStore.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
+ INVENTORY_PATH,
+ this,
+ DataBroker.DataChangeScope.SUBTREE);
+
+ LOGGER.info("EventSourceManager initialized.");
+ }
+
+ @Override
+ public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event) {
+ //FIXME: Prevent creating new event source on subsequent changes in inventory, like disconnect.
+ LOGGER.debug("[DataChangeEvent<InstanceIdentifier<?>, DataObject>: {}]", event);
+
+ Node node = Util.getAffectedNode(event);
+ // we listen on node tree, therefore we should rather throw IllegalStateException when node is null
+ if ( node == null ) {
+ LOGGER.debug("OnDataChanged Event. Node is null.");
+ return;
+ }
+ if ( isNetconfNode(node) == false ) {
+ LOGGER.debug("OnDataChanged Event. Not a Netconf node.");
+ return;
+ }
+ if ( isEventSource(node) == false ) {
+ LOGGER.debug("OnDataChanged Event. Node an EventSource node.");
+ return;
+ }
+
+ NetconfEventSource netconfEventSource = new NetconfEventSource(mdSal,
+ node.getKey().getId().getValue(),
+ streamMap);
+ mdSal.addRpcImplementation(node, EventSourceService.class, netconfEventSource);
+
+ InstanceIdentifier<NetconfNode> nodeInstanceIdentifier =
+ InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, node.getKey())
+ .augmentation(NetconfNode.class);
+
+ dataStore.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
+ nodeInstanceIdentifier,
+ netconfEventSource,
+ DataBroker.DataChangeScope.SUBTREE);
+
+ eventSourceTopology.insert(node);
+ }
+
+ private boolean isNetconfNode(Node node) {
+ return node.getAugmentation(NetconfNode.class) != null ;
+ }
+
+ public boolean isEventSource(Node node) {
+ NetconfNode netconfNode = node.getAugmentation(NetconfNode.class);
+
+ return isEventSource(netconfNode);
+ }
+
+ private boolean isEventSource(NetconfNode node) {
+ for (String capability : node.getInitialCapability()) {
+ if(capability.startsWith("urn:ietf:params:xml:ns:netconf:notification")) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.messagebus.app.impl;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.mdsal.DataStore;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.Node1;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.Node1Builder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.TopologyTypes1;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.TopologyTypes1Builder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.topology.event.source.type.TopologyEventSource;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.topology.event.source.type.TopologyEventSourceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.TopologyTypes;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class EventSourceTopology {
+ private static final Logger LOGGER = LoggerFactory.getLogger(EventSourceTopology.class);
+
+ private static final String topologyId = "EVENT-SOURCE-TOPOLOGY" ;
+ private static final TopologyKey topologyKey = new TopologyKey(new TopologyId(topologyId));
+ private static final LogicalDatastoreType datastoreType = LogicalDatastoreType.OPERATIONAL;
+
+ private static final InstanceIdentifier<Topology> topologyInstanceIdentifier =
+ InstanceIdentifier.create(NetworkTopology.class)
+ .child(Topology.class, topologyKey);
+
+ private static final InstanceIdentifier<TopologyTypes1> topologyTypeInstanceIdentifier =
+ topologyInstanceIdentifier
+ .child(TopologyTypes.class)
+ .augmentation(TopologyTypes1.class);
+
+ private static final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang
+ .network.topology.rev131021.network.topology.topology.Node> eventSourceTopologyPath =
+ InstanceIdentifier.create(NetworkTopology.class)
+ .child(Topology.class)
+ .child(org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang
+ .network.topology.rev131021.network.topology.topology.Node.class);
+
+ private final Map<DataChangeListener, ListenerRegistration<DataChangeListener>> registrations =
+ new ConcurrentHashMap<>();
+
+ private final DataStore dataStore;
+
+ public EventSourceTopology(DataStore dataStore) {
+ this.dataStore = dataStore;
+ }
+
+ public void mdsalReady() {
+ TopologyEventSource topologySource = new TopologyEventSourceBuilder().build();
+ TopologyTypes1 topologyTypeAugment = new TopologyTypes1Builder().setTopologyEventSource(topologySource).build();
+
+ dataStore.asyncPUT(datastoreType, topologyTypeInstanceIdentifier, topologyTypeAugment);
+ }
+
+ public void insert(Node node) {
+ String nodeId = node.getKey().getId().getValue();
+ NodeKey nodeKey = new NodeKey(new NodeId(nodeId));
+ InstanceIdentifier<Node1> topologyNodeAugment
+ = topologyInstanceIdentifier
+ .child(org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang
+ .network.topology.rev131021.network.topology.topology.Node.class, nodeKey)
+ .augmentation(Node1.class);
+
+ Node1 nodeAgument = new Node1Builder().setEventSourceNode(node.getId()).build();
+ dataStore.asyncPUT(datastoreType, topologyNodeAugment, nodeAgument);
+ }
+
+ // TODO: Should we expose this functioanlity over RPC?
+ public List<org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang
+ .network.topology.rev131021.network.topology.topology.Node> snapshot() {
+ Topology topology = dataStore.read(datastoreType, topologyInstanceIdentifier);
+ return topology.getNode();
+ }
+
+ public void registerDataChangeListener(DataChangeListener listener) {
+ ListenerRegistration<DataChangeListener> listenerRegistration = dataStore.registerDataChangeListener(datastoreType,
+ eventSourceTopologyPath,
+ listener,
+ DataBroker.DataChangeScope.SUBTREE);
+
+ registrations.put(listener, listenerRegistration);
+ }
+}
--- /dev/null
+/*
+ * 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.messagebus.app.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Future;
+import java.util.regex.Pattern;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.mdsal.MdSAL;
+import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventaggregator.rev141202.NotificationPattern;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventaggregator.rev141202.TopicNotification;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.EventSourceService;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.JoinTopicInput;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.JoinTopicOutput;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.CreateSubscriptionInput;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.CreateSubscriptionInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.NotificationsService;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.StreamNameType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.inventory.rev140108.NetconfNode;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NetconfEventSource implements EventSourceService, NotificationListener, DataChangeListener {
+ private static final Logger LOGGER = LoggerFactory.getLogger(NetconfEventSource.class);
+
+ private final MdSAL mdSal;
+ private final String nodeId;
+
+ private final List<String> activeStreams = new ArrayList<>();
+
+ private final Map<String, String> urnPrefixToStreamMap;
+
+ public NetconfEventSource(final MdSAL mdSal, final String nodeId, final Map<String, String> streamMap) {
+ Preconditions.checkNotNull(mdSal);
+ Preconditions.checkNotNull(nodeId);
+
+ this.mdSal = mdSal;
+ this.nodeId = nodeId;
+ this.urnPrefixToStreamMap = streamMap;
+
+ LOGGER.info("NetconfEventSource [{}] created.", nodeId);
+ }
+
+ @Override
+ public Future<RpcResult<JoinTopicOutput>> joinTopic(final JoinTopicInput input) {
+ final NotificationPattern notificationPattern = input.getNotificationPattern();
+
+ // FIXME: default language should already be regex
+ final String regex = Util.wildcardToRegex(notificationPattern.getValue());
+
+ final Pattern pattern = Pattern.compile(regex);
+ List<QName> matchingNotifications = Util.expandQname(availableNotifications(), pattern);
+ registerNotificationListener(matchingNotifications);
+ return null;
+ }
+
+ private List<QName> availableNotifications() {
+ // FIXME: use SchemaContextListener to get changes asynchronously
+ Set<NotificationDefinition> availableNotifications = mdSal.getSchemaContext(nodeId).getNotifications();
+ List<QName> qNs = new ArrayList<>(availableNotifications.size());
+ for (NotificationDefinition nd : availableNotifications) {
+ qNs.add(nd.getQName());
+ }
+
+ return qNs;
+ }
+
+ private void registerNotificationListener(final List<QName> notificationsToSubscribe) {
+ for (QName qName : notificationsToSubscribe) {
+ startSubscription(qName);
+ // FIXME: do not lose this registration
+ final ListenerRegistration<NotificationListener> reg = mdSal.addNotificationListener(nodeId, qName, this);
+ }
+ }
+
+ private synchronized void startSubscription(final QName qName) {
+ String streamName = resolveStream(qName);
+
+ if (streamIsActive(streamName) == false) {
+ LOGGER.info("Stream {} is not active on node {}. Will subscribe.", streamName, nodeId);
+ startSubscription(streamName);
+ }
+ }
+
+ private synchronized void resubscribeToActiveStreams() {
+ for (String streamName : activeStreams) {
+ startSubscription(streamName);
+ }
+ }
+
+ private synchronized void startSubscription(final String streamName) {
+ CreateSubscriptionInput subscriptionInput = getSubscriptionInput(streamName);
+ mdSal.getRpcService(nodeId, NotificationsService.class).createSubscription(subscriptionInput);
+ activeStreams.add(streamName);
+ }
+
+ private static CreateSubscriptionInput getSubscriptionInput(final String streamName) {
+ CreateSubscriptionInputBuilder csib = new CreateSubscriptionInputBuilder();
+ csib.setStream(new StreamNameType(streamName));
+ return csib.build();
+ }
+
+ private String resolveStream(final QName qName) {
+ String streamName = null;
+
+ for (Map.Entry<String, String> entry : urnPrefixToStreamMap.entrySet()) {
+ String nameSpace = qName.getNamespace().toString();
+ String urnPrefix = entry.getKey();
+ if( nameSpace.startsWith(urnPrefix) ) {
+ streamName = entry.getValue();
+ break;
+ }
+ }
+
+ return streamName;
+ }
+
+ private boolean streamIsActive(final String streamName) {
+ return activeStreams.contains(streamName);
+ }
+
+ // PASS
+ @Override public Set<QName> getSupportedNotifications() {
+ return null;
+ }
+
+ @Override
+ public void onNotification(final CompositeNode notification) {
+ LOGGER.info("NetconfEventSource {} received notification {}. Will publish to MD-SAL.", nodeId, notification);
+ ImmutableCompositeNode payload = ImmutableCompositeNode.builder()
+ .setQName(QName.create(TopicNotification.QNAME, "payload"))
+ .add(notification).toInstance();
+ ImmutableCompositeNode icn = ImmutableCompositeNode.builder()
+ .setQName(TopicNotification.QNAME)
+ .add(payload)
+ .addLeaf("event-source", nodeId)
+ .toInstance();
+
+ mdSal.publishNotification(icn);
+ }
+
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ boolean wasConnected = false;
+ boolean nowConnected = false;
+
+ for (Map.Entry<InstanceIdentifier<?>, DataObject> changeEntry : change.getOriginalData().entrySet()) {
+ if ( isNetconfNode(changeEntry) ) {
+ NetconfNode nn = (NetconfNode)changeEntry.getValue();
+ wasConnected = nn.isConnected();
+ }
+ }
+
+ for (Map.Entry<InstanceIdentifier<?>, DataObject> changeEntry : change.getUpdatedData().entrySet()) {
+ if ( isNetconfNode(changeEntry) ) {
+ NetconfNode nn = (NetconfNode)changeEntry.getValue();
+ nowConnected = nn.isConnected();
+ }
+ }
+
+ if (wasConnected == false && nowConnected == true) {
+ resubscribeToActiveStreams();
+ }
+ }
+
+ private static boolean isNetconfNode(final Map.Entry<InstanceIdentifier<?>, DataObject> changeEntry ) {
+ return NetconfNode.class.equals(changeEntry.getKey().getTargetType());
+ }
+
+}
--- /dev/null
+/*
+ * 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.messagebus.app.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.regex.Pattern;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.mdsal.MdSAL;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventaggregator.rev141202.NotificationPattern;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventaggregator.rev141202.TopicId;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.EventSourceService;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.JoinTopicInput;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventsource.rev141202.JoinTopicInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.LoggerFactory;
+
+public class Topic implements DataChangeListener {
+ private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(Topic.class);
+ private final NotificationPattern notificationPattern;
+ private final Pattern nodeIdPattern;
+ private final TopicId topicId;
+ private final MdSAL mdSal;
+
+ public Topic(final NotificationPattern notificationPattern, final String nodeIdPattern, final MdSAL mdSal) {
+ this.notificationPattern = Preconditions.checkNotNull(notificationPattern);
+
+ // FIXME: regex should be the language of nodeIdPattern
+ final String regex = Util.wildcardToRegex(nodeIdPattern);
+ this.nodeIdPattern = Pattern.compile(regex);
+ this.mdSal = Preconditions.checkNotNull(mdSal);
+
+ // FIXME: We need to perform some salting in order to make
+ // the topic IDs less predictable.
+ this.topicId = new TopicId(Util.md5String(notificationPattern + nodeIdPattern));
+ }
+
+ public TopicId getTopicId() {
+ return topicId;
+ }
+
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event) {
+ // TODO: affected must return topologyNode !!!
+ final Node node = Util.getAffectedNode(event);
+ if (nodeIdPattern.matcher(node.getId().getValue()).matches()) {
+ notifyNode(node.getId());
+ } else {
+ LOG.debug("Skipping node {}", node.getId());
+ }
+ }
+
+ public void notifyNode(final NodeId nodeId) {
+ JoinTopicInput jti = getJoinTopicInputArgument(nodeId);
+ EventSourceService ess = mdSal.getRpcService(EventSourceService.class);
+
+ if (ess == null) {
+ throw new IllegalStateException("EventSourceService is not registered.");
+ }
+
+ ess.joinTopic(jti);
+ }
+
+ private JoinTopicInput getJoinTopicInputArgument(final NodeId nodeId) {
+ NodeRef nodeRef = MdSAL.createNodeRef(nodeId);
+ JoinTopicInput jti =
+ new JoinTopicInputBuilder()
+ .setNode(nodeRef.getValue())
+ .setTopicId(topicId)
+ .setNotificationPattern(notificationPattern)
+ .build();
+ return jti;
+ }
+}
--- /dev/null
+/*
+ * 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.messagebus.app.impl;
+
+import com.google.common.util.concurrent.Futures;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.regex.Pattern;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public final class Util {
+ private static final MessageDigest messageDigestTemplate = getDigestInstance();
+
+ private static MessageDigest getDigestInstance() {
+ try {
+ return MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("Unable to get MD5 instance");
+ }
+ }
+
+ public static String md5String(final String inputString) {
+
+ try {
+ MessageDigest md = (MessageDigest)messageDigestTemplate.clone();
+ md.update(inputString.getBytes("UTF-8"), 0, inputString.length());
+ return new BigInteger(1, md.digest()).toString(16);
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to get MD5 instance");
+ }
+ }
+
+ public static <T> Future<RpcResult<T>> resultFor(final T output) {
+ RpcResult<T> result = Rpcs.getRpcResult(true, output, Collections.<RpcError>emptyList());
+ return Futures.immediateFuture(result);
+ }
+
+ /**
+ * Extracts affected node from data change event.
+ * @param event
+ * @return
+ */
+ public static Node getAffectedNode(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event) {
+ // TODO: expect listener method to be called even when change impact node
+ // TODO: test with change.getCreatedData()
+ for (Map.Entry<InstanceIdentifier<?>, DataObject> changeEntry : event.getUpdatedData().entrySet()) {
+ if (isNode(changeEntry)) {
+ return (Node) changeEntry.getValue();
+ }
+ }
+
+ return null;
+ }
+
+ private static boolean isNode(final Map.Entry<InstanceIdentifier<?>, DataObject> changeEntry ) {
+ return Node.class.equals(changeEntry.getKey().getTargetType());
+ }
+
+ /**
+ * Method filters qnames based on wildcard strings
+ *
+ * @param availableQnames
+ * @param patterh matching pattern
+ * @return list of filtered qnames
+ */
+ public static List<QName> expandQname(final List<QName> availableQnames, final Pattern pattern) {
+ List<QName> matchingQnames = new ArrayList<>();
+
+ for (QName qname : availableQnames) {
+ String namespace = qname.getNamespace().toString();
+ if (pattern.matcher(namespace).matches()) {
+ matchingQnames.add(qname);
+ }
+ }
+
+ return matchingQnames;
+ }
+
+ /**
+ * CREDIT to http://www.rgagnon.com/javadetails/java-0515.html
+ * @param wildcard
+ * @return
+ */
+ static String wildcardToRegex(final String wildcard){
+ StringBuffer s = new StringBuffer(wildcard.length());
+ s.append('^');
+ for (char c : wildcard.toCharArray()) {
+ switch(c) {
+ case '*':
+ s.append(".*");
+ break;
+ case '?':
+ s.append('.');
+ break;
+ // escape special regexp-characters
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ case '$':
+ case '^':
+ case '.':
+ case '{':
+ case '}':
+ case '|':
+ case '\\':
+ s.append("\\");
+ s.append(c);
+ break;
+ default:
+ s.append(c);
+ break;
+ }
+ }
+ s.append('$');
+ return s.toString();
+ }
+}
--- /dev/null
+module messagebus-app-impl {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:messagebus:app:impl";
+ prefix "binding-impl";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import opendaylight-md-sal-binding {prefix sal;}
+ import opendaylight-md-sal-dom {prefix dom;}
+
+
+ description
+ "Service definition for Message Bus application implementation.";
+
+ revision "2015-02-03" {
+ description "Second revision. Message Bus opensourcing";
+ }
+
+ identity messagebus-app-impl {
+ base config:module-type;
+ config:java-name-prefix MessageBusAppImpl;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case messagebus-app-impl {
+ when "/config:modules/config:module/config:type = 'messagebus-app-impl'";
+
+ container binding-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity sal:binding-broker-osgi-registry;
+ }
+ }
+ }
+
+ container dom-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dom:dom-broker-osgi-registry;
+ }
+ }
+ }
+
+ list namespace-to-stream {
+ key urn-prefix;
+
+ leaf urn-prefix {
+ type string;
+ }
+
+ leaf stream-name {
+ type string;
+ }
+ }
+ }
+ }
+
+ augment "/config:modules/config:module/config:state" {
+ case messagebus-app-impl {
+ when "/config:modules/config:module/config:type = 'messagebus-app-impl'";
+ }
+ }
+}
\ No newline at end of file
<!-- Clustering -->
<module>sal-remoterpc-connector</module>
+
+ <!-- Message Bus -->
+ <module>messagebus-api</module>
+ <module>messagebus-impl</module>
</modules>
<build>
}
protected int adjustedIndex(long logEntryIndex) {
- if(snapshotIndex < 0){
+ if (snapshotIndex < 0) {
return (int) logEntryIndex;
}
return (int) (logEntryIndex - (snapshotIndex + 1));
return journal.size();
}
+ @Override
+ public int dataSize() {
+ return dataSize;
+ }
+
@Override
public boolean isPresent(long logEntryIndex) {
if (logEntryIndex > lastIndex()) {
previousSnapshotIndex = -1;
previousSnapshotTerm = -1;
dataSize = 0;
+ // need to recalc the datasize based on the entries left after precommit.
+ for(ReplicatedLogEntry logEntry : journal) {
+ dataSize += logEntry.size();
+ }
+
}
@Override
* The interval in which the leader needs to check itself if its isolated
* @return FiniteDuration
*/
- FiniteDuration getIsolatedCheckInterval();
+ long getIsolatedCheckIntervalInMillis();
/**
private FiniteDuration heartBeatInterval = HEART_BEAT_INTERVAL;
private long snapshotBatchCount = SNAPSHOT_BATCH_COUNT;
private int journalRecoveryLogBatchSize = JOURNAL_RECOVERY_LOG_BATCH_SIZE;
- private FiniteDuration isolatedLeaderCheckInterval =
- new FiniteDuration(HEART_BEAT_INTERVAL.length() * 1000, HEART_BEAT_INTERVAL.unit());
+ private long isolatedLeaderCheckInterval = HEART_BEAT_INTERVAL.$times(1000).toMillis();
// 12 is just an arbitrary percentage. This is the amount of the total memory that a raft actor's
// in-memory journal can use before it needs to snapshot
}
public void setIsolatedLeaderCheckInterval(FiniteDuration isolatedLeaderCheckInterval) {
- this.isolatedLeaderCheckInterval = isolatedLeaderCheckInterval;
+ this.isolatedLeaderCheckInterval = isolatedLeaderCheckInterval.toMillis();
}
public void setElectionTimeoutFactor(long electionTimeoutFactor){
}
@Override
- public FiniteDuration getIsolatedCheckInterval() {
+ public long getIsolatedCheckIntervalInMillis() {
return isolatedLeaderCheckInterval;
}
public long timeSinceLastActivity() {
return stopwatch.elapsed(TimeUnit.MILLISECONDS);
}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("FollowerLogInformationImpl [id=").append(id).append(", nextIndex=").append(nextIndex)
+ .append(", matchIndex=").append(matchIndex).append(", stopwatch=")
+ .append(stopwatch.elapsed(TimeUnit.MILLISECONDS))
+ .append(", followerTimeoutMillis=").append(followerTimeoutMillis).append("]");
+ return builder.toString();
+ }
+
+
}
import com.google.protobuf.ByteString;
import java.io.Serializable;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import org.opendaylight.controller.cluster.DataPersistenceProvider;
import org.opendaylight.controller.cluster.common.actor.AbstractUntypedPersistentActor;
import org.opendaylight.controller.cluster.notifications.RoleChanged;
import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot;
import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply;
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.behaviors.AbstractRaftActorBehavior;
import org.opendaylight.controller.cluster.raft.behaviors.Follower;
import org.opendaylight.controller.cluster.raft.behaviors.RaftActorBehavior;
import org.opendaylight.controller.cluster.raft.client.messages.FindLeader;
import org.opendaylight.controller.cluster.raft.client.messages.FindLeaderReply;
-import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
-import org.opendaylight.controller.protobuff.messages.cluster.raft.AppendEntriesMessages;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
* </ul>
*/
public abstract class RaftActor extends AbstractUntypedPersistentActor {
+
+ private static final long APPLY_STATE_DELAY_THRESHOLD_IN_NANOS = TimeUnit.MILLISECONDS.toNanos(50L); // 50 millis
+
protected final Logger LOG = LoggerFactory.getLogger(getClass());
/**
if (message instanceof ApplyState){
ApplyState applyState = (ApplyState) message;
+ long elapsedTime = (System.nanoTime() - applyState.getStartTime());
+ if(elapsedTime >= APPLY_STATE_DELAY_THRESHOLD_IN_NANOS){
+ LOG.warn("ApplyState took more time than expected. Elapsed Time = {} ms ApplyState = {}",
+ TimeUnit.NANOSECONDS.toMillis(elapsedTime), applyState);
+ }
+
if(LOG.isDebugEnabled()) {
LOG.debug("{}: Applying state for log index {} data {}",
persistenceId(), applyState.getReplicatedLogEntry().getIndex(),
handleCaptureSnapshotReply(((CaptureSnapshotReply) message).getSnapshot());
} else {
- if (!(message instanceof AppendEntriesMessages.AppendEntries)
- && !(message instanceof AppendEntriesReply) && !(message instanceof SendHeartBeat)) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: onReceiveCommand: message: {}", persistenceId(), message.getClass());
- }
- }
-
RaftActorBehavior oldBehavior = currentBehavior;
currentBehavior = currentBehavior.handleMessage(getSender(), message);
/**
* This method is called during recovery to reconstruct the state of the actor.
*
- * @param snapshot A snapshot of the state of the actor
+ * @param snapshotBytes A snapshot of the state of the actor
*/
protected abstract void applyRecoverySnapshot(byte[] snapshotBytes);
LOG.info("{}: Persisting of snapshot done:{}", persistenceId(), 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,
+ long dataThreshold = Runtime.getRuntime().totalMemory() *
+ getRaftActorContext().getConfigParams().getSnapshotDataThresholdPercentage() / 100;
+ if (context.getReplicatedLog().dataSize() > dataThreshold) {
+ // if memory is less, clear the log based on lastApplied.
+ // this could/should only happen if one of the followers is down
+ // as normally we keep removing from the log when its replicated to all.
+ context.getReplicatedLog().snapshotPreCommit(captureSnapshot.getLastAppliedIndex(),
+ captureSnapshot.getLastAppliedTerm());
- context.getReplicatedLog().snapshotPreCommit(
- captureSnapshot.getLastAppliedIndex(),
- captureSnapshot.getLastAppliedTerm());
+ } else {
+ // clear the log based on replicatedToAllIndex
+ context.getReplicatedLog().snapshotPreCommit(captureSnapshot.getReplicatedToAllIndex(),
+ captureSnapshot.getReplicatedToAllTerm());
+ }
+ getCurrentBehavior().setReplicatedToAllIndex(captureSnapshot.getReplicatedToAllIndex());
LOG.info("{}: Removed in-memory snapshotted entries, adjusted snaphsotIndex:{} " +
"and term:{}", persistenceId(), captureSnapshot.getLastAppliedIndex(),
// FIXME: Maybe this should be done after the command is saved
journal.subList(adjustedIndex , journal.size()).clear();
- persistence().persist(new DeleteEntries(adjustedIndex), new Procedure<DeleteEntries>(){
+ persistence().persist(new DeleteEntries(adjustedIndex), new Procedure<DeleteEntries>() {
- @Override public void apply(DeleteEntries param)
- throws Exception {
+ @Override
+ public void apply(DeleteEntries param)
+ throws Exception {
//FIXME : Doing nothing for now
dataSize = 0;
- for(ReplicatedLogEntry entry : journal){
+ for (ReplicatedLogEntry entry : journal) {
dataSize += entry.size();
}
}
appendAndPersist(replicatedLogEntry, null);
}
- @Override
- public int dataSize() {
- return dataSize;
- }
-
public void appendAndPersist(
final ReplicatedLogEntry replicatedLogEntry,
final Procedure<ReplicatedLogEntry> callback) {
long dataSizeForCheck = dataSize;
dataSizeSinceLastSnapshot += logEntrySize;
- long journalSize = lastIndex()+1;
+ long journalSize = lastIndex() + 1;
if(!hasFollowers()) {
// When we do not have followers we do not maintain an in-memory log
dataSizeSinceLastSnapshot = 0;
- LOG.info("{}: Initiating Snapshot Capture..", persistenceId());
+ LOG.info("{}: Initiating Snapshot Capture, journalSize = {}, dataSizeForCheck = {}," +
+ " dataThreshold = {}", persistenceId(), journalSize, dataSizeForCheck, dataThreshold);
+
long lastAppliedIndex = -1;
long lastAppliedTerm = -1;
}
// send a CaptureSnapshot to self to make the expensive operation async.
- getSelf().tell(new CaptureSnapshot(
- lastIndex(), lastTerm(), lastAppliedIndex, lastAppliedTerm),
+ long replicatedToAllIndex = getCurrentBehavior().getReplicatedToAllIndex();
+ ReplicatedLogEntry replicatedToAllEntry = context.getReplicatedLog().get(replicatedToAllIndex);
+ getSelf().tell(new CaptureSnapshot(lastIndex(), lastTerm(), lastAppliedIndex, lastAppliedTerm,
+ (replicatedToAllEntry != null ? replicatedToAllEntry.getIndex() : -1),
+ (replicatedToAllEntry != null ? replicatedToAllEntry.getTerm() : -1)),
null);
context.setSnapshotCaptureInitiated(true);
}
- if(callback != null){
+ if (callback != null){
callback.apply(replicatedLogEntry);
}
}
* sets snapshot term
* @param snapshotTerm
*/
- public void setSnapshotTerm(long snapshotTerm);
+ void setSnapshotTerm(long snapshotTerm);
/**
* Clears the journal entries with startIndex(inclusive) and endIndex (exclusive)
* @param startIndex
* @param endIndex
*/
- public void clear(int startIndex, int endIndex);
+ void clear(int startIndex, int endIndex);
/**
* Handles all the bookkeeping in order to perform a rollback in the
* @param snapshotCapturedIndex
* @param snapshotCapturedTerm
*/
- public void snapshotPreCommit(long snapshotCapturedIndex, long snapshotCapturedTerm);
+ void snapshotPreCommit(long snapshotCapturedIndex, long snapshotCapturedTerm);
/**
* Sets the Replicated log to state after snapshot success.
*/
- public void snapshotCommit();
+ void snapshotCommit();
/**
* Restores the replicated log to a state in the event of a save snapshot failure
*/
- public void snapshotRollback();
+ void snapshotRollback();
/**
* Size of the data in the log (in bytes)
*/
- public int dataSize();
+ int dataSize();
+
}
package org.opendaylight.controller.cluster.raft.base.messages;
import akka.actor.ActorRef;
-import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
-
import java.io.Serializable;
+import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
public class ApplyState implements Serializable {
private static final long serialVersionUID = 1L;
private final ActorRef clientActor;
private final String identifier;
private final ReplicatedLogEntry replicatedLogEntry;
+ private final long startTime;
public ApplyState(ActorRef clientActor, String identifier,
ReplicatedLogEntry replicatedLogEntry) {
this.clientActor = clientActor;
this.identifier = identifier;
this.replicatedLogEntry = replicatedLogEntry;
+ this.startTime = System.nanoTime();
}
public ActorRef getClientActor() {
public ReplicatedLogEntry getReplicatedLogEntry() {
return replicatedLogEntry;
}
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ @Override
+ public String toString() {
+ return "ApplyState{" +
+ "identifier='" + identifier + '\'' +
+ ", replicatedLogEntry.index =" + replicatedLogEntry.getIndex() +
+ ", startTime=" + startTime +
+ '}';
+ }
}
private long lastIndex;
private long lastTerm;
private boolean installSnapshotInitiated;
+ private long replicatedToAllIndex;
+ private long replicatedToAllTerm;
public CaptureSnapshot(long lastIndex, long lastTerm,
- long lastAppliedIndex, long lastAppliedTerm) {
- this(lastIndex, lastTerm, lastAppliedIndex, lastAppliedTerm, false);
+ long lastAppliedIndex, long lastAppliedTerm, long replicatedToAllIndex, long replicatedToAllTerm) {
+ this(lastIndex, lastTerm, lastAppliedIndex, lastAppliedTerm, replicatedToAllIndex , replicatedToAllTerm, false);
}
public CaptureSnapshot(long lastIndex, long lastTerm,long lastAppliedIndex,
- long lastAppliedTerm, boolean installSnapshotInitiated) {
+ long lastAppliedTerm, long replicatedToAllIndex, long replicatedToAllTerm, boolean installSnapshotInitiated) {
this.lastIndex = lastIndex;
this.lastTerm = lastTerm;
this.lastAppliedIndex = lastAppliedIndex;
this.lastAppliedTerm = lastAppliedTerm;
this.installSnapshotInitiated = installSnapshotInitiated;
+ this.replicatedToAllIndex = replicatedToAllIndex;
+ this.replicatedToAllTerm = replicatedToAllTerm;
}
public long getLastAppliedIndex() {
public boolean isInstallSnapshotInitiated() {
return installSnapshotInitiated;
}
+
+ public long getReplicatedToAllIndex() {
+ return replicatedToAllIndex;
+ }
+
+ public long getReplicatedToAllTerm() {
+ return replicatedToAllTerm;
+ }
}
import org.opendaylight.controller.cluster.raft.RaftState;
import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot;
-import org.opendaylight.controller.cluster.raft.base.messages.InitiateInstallSnapshot;
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;
private Optional<ByteString> snapshot;
- private long replicatedToAllIndex = -1;
-
public AbstractLeader(RaftActorContext context) {
- super(context);
+ super(context, RaftState.Leader);
final Builder<String, FollowerLogInformation> ftlBuilder = ImmutableMap.builder();
for (String followerId : context.getPeerAddresses().keySet()) {
leaderId = context.getId();
- LOG.debug("{}: Election: Leader has following peers: {}", context.getId(), getFollowerIds());
+ LOG.debug("{}: Election: Leader has following peers: {}", logName(), getFollowerIds());
minReplicationCount = getMajorityVoteCount(getFollowerIds().size());
// Upon election: send initial empty AppendEntries RPCs
// (heartbeat) to each server; repeat during idle periods to
// prevent election timeouts (§5.2)
- sendAppendEntries(0);
+ sendAppendEntries(0, false);
}
/**
return followerToLog.keySet();
}
- private Optional<ByteString> getSnapshot() {
- return snapshot;
- }
-
@VisibleForTesting
void setSnapshot(Optional<ByteString> snapshot) {
this.snapshot = snapshot;
protected RaftActorBehavior handleAppendEntries(ActorRef sender,
AppendEntries appendEntries) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: handleAppendEntries: {}", context.getId(), appendEntries);
- }
+ LOG.debug("{}: handleAppendEntries: {}", logName(), appendEntries);
return this;
}
protected RaftActorBehavior handleAppendEntriesReply(ActorRef sender,
AppendEntriesReply appendEntriesReply) {
- if(! appendEntriesReply.isSuccess()) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: handleAppendEntriesReply: {}", context.getId(), appendEntriesReply);
- }
+ if(LOG.isTraceEnabled()) {
+ LOG.trace("{}: handleAppendEntriesReply: {}", logName(), appendEntriesReply);
+ } else if(LOG.isDebugEnabled() && !appendEntriesReply.isSuccess()) {
+ LOG.debug("{}: handleAppendEntriesReply: {}", logName(), appendEntriesReply);
}
// Update the FollowerLogInformation
followerToLog.get(followerId);
if(followerLogInformation == null){
- LOG.error("{}: handleAppendEntriesReply - unknown follower {}", context.getId(), followerId);
+ LOG.error("{}: handleAppendEntriesReply - unknown follower {}", logName(), followerId);
return this;
}
+ if(followerLogInformation.timeSinceLastActivity() >
+ context.getConfigParams().getElectionTimeOutInterval().toMillis()) {
+ LOG.error("{} : handleAppendEntriesReply delayed beyond election timeout, " +
+ "appendEntriesReply : {}, timeSinceLastActivity : {}, lastApplied : {}, commitIndex : {}",
+ logName(), appendEntriesReply, followerLogInformation.timeSinceLastActivity(),
+ context.getLastApplied(), context.getCommitIndex());
+ }
+
followerLogInformation.markFollowerActive();
if (appendEntriesReply.isSuccess()) {
// Apply the change to the state machine
if (context.getCommitIndex() > context.getLastApplied()) {
+ LOG.debug("{}: handleAppendEntriesReply: applying to log - commitIndex: {}, lastAppliedIndex: {}",
+ logName(), context.getCommitIndex(), context.getLastApplied());
+
applyLogToStateMachine(context.getCommitIndex());
}
}
//Send the next log entry immediately, if possible, no need to wait for heartbeat to trigger that event
- sendUpdatesToFollower(followerId, followerLogInformation, false);
+ sendUpdatesToFollower(followerId, followerLogInformation, false, false);
return this;
}
private void purgeInMemoryLog() {
- //find the lowest index across followers which has been replicated to all. -1 if there are no followers.
+ //find the lowest index across followers which has been replicated to all.
+ // lastApplied if there are no followers, so that we keep clearing the log for single-node
// we would delete the in-mem log from that index on, in-order to minimize mem usage
// we would also share this info thru AE with the followers so that they can delete their log entries as well.
- long minReplicatedToAllIndex = followerToLog.isEmpty() ? -1 : Long.MAX_VALUE;
+ long minReplicatedToAllIndex = followerToLog.isEmpty() ? context.getLastApplied() : Long.MAX_VALUE;
for (FollowerLogInformation info : followerToLog.values()) {
minReplicatedToAllIndex = Math.min(minReplicatedToAllIndex, info.getMatchIndex());
}
- replicatedToAllIndex = fakeSnapshot(minReplicatedToAllIndex, replicatedToAllIndex);
+ super.performSnapshotWithoutCapture(minReplicatedToAllIndex);
}
@Override
return this;
}
- @Override
- public RaftState state() {
- return RaftState.Leader;
- }
+ protected void beforeSendHeartbeat(){}
@Override
public RaftActorBehavior handleMessage(ActorRef sender, Object originalMessage) {
// set currentTerm = T, convert to follower (§5.1)
// This applies to all RPC messages and responses
if (rpc.getTerm() > context.getTermInformation().getCurrentTerm()) {
- LOG.debug("{}: Term {} in \"{}\" message is greater than leader's term {}", context.getId(),
- rpc.getTerm(), rpc, context.getTermInformation().getCurrentTerm());
+ LOG.debug("{}: Term {} in \"{}\" message is greater than leader's term {} - switching to Follower",
+ logName(), rpc.getTerm(), rpc, context.getTermInformation().getCurrentTerm());
context.getTermInformation().updateAndPersist(rpc.getTerm(), null);
try {
if (message instanceof SendHeartBeat) {
+ beforeSendHeartbeat();
sendHeartBeat();
return this;
- } else if(message instanceof InitiateInstallSnapshot) {
- installSnapshotIfNeeded();
-
} else if(message instanceof SendInstallSnapshot) {
// received from RaftActor
setSnapshot(Optional.of(((SendInstallSnapshot) message).getSnapshot()));
}
private void handleInstallSnapshotReply(InstallSnapshotReply reply) {
+ LOG.debug("{}: handleInstallSnapshotReply: {}", logName(), reply);
+
String followerId = reply.getFollowerId();
FollowerToSnapshot followerToSnapshot = mapFollowerToSnapshot.get(followerId);
if (followerToSnapshot == null) {
LOG.error("{}: FollowerId {} in InstallSnapshotReply not known to Leader",
- context.getId(), followerId);
+ logName(), followerId);
return;
}
//this was the last chunk reply
if(LOG.isDebugEnabled()) {
LOG.debug("{}: InstallSnapshotReply received, " +
- "last chunk received, Chunk:{}. Follower:{} Setting nextIndex:{}",
- context.getId(), reply.getChunkIndex(), followerId,
+ "last chunk received, Chunk: {}. Follower: {} Setting nextIndex: {}",
+ logName(), reply.getChunkIndex(), followerId,
context.getReplicatedLog().getSnapshotIndex() + 1
);
}
context.getReplicatedLog().getSnapshotIndex() + 1);
mapFollowerToSnapshot.remove(followerId);
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: followerToLog.get(followerId).getNextIndex()=" +
- context.getId(), followerToLog.get(followerId).getNextIndex());
- }
+ LOG.debug("{}: follower: {}, matchIndex set to {}, nextIndex set to {}",
+ logName(), followerId, followerLogInformation.getMatchIndex(),
+ followerLogInformation.getNextIndex());
if (mapFollowerToSnapshot.isEmpty()) {
// once there are no pending followers receiving snapshots
}
} else {
LOG.info("{}: InstallSnapshotReply received sending snapshot chunk failed, Will retry, Chunk: {}",
- context.getId(), reply.getChunkIndex());
+ logName(), reply.getChunkIndex());
followerToSnapshot.markSendStatus(false);
}
} else {
LOG.error("{}: Chunk index {} in InstallSnapshotReply from follower {} does not match expected index {}",
- context.getId(), reply.getChunkIndex(), followerId,
+ logName(), reply.getChunkIndex(), followerId,
followerToSnapshot.getChunkIndex());
if(reply.getChunkIndex() == INVALID_CHUNK_INDEX){
private void replicate(Replicate replicate) {
long logIndex = replicate.getReplicatedLogEntry().getIndex();
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: Replicate message {}", context.getId(), logIndex);
- }
+ LOG.debug("{}: Replicate message: identifier: {}, logIndex: {}", logName(),
+ replicate.getIdentifier(), logIndex);
// Create a tracker entry we will use this later to notify the
// client actor
context.setCommitIndex(logIndex);
applyLogToStateMachine(logIndex);
} else {
- sendAppendEntries(0);
+ sendAppendEntries(0, false);
}
}
- private void sendAppendEntries(long timeSinceLastActivityInterval) {
+ private void sendAppendEntries(long timeSinceLastActivityInterval, boolean isHeartbeat) {
// Send an AppendEntries to all followers
for (Entry<String, FollowerLogInformation> e : followerToLog.entrySet()) {
final String followerId = e.getKey();
// This checks helps not to send a repeat message to the follower
if(!followerLogInformation.isFollowerActive() ||
followerLogInformation.timeSinceLastActivity() >= timeSinceLastActivityInterval) {
- sendUpdatesToFollower(followerId, followerLogInformation, true);
+ sendUpdatesToFollower(followerId, followerLogInformation, true, isHeartbeat);
}
}
}
*/
private void sendUpdatesToFollower(String followerId, FollowerLogInformation followerLogInformation,
- boolean sendHeartbeat) {
+ boolean sendHeartbeat, boolean isHeartbeat) {
ActorSelection followerActor = context.getPeerActorSelection(followerId);
if (followerActor != null) {
} else {
long leaderLastIndex = context.getReplicatedLog().lastIndex();
long leaderSnapShotIndex = context.getReplicatedLog().getSnapshotIndex();
- if (isFollowerActive &&
- context.getReplicatedLog().isPresent(followerNextIndex)) {
+
+ if(!isHeartbeat || LOG.isTraceEnabled()) {
+ LOG.debug("{}: Checking sendAppendEntries for follower {}, leaderLastIndex: {}, leaderSnapShotIndex: {}",
+ logName(), followerId, leaderLastIndex, leaderSnapShotIndex);
+ }
+
+ if (isFollowerActive && context.getReplicatedLog().isPresent(followerNextIndex)) {
+
+ LOG.debug("{}: sendAppendEntries: {} is present for follower {}", logName(),
+ followerNextIndex, followerId);
+
// FIXME : Sending one entry at a time
final List<ReplicatedLogEntry> entries = context.getReplicatedLog().getFrom(followerNextIndex, 1);
sendAppendEntriesToFollower(followerActor, followerNextIndex, entries, followerId);
} else if (isFollowerActive && followerNextIndex >= 0 &&
- leaderLastIndex >= followerNextIndex) {
+ leaderLastIndex > followerNextIndex && !context.isSnapshotCaptureInitiated()) {
// if the followers next index is not present in the leaders log, and
// if the follower is just not starting and if leader's index is more than followers index
// then snapshot should be sent
if (LOG.isDebugEnabled()) {
- LOG.debug("InitiateInstallSnapshot to follower:{}," +
- "follower-nextIndex:{}, leader-snapshot-index:{}, " +
- "leader-last-index:{}", followerId,
- followerNextIndex, leaderSnapShotIndex, leaderLastIndex
- );
+ LOG.debug(String.format("%s: InitiateInstallSnapshot to follower: %s," +
+ "follower-nextIndex: %d, leader-snapshot-index: %d, " +
+ "leader-last-index: %d", logName(), followerId,
+ followerNextIndex, leaderSnapShotIndex, leaderLastIndex));
}
- actor().tell(new InitiateInstallSnapshot(), actor());
// Send heartbeat to follower whenever install snapshot is initiated.
sendAppendEntriesToFollower(followerActor, followerLogInformation.getNextIndex(),
Collections.<ReplicatedLogEntry>emptyList(), followerId);
+ initiateCaptureSnapshot(followerId, followerNextIndex);
+
} else if(sendHeartbeat) {
//we send an AppendEntries, even if the follower is inactive
// in-order to update the followers timestamp, in case it becomes active again
AppendEntries appendEntries = new AppendEntries(currentTerm(), context.getId(),
prevLogIndex(followerNextIndex),
prevLogTerm(followerNextIndex), entries,
- context.getCommitIndex(), replicatedToAllIndex);
+ context.getCommitIndex(), super.getReplicatedToAllIndex());
- if(!entries.isEmpty()) {
- LOG.debug("{}: Sending AppendEntries to follower {}: {}", context.getId(), followerId,
+ if(!entries.isEmpty() || LOG.isTraceEnabled()) {
+ LOG.debug("{}: Sending AppendEntries to follower {}: {}", logName(), followerId,
appendEntries);
}
}
/**
- * An installSnapshot is scheduled at a interval that is a multiple of
- * a HEARTBEAT_INTERVAL. This is to avoid the need to check for installing
- * snapshots at every heartbeat.
- *
* Install Snapshot works as follows
- * 1. Leader sends a InitiateInstallSnapshot message to self
- * 2. Leader then initiates the capture snapshot by sending a CaptureSnapshot message to actor
- * 3. RaftActor on receipt of the CaptureSnapshotReply (from Shard), stores the received snapshot in the replicated log
+ * 1. Leader initiates the capture snapshot by sending a CaptureSnapshot message to actor
+ * 2. RaftActor on receipt of the CaptureSnapshotReply (from Shard), stores the received snapshot in the replicated log
* and makes a call to Leader's handleMessage , with SendInstallSnapshot message.
- * 4. Leader , picks the snapshot from im-mem ReplicatedLog and sends it in chunks to the Follower
- * 5. On complete, Follower sends back a InstallSnapshotReply.
- * 6. On receipt of the InstallSnapshotReply for the last chunk, Leader marks the install complete for that follower
+ * 3. Leader , picks the snapshot from im-mem ReplicatedLog and sends it in chunks to the Follower
+ * 4. On complete, Follower sends back a InstallSnapshotReply.
+ * 5. On receipt of the InstallSnapshotReply for the last chunk, Leader marks the install complete for that follower
* and replenishes the memory by deleting the snapshot in Replicated log.
- *
+ * 6. If another follower requires a snapshot and a snapshot has been collected (via CaptureSnapshotReply)
+ * then send the existing snapshot in chunks to the follower.
+ * @param followerId
+ * @param followerNextIndex
*/
- private void installSnapshotIfNeeded() {
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: installSnapshotIfNeeded, followers {}", context.getId(), followerToLog.keySet());
- }
-
- for (Entry<String, FollowerLogInformation> e : followerToLog.entrySet()) {
- final ActorSelection followerActor = context.getPeerActorSelection(e.getKey());
-
- if (followerActor != null) {
- long nextIndex = e.getValue().getNextIndex();
-
- if (!context.getReplicatedLog().isPresent(nextIndex) &&
- context.getReplicatedLog().isInSnapshot(nextIndex)) {
- LOG.info("{}: {} follower needs a snapshot install", context.getId(), e.getKey());
- if (snapshot.isPresent()) {
- // if a snapshot is present in the memory, most likely another install is in progress
- // no need to capture snapshot
- sendSnapshotChunk(followerActor, e.getKey());
-
- } else if (!context.isSnapshotCaptureInitiated()) {
- initiateCaptureSnapshot();
- //we just need 1 follower who would need snapshot to be installed.
- // when we have the snapshot captured, we would again check (in SendInstallSnapshot)
- // who needs an install and send to all who need
- break;
- }
+ private void initiateCaptureSnapshot(String followerId, long followerNextIndex) {
+ if (!context.getReplicatedLog().isPresent(followerNextIndex) &&
+ context.getReplicatedLog().isInSnapshot(followerNextIndex)) {
+ if (snapshot.isPresent()) {
+ // if a snapshot is present in the memory, most likely another install is in progress
+ // no need to capture snapshot.
+ // This could happen if another follower needs an install when one is going on.
+ final ActorSelection followerActor = context.getPeerActorSelection(followerId);
+ sendSnapshotChunk(followerActor, followerId);
+
+ } else if (!context.isSnapshotCaptureInitiated()) {
+
+ LOG.info("{}: Initiating Snapshot Capture to Install Snapshot, Leader:{}", logName(), getLeaderId());
+ ReplicatedLogEntry lastAppliedEntry = context.getReplicatedLog().get(context.getLastApplied());
+ long lastAppliedIndex = -1;
+ long lastAppliedTerm = -1;
+
+ if (lastAppliedEntry != null) {
+ lastAppliedIndex = lastAppliedEntry.getIndex();
+ lastAppliedTerm = lastAppliedEntry.getTerm();
+ } else if (context.getReplicatedLog().getSnapshotIndex() > -1) {
+ lastAppliedIndex = context.getReplicatedLog().getSnapshotIndex();
+ lastAppliedTerm = context.getReplicatedLog().getSnapshotTerm();
}
+
+ boolean isInstallSnapshotInitiated = true;
+ long replicatedToAllIndex = super.getReplicatedToAllIndex();
+ ReplicatedLogEntry replicatedToAllEntry = context.getReplicatedLog().get(replicatedToAllIndex);
+ actor().tell(new CaptureSnapshot(lastIndex(), lastTerm(), lastAppliedIndex, lastAppliedTerm,
+ (replicatedToAllEntry != null ? replicatedToAllEntry.getIndex() : -1),
+ (replicatedToAllEntry != null ? replicatedToAllEntry.getTerm() : -1),
+ isInstallSnapshotInitiated), actor());
+ context.setSnapshotCaptureInitiated(true);
}
}
}
- // on every install snapshot, we try to capture the snapshot.
- // Once a capture is going on, another one issued will get ignored by RaftActor.
- private void initiateCaptureSnapshot() {
- LOG.info("{}: Initiating Snapshot Capture to Install Snapshot, Leader:{}", context.getId(), getLeaderId());
- ReplicatedLogEntry lastAppliedEntry = context.getReplicatedLog().get(context.getLastApplied());
- long lastAppliedIndex = -1;
- long lastAppliedTerm = -1;
-
- if (lastAppliedEntry != null) {
- lastAppliedIndex = lastAppliedEntry.getIndex();
- lastAppliedTerm = lastAppliedEntry.getTerm();
- } else if (context.getReplicatedLog().getSnapshotIndex() > -1) {
- lastAppliedIndex = context.getReplicatedLog().getSnapshotIndex();
- lastAppliedTerm = context.getReplicatedLog().getSnapshotTerm();
- }
-
- boolean isInstallSnapshotInitiated = true;
- actor().tell(new CaptureSnapshot(lastIndex(), lastTerm(),
- lastAppliedIndex, lastAppliedTerm, isInstallSnapshotInitiated),
- actor());
- context.setSnapshotCaptureInitiated(true);
- }
-
private void sendInstallSnapshot() {
+ LOG.debug("{}: sendInstallSnapshot", logName());
for (Entry<String, FollowerLogInformation> e : followerToLog.entrySet()) {
ActorSelection followerActor = context.getPeerActorSelection(e.getKey());
context.getReplicatedLog().getSnapshotIndex(),
context.getReplicatedLog().getSnapshotTerm(),
nextSnapshotChunk,
- followerToSnapshot.incrementChunkIndex(),
- followerToSnapshot.getTotalChunks(),
+ followerToSnapshot.incrementChunkIndex(),
+ followerToSnapshot.getTotalChunks(),
Optional.of(followerToSnapshot.getLastChunkHashCode())
).toSerializable(),
actor()
);
LOG.info("{}: InstallSnapshot sent to follower {}, Chunk: {}/{}",
- context.getId(), followerActor.path(),
+ logName(), followerActor.path(),
followerToSnapshot.getChunkIndex(),
followerToSnapshot.getTotalChunks());
}
} catch (IOException e) {
- LOG.error("{}: InstallSnapshot failed for Leader.", context.getId(), e);
+ LOG.error("{}: InstallSnapshot failed for Leader.", logName(), e);
}
}
mapFollowerToSnapshot.put(followerId, followerToSnapshot);
}
ByteString nextChunk = followerToSnapshot.getNextChunk();
- if (LOG.isDebugEnabled()) {
- LOG.debug("{}: Leader's snapshot nextChunk size:{}", context.getId(), nextChunk.size());
- }
+
+ LOG.debug("{}: next snapshot chunk size for follower {}: {}", logName(), followerId, nextChunk.size());
+
return nextChunk;
}
private void sendHeartBeat() {
if (!followerToLog.isEmpty()) {
- sendAppendEntries(context.getConfigParams().getHeartBeatInterval().toMillis());
+ LOG.trace("{}: Sending heartbeat", logName());
+ sendAppendEntries(context.getConfigParams().getHeartBeatInterval().toMillis(), true);
}
}
((size % context.getConfigParams().getSnapshotChunkSize()) > 0 ? 1 : 0);
if(LOG.isDebugEnabled()) {
LOG.debug("{}: Snapshot {} bytes, total chunks to send:{}",
- context.getId(), size, totalChunks);
+ logName(), size, totalChunks);
}
replyReceivedForOffset = -1;
chunkIndex = AbstractLeader.FIRST_CHUNK_INDEX;
}
}
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: Next chunk: length={}, offset={},size={}", context.getId(),
+
+ LOG.debug("{}: Next chunk: length={}, offset={},size={}", logName(),
snapshotLength, start, size);
- }
+
ByteString substring = getSnapshotBytes().substring(start, start + size);
nextChunkHashCode = substring.hashCode();
return substring;
import java.util.concurrent.TimeUnit;
import org.opendaylight.controller.cluster.raft.ClientRequestTracker;
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.base.messages.ApplyLogEntries;
*/
protected String leaderId = null;
+ private long replicatedToAllIndex = -1;
- protected AbstractRaftActorBehavior(RaftActorContext context) {
+ private final String logName;
+
+ private final RaftState state;
+
+ protected AbstractRaftActorBehavior(RaftActorContext context, RaftState state) {
this.context = context;
+ this.state = state;
this.LOG = context.getLogger();
+
+ logName = String.format("%s (%s)", context.getId(), state);
+ }
+
+ @Override
+ public RaftState state() {
+ return state;
+ }
+
+ public String logName() {
+ return logName;
+ }
+
+ @Override
+ public void setReplicatedToAllIndex(long replicatedToAllIndex) {
+ this.replicatedToAllIndex = replicatedToAllIndex;
+ }
+
+ @Override
+ public long getReplicatedToAllIndex() {
+ return replicatedToAllIndex;
}
/**
if (appendEntries.getTerm() < currentTerm()) {
if(LOG.isDebugEnabled()) {
LOG.debug("{}: Cannot append entries because sender term {} is less than {}",
- context.getId(), appendEntries.getTerm(), currentTerm());
+ logName(), appendEntries.getTerm(), currentTerm());
}
sender.tell(
* @param requestVote
* @return
*/
- protected RaftActorBehavior requestVote(ActorRef sender,
- RequestVote requestVote) {
+ protected RaftActorBehavior requestVote(ActorRef sender, RequestVote requestVote) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: Received {}", context.getId(), requestVote);
- }
+ LOG.debug("{}: In requestVote: {}", logName(), requestVote);
boolean grantVote = false;
}
}
- sender.tell(new RequestVoteReply(currentTerm(), grantVote), actor());
+ RequestVoteReply reply = new RequestVoteReply(currentTerm(), grantVote);
+
+ LOG.debug("{}: requestVote returning: {}", logName(), reply);
+
+ sender.tell(reply, actor());
return this;
}
// around as the rest wont be present either
LOG.warn(
"{}: Missing index {} from log. Cannot apply state. Ignoring {} to {}",
- context.getId(), i, i, index);
+ logName(), i, i, index);
break;
}
}
if(LOG.isDebugEnabled()) {
- LOG.debug("{}: Setting last applied to {}", context.getId(), newLastApplied);
+ LOG.debug("{}: Setting last applied to {}", logName(), newLastApplied);
}
context.setLastApplied(newLastApplied);
}
protected RaftActorBehavior switchBehavior(RaftActorBehavior behavior) {
- LOG.info("{} :- Switching from behavior {} to {}", context.getId(), this.state(), behavior.state());
+ LOG.info("{} :- Switching from behavior {} to {}", logName(), this.state(), behavior.state());
try {
close();
} catch (Exception e) {
- LOG.error("{}: Failed to close behavior : {}", context.getId(), this.state(), e);
+ LOG.error("{}: Failed to close behavior : {}", logName(), this.state(), e);
}
return behavior;
}
- protected long fakeSnapshot(final long minReplicatedToAllIndex, final long currentReplicatedIndex) {
+ /**
+ * Performs a snapshot with no capture on the replicated log.
+ * It clears the log from the supplied index or last-applied-1 which ever is minimum.
+ *
+ * @param snapshotCapturedIndex
+ */
+ protected void performSnapshotWithoutCapture(final long snapshotCapturedIndex) {
// we would want to keep the lastApplied as its used while capturing snapshots
- long tempMin = Math.min(minReplicatedToAllIndex,
- (context.getLastApplied() > -1 ? context.getLastApplied() - 1 : -1));
+ long lastApplied = context.getLastApplied();
+ long tempMin = Math.min(snapshotCapturedIndex, (lastApplied > -1 ? lastApplied - 1 : -1));
if (tempMin > -1 && context.getReplicatedLog().isPresent(tempMin)) {
- context.getReplicatedLog().snapshotPreCommit(tempMin, context.getTermInformation().getCurrentTerm());
+ LOG.debug("{}: fakeSnapshot purging log to {} for term {}", logName(), tempMin,
+ context.getTermInformation().getCurrentTerm());
+
+ //use the term of the temp-min, since we check for isPresent, entry will not be null
+ ReplicatedLogEntry entry = context.getReplicatedLog().get(tempMin);
+ context.getReplicatedLog().snapshotPreCommit(tempMin, entry.getTerm());
context.getReplicatedLog().snapshotCommit();
- return tempMin;
+ setReplicatedToAllIndex(tempMin);
}
- return currentReplicatedIndex;
}
+
}
private final Set<String> peers;
public Candidate(RaftActorContext context) {
- super(context);
+ super(context, RaftState.Candidate);
peers = context.getPeerAddresses().keySet();
if(LOG.isDebugEnabled()) {
- LOG.debug("{}: Election: Candidate has following peers: {}", context.getId(), peers);
+ LOG.debug("{}: Election: Candidate has following peers: {}", logName(), peers);
}
votesRequired = getMajorityVoteCount(peers.size());
AppendEntries appendEntries) {
if(LOG.isDebugEnabled()) {
- LOG.debug("{}: handleAppendEntries: {}", context.getId(), appendEntries);
+ LOG.debug("{}: handleAppendEntries: {}", logName(), appendEntries);
}
return this;
}
@Override protected RaftActorBehavior handleRequestVoteReply(ActorRef sender,
- RequestVoteReply requestVoteReply) {
+ RequestVoteReply requestVoteReply) {
+
+ LOG.debug("{}: handleRequestVoteReply: {}, current voteCount: {}", logName(), requestVoteReply,
+ voteCount);
if (requestVoteReply.isVoteGranted()) {
voteCount++;
return this;
}
- @Override public RaftState state() {
- return RaftState.Candidate;
- }
-
@Override
public RaftActorBehavior handleMessage(ActorRef sender, Object originalMessage) {
RaftRPC rpc = (RaftRPC) message;
if(LOG.isDebugEnabled()) {
- LOG.debug("{}: RaftRPC message received {} my term is {}", context.getId(), rpc,
+ LOG.debug("{}: RaftRPC message received {}, my term is {}", logName(), rpc,
context.getTermInformation().getCurrentTerm());
}
}
if (message instanceof ElectionTimeout) {
+ LOG.debug("{}: Received ElectionTimeout", logName());
+
if (votesRequired == 0) {
// If there are no peers then we should be a Leader
// We wait for the election timeout to occur before declare
// Increment the election term and vote for self
long currentTerm = context.getTermInformation().getCurrentTerm();
- context.getTermInformation().updateAndPersist(currentTerm + 1,
- context.getId());
+ long newTerm = currentTerm + 1;
+ context.getTermInformation().updateAndPersist(newTerm, context.getId());
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: Starting new term {}", context.getId(), (currentTerm + 1));
- }
+ LOG.debug("{}: Starting new term {}", logName(), newTerm);
// Request for a vote
// TODO: Retry request for vote if replies do not arrive in a reasonable
for (String peerId : peers) {
ActorSelection peerActor = context.getPeerActorSelection(peerId);
if(peerActor != null) {
- peerActor.tell(new RequestVote(
+ RequestVote requestVote = new RequestVote(
context.getTermInformation().getCurrentTerm(),
context.getId(),
context.getReplicatedLog().lastIndex(),
- context.getReplicatedLog().lastTerm()),
- context.getActor()
- );
- }
- }
+ context.getReplicatedLog().lastTerm());
+ LOG.debug("{}: Sending {} to peer {}", logName(), requestVote, peerId);
+ peerActor.tell(requestVote, context.getActor());
+ }
+ }
}
@Override public void close() throws Exception {
import akka.actor.ActorRef;
import com.google.common.annotations.VisibleForTesting;
-import com.google.protobuf.ByteString;
import java.util.ArrayList;
import org.opendaylight.controller.cluster.raft.RaftActorContext;
import org.opendaylight.controller.cluster.raft.RaftState;
private SnapshotTracker snapshotTracker = null;
public Follower(RaftActorContext context) {
- super(context);
+ super(context, RaftState.Follower);
scheduleElection(electionDuration());
}
@Override protected RaftActorBehavior handleAppendEntries(ActorRef sender,
AppendEntries appendEntries) {
- if(appendEntries.getEntries() != null && appendEntries.getEntries().size() > 0) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: handleAppendEntries: {}", context.getId(), appendEntries);
- }
+ int numLogEntries = appendEntries.getEntries() != null ? appendEntries.getEntries().size() : 0;
+ if(LOG.isTraceEnabled()) {
+ LOG.trace("{}: handleAppendEntries: {}", logName(), appendEntries);
+ } else if(LOG.isDebugEnabled() && numLogEntries > 0) {
+ LOG.debug("{}: handleAppendEntries: {}", logName(), appendEntries);
}
// TODO : Refactor this method into a bunch of smaller methods
boolean outOfSync = true;
// First check if the logs are in sync or not
- if (lastIndex() == -1
- && appendEntries.getPrevLogIndex() != -1) {
+ long lastIndex = lastIndex();
+ if (lastIndex == -1 && appendEntries.getPrevLogIndex() != -1) {
// The follower's log is out of sync because the leader does have
// an entry at prevLogIndex and this follower has no entries in
// it's log.
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: The followers log is empty and the senders prevLogIndex is {}",
- context.getId(), appendEntries.getPrevLogIndex());
- }
-
- } else if (lastIndex() > -1
- && appendEntries.getPrevLogIndex() != -1
- && !prevEntryPresent) {
+ LOG.debug("{}: The followers log is empty and the senders prevLogIndex is {}",
+ logName(), appendEntries.getPrevLogIndex());
+ } else if (lastIndex > -1 && appendEntries.getPrevLogIndex() != -1 && !prevEntryPresent) {
// The follower's log is out of sync because the Leader's
// prevLogIndex entry was not found in it's log
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: The log is not empty but the prevLogIndex {} was not found in it",
- context.getId(), appendEntries.getPrevLogIndex());
- }
-
- } else if (lastIndex() > -1
- && prevEntryPresent
- && prevLogTerm != appendEntries.getPrevLogTerm()) {
+ LOG.debug("{}: The log is not empty but the prevLogIndex {} was not found in it",
+ logName(), appendEntries.getPrevLogIndex());
+ } else if (lastIndex > -1 && prevEntryPresent && prevLogTerm != appendEntries.getPrevLogTerm()) {
// The follower's log is out of sync because the Leader's
// prevLogIndex entry does exist in the follower's log but it has
// a different term in it
- if (LOG.isDebugEnabled()) {
- LOG.debug(
- "{}: Cannot append entries because previous entry term {} is not equal to append entries prevLogTerm {}"
- , context.getId(), prevLogTerm
- , appendEntries.getPrevLogTerm());
- }
+ LOG.debug(
+ "{}: Cannot append entries because previous entry term {} is not equal to append entries prevLogTerm {}",
+ logName(), prevLogTerm, appendEntries.getPrevLogTerm());
} else {
outOfSync = false;
}
if (outOfSync) {
// We found that the log was out of sync so just send a negative
// reply and return
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: Follower ({}) is out-of-sync, " +
- "so sending negative reply, lastIndex():{}, lastTerm():{}",
- context.getId(), context.getId(), lastIndex(), lastTerm()
- );
- }
- sender.tell(
- new AppendEntriesReply(context.getId(), currentTerm(), false,
- lastIndex(), lastTerm()), actor()
- );
+
+ LOG.debug("{}: Follower is out-of-sync, so sending negative reply, lastIndex: {}, lastTerm: {}",
+ logName(), lastIndex, lastTerm());
+
+ sender.tell(new AppendEntriesReply(context.getId(), currentTerm(), false, lastIndex,
+ lastTerm()), actor());
return this;
}
- if (appendEntries.getEntries() != null
- && appendEntries.getEntries().size() > 0) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: Number of entries to be appended = {}", context.getId(),
+ if (appendEntries.getEntries() != null && appendEntries.getEntries().size() > 0) {
+
+ LOG.debug("{}: Number of entries to be appended = {}", logName(),
appendEntries.getEntries().size());
- }
// 3. If an existing entry conflicts with a new one (same index
// but different terms), delete the existing entry and all that
break;
}
- if (newEntry.getTerm() == matchEntry
- .getTerm()) {
+ if (newEntry.getTerm() == matchEntry.getTerm()) {
continue;
}
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: Removing entries from log starting at {}", context.getId(),
+ LOG.debug("{}: Removing entries from log starting at {}", logName(),
matchEntry.getIndex());
- }
// Entries do not match so remove all subsequent entries
- context.getReplicatedLog()
- .removeFromAndPersist(matchEntry.getIndex());
+ context.getReplicatedLog().removeFromAndPersist(matchEntry.getIndex());
break;
}
}
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: After cleanup entries to be added from = {}", context.getId(),
- (addEntriesFrom + lastIndex()));
- }
+ lastIndex = lastIndex();
+ LOG.debug("{}: After cleanup entries to be added from = {}", logName(),
+ (addEntriesFrom + lastIndex));
// 4. Append any new entries not already in the log
- for (int i = addEntriesFrom;
- i < appendEntries.getEntries().size(); i++) {
+ for (int i = addEntriesFrom; i < appendEntries.getEntries().size(); i++) {
+ ReplicatedLogEntry entry = appendEntries.getEntries().get(i);
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: Append entry to log {}", context.getId(),
- appendEntries.getEntries().get(i).getData());
- }
- context.getReplicatedLog().appendAndPersist(appendEntries.getEntries().get(i));
- }
+ LOG.debug("{}: Append entry to log {}", logName(), entry.getData());
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: Log size is now {}", context.getId(), context.getReplicatedLog().size());
+ context.getReplicatedLog().appendAndPersist(entry);
}
- }
+ LOG.debug("{}: Log size is now {}", logName(), context.getReplicatedLog().size());
+ }
// 5. If leaderCommit > commitIndex, set commitIndex =
// min(leaderCommit, index of last new entry)
+ lastIndex = lastIndex();
long prevCommitIndex = context.getCommitIndex();
- context.setCommitIndex(Math.min(appendEntries.getLeaderCommit(),
- context.getReplicatedLog().lastIndex()));
+ context.setCommitIndex(Math.min(appendEntries.getLeaderCommit(), lastIndex));
if (prevCommitIndex != context.getCommitIndex()) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: Commit index set to {}", context.getId(), context.getCommitIndex());
- }
+ LOG.debug("{}: Commit index set to {}", logName(), context.getCommitIndex());
}
// If commitIndex > lastApplied: increment lastApplied, apply
// log[lastApplied] to state machine (§5.3)
// check if there are any entries to be applied. last-applied can be equal to last-index
if (appendEntries.getLeaderCommit() > context.getLastApplied() &&
- context.getLastApplied() < lastIndex()) {
+ context.getLastApplied() < lastIndex) {
if(LOG.isDebugEnabled()) {
LOG.debug("{}: applyLogToStateMachine, " +
- "appendEntries.getLeaderCommit():{}," +
- "context.getLastApplied():{}, lastIndex():{}", context.getId(),
- appendEntries.getLeaderCommit(), context.getLastApplied(), lastIndex()
- );
+ "appendEntries.getLeaderCommit(): {}," +
+ "context.getLastApplied(): {}, lastIndex(): {}", logName(),
+ appendEntries.getLeaderCommit(), context.getLastApplied(), lastIndex);
}
applyLogToStateMachine(appendEntries.getLeaderCommit());
}
- sender.tell(new AppendEntriesReply(context.getId(), currentTerm(), true,
- lastIndex(), lastTerm()), actor());
+ AppendEntriesReply reply = new AppendEntriesReply(context.getId(), currentTerm(), true,
+ lastIndex, lastTerm());
+
+ if(LOG.isTraceEnabled()) {
+ LOG.trace("{}: handleAppendEntries returning : {}", logName(), reply);
+ } else if(LOG.isDebugEnabled() && numLogEntries > 0) {
+ LOG.debug("{}: handleAppendEntries returning : {}", logName(), reply);
+ }
+
+ sender.tell(reply, actor());
if (!context.isSnapshotCaptureInitiated()) {
- fakeSnapshot(appendEntries.getReplicatedToAllIndex(), appendEntries.getReplicatedToAllIndex());
+ super.performSnapshotWithoutCapture(appendEntries.getReplicatedToAllIndex());
}
return this;
return this;
}
- @Override public RaftState state() {
- return RaftState.Follower;
- }
-
@Override public RaftActorBehavior handleMessage(ActorRef sender, Object originalMessage) {
Object message = fromSerializableMessage(originalMessage);
// set currentTerm = T, convert to follower (§5.1)
// This applies to all RPC messages and responses
if (rpc.getTerm() > context.getTermInformation().getCurrentTerm()) {
+ LOG.debug("{}: Term {} in \"{}\" message is greater than follower's term {} - updating term",
+ logName(), rpc.getTerm(), rpc, context.getTermInformation().getCurrentTerm());
+
context.getTermInformation().updateAndPersist(rpc.getTerm(), null);
}
}
if (message instanceof ElectionTimeout) {
+ LOG.debug("{}: Received ElectionTimeout - switching to Candidate", logName());
return switchBehavior(new Candidate(context));
} else if (message instanceof InstallSnapshot) {
private void handleInstallSnapshot(ActorRef sender, InstallSnapshot installSnapshot) {
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: InstallSnapshot received by follower " +
- "datasize:{} , Chunk:{}/{}", context.getId(), installSnapshot.getData().size(),
- installSnapshot.getChunkIndex(), installSnapshot.getTotalChunks()
- );
- }
+
+ LOG.debug("{}: InstallSnapshot received from leader {}, datasize: {} , Chunk: {}/{}",
+ logName(), installSnapshot.getLeaderId(), installSnapshot.getData().size(),
+ installSnapshot.getChunkIndex(), installSnapshot.getTotalChunks());
if(snapshotTracker == null){
snapshotTracker = new SnapshotTracker(LOG, installSnapshot.getTotalChunks());
}
- sender.tell(new InstallSnapshotReply(
- currentTerm(), context.getId(), installSnapshot.getChunkIndex(),
- true), actor());
+ InstallSnapshotReply reply = new InstallSnapshotReply(
+ currentTerm(), context.getId(), installSnapshot.getChunkIndex(), true);
+
+ LOG.debug("{}: handleInstallSnapshot returning: {}", logName(), reply);
+
+ sender.tell(reply, actor());
} catch (SnapshotTracker.InvalidChunkException e) {
+ LOG.debug("{}: Exception in InstallSnapshot of follower", logName(), e);
sender.tell(new InstallSnapshotReply(currentTerm(), context.getId(),
-1, false), actor());
snapshotTracker = null;
} catch (Exception e){
- LOG.error("{}: Exception in InstallSnapshot of follower", context.getId(), e);
+ LOG.error("{}: Exception in InstallSnapshot of follower", logName(), e);
+
//send reply with success as false. The chunk will be sent again on failure
sender.tell(new InstallSnapshotReply(currentTerm(), context.getId(),
installSnapshot.getChunkIndex(), false), actor());
}
}
- @Override public void close() throws Exception {
+ @Override
+ public void close() throws Exception {
stopElection();
}
@VisibleForTesting
- ByteString getSnapshotChunksCollected(){
- return snapshotTracker != null ? snapshotTracker.getCollectedChunks() : ByteString.EMPTY;
+ SnapshotTracker getSnapshotTracker(){
+ return snapshotTracker;
}
-
-
}
package org.opendaylight.controller.cluster.raft.behaviors;
import akka.actor.ActorRef;
-import akka.actor.Cancellable;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import com.google.common.base.Stopwatch;
+import java.util.concurrent.TimeUnit;
import org.opendaylight.controller.cluster.raft.RaftActorContext;
import org.opendaylight.controller.cluster.raft.base.messages.IsolatedLeaderCheck;
-import scala.concurrent.duration.FiniteDuration;
/**
* The behavior of a RaftActor when it is in the Leader state
* set commitIndex = N (§5.3, §5.4).
*/
public class Leader extends AbstractLeader {
- private Cancellable installSnapshotSchedule = null;
- private Cancellable isolatedLeaderCheckSchedule = null;
+ private static final IsolatedLeaderCheck ISOLATED_LEADER_CHECK = new IsolatedLeaderCheck();
+ private final Stopwatch isolatedLeaderCheck;
public Leader(RaftActorContext context) {
super(context);
-
- scheduleIsolatedLeaderCheck(
- new FiniteDuration(context.getConfigParams().getHeartBeatInterval().length() * 10,
- context.getConfigParams().getHeartBeatInterval().unit()));
+ isolatedLeaderCheck = Stopwatch.createStarted();
}
@Override public RaftActorBehavior handleMessage(ActorRef sender, Object originalMessage) {
if (originalMessage instanceof IsolatedLeaderCheck) {
if (isLeaderIsolated()) {
- LOG.info("{}: At least {} followers need to be active, Switching {} from Leader to IsolatedLeader",
+ LOG.warn("{}: At least {} followers need to be active, Switching {} from Leader to IsolatedLeader",
context.getId(), minIsolatedLeaderPeerCount, leaderId);
+
return switchBehavior(new IsolatedLeader(context));
}
}
return super.handleMessage(sender, originalMessage);
}
- protected void stopIsolatedLeaderCheckSchedule() {
- if (isolatedLeaderCheckSchedule != null && !isolatedLeaderCheckSchedule.isCancelled()) {
- isolatedLeaderCheckSchedule.cancel();
+ @Override
+ protected void beforeSendHeartbeat(){
+ if(isolatedLeaderCheck.elapsed(TimeUnit.MILLISECONDS) > context.getConfigParams().getIsolatedCheckIntervalInMillis()){
+ context.getActor().tell(ISOLATED_LEADER_CHECK, context.getActor());
+ isolatedLeaderCheck.reset().start();
}
- }
- protected void scheduleIsolatedLeaderCheck(FiniteDuration isolatedCheckInterval) {
- isolatedLeaderCheckSchedule = context.getActorSystem().scheduler().schedule(isolatedCheckInterval, isolatedCheckInterval,
- context.getActor(), new IsolatedLeaderCheck(),
- context.getActorSystem().dispatcher(), context.getActor());
}
@Override
public void close() throws Exception {
- stopIsolatedLeaderCheckSchedule();
super.close();
}
* @return
*/
String getLeaderId();
+
+ /**
+ * setting the index of the log entry which is replicated to all nodes
+ * @param replicatedToAllIndex
+ */
+ void setReplicatedToAllIndex(long replicatedToAllIndex);
+
+ /**
+ * getting the index of the log entry which is replicated to all nodes
+ * @return
+ */
+ long getReplicatedToAllIndex();
}
return replicatedToAllIndex;
}
+
@Override
public String toString() {
- 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(", replicatedToAllIndex=").append(replicatedToAllIndex);
- sb.append('}');
- return sb.toString();
+ StringBuilder builder = new StringBuilder();
+ builder.append("AppendEntries [term=").append(term).append(", leaderId=").append(leaderId)
+ .append(", prevLogIndex=").append(prevLogIndex).append(", prevLogTerm=").append(prevLogTerm)
+ .append(", entries=").append(entries).append(", leaderCommit=").append(leaderCommit)
+ .append(", replicatedToAllIndex=").append(replicatedToAllIndex).append("]");
+ return builder.toString();
}
public <T extends Object> Object toSerializable() {
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();
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("AppendEntriesReply [term=").append(term).append(", success=").append(success)
+ .append(", logLastIndex=").append(logLastIndex).append(", logLastTerm=").append(logLastTerm)
+ .append(", followerId=").append(followerId).append("]");
+ return builder.toString();
}
}
public <T extends Object> Object toSerializable(){
InstallSnapshotMessages.InstallSnapshot.Builder builder = InstallSnapshotMessages.InstallSnapshot.newBuilder()
+ .setTerm(this.getTerm())
.setLeaderId(this.getLeaderId())
.setChunkIndex(this.getChunkIndex())
.setData(this.getData())
return installSnapshot;
}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("InstallSnapshot [term=").append(term).append(", leaderId=").append(leaderId)
+ .append(", lastIncludedIndex=").append(lastIncludedIndex).append(", lastIncludedTerm=")
+ .append(lastIncludedTerm).append(", data=").append(data).append(", chunkIndex=").append(chunkIndex)
+ .append(", totalChunks=").append(totalChunks).append(", lastChunkHashCode=").append(lastChunkHashCode)
+ .append("]");
+ return builder.toString();
+ }
}
public boolean isSuccess() {
return success;
}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("InstallSnapshotReply [term=").append(term).append(", followerId=").append(followerId)
+ .append(", chunkIndex=").append(chunkIndex).append(", success=").append(success).append("]");
+ return builder.toString();
+ }
}
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();
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("RequestVote [term=").append(term).append(", candidateId=").append(candidateId)
+ .append(", lastLogIndex=").append(lastLogIndex).append(", lastLogTerm=").append(lastLogTerm)
+ .append("]");
+ return builder.toString();
}
}
public boolean isVoteGranted() {
return voteGranted;
}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("RequestVoteReply [term=").append(term).append(", voteGranted=").append(voteGranted).append("]");
+ return builder.toString();
+ }
}
import org.junit.Test;
import org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload;
import org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockReplicatedLogEntry;
+
/**
*
*/
@Test
public void testSnapshotPreCommit() {
+ //add 4 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")));
+ //sending negative values should not cause any changes
+ replicatedLogImpl.snapshotPreCommit(-1, -1);
+ assertEquals(8, replicatedLogImpl.size());
+ assertEquals(-1, replicatedLogImpl.getSnapshotIndex());
+
replicatedLogImpl.snapshotPreCommit(4, 3);
assertEquals(3, replicatedLogImpl.size());
assertEquals(4, replicatedLogImpl.getSnapshotIndex());
assertEquals(0, replicatedLogImpl.size());
assertEquals(7, replicatedLogImpl.getSnapshotIndex());
+ }
+
+ @Test
+ public void testIsPresent() {
+ assertTrue(replicatedLogImpl.isPresent(0));
+ assertTrue(replicatedLogImpl.isPresent(1));
+ assertTrue(replicatedLogImpl.isPresent(2));
+ assertTrue(replicatedLogImpl.isPresent(3));
+
+ replicatedLogImpl.append(new MockReplicatedLogEntry(2, 4, new MockPayload("D")));
+ replicatedLogImpl.snapshotPreCommit(3, 2); //snapshot on 3
+ replicatedLogImpl.snapshotCommit();
+
+ assertFalse(replicatedLogImpl.isPresent(0));
+ assertFalse(replicatedLogImpl.isPresent(1));
+ assertFalse(replicatedLogImpl.isPresent(2));
+ assertFalse(replicatedLogImpl.isPresent(3));
+ assertTrue(replicatedLogImpl.isPresent(4));
+
+ replicatedLogImpl.snapshotPreCommit(4, 2); //snapshot on 4
+ replicatedLogImpl.snapshotCommit();
+ assertFalse(replicatedLogImpl.isPresent(4));
+ replicatedLogImpl.append(new MockReplicatedLogEntry(2, 5, new MockPayload("D")));
+ assertTrue(replicatedLogImpl.isPresent(5));
}
// create a snapshot for test
* Identifier of the actor whose election term information this is
*/
private final String id = id1;
- private long currentTerm = 0;
+ private long currentTerm = 1;
private String votedFor = "";
@Override
public void initReplicatedLog(){
this.replicatedLog = new SimpleReplicatedLog();
- this.replicatedLog.append(new MockReplicatedLogEntry(1, 0, new MockPayload("1")));
- this.replicatedLog.append(new MockReplicatedLogEntry(1, 1, new MockPayload("2")));
+ long term = getTermInformation().getCurrentTerm();
+ this.replicatedLog.append(new MockReplicatedLogEntry(term, 0, new MockPayload("1")));
+ this.replicatedLog.append(new MockReplicatedLogEntry(term, 1, new MockPayload("2")));
}
@Override public ActorRef actorOf(Props props) {
public String toString() {
return value;
}
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((value == null) ? 0 : value.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ MockPayload other = (MockPayload) obj;
+ if (value == null) {
+ if (other.value != null) {
+ return false;
+ }
+ } else if (!value.equals(other.value)) {
+ return false;
+ }
+ return true;
+ }
}
public static class MockReplicatedLogEntry implements ReplicatedLogEntry, Serializable {
public int size() {
return getData().size();
}
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((data == null) ? 0 : data.hashCode());
+ result = prime * result + (int) (index ^ (index >>> 32));
+ result = prime * result + (int) (term ^ (term >>> 32));
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ MockReplicatedLogEntry other = (MockReplicatedLogEntry) obj;
+ if (data == null) {
+ if (other.data != null) {
+ return false;
+ }
+ } else if (!data.equals(other.data)) {
+ return false;
+ }
+ if (index != other.index) {
+ return false;
+ }
+ if (term != other.term) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("MockReplicatedLogEntry [term=").append(term).append(", index=").append(index)
+ .append(", data=").append(data).append("]");
+ return builder.toString();
+ }
}
public static class MockReplicatedLogBuilder {
import org.opendaylight.controller.cluster.raft.base.messages.ApplyState;
import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot;
import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply;
-import org.opendaylight.controller.cluster.raft.base.messages.InitiateInstallSnapshot;
+import org.opendaylight.controller.cluster.raft.base.messages.SendHeartBeat;
import org.opendaylight.controller.cluster.raft.behaviors.Follower;
import org.opendaylight.controller.cluster.raft.behaviors.Leader;
import org.opendaylight.controller.cluster.raft.client.messages.FindLeader;
new MockRaftActorContext.MockPayload("C"),
new MockRaftActorContext.MockPayload("D")));
- mockRaftActor.onReceiveCommand(new CaptureSnapshot(-1, 1, -1, 1));
+ mockRaftActor.onReceiveCommand(new CaptureSnapshot(-1, 1,-1, 1, -1, 1));
RaftActorContext raftActorContext = mockRaftActor.getRaftActorContext();
RaftActorContext raftActorContext = mockRaftActor.getRaftActorContext();
mockRaftActor.setCurrentBehavior(new Follower(raftActorContext));
- mockRaftActor.onReceiveCommand(new CaptureSnapshot(-1, 1, 2, 1));
+ long replicatedToAllIndex = 1;
+ mockRaftActor.onReceiveCommand(new CaptureSnapshot(-1, 1, 2, 1, replicatedToAllIndex, 1));
verify(mockRaftActor.delegate).createSnapshot();
verify(dataPersistenceProvider).deleteMessages(100);
- assertEquals(2, mockRaftActor.getReplicatedLog().size());
+ assertEquals(3, mockRaftActor.getReplicatedLog().size());
+ assertEquals(1, mockRaftActor.getCurrentBehavior().getReplicatedToAllIndex());
+ assertNotNull(mockRaftActor.getReplicatedLog().get(2));
assertNotNull(mockRaftActor.getReplicatedLog().get(3));
assertNotNull(mockRaftActor.getReplicatedLog().get(4));
// Index 2 will not be in the log because it was removed due to snapshotting
- assertNull(mockRaftActor.getReplicatedLog().get(2));
+ assertNull(mockRaftActor.getReplicatedLog().get(1));
+ assertNull(mockRaftActor.getReplicatedLog().get(0));
}
};
mockRaftActor.setCurrentBehavior(new Leader(raftActorContext));
- mockRaftActor.onReceiveCommand(new CaptureSnapshot(-1, 1, -1, 1));
+ mockRaftActor.onReceiveCommand(new CaptureSnapshot(-1, 1, -1, 1, -1, 1));
mockRaftActor.onReceiveCommand(new CaptureSnapshotReply(snapshotBytes.toByteArray()));
// sleeping for a minimum of 2 seconds, if it spans more its fine.
Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS);
- List<Object> matches = MessageCollectorActor.getAllMatching(notifierActor, RoleChanged.class);
+ List<RoleChanged> matches = MessageCollectorActor.getAllMatching(notifierActor, RoleChanged.class);
assertNotNull(matches);
assertEquals(3, matches.size());
// check if the notifier got a role change from null to Follower
- RoleChanged raftRoleChanged = (RoleChanged) matches.get(0);
+ RoleChanged raftRoleChanged = matches.get(0);
assertEquals(persistenceId, raftRoleChanged.getMemberId());
assertNull(raftRoleChanged.getOldRole());
assertEquals(RaftState.Follower.name(), raftRoleChanged.getNewRole());
// check if the notifier got a role change from Follower to Candidate
- raftRoleChanged = (RoleChanged) matches.get(1);
+ raftRoleChanged = matches.get(1);
assertEquals(persistenceId, raftRoleChanged.getMemberId());
assertEquals(RaftState.Follower.name(), raftRoleChanged.getOldRole());
assertEquals(RaftState.Candidate.name(), raftRoleChanged.getNewRole());
// check if the notifier got a role change from Candidate to Leader
- raftRoleChanged = (RoleChanged) matches.get(2);
+ raftRoleChanged = matches.get(2);
assertEquals(persistenceId, raftRoleChanged.getMemberId());
assertEquals(RaftState.Candidate.name(), raftRoleChanged.getOldRole());
assertEquals(RaftState.Leader.name(), raftRoleChanged.getNewRole());
assertEquals(8, leaderActor.getReplicatedLog().size());
- leaderActor.onReceiveCommand(new CaptureSnapshot(6, 1, 4, 1));
+ leaderActor.onReceiveCommand(new CaptureSnapshot(6, 1, 4, 1, 4, 1));
+
leaderActor.getRaftActorContext().setSnapshotCaptureInitiated(true);
verify(leaderActor.delegate).createSnapshot();
followerActor.waitForInitializeBehaviorComplete();
- // create 8 entries in the log - 0 to 4 are applied and will get picked up as part of the capture snapshot
+
Follower follower = new Follower(followerActor.getRaftActorContext());
followerActor.setCurrentBehavior(follower);
assertEquals(RaftState.Follower, followerActor.getCurrentBehavior().state());
+ // create 6 entries in the log - 0 to 4 are applied and will get picked up as part of the capture snapshot
MockRaftActorContext.MockReplicatedLogBuilder logBuilder = new MockRaftActorContext.MockReplicatedLogBuilder();
followerActor.getRaftActorContext().setReplicatedLog(logBuilder.createEntries(0, 6, 1).build());
- // log as indices 0-5
+ // log has indices 0-5
assertEquals(6, followerActor.getReplicatedLog().size());
//snapshot on 4
- followerActor.onReceiveCommand(new CaptureSnapshot(5, 1, 4, 1));
+ followerActor.onReceiveCommand(new CaptureSnapshot(5, 1, 4, 1, 4, 1));
+
followerActor.getRaftActorContext().setSnapshotCaptureInitiated(true);
verify(followerActor.delegate).createSnapshot();
followerActor.onReceiveCommand(new CaptureSnapshotReply(snapshotBytes.toByteArray()));
assertFalse(followerActor.getRaftActorContext().isSnapshotCaptureInitiated());
- // capture snapshot reply should remove the snapshotted entries only
+ // capture snapshot reply should remove the snapshotted entries only till replicatedToAllIndex
assertEquals(3, followerActor.getReplicatedLog().size()); //indexes 5,6,7 left in the log
assertEquals(7, followerActor.getReplicatedLog().lastIndex());
// create 5 entries in the log
MockRaftActorContext.MockReplicatedLogBuilder logBuilder = new MockRaftActorContext.MockReplicatedLogBuilder();
leaderActor.getRaftActorContext().setReplicatedLog(logBuilder.createEntries(5, 10, 1).build());
+
//set the snapshot index to 4 , 0 to 4 are snapshotted
leaderActor.getRaftActorContext().getReplicatedLog().setSnapshotIndex(4);
+ //setting replicatedToAllIndex = 9, for the log to clear
+ leader.setReplicatedToAllIndex(9);
assertEquals(5, leaderActor.getReplicatedLog().size());
+ assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state());
leaderActor.onReceiveCommand(new AppendEntriesReply(follower1Id, 1, true, 9, 1));
assertEquals(5, leaderActor.getReplicatedLog().size());
+ assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state());
// set the 2nd follower nextIndex to 1 which has been snapshotted
leaderActor.onReceiveCommand(new AppendEntriesReply(follower2Id, 1, true, 0, 1));
assertEquals(5, leaderActor.getReplicatedLog().size());
+ assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state());
// simulate a real snapshot
- leaderActor.onReceiveCommand(new InitiateInstallSnapshot());
+ leaderActor.onReceiveCommand(new SendHeartBeat());
assertEquals(5, leaderActor.getReplicatedLog().size());
assertEquals(String.format("expected to be Leader but was %s. Current Leader = %s ",
leaderActor.getCurrentBehavior().state(), leaderActor.getLeaderId())
leaderActor.onReceiveCommand(new CaptureSnapshotReply(snapshotBytes.toByteArray()));
assertFalse(leaderActor.getRaftActorContext().isSnapshotCaptureInitiated());
- assertEquals("Real snapshot didn't clear the log till lastApplied", 0, leaderActor.getReplicatedLog().size());
+ assertEquals("Real snapshot didn't clear the log till replicatedToAllIndex", 0, leaderActor.getReplicatedLog().size());
//reply from a slow follower after should not raise errors
leaderActor.onReceiveCommand(new AppendEntriesReply(follower2Id, 1, true, 5, 1));
import akka.actor.ActorSystem;
import akka.actor.PoisonPill;
import akka.actor.Props;
+import akka.testkit.JavaTestKit;
import akka.testkit.TestActorRef;
import java.util.LinkedList;
import java.util.List;
}
@Override
- public void close() throws Exception {
- for(ActorRef actor : createdActors){
- LOG.info("Killing actor {}", actor);
- actor.tell(PoisonPill.getInstance(), null);
- }
+ public void close() {
+ new JavaTestKit(system) {{
+ for(ActorRef actor : createdActors) {
+ watch(actor);
+ LOG.info("Killing actor {}", actor);
+ actor.tell(PoisonPill.getInstance(), ActorRef.noSender());
+ expectTerminated(duration("5 seconds"), actor);
+ }
+ }};
}
}
\ No newline at end of file
package org.opendaylight.controller.cluster.raft.behaviors;
+import static org.junit.Assert.assertEquals;
import akka.actor.ActorRef;
import akka.actor.Props;
-import akka.testkit.JavaTestKit;
+import akka.testkit.TestActorRef;
+import com.google.protobuf.ByteString;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.junit.After;
+import org.junit.Assert;
import org.junit.Test;
import org.opendaylight.controller.cluster.raft.AbstractActorTest;
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.SerializationUtils;
+import org.opendaylight.controller.cluster.raft.TestActorFactory;
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 org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
-import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
+import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
+import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
-import java.util.List;
+public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest {
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+ protected final TestActorFactory actorFactory = new TestActorFactory(getSystem());
-public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest {
+ private final TestActorRef<MessageCollectorActor> behaviorActor = actorFactory.createTestActor(
+ Props.create(MessageCollectorActor.class), actorFactory.generateActorId("behavior"));
+
+ RaftActorBehavior behavior;
- private final ActorRef behaviorActor = getSystem().actorOf(Props.create(
- DoNothingActor.class));
+ @After
+ public void tearDown() throws Exception {
+ if(behavior != null) {
+ behavior.close();
+ }
+
+ actorFactory.close();
+ }
/**
* This test checks that when a new Raft RPC message is received with a newer
*/
@Test
public void testHandleRaftRPCWithNewerTerm() throws Exception {
- new JavaTestKit(getSystem()) {{
+ RaftActorContext actorContext = createActorContext();
- assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(getTestActor(),
+ assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, behaviorActor,
createAppendEntriesWithNewerTerm());
- assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(getTestActor(),
+ assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, behaviorActor,
createAppendEntriesReplyWithNewerTerm());
- assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(getTestActor(),
+ assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, behaviorActor,
createRequestVoteWithNewerTerm());
- assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(getTestActor(),
+ assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, behaviorActor,
createRequestVoteReplyWithNewerTerm());
-
-
- }};
}
* @throws Exception
*/
@Test
- public void testHandleAppendEntriesSenderTermLessThanReceiverTerm()
- throws Exception {
- new JavaTestKit(getSystem()) {{
-
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
+ public void testHandleAppendEntriesSenderTermLessThanReceiverTerm() throws Exception {
+ MockRaftActorContext context = createActorContext();
// First set the receivers term to a high number (1000)
context.getTermInformation().update(1000, "test");
- AppendEntries appendEntries =
- new AppendEntries(100, "leader-1", 0, 0, null, 101, -1);
+ AppendEntries appendEntries = new AppendEntries(100, "leader-1", 0, 0, null, 101, -1);
- RaftActorBehavior behavior = createBehavior(context);
+ behavior = createBehavior(context);
// Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
+ RaftActorBehavior expected = behavior.handleMessage(behaviorActor, "unknown");
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
+ RaftActorBehavior raftBehavior = behavior.handleMessage(behaviorActor, appendEntries);
- assertEquals(expected, raftBehavior);
+ assertEquals("Raft state", expected.state(), raftBehavior.state());
// Also expect an AppendEntriesReply to be sent where success is false
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
- "AppendEntriesReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(false, out);
-
-
- }};
- }
+ AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(
+ behaviorActor, AppendEntriesReply.class);
- @Test
- public void testHandleAppendEntriesAddSameEntryToLog(){
- new JavaTestKit(getSystem()) {
- {
+ assertEquals("isSuccess", false, reply.isSuccess());
+ }
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
- // First set the receivers term to lower number
- context.getTermInformation().update(2, "test");
+ @Test
+ public void testHandleAppendEntriesAddSameEntryToLog() throws Exception {
+ MockRaftActorContext context = createActorContext();
- // Prepare the receivers log
- MockRaftActorContext.SimpleReplicatedLog log =
- new MockRaftActorContext.SimpleReplicatedLog();
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero")));
+ context.getTermInformation().update(2, "test");
- context.setReplicatedLog(log);
+ // Prepare the receivers log
+ MockRaftActorContext.MockPayload payload = new MockRaftActorContext.MockPayload("zero");
+ setLastLogEntry(context, 2, 0, payload);
- List<ReplicatedLogEntry> entries = new ArrayList<>();
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero")));
+ List<ReplicatedLogEntry> entries = new ArrayList<>();
+ entries.add(new MockRaftActorContext.MockReplicatedLogEntry(2, 0, payload));
- AppendEntries appendEntries =
- new AppendEntries(2, "leader-1", -1, 1, entries, 0, -1);
+ AppendEntries appendEntries = new AppendEntries(2, "leader-1", -1, -1, entries, 2, -1);
- RaftActorBehavior behavior = createBehavior(context);
+ behavior = createBehavior(context);
- if (AbstractRaftActorBehaviorTest.this instanceof CandidateTest) {
- // Resetting the Candidates term to make sure it will match
- // the term sent by AppendEntries. If this was not done then
- // the test will fail because the Candidate will assume that
- // the message was sent to it from a lower term peer and will
- // thus respond with a failure
- context.getTermInformation().update(2, "test");
- }
+ if (behavior instanceof Candidate) {
+ // Resetting the Candidates term to make sure it will match
+ // the term sent by AppendEntries. If this was not done then
+ // the test will fail because the Candidate will assume that
+ // the message was sent to it from a lower term peer and will
+ // thus respond with a failure
+ context.getTermInformation().update(2, "test");
+ }
- // Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
+ // Send an unknown message so that the state of the RaftActor remains unchanged
+ RaftActorBehavior expected = behavior.handleMessage(behaviorActor, "unknown");
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
+ RaftActorBehavior raftBehavior = behavior.handleMessage(behaviorActor, appendEntries);
- assertEquals(expected, raftBehavior);
+ assertEquals("Raft state", expected.state(), raftBehavior.state());
- assertEquals(1, log.size());
+ assertEquals("ReplicatedLog size", 1, context.getReplicatedLog().size());
+ handleAppendEntriesAddSameEntryToLogReply(behaviorActor);
+ }
- }};
+ protected void handleAppendEntriesAddSameEntryToLogReply(TestActorRef<MessageCollectorActor> replyActor)
+ throws Exception {
+ AppendEntriesReply reply = MessageCollectorActor.getFirstMatching(replyActor, AppendEntriesReply.class);
+ Assert.assertNull("Expected no AppendEntriesReply", reply);
}
/**
* This test verifies that when a RequestVote is received by the RaftActor
- * with a term which is greater than the RaftActors' currentTerm and the
- * senders' log is more upto date than the receiver that the receiver grants
- * the vote to the sender
+ * with the senders' log is more up to date than the receiver that the receiver grants
+ * the vote to the sender.
*/
@Test
- public void testHandleRequestVoteWhenSenderTermGreaterThanCurrentTermAndSenderLogMoreUpToDate() {
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- RaftActorBehavior behavior = createBehavior(
- createActorContext(behaviorActor));
-
- RaftActorBehavior raftBehavior = behavior.handleMessage(getTestActor(),
- new RequestVote(1000, "test", 10000, 999));
-
- if(!(behavior instanceof Follower)){
- assertTrue(raftBehavior instanceof Follower);
- } else {
-
- final Boolean out =
- new ExpectMsg<Boolean>(duration("1 seconds"),
- "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply =
- (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(true, out);
- }
- }
- };
- }};
+ public void testHandleRequestVoteWhenSenderLogMoreUpToDate() {
+ MockRaftActorContext context = createActorContext();
+
+ behavior = createBehavior(context);
+
+ context.getTermInformation().update(1, "test");
+
+ behavior.handleMessage(behaviorActor, new RequestVote(context.getTermInformation().getCurrentTerm(),
+ "test", 10000, 999));
+
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(behaviorActor,
+ RequestVoteReply.class);
+ assertEquals("isVoteGranted", true, reply.isVoteGranted());
}
/**
* log then the receiving RaftActor will not grant the vote to the sender
*/
@Test
- public void testHandleRequestVoteWhenSenderTermGreaterThanCurrentTermButSenderLogLessUptoDate() {
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- RaftActorContext actorContext =
- createActorContext(behaviorActor);
-
- MockRaftActorContext.SimpleReplicatedLog
- log = new MockRaftActorContext.SimpleReplicatedLog();
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(20000,
- 1000000, new MockRaftActorContext.MockPayload("")));
-
- ((MockRaftActorContext) actorContext).setReplicatedLog(log);
-
- RaftActorBehavior behavior = createBehavior(actorContext);
-
- RaftActorBehavior raftBehavior = behavior.handleMessage(getTestActor(),
- new RequestVote(1000, "test", 10000, 999));
-
- if(!(behavior instanceof Follower)){
- assertTrue(raftBehavior instanceof Follower);
- } else {
- final Boolean out =
- new ExpectMsg<Boolean>(duration("1 seconds"),
- "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply =
- (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(false, out);
- }
- }
- };
- }};
+ public void testHandleRequestVoteWhenSenderLogLessUptoDate() {
+ MockRaftActorContext context = createActorContext();
+
+ behavior = createBehavior(context);
+
+ context.getTermInformation().update(1, "test");
+
+ int index = 2000;
+ setLastLogEntry(context, context.getTermInformation().getCurrentTerm(), index,
+ new MockRaftActorContext.MockPayload(""));
+
+ behavior.handleMessage(behaviorActor, new RequestVote(
+ context.getTermInformation().getCurrentTerm(), "test",
+ index - 1, context.getTermInformation().getCurrentTerm()));
+
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(behaviorActor,
+ RequestVoteReply.class);
+ assertEquals("isVoteGranted", false, reply.isVoteGranted());
}
*/
@Test
public void testHandleRequestVoteWhenSenderTermLessThanCurrentTerm() {
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- RaftActorContext context =
- createActorContext(behaviorActor);
-
- context.getTermInformation().update(1000, null);
-
- RaftActorBehavior follower = createBehavior(context);
-
- follower.handleMessage(getTestActor(),
- new RequestVote(999, "test", 10000, 999));
-
- final Boolean out =
- new ExpectMsg<Boolean>(duration("1 seconds"),
- "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply =
- (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(false, out);
- }
- };
- }};
+ RaftActorContext context = createActorContext();
+
+ context.getTermInformation().update(1000, null);
+
+ behavior = createBehavior(context);
+
+ behavior.handleMessage(behaviorActor, new RequestVote(999, "test", 10000, 999));
+
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(behaviorActor,
+ RequestVoteReply.class);
+ assertEquals("isVoteGranted", false, reply.isVoteGranted());
}
@Test
- public void testFakeSnapshots() {
+ public void testPerformSnapshot() {
MockRaftActorContext context = new MockRaftActorContext("test", getSystem(), behaviorActor);
- AbstractRaftActorBehavior behavior = new Leader(context);
- context.getTermInformation().update(1, "leader");
+ AbstractRaftActorBehavior abstractBehavior = (AbstractRaftActorBehavior) createBehavior(context);
+ if (abstractBehavior instanceof Candidate) {
+ return;
+ }
+
+ context.getTermInformation().update(1, "test");
- //entry with 1 index=0 entry with replicatedToAllIndex = 0, does not do anything, returns the
+ //log has 1 entry with replicatedToAllIndex = 0, does not do anything, returns the
context.setReplicatedLog(new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 1, 1).build());
context.setLastApplied(0);
- assertEquals(-1, behavior.fakeSnapshot(0, -1));
+ abstractBehavior.performSnapshotWithoutCapture(0);
+ assertEquals(-1, abstractBehavior.getReplicatedToAllIndex());
assertEquals(1, context.getReplicatedLog().size());
//2 entries, lastApplied still 0, no purging.
- context.setReplicatedLog(new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0,2,1).build());
+ context.setReplicatedLog(new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 2, 1).build());
context.setLastApplied(0);
- assertEquals(-1, behavior.fakeSnapshot(0, -1));
+ abstractBehavior.performSnapshotWithoutCapture(0);
+ assertEquals(-1, abstractBehavior.getReplicatedToAllIndex());
assertEquals(2, context.getReplicatedLog().size());
//2 entries, lastApplied still 0, no purging.
- context.setReplicatedLog(new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0,2,1).build());
+ context.setReplicatedLog(new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 2, 1).build());
context.setLastApplied(1);
- assertEquals(0, behavior.fakeSnapshot(0, -1));
+ abstractBehavior.performSnapshotWithoutCapture(0);
+ assertEquals(0, abstractBehavior.getReplicatedToAllIndex());
assertEquals(1, context.getReplicatedLog().size());
//5 entries, lastApplied =2 and replicatedIndex = 3, but since we want to keep the lastapplied, indices 0 and 1 will only get purged
- context.setReplicatedLog(new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0,5,1).build());
+ context.setReplicatedLog(new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 5, 1).build());
context.setLastApplied(2);
- assertEquals(1, behavior.fakeSnapshot(3, 1));
+ abstractBehavior.performSnapshotWithoutCapture(3);
+ assertEquals(1, abstractBehavior.getReplicatedToAllIndex());
assertEquals(3, context.getReplicatedLog().size());
-
+ // scenario where Last applied > Replicated to all index (becoz of a slow follower)
+ context.setReplicatedLog(new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
+ context.setLastApplied(2);
+ abstractBehavior.performSnapshotWithoutCapture(1);
+ assertEquals(1, abstractBehavior.getReplicatedToAllIndex());
+ assertEquals(1, context.getReplicatedLog().size());
}
- protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(
- ActorRef actorRef, RaftRPC rpc) {
- RaftActorContext actorContext = createActorContext();
+ protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext,
+ ActorRef actorRef, RaftRPC rpc) throws Exception {
+
Payload p = new MockRaftActorContext.MockPayload("");
- setLastLogEntry(
- (MockRaftActorContext) actorContext, 0, 0, p);
+ setLastLogEntry((MockRaftActorContext) actorContext, 1, 0, p);
+ actorContext.getTermInformation().update(1, "test");
+
+ RaftActorBehavior origBehavior = createBehavior(actorContext);
+ RaftActorBehavior raftBehavior = origBehavior.handleMessage(actorRef, rpc);
- RaftActorBehavior raftBehavior = createBehavior(actorContext)
- .handleMessage(actorRef, rpc);
+ assertEquals("New raft state", RaftState.Follower, raftBehavior.state());
+ assertEquals("New election term", rpc.getTerm(), actorContext.getTermInformation().getCurrentTerm());
- assertTrue(raftBehavior instanceof Follower);
+ origBehavior.close();
+ raftBehavior.close();
}
protected MockRaftActorContext.SimpleReplicatedLog setLastLogEntry(
new MockRaftActorContext.MockReplicatedLogEntry(term, index, data));
}
- protected MockRaftActorContext.SimpleReplicatedLog setLastLogEntry(
- MockRaftActorContext actorContext, ReplicatedLogEntry logEntry) {
- MockRaftActorContext.SimpleReplicatedLog
- log = new MockRaftActorContext.SimpleReplicatedLog();
+ protected MockRaftActorContext.SimpleReplicatedLog setLastLogEntry(MockRaftActorContext actorContext,
+ ReplicatedLogEntry logEntry) {
+ MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
log.append(logEntry);
actorContext.setReplicatedLog(log);
return createBehavior(createActorContext());
}
- protected RaftActorContext createActorContext() {
+ protected MockRaftActorContext createActorContext() {
return new MockRaftActorContext();
}
- protected RaftActorContext createActorContext(ActorRef actor) {
+ protected MockRaftActorContext createActorContext(ActorRef actor) {
return new MockRaftActorContext("test", getSystem(), actor);
}
protected Object fromSerializableMessage(Object serializable){
return SerializationUtils.fromSerializable(serializable);
}
+
+ protected ByteString toByteString(Map<String, String> state) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try(ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+ oos.writeObject(state);
+ return ByteString.copyFrom(bos.toByteArray());
+ } catch (IOException e) {
+ throw new AssertionError("IOException occurred converting Map to Bytestring", e);
+ }
+ }
+
+ protected void logStart(String name) {
+ LoggerFactory.getLogger(LeaderTest.class).info("Starting " + name);
+ }
}
package org.opendaylight.controller.cluster.raft.behaviors;
+import static org.junit.Assert.assertEquals;
import akka.actor.ActorRef;
import akka.actor.Props;
-import akka.testkit.JavaTestKit;
+import akka.testkit.TestActorRef;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-import org.junit.Assert;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
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.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 org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
-
-import static org.junit.Assert.assertEquals;
+import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
public class CandidateTest extends AbstractRaftActorBehaviorTest {
- private final ActorRef candidateActor = getSystem().actorOf(Props.create(
- DoNothingActor.class));
-
- private final ActorRef peerActor1 = getSystem().actorOf(Props.create(
- DoNothingActor.class));
+ private final TestActorRef<MessageCollectorActor> candidateActor = actorFactory.createTestActor(
+ Props.create(MessageCollectorActor.class), actorFactory.generateActorId("candidate"));
- private final ActorRef peerActor2 = getSystem().actorOf(Props.create(
- DoNothingActor.class));
+ private TestActorRef<MessageCollectorActor>[] peerActors;
- private final ActorRef peerActor3 = getSystem().actorOf(Props.create(
- DoNothingActor.class));
-
- private final ActorRef peerActor4 = getSystem().actorOf(Props.create(
- DoNothingActor.class));
-
- private final Map<String, String> onePeer = new HashMap<>();
- private final Map<String, String> twoPeers = new HashMap<>();
- private final Map<String, String> fourPeers = new HashMap<>();
+ private RaftActorBehavior candidate;
@Before
public void setUp(){
- onePeer.put(peerActor1.path().toString(),
- peerActor1.path().toString());
-
- twoPeers.put(peerActor1.path().toString(),
- peerActor1.path().toString());
- twoPeers.put(peerActor2.path().toString(),
- peerActor2.path().toString());
-
- fourPeers.put(peerActor1.path().toString(),
- peerActor1.path().toString());
- fourPeers.put(peerActor2.path().toString(),
- peerActor2.path().toString());
- fourPeers.put(peerActor3.path().toString(),
- peerActor3.path().toString());
- fourPeers.put(peerActor4.path().toString(),
- peerActor3.path().toString());
+ }
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ if(candidate != null) {
+ candidate.close();
+ }
+ super.tearDown();
}
@Test
RaftActorContext raftActorContext = createActorContext();
long expectedTerm = raftActorContext.getTermInformation().getCurrentTerm();
- new Candidate(raftActorContext);
+ candidate = new Candidate(raftActorContext);
- assertEquals(expectedTerm+1, raftActorContext.getTermInformation().getCurrentTerm());
- assertEquals(raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor());
+ assertEquals("getCurrentTerm", expectedTerm+1, raftActorContext.getTermInformation().getCurrentTerm());
+ assertEquals("getVotedFor", raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor());
}
@Test
public void testThatAnElectionTimeoutIsTriggered(){
- new JavaTestKit(getSystem()) {{
-
- new Within(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6)) {
- protected void run() {
-
- Candidate candidate = new Candidate(createActorContext(getTestActor()));
-
- final Boolean out = new ExpectMsg<Boolean>(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6), "ElectionTimeout") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof ElectionTimeout) {
- return true;
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(true, out);
- }
- };
- }};
+ MockRaftActorContext actorContext = createActorContext();
+ candidate = new Candidate(actorContext);
+
+ MessageCollectorActor.expectFirstMatching(candidateActor, ElectionTimeout.class,
+ actorContext.getConfigParams().getElectionTimeOutInterval().$times(6).toMillis());
}
@Test
public void testHandleElectionTimeoutWhenThereAreZeroPeers(){
RaftActorContext raftActorContext = createActorContext();
- Candidate candidate =
- new Candidate(raftActorContext);
+ candidate = new Candidate(raftActorContext);
- RaftActorBehavior raftBehavior =
+ RaftActorBehavior newBehavior =
candidate.handleMessage(candidateActor, new ElectionTimeout());
- Assert.assertTrue(raftBehavior instanceof Leader);
+ assertEquals("Behavior", RaftState.Leader, newBehavior.state());
}
@Test
- public void testHandleElectionTimeoutWhenThereAreTwoNodesInCluster(){
- MockRaftActorContext raftActorContext =
- (MockRaftActorContext) createActorContext();
- raftActorContext.setPeerAddresses(onePeer);
- Candidate candidate =
- new Candidate(raftActorContext);
-
- RaftActorBehavior raftBehavior =
- candidate.handleMessage(candidateActor, new ElectionTimeout());
+ public void testHandleElectionTimeoutWhenThereAreTwoNodeCluster(){
+ MockRaftActorContext raftActorContext = createActorContext();
+ raftActorContext.setPeerAddresses(setupPeers(1));
+ candidate = new Candidate(raftActorContext);
+
+ candidate = candidate.handleMessage(candidateActor, new ElectionTimeout());
- Assert.assertTrue(raftBehavior instanceof Candidate);
+ assertEquals("Behavior", RaftState.Candidate, candidate.state());
}
@Test
- public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodesInCluster(){
- MockRaftActorContext raftActorContext =
- (MockRaftActorContext) createActorContext();
- raftActorContext.setPeerAddresses(twoPeers);
- Candidate candidate =
- new Candidate(raftActorContext);
-
- RaftActorBehavior behaviorOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
+ public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodeCluster(){
+ MockRaftActorContext raftActorContext = createActorContext();
+ raftActorContext.setPeerAddresses(setupPeers(2));
+ candidate = new Candidate(raftActorContext);
- Assert.assertTrue(behaviorOnFirstVote instanceof Leader);
+ candidate = candidate.handleMessage(peerActors[0], new RequestVoteReply(1, true));
+ assertEquals("Behavior", RaftState.Leader, candidate.state());
}
@Test
- public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodesInCluster(){
- MockRaftActorContext raftActorContext =
- (MockRaftActorContext) createActorContext();
- raftActorContext.setPeerAddresses(fourPeers);
- Candidate candidate =
- new Candidate(raftActorContext);
+ public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodeCluster(){
+ MockRaftActorContext raftActorContext = createActorContext();
+ raftActorContext.setPeerAddresses(setupPeers(4));
+ candidate = new Candidate(raftActorContext);
- RaftActorBehavior behaviorOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
+ // First peers denies the vote.
+ candidate = candidate.handleMessage(peerActors[0], new RequestVoteReply(1, false));
- RaftActorBehavior behaviorOnSecondVote = candidate.handleMessage(peerActor2, new RequestVoteReply(0, true));
+ assertEquals("Behavior", RaftState.Candidate, candidate.state());
- Assert.assertTrue(behaviorOnFirstVote instanceof Candidate);
- Assert.assertTrue(behaviorOnSecondVote instanceof Leader);
+ candidate = candidate.handleMessage(peerActors[1], new RequestVoteReply(1, true));
- }
+ assertEquals("Behavior", RaftState.Candidate, candidate.state());
- @Test
- public void testResponseToAppendEntriesWithLowerTerm(){
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- Candidate candidate = new Candidate(createActorContext(getTestActor()));
-
- candidate.handleMessage(getTestActor(), new AppendEntries(0, "test", 0,0,Collections.<ReplicatedLogEntry>emptyList(), 0, -1));
-
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(false, out);
- }
- };
- }};
+ candidate = candidate.handleMessage(peerActors[2], new RequestVoteReply(1, true));
+
+ assertEquals("Behavior", RaftState.Leader, candidate.state());
}
@Test
- public void testResponseToRequestVoteWithLowerTerm(){
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- Candidate candidate = new Candidate(createActorContext(getTestActor()));
-
- candidate.handleMessage(getTestActor(), new RequestVote(0, "test", 0, 0));
-
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply = (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(false, out);
- }
- };
- }};
+ public void testResponseToHandleAppendEntriesWithLowerTerm() {
+ candidate = new Candidate(createActorContext());
+
+ setupPeers(1);
+ candidate.handleMessage(peerActors[0], new AppendEntries(1, "test", 0, 0,
+ Collections.<ReplicatedLogEntry>emptyList(), 0, -1));
+
+ AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(
+ peerActors[0], AppendEntriesReply.class);
+ assertEquals("isSuccess", false, reply.isSuccess());
+ assertEquals("getTerm", 2, reply.getTerm());
}
@Test
- public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNull(){
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- RaftActorContext context = createActorContext(getTestActor());
-
- context.getTermInformation().update(1000, null);
-
- // Once a candidate is created it will immediately increment the current term so after
- // construction the currentTerm should be 1001
- RaftActorBehavior follower = createBehavior(context);
-
- follower.handleMessage(getTestActor(), new RequestVote(1001, "test", 10000, 999));
-
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply = (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(true, out);
- }
- };
- }};
+ public void testResponseToRequestVoteWithLowerTerm() {
+ candidate = new Candidate(createActorContext());
+
+ setupPeers(1);
+ candidate.handleMessage(peerActors[0], new RequestVote(1, "test", 0, 0));
+
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
+ peerActors[0], RequestVoteReply.class);
+ assertEquals("isVoteGranted", false, reply.isVoteGranted());
+ assertEquals("getTerm", 2, reply.getTerm());
}
@Test
- public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNotTheSameAsCandidateId(){
- new JavaTestKit(getSystem()) {{
+ public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForMatches() {
+ MockRaftActorContext context = createActorContext();
+ context.getTermInformation().update(1000, null);
- new Within(duration("1 seconds")) {
- protected void run() {
+ // Once a candidate is created it will immediately increment the current term so after
+ // construction the currentTerm should be 1001
+ candidate = new Candidate(context);
- RaftActorContext context = createActorContext(getTestActor());
+ setupPeers(1);
+ candidate.handleMessage(peerActors[0], new RequestVote(1001, context.getId(), 10000, 999));
- context.getTermInformation().update(1000, "test");
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
+ peerActors[0], RequestVoteReply.class);
+ assertEquals("isVoteGranted", true, reply.isVoteGranted());
+ assertEquals("getTerm", 1001, reply.getTerm());
+ }
+
+ @Test
+ public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForDoesNotMatch() {
+ MockRaftActorContext context = createActorContext();
+ context.getTermInformation().update(1000, null);
- RaftActorBehavior follower = createBehavior(context);
+ // Once a candidate is created it will immediately increment the current term so after
+ // construction the currentTerm should be 1001
+ candidate = new Candidate(context);
- follower.handleMessage(getTestActor(), new RequestVote(1001, "candidate", 10000, 999));
+ setupPeers(1);
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply = (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
+ // RequestVote candidate ID ("candidate2") does not match this candidate's votedFor
+ // (it votes for itself)
+ candidate.handleMessage(peerActors[0], new RequestVote(1001, "candidate2", 10000, 999));
- assertEquals(false, out);
- }
- };
- }};
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
+ peerActors[0], RequestVoteReply.class);
+ assertEquals("isVoteGranted", false, reply.isVoteGranted());
+ assertEquals("getTerm", 1001, reply.getTerm());
}
- @Override protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
+ @Override
+ protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
return new Candidate(actorContext);
}
- @Override protected RaftActorContext createActorContext() {
- return new MockRaftActorContext("test", getSystem(), candidateActor);
+ @Override protected MockRaftActorContext createActorContext() {
+ return new MockRaftActorContext("candidate", getSystem(), candidateActor);
}
+ private Map<String, String> setupPeers(int count) {
+ Map<String, String> peerMap = new HashMap<>();
+ peerActors = new TestActorRef[count];
+ for(int i = 0; i < count; i++) {
+ peerActors[i] = actorFactory.createTestActor(Props.create(MessageCollectorActor.class),
+ actorFactory.generateActorId("peer"));
+ peerMap.put("peer" + (i+1), peerActors[i].path().toString());
+ }
+ return peerMap;
+ }
+
+ @Override
+ protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext,
+ ActorRef actorRef, RaftRPC rpc) throws Exception {
+ super.assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, actorRef, rpc);
+ assertEquals("New votedFor", null, actorContext.getTermInformation().getVotedFor());
+ }
}
package org.opendaylight.controller.cluster.raft.behaviors;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import akka.actor.ActorRef;
import akka.actor.Props;
-import akka.testkit.JavaTestKit;
+import akka.testkit.TestActorRef;
import com.google.protobuf.ByteString;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
-import java.util.Map;
+import org.junit.After;
+import org.junit.Assert;
import org.junit.Test;
-import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
import org.opendaylight.controller.cluster.raft.RaftActorContext;
import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
+import org.opendaylight.controller.cluster.raft.Snapshot;
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;
import org.opendaylight.controller.cluster.raft.messages.InstallSnapshotReply;
+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 org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
public class FollowerTest extends AbstractRaftActorBehaviorTest {
- private final ActorRef followerActor = getSystem().actorOf(Props.create(
- DoNothingActor.class));
+ private final TestActorRef<MessageCollectorActor> followerActor = actorFactory.createTestActor(
+ Props.create(MessageCollectorActor.class), actorFactory.generateActorId("follower"));
+ private final TestActorRef<MessageCollectorActor> leaderActor = actorFactory.createTestActor(
+ Props.create(MessageCollectorActor.class), actorFactory.generateActorId("leader"));
- @Override protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
+ private RaftActorBehavior follower;
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ if(follower != null) {
+ follower.close();
+ }
+
+ super.tearDown();
+ }
+
+ @Override
+ protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
return new Follower(actorContext);
}
- @Override protected RaftActorContext createActorContext() {
+ @Override
+ protected MockRaftActorContext createActorContext() {
return createActorContext(followerActor);
}
- protected RaftActorContext createActorContext(ActorRef actorRef){
- return new MockRaftActorContext("test", getSystem(), actorRef);
+ @Override
+ protected MockRaftActorContext createActorContext(ActorRef actorRef){
+ return new MockRaftActorContext("follower", getSystem(), actorRef);
}
@Test
public void testThatAnElectionTimeoutIsTriggered(){
- new JavaTestKit(getSystem()) {{
-
- new Within(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6)) {
- protected void run() {
-
- Follower follower = new Follower(createActorContext(getTestActor()));
-
- final Boolean out = new ExpectMsg<Boolean>(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6), "ElectionTimeout") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof ElectionTimeout) {
- return true;
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(true, out);
- }
- };
- }};
+ MockRaftActorContext actorContext = createActorContext();
+ follower = new Follower(actorContext);
+
+ MessageCollectorActor.expectFirstMatching(followerActor, ElectionTimeout.class,
+ actorContext.getConfigParams().getElectionTimeOutInterval().$times(6).toMillis());
}
@Test
public void testHandleElectionTimeout(){
- RaftActorContext raftActorContext = createActorContext();
- Follower follower =
- new Follower(raftActorContext);
+ logStart("testHandleElectionTimeout");
- RaftActorBehavior raftBehavior =
- follower.handleMessage(followerActor, new ElectionTimeout());
+ follower = new Follower(createActorContext());
+
+ RaftActorBehavior raftBehavior = follower.handleMessage(followerActor, new ElectionTimeout());
assertTrue(raftBehavior instanceof Candidate);
}
@Test
public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNull(){
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- RaftActorContext context = createActorContext(getTestActor());
+ logStart("testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNull");
- context.getTermInformation().update(1000, null);
+ RaftActorContext context = createActorContext();
+ long term = 1000;
+ context.getTermInformation().update(term, null);
- RaftActorBehavior follower = createBehavior(context);
+ follower = createBehavior(context);
- follower.handleMessage(getTestActor(), new RequestVote(1000, "test", 10000, 999));
+ follower.handleMessage(leaderActor, new RequestVote(term, "test", 10000, 999));
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply = (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(leaderActor, RequestVoteReply.class);
- assertEquals(true, out);
- }
- };
- }};
+ assertEquals("isVoteGranted", true, reply.isVoteGranted());
+ assertEquals("getTerm", term, reply.getTerm());
}
@Test
public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNotTheSameAsCandidateId(){
- new JavaTestKit(getSystem()) {{
-
- new Within(duration("1 seconds")) {
- protected void run() {
-
- RaftActorContext context = createActorContext(getTestActor());
+ logStart("testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNotTheSameAsCandidateId");
- context.getTermInformation().update(1000, "test");
+ RaftActorContext context = createActorContext();
+ long term = 1000;
+ context.getTermInformation().update(term, "test");
- RaftActorBehavior follower = createBehavior(context);
+ follower = createBehavior(context);
- follower.handleMessage(getTestActor(), new RequestVote(1000, "candidate", 10000, 999));
+ follower.handleMessage(leaderActor, new RequestVote(term, "candidate", 10000, 999));
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof RequestVoteReply) {
- RequestVoteReply reply = (RequestVoteReply) in;
- return reply.isVoteGranted();
- } else {
- throw noMatch();
- }
- }
- }.get();
+ RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(leaderActor, RequestVoteReply.class);
- assertEquals(false, out);
- }
- };
- }};
+ assertEquals("isVoteGranted", false, reply.isVoteGranted());
}
/**
*/
@Test
public void testHandleAppendEntriesWithNewerCommitIndex() throws Exception {
- new JavaTestKit(getSystem()) {{
+ logStart("testHandleAppendEntriesWithNewerCommitIndex");
- RaftActorContext context =
- createActorContext();
+ MockRaftActorContext context = createActorContext();
- context.setLastApplied(100);
- setLastLogEntry((MockRaftActorContext) context, 1, 100,
+ context.setLastApplied(100);
+ setLastLogEntry(context, 1, 100,
new MockRaftActorContext.MockPayload(""));
- ((MockRaftActorContext) context).getReplicatedLog().setSnapshotIndex(99);
+ context.getReplicatedLog().setSnapshotIndex(99);
- List<ReplicatedLogEntry> entries =
- Arrays.asList(
- (ReplicatedLogEntry) new MockRaftActorContext.MockReplicatedLogEntry(2, 101,
- new MockRaftActorContext.MockPayload("foo"))
- );
+ List<ReplicatedLogEntry> entries = Arrays.<ReplicatedLogEntry>asList(
+ newReplicatedLogEntry(2, 101, "foo"));
- // The new commitIndex is 101
- AppendEntries appendEntries =
- new AppendEntries(2, "leader-1", 100, 1, entries, 101, 100);
+ // The new commitIndex is 101
+ AppendEntries appendEntries = new AppendEntries(2, "leader-1", 100, 1, entries, 101, 100);
- RaftActorBehavior raftBehavior =
- createBehavior(context).handleMessage(getRef(), appendEntries);
+ follower = createBehavior(context);
+ follower.handleMessage(leaderActor, appendEntries);
- assertEquals(101L, context.getLastApplied());
-
- }};
+ assertEquals("getLastApplied", 101L, context.getLastApplied());
}
/**
* @throws Exception
*/
@Test
- public void testHandleAppendEntriesSenderPrevLogTermNotSameAsReceiverPrevLogTerm()
- throws Exception {
- new JavaTestKit(getSystem()) {{
-
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
-
- // First set the receivers term to lower number
- context.getTermInformation().update(95, "test");
-
- // Set the last log entry term for the receiver to be greater than
- // what we will be sending as the prevLogTerm in AppendEntries
- MockRaftActorContext.SimpleReplicatedLog mockReplicatedLog =
- setLastLogEntry(context, 20, 0, new MockRaftActorContext.MockPayload(""));
-
- // AppendEntries is now sent with a bigger term
- // this will set the receivers term to be the same as the sender's term
- AppendEntries appendEntries =
- new AppendEntries(100, "leader-1", 0, 0, null, 101, -1);
+ public void testHandleAppendEntriesSenderPrevLogTermNotSameAsReceiverPrevLogTerm() {
+ logStart("testHandleAppendEntriesSenderPrevLogTermNotSameAsReceiverPrevLogTerm");
- RaftActorBehavior behavior = createBehavior(context);
+ MockRaftActorContext context = createActorContext();
- // Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
+ // First set the receivers term to lower number
+ context.getTermInformation().update(95, "test");
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
+ // AppendEntries is now sent with a bigger term
+ // this will set the receivers term to be the same as the sender's term
+ AppendEntries appendEntries = new AppendEntries(100, "leader", 0, 0, null, 101, -1);
- assertEquals(expected, raftBehavior);
+ follower = createBehavior(context);
- // Also expect an AppendEntriesReply to be sent where success is false
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
- "AppendEntriesReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
+ RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
- assertEquals(false, out);
+ Assert.assertSame(follower, newBehavior);
+ AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(leaderActor,
+ AppendEntriesReply.class);
- }};
+ assertEquals("isSuccess", false, reply.isSuccess());
}
-
-
/**
* This test verifies that when a new AppendEntries message is received with
* new entries and the logs of the sender and receiver match that the new
* @throws Exception
*/
@Test
- public void testHandleAppendEntriesAddNewEntries() throws Exception {
- new JavaTestKit(getSystem()) {{
-
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
-
- // First set the receivers term to lower number
- context.getTermInformation().update(1, "test");
-
- // Prepare the receivers log
- MockRaftActorContext.SimpleReplicatedLog log =
- new MockRaftActorContext.SimpleReplicatedLog();
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero")));
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 1, new MockRaftActorContext.MockPayload("one")));
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 2, new MockRaftActorContext.MockPayload("two")));
-
- context.setReplicatedLog(log);
-
- // Prepare the entries to be sent with AppendEntries
- List<ReplicatedLogEntry> entries = new ArrayList<>();
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 3, new MockRaftActorContext.MockPayload("three")));
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 4, new MockRaftActorContext.MockPayload("four")));
-
- // Send appendEntries with the same term as was set on the receiver
- // before the new behavior was created (1 in this case)
- // This will not work for a Candidate because as soon as a Candidate
- // is created it increments the term
- AppendEntries appendEntries =
- new AppendEntries(1, "leader-1", 2, 1, entries, 4, -1);
-
- RaftActorBehavior behavior = createBehavior(context);
-
- // Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
-
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
-
- assertEquals(expected, raftBehavior);
- assertEquals(5, log.last().getIndex() + 1);
- assertNotNull(log.get(3));
- assertNotNull(log.get(4));
-
- // Also expect an AppendEntriesReply to be sent where success is false
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
- "AppendEntriesReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(true, out);
-
-
- }};
- }
+ public void testHandleAppendEntriesAddNewEntries() {
+ logStart("testHandleAppendEntriesAddNewEntries");
+
+ MockRaftActorContext context = createActorContext();
+
+ // First set the receivers term to lower number
+ context.getTermInformation().update(1, "test");
+ // Prepare the receivers log
+ MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
+ log.append(newReplicatedLogEntry(1, 0, "zero"));
+ log.append(newReplicatedLogEntry(1, 1, "one"));
+ log.append(newReplicatedLogEntry(1, 2, "two"));
+ context.setReplicatedLog(log);
+
+ // Prepare the entries to be sent with AppendEntries
+ List<ReplicatedLogEntry> entries = new ArrayList<>();
+ entries.add(newReplicatedLogEntry(1, 3, "three"));
+ entries.add(newReplicatedLogEntry(1, 4, "four"));
+
+ // Send appendEntries with the same term as was set on the receiver
+ // before the new behavior was created (1 in this case)
+ // This will not work for a Candidate because as soon as a Candidate
+ // is created it increments the term
+ AppendEntries appendEntries = new AppendEntries(1, "leader-1", 2, 1, entries, 4, -1);
+
+ follower = createBehavior(context);
+
+ RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
+
+ Assert.assertSame(follower, newBehavior);
+
+ assertEquals("Next index", 5, log.last().getIndex() + 1);
+ assertEquals("Entry 3", entries.get(0), log.get(3));
+ assertEquals("Entry 4", entries.get(1), log.get(4));
+
+ expectAndVerifyAppendEntriesReply(1, true, context.getId(), 1, 4);
+ }
/**
* This test verifies that when a new AppendEntries message is received with
* new entries and the logs of the sender and receiver are out-of-sync that
* the log is first corrected by removing the out of sync entries from the
* log and then adding in the new entries sent with the AppendEntries message
- *
- * @throws Exception
*/
@Test
- public void testHandleAppendEntriesCorrectReceiverLogEntries()
- throws Exception {
- new JavaTestKit(getSystem()) {{
-
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
-
- // First set the receivers term to lower number
- context.getTermInformation().update(2, "test");
-
- // Prepare the receivers log
- MockRaftActorContext.SimpleReplicatedLog log =
- new MockRaftActorContext.SimpleReplicatedLog();
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero")));
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 1, new MockRaftActorContext.MockPayload("one")));
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 2, new MockRaftActorContext.MockPayload("two")));
-
- context.setReplicatedLog(log);
-
- // Prepare the entries to be sent with AppendEntries
- List<ReplicatedLogEntry> entries = new ArrayList<>();
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(2, 2, new MockRaftActorContext.MockPayload("two-1")));
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(2, 3, new MockRaftActorContext.MockPayload("three")));
-
- // Send appendEntries with the same term as was set on the receiver
- // before the new behavior was created (1 in this case)
- // This will not work for a Candidate because as soon as a Candidate
- // is created it increments the term
- AppendEntries appendEntries =
- new AppendEntries(2, "leader-1", 1, 1, entries, 3, -1);
-
- RaftActorBehavior behavior = createBehavior(context);
-
- // Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
-
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
-
- assertEquals(expected, raftBehavior);
-
- // The entry at index 2 will be found out-of-sync with the leader
- // and will be removed
- // Then the two new entries will be added to the log
- // Thus making the log to have 4 entries
- assertEquals(4, log.last().getIndex() + 1);
- assertNotNull(log.get(2));
-
- assertEquals("one", log.get(1).getData().toString());
-
- // Check that the entry at index 2 has the new data
- assertEquals("two-1", log.get(2).getData().toString());
-
- assertEquals("three", log.get(3).getData().toString());
-
- assertNotNull(log.get(3));
-
- // Also expect an AppendEntriesReply to be sent where success is false
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
- "AppendEntriesReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
-
- assertEquals(true, out);
-
-
- }};
+ public void testHandleAppendEntriesCorrectReceiverLogEntries() {
+ logStart("testHandleAppendEntriesCorrectReceiverLogEntries");
+
+ MockRaftActorContext context = createActorContext();
+
+ // First set the receivers term to lower number
+ context.getTermInformation().update(1, "test");
+
+ // Prepare the receivers log
+ MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
+ log.append(newReplicatedLogEntry(1, 0, "zero"));
+ log.append(newReplicatedLogEntry(1, 1, "one"));
+ log.append(newReplicatedLogEntry(1, 2, "two"));
+
+ context.setReplicatedLog(log);
+
+ // Prepare the entries to be sent with AppendEntries
+ List<ReplicatedLogEntry> entries = new ArrayList<>();
+ entries.add(newReplicatedLogEntry(2, 2, "two-1"));
+ entries.add(newReplicatedLogEntry(2, 3, "three"));
+
+ // Send appendEntries with the same term as was set on the receiver
+ // before the new behavior was created (1 in this case)
+ // This will not work for a Candidate because as soon as a Candidate
+ // is created it increments the term
+ AppendEntries appendEntries = new AppendEntries(2, "leader", 1, 1, entries, 3, -1);
+
+ follower = createBehavior(context);
+
+ RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
+
+ Assert.assertSame(follower, newBehavior);
+
+ // The entry at index 2 will be found out-of-sync with the leader
+ // and will be removed
+ // Then the two new entries will be added to the log
+ // Thus making the log to have 4 entries
+ assertEquals("Next index", 4, log.last().getIndex() + 1);
+ //assertEquals("Entry 2", entries.get(0), log.get(2));
+
+ assertEquals("Entry 1 data", "one", log.get(1).getData().toString());
+
+ // Check that the entry at index 2 has the new data
+ assertEquals("Entry 2", entries.get(0), log.get(2));
+
+ assertEquals("Entry 3", entries.get(1), log.get(3));
+
+ expectAndVerifyAppendEntriesReply(2, true, context.getId(), 2, 3);
}
@Test
public void testHandleAppendEntriesPreviousLogEntryMissing(){
- new JavaTestKit(getSystem()) {{
+ logStart("testHandleAppendEntriesPreviousLogEntryMissing");
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
+ MockRaftActorContext context = createActorContext();
- // Prepare the receivers log
- MockRaftActorContext.SimpleReplicatedLog log =
- new MockRaftActorContext.SimpleReplicatedLog();
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero")));
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 1, new MockRaftActorContext.MockPayload("one")));
- log.append(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 2, new MockRaftActorContext.MockPayload("two")));
+ // Prepare the receivers log
+ MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
+ log.append(newReplicatedLogEntry(1, 0, "zero"));
+ log.append(newReplicatedLogEntry(1, 1, "one"));
+ log.append(newReplicatedLogEntry(1, 2, "two"));
- context.setReplicatedLog(log);
+ context.setReplicatedLog(log);
- // Prepare the entries to be sent with AppendEntries
- List<ReplicatedLogEntry> entries = new ArrayList<>();
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 4, new MockRaftActorContext.MockPayload("two-1")));
+ // Prepare the entries to be sent with AppendEntries
+ List<ReplicatedLogEntry> entries = new ArrayList<>();
+ entries.add(newReplicatedLogEntry(1, 4, "four"));
- AppendEntries appendEntries =
- new AppendEntries(1, "leader-1", 3, 1, entries, 4, -1);
+ AppendEntries appendEntries = new AppendEntries(1, "leader", 3, 1, entries, 4, -1);
- RaftActorBehavior behavior = createBehavior(context);
+ follower = createBehavior(context);
- // Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
+ RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
+ Assert.assertSame(follower, newBehavior);
- assertEquals(expected, raftBehavior);
+ expectAndVerifyAppendEntriesReply(1, false, context.getId(), 1, 2);
+ }
- // Also expect an AppendEntriesReply to be sent where success is false
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
- "AppendEntriesReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
+ @Test
+ public void testHandleAppendEntriesWithExistingLogEntry() {
+ logStart("testHandleAppendEntriesWithExistingLogEntry");
- assertEquals(false, out);
+ MockRaftActorContext context = createActorContext();
- }};
+ context.getTermInformation().update(1, "test");
- }
+ // Prepare the receivers log
+ MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
+ log.append(newReplicatedLogEntry(1, 0, "zero"));
+ log.append(newReplicatedLogEntry(1, 1, "one"));
- @Test
- public void testHandleAppendAfterInstallingSnapshot(){
- new JavaTestKit(getSystem()) {{
+ context.setReplicatedLog(log);
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext();
+ // Send the last entry again.
+ List<ReplicatedLogEntry> entries = Arrays.asList(newReplicatedLogEntry(1, 1, "one"));
+ follower = createBehavior(context);
- // Prepare the receivers log
- MockRaftActorContext.SimpleReplicatedLog log =
- new MockRaftActorContext.SimpleReplicatedLog();
+ follower.handleMessage(leaderActor, new AppendEntries(1, "leader", 0, 1, entries, 1, -1));
- // Set up a log as if it has been snapshotted
- log.setSnapshotIndex(3);
- log.setSnapshotTerm(1);
+ assertEquals("Next index", 2, log.last().getIndex() + 1);
+ assertEquals("Entry 1", entries.get(0), log.get(1));
- context.setReplicatedLog(log);
+ expectAndVerifyAppendEntriesReply(1, true, context.getId(), 1, 1);
- // Prepare the entries to be sent with AppendEntries
- List<ReplicatedLogEntry> entries = new ArrayList<>();
- entries.add(
- new MockRaftActorContext.MockReplicatedLogEntry(1, 4, new MockRaftActorContext.MockPayload("two-1")));
+ // Send the last entry again and also a new one.
- AppendEntries appendEntries =
- new AppendEntries(1, "leader-1", 3, 1, entries, 4, 3);
+ entries = Arrays.asList(newReplicatedLogEntry(1, 1, "one"), newReplicatedLogEntry(1, 2, "two"));
- RaftActorBehavior behavior = createBehavior(context);
+ leaderActor.underlyingActor().clear();
+ follower.handleMessage(leaderActor, new AppendEntries(1, "leader", 0, 1, entries, 2, -1));
- // Send an unknown message so that the state of the RaftActor remains unchanged
- RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
+ assertEquals("Next index", 3, log.last().getIndex() + 1);
+ assertEquals("Entry 1", entries.get(0), log.get(1));
+ assertEquals("Entry 2", entries.get(1), log.get(2));
- RaftActorBehavior raftBehavior =
- behavior.handleMessage(getRef(), appendEntries);
+ expectAndVerifyAppendEntriesReply(1, true, context.getId(), 1, 2);
+ }
- assertEquals(expected, raftBehavior);
+ @Test
+ public void testHandleAppendAfterInstallingSnapshot(){
+ logStart("testHandleAppendAfterInstallingSnapshot");
- // Also expect an AppendEntriesReply to be sent where success is false
- final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
- "AppendEntriesReply") {
- // do not put code outside this method, will run afterwards
- protected Boolean match(Object in) {
- if (in instanceof AppendEntriesReply) {
- AppendEntriesReply reply = (AppendEntriesReply) in;
- return reply.isSuccess();
- } else {
- throw noMatch();
- }
- }
- }.get();
+ MockRaftActorContext context = createActorContext();
- assertEquals(true, out);
+ // Prepare the receivers log
+ MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog();
- }};
+ // Set up a log as if it has been snapshotted
+ log.setSnapshotIndex(3);
+ log.setSnapshotTerm(1);
+ context.setReplicatedLog(log);
+
+ // Prepare the entries to be sent with AppendEntries
+ List<ReplicatedLogEntry> entries = new ArrayList<>();
+ entries.add(newReplicatedLogEntry(1, 4, "four"));
+
+ AppendEntries appendEntries = new AppendEntries(1, "leader", 3, 1, entries, 4, 3);
+
+ follower = createBehavior(context);
+
+ RaftActorBehavior newBehavior = follower.handleMessage(leaderActor, appendEntries);
+
+ Assert.assertSame(follower, newBehavior);
+
+ expectAndVerifyAppendEntriesReply(1, true, context.getId(), 1, 4);
}
*/
@Test
public void testHandleInstallSnapshot() throws Exception {
- JavaTestKit javaTestKit = new JavaTestKit(getSystem()) {{
-
- ActorRef leaderActor = getSystem().actorOf(Props.create(
- MessageCollectorActor.class));
-
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext(getRef());
-
- Follower follower = (Follower)createBehavior(context);
-
- HashMap<String, String> followerSnapshot = new HashMap<>();
- followerSnapshot.put("1", "A");
- followerSnapshot.put("2", "B");
- followerSnapshot.put("3", "C");
-
- ByteString bsSnapshot = toByteString(followerSnapshot);
- ByteString chunkData = ByteString.EMPTY;
- int offset = 0;
- int snapshotLength = bsSnapshot.size();
- int i = 1;
- int chunkIndex = 1;
-
- do {
- chunkData = getNextChunk(bsSnapshot, offset);
- final InstallSnapshot installSnapshot =
- new InstallSnapshot(1, "leader-1", i, 1,
- chunkData, chunkIndex, 3);
- follower.handleMessage(leaderActor, installSnapshot);
- offset = offset + 50;
- i++;
- chunkIndex++;
- } while ((offset+50) < snapshotLength);
-
- final InstallSnapshot installSnapshot3 = new InstallSnapshot(1, "leader-1", 3, 1, chunkData, chunkIndex, 3);
- follower.handleMessage(leaderActor, installSnapshot3);
-
- String[] matches = new ReceiveWhile<String>(String.class, duration("2 seconds")) {
- @Override
- protected String match(Object o) throws Exception {
- if (o instanceof ApplySnapshot) {
- ApplySnapshot as = (ApplySnapshot)o;
- if (as.getSnapshot().getLastIndex() != installSnapshot3.getLastIncludedIndex()) {
- return "applySnapshot-lastIndex-mismatch";
- }
- if (as.getSnapshot().getLastAppliedTerm() != installSnapshot3.getLastIncludedTerm()) {
- return "applySnapshot-lastAppliedTerm-mismatch";
- }
- if (as.getSnapshot().getLastAppliedIndex() != installSnapshot3.getLastIncludedIndex()) {
- return "applySnapshot-lastAppliedIndex-mismatch";
- }
- if (as.getSnapshot().getLastTerm() != installSnapshot3.getLastIncludedTerm()) {
- return "applySnapshot-lastTerm-mismatch";
- }
- return "applySnapshot";
- }
-
- return "ignoreCase";
- }
- }.get();
-
- // Verify that after a snapshot is successfully applied the collected snapshot chunks is reset to empty
- assertEquals(ByteString.EMPTY, follower.getSnapshotChunksCollected());
-
- String applySnapshotMatch = "";
- for (String reply: matches) {
- if (reply.startsWith("applySnapshot")) {
- applySnapshotMatch = reply;
- }
- }
-
- assertEquals("applySnapshot", applySnapshotMatch);
-
- Object messages = executeLocalOperation(leaderActor, "get-all-messages");
-
- assertNotNull(messages);
- assertTrue(messages instanceof List);
- List<Object> listMessages = (List<Object>) messages;
-
- int installSnapshotReplyReceivedCount = 0;
- for (Object message: listMessages) {
- if (message instanceof InstallSnapshotReply) {
- ++installSnapshotReplyReceivedCount;
- }
- }
+ logStart("testHandleInstallSnapshot");
+
+ MockRaftActorContext context = createActorContext();
+
+ follower = createBehavior(context);
+
+ HashMap<String, String> followerSnapshot = new HashMap<>();
+ followerSnapshot.put("1", "A");
+ followerSnapshot.put("2", "B");
+ followerSnapshot.put("3", "C");
+
+ ByteString bsSnapshot = toByteString(followerSnapshot);
+ int offset = 0;
+ int snapshotLength = bsSnapshot.size();
+ int chunkSize = 50;
+ int totalChunks = (snapshotLength / chunkSize) + ((snapshotLength % chunkSize) > 0 ? 1 : 0);
+ int lastIncludedIndex = 1;
+ int chunkIndex = 1;
+ InstallSnapshot lastInstallSnapshot = null;
+
+ for(int i = 0; i < totalChunks; i++) {
+ ByteString chunkData = getNextChunk(bsSnapshot, offset, chunkSize);
+ lastInstallSnapshot = new InstallSnapshot(1, "leader", lastIncludedIndex, 1,
+ chunkData, chunkIndex, totalChunks);
+ follower.handleMessage(leaderActor, lastInstallSnapshot);
+ offset = offset + 50;
+ lastIncludedIndex++;
+ chunkIndex++;
+ }
- assertEquals(3, installSnapshotReplyReceivedCount);
+ ApplySnapshot applySnapshot = MessageCollectorActor.expectFirstMatching(followerActor,
+ ApplySnapshot.class);
+ Snapshot snapshot = applySnapshot.getSnapshot();
+ assertEquals("getLastIndex", lastInstallSnapshot.getLastIncludedIndex(), snapshot.getLastIndex());
+ assertEquals("getLastIncludedTerm", lastInstallSnapshot.getLastIncludedTerm(),
+ snapshot.getLastAppliedTerm());
+ assertEquals("getLastAppliedIndex", lastInstallSnapshot.getLastIncludedIndex(),
+ snapshot.getLastAppliedIndex());
+ assertEquals("getLastTerm", lastInstallSnapshot.getLastIncludedTerm(), snapshot.getLastTerm());
+ Assert.assertArrayEquals("getState", bsSnapshot.toByteArray(), snapshot.getState());
+
+ List<InstallSnapshotReply> replies = MessageCollectorActor.getAllMatching(
+ leaderActor, InstallSnapshotReply.class);
+ assertEquals("InstallSnapshotReply count", totalChunks, replies.size());
+
+ chunkIndex = 1;
+ for(InstallSnapshotReply reply: replies) {
+ assertEquals("getChunkIndex", chunkIndex++, reply.getChunkIndex());
+ assertEquals("getTerm", 1, reply.getTerm());
+ assertEquals("isSuccess", true, reply.isSuccess());
+ assertEquals("getFollowerId", context.getId(), reply.getFollowerId());
+ }
- }};
+ Assert.assertNull("Expected null SnapshotTracker", ((Follower)follower).getSnapshotTracker());
}
@Test
public void testHandleOutOfSequenceInstallSnapshot() throws Exception {
- JavaTestKit javaTestKit = new JavaTestKit(getSystem()) {
- {
-
- ActorRef leaderActor = getSystem().actorOf(Props.create(
- MessageCollectorActor.class));
-
- MockRaftActorContext context = (MockRaftActorContext)
- createActorContext(getRef());
+ logStart("testHandleOutOfSequenceInstallSnapshot");
- Follower follower = (Follower) createBehavior(context);
+ MockRaftActorContext context = createActorContext();
- HashMap<String, String> followerSnapshot = new HashMap<>();
- followerSnapshot.put("1", "A");
- followerSnapshot.put("2", "B");
- followerSnapshot.put("3", "C");
+ follower = createBehavior(context);
- ByteString bsSnapshot = toByteString(followerSnapshot);
+ HashMap<String, String> followerSnapshot = new HashMap<>();
+ followerSnapshot.put("1", "A");
+ followerSnapshot.put("2", "B");
+ followerSnapshot.put("3", "C");
- final InstallSnapshot installSnapshot = new InstallSnapshot(1, "leader-1", 3, 1, getNextChunk(bsSnapshot, 10), 3, 3);
- follower.handleMessage(leaderActor, installSnapshot);
+ ByteString bsSnapshot = toByteString(followerSnapshot);
- Object messages = executeLocalOperation(leaderActor, "get-all-messages");
+ InstallSnapshot installSnapshot = new InstallSnapshot(1, "leader", 3, 1,
+ getNextChunk(bsSnapshot, 10, 50), 3, 3);
+ follower.handleMessage(leaderActor, installSnapshot);
- assertNotNull(messages);
- assertTrue(messages instanceof List);
- List<Object> listMessages = (List<Object>) messages;
+ InstallSnapshotReply reply = MessageCollectorActor.expectFirstMatching(leaderActor,
+ InstallSnapshotReply.class);
- int installSnapshotReplyReceivedCount = 0;
- for (Object message: listMessages) {
- if (message instanceof InstallSnapshotReply) {
- ++installSnapshotReplyReceivedCount;
- }
- }
+ assertEquals("isSuccess", false, reply.isSuccess());
+ assertEquals("getChunkIndex", -1, reply.getChunkIndex());
+ assertEquals("getTerm", 1, reply.getTerm());
+ assertEquals("getFollowerId", context.getId(), reply.getFollowerId());
- assertEquals(1, installSnapshotReplyReceivedCount);
- InstallSnapshotReply reply = (InstallSnapshotReply) listMessages.get(0);
- assertEquals(false, reply.isSuccess());
- assertEquals(-1, reply.getChunkIndex());
- assertEquals(ByteString.EMPTY, follower.getSnapshotChunksCollected());
-
-
- }};
+ Assert.assertNull("Expected null SnapshotTracker", ((Follower)follower).getSnapshotTracker());
}
- public Object executeLocalOperation(ActorRef actor, Object message) throws Exception {
- return MessageCollectorActor.getAllMessages(actor);
- }
-
- public ByteString getNextChunk (ByteString bs, int offset){
+ public ByteString getNextChunk (ByteString bs, int offset, int chunkSize){
int snapshotLength = bs.size();
int start = offset;
- int size = 50;
- if (50 > snapshotLength) {
+ int size = chunkSize;
+ if (chunkSize > snapshotLength) {
size = snapshotLength;
} else {
- if ((start + 50) > snapshotLength) {
+ if ((start + chunkSize) > snapshotLength) {
size = snapshotLength - start;
}
}
return bs.substring(start, start + size);
}
- private ByteString toByteString(Map<String, String> state) {
- ByteArrayOutputStream b = null;
- ObjectOutputStream o = null;
- try {
- try {
- b = new ByteArrayOutputStream();
- o = new ObjectOutputStream(b);
- o.writeObject(state);
- byte[] snapshotBytes = b.toByteArray();
- return ByteString.copyFrom(snapshotBytes);
- } finally {
- if (o != null) {
- o.flush();
- o.close();
- }
- if (b != null) {
- b.close();
- }
- }
- } catch (IOException e) {
- org.junit.Assert.fail("IOException in converting Hashmap to Bytestring:" + e);
- }
- return null;
+ private void expectAndVerifyAppendEntriesReply(int expTerm, boolean expSuccess,
+ String expFollowerId, long expLogLastTerm, long expLogLastIndex) {
+
+ AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(leaderActor,
+ AppendEntriesReply.class);
+
+ assertEquals("isSuccess", expSuccess, reply.isSuccess());
+ assertEquals("getTerm", expTerm, reply.getTerm());
+ assertEquals("getFollowerId", expFollowerId, reply.getFollowerId());
+ assertEquals("getLogLastTerm", expLogLastTerm, reply.getLogLastTerm());
+ assertEquals("getLogLastIndex", expLogLastIndex, reply.getLogLastIndex());
+ }
+
+ private ReplicatedLogEntry newReplicatedLogEntry(long term, long index, String data) {
+ return new MockRaftActorContext.MockReplicatedLogEntry(term, index,
+ new MockRaftActorContext.MockPayload(data));
+ }
+
+ @Override
+ protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext,
+ ActorRef actorRef, RaftRPC rpc) throws Exception {
+ super.assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, actorRef, rpc);
+
+ String expVotedFor = RequestVote.class.isInstance(rpc) ? ((RequestVote)rpc).getCandidateId() : null;
+ assertEquals("New votedFor", expVotedFor, actorContext.getTermInformation().getVotedFor());
+ }
+
+ @Override
+ protected void handleAppendEntriesAddSameEntryToLogReply(TestActorRef<MessageCollectorActor> replyActor)
+ throws Exception {
+ AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(replyActor, AppendEntriesReply.class);
+ assertEquals("isSuccess", true, reply.isSuccess());
}
}
*/
package org.opendaylight.controller.cluster.raft.behaviors;
+import static org.junit.Assert.assertEquals;
import akka.actor.ActorRef;
import akka.actor.Props;
-import akka.testkit.JavaTestKit;
+import akka.testkit.TestActorRef;
import java.util.HashMap;
import java.util.Map;
+import org.junit.After;
import org.junit.Test;
+import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
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.messages.AppendEntriesReply;
-import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
public class IsolatedLeaderTest extends AbstractRaftActorBehaviorTest {
- private ActorRef leaderActor =
- getSystem().actorOf(Props.create(DoNothingActor.class));
+ private final TestActorRef<MessageCollectorActor> leaderActor = actorFactory.createTestActor(
+ Props.create(MessageCollectorActor.class), actorFactory.generateActorId("leader"));
+
+ private final TestActorRef<MessageCollectorActor> senderActor = actorFactory.createTestActor(
+ Props.create(MessageCollectorActor.class), actorFactory.generateActorId("sender"));
+
+ private AbstractLeader isolatedLeader;
- private ActorRef senderActor =
- getSystem().actorOf(Props.create(DoNothingActor.class));
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ if(isolatedLeader != null) {
+ isolatedLeader.close();
+ }
+
+ super.tearDown();
+ }
@Override
- protected RaftActorBehavior createBehavior(
- RaftActorContext actorContext) {
+ protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
return new Leader(actorContext);
}
@Override
- protected RaftActorContext createActorContext() {
+ protected MockRaftActorContext createActorContext() {
return createActorContext(leaderActor);
}
+ @Override
+ protected MockRaftActorContext createActorContext(ActorRef actor) {
+ DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl();
+ configParams.setElectionTimeoutFactor(100000);
+ MockRaftActorContext context = new MockRaftActorContext("isolated-leader", getSystem(), actor);
+ context.setConfigParams(configParams);
+ return context;
+ }
@Test
- public void testHandleMessageWithThreeMembers() {
- new JavaTestKit(getSystem()) {{
- String followerAddress1 = "akka://test/user/$a";
- String followerAddress2 = "akka://test/user/$b";
-
- MockRaftActorContext leaderActorContext = (MockRaftActorContext) createActorContext();
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-1", followerAddress1);
- peerAddresses.put("follower-2", followerAddress2);
- leaderActorContext.setPeerAddresses(peerAddresses);
-
- IsolatedLeader isolatedLeader = new IsolatedLeader(leaderActorContext);
- assertTrue(isolatedLeader.state() == RaftState.IsolatedLeader);
-
- // in a 3 node cluster, even if 1 follower is returns a reply, the isolatedLeader is not isolated
- RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor,
+ public void testHandleMessageWithThreeMembers() throws Exception {
+ String followerAddress1 = "akka://test/user/$a";
+ String followerAddress2 = "akka://test/user/$b";
+
+ MockRaftActorContext leaderActorContext = createActorContext();
+ Map<String, String> peerAddresses = new HashMap<>();
+ peerAddresses.put("follower-1", followerAddress1);
+ peerAddresses.put("follower-2", followerAddress2);
+ leaderActorContext.setPeerAddresses(peerAddresses);
+
+ isolatedLeader = new IsolatedLeader(leaderActorContext);
+ assertEquals("Raft state", RaftState.IsolatedLeader, isolatedLeader.state());
+
+ // in a 3 node cluster, even if 1 follower is returns a reply, the isolatedLeader is not isolated
+ RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor,
new AppendEntriesReply("follower-1", isolatedLeader.lastTerm() - 1, true,
- isolatedLeader.lastIndex() - 1, isolatedLeader.lastTerm() - 1));
+ isolatedLeader.lastIndex() - 1, isolatedLeader.lastTerm() - 1));
+
+ assertEquals("Raft state", RaftState.Leader, behavior.state());
- assertEquals(RaftState.Leader, behavior.state());
+ isolatedLeader.close();
+ isolatedLeader = (AbstractLeader) behavior;
- behavior = isolatedLeader.handleMessage(senderActor,
+ behavior = isolatedLeader.handleMessage(senderActor,
new AppendEntriesReply("follower-2", isolatedLeader.lastTerm() - 1, true,
- isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
+ isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
- assertEquals(RaftState.Leader, behavior.state());
- }};
+ assertEquals("Raft state", RaftState.Leader, behavior.state());
}
@Test
- public void testHandleMessageWithFiveMembers() {
- new JavaTestKit(getSystem()) {{
-
- String followerAddress1 = "akka://test/user/$a";
- String followerAddress2 = "akka://test/user/$b";
- String followerAddress3 = "akka://test/user/$c";
- String followerAddress4 = "akka://test/user/$d";
-
- MockRaftActorContext leaderActorContext = (MockRaftActorContext) createActorContext();
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-1", followerAddress1);
- peerAddresses.put("follower-2", followerAddress2);
- peerAddresses.put("follower-3", followerAddress3);
- peerAddresses.put("follower-4", followerAddress4);
- leaderActorContext.setPeerAddresses(peerAddresses);
-
- IsolatedLeader isolatedLeader = new IsolatedLeader(leaderActorContext);
- assertEquals(RaftState.IsolatedLeader, isolatedLeader.state());
-
- // in a 5 member cluster, atleast 2 followers need to be active and return a reply
- RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor,
+ public void testHandleMessageWithFiveMembers() throws Exception {
+ String followerAddress1 = "akka://test/user/$a";
+ String followerAddress2 = "akka://test/user/$b";
+ String followerAddress3 = "akka://test/user/$c";
+ String followerAddress4 = "akka://test/user/$d";
+
+ MockRaftActorContext leaderActorContext = createActorContext();
+ Map<String, String> peerAddresses = new HashMap<>();
+ peerAddresses.put("follower-1", followerAddress1);
+ peerAddresses.put("follower-2", followerAddress2);
+ peerAddresses.put("follower-3", followerAddress3);
+ peerAddresses.put("follower-4", followerAddress4);
+ leaderActorContext.setPeerAddresses(peerAddresses);
+
+ isolatedLeader = new IsolatedLeader(leaderActorContext);
+ assertEquals("Raft state", RaftState.IsolatedLeader, isolatedLeader.state());
+
+ // in a 5 member cluster, atleast 2 followers need to be active and return a reply
+ RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor,
new AppendEntriesReply("follower-1", isolatedLeader.lastTerm() - 1, true,
- isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
+ isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
- assertEquals(RaftState.IsolatedLeader, behavior.state());
+ assertEquals("Raft state", RaftState.IsolatedLeader, behavior.state());
- behavior = isolatedLeader.handleMessage(senderActor,
+ behavior = isolatedLeader.handleMessage(senderActor,
new AppendEntriesReply("follower-2", isolatedLeader.lastTerm() - 1, true,
- isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
+ isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
+
+ assertEquals("Raft state", RaftState.Leader, behavior.state());
- assertEquals(RaftState.Leader, behavior.state());
+ isolatedLeader.close();
+ isolatedLeader = (AbstractLeader) behavior;
- behavior = isolatedLeader.handleMessage(senderActor,
+ behavior = isolatedLeader.handleMessage(senderActor,
new AppendEntriesReply("follower-3", isolatedLeader.lastTerm() - 1, true,
- isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
+ isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 ));
- assertEquals(RaftState.Leader, behavior.state());
- }};
+ assertEquals("Raft state", RaftState.Leader, behavior.state());
}
@Test
- public void testHandleMessageFromAnotherLeader() {
- new JavaTestKit(getSystem()) {{
- String followerAddress1 = "akka://test/user/$a";
- String followerAddress2 = "akka://test/user/$b";
-
- MockRaftActorContext leaderActorContext = (MockRaftActorContext) createActorContext();
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-1", followerAddress1);
- peerAddresses.put("follower-2", followerAddress2);
- leaderActorContext.setPeerAddresses(peerAddresses);
-
- IsolatedLeader isolatedLeader = new IsolatedLeader(leaderActorContext);
- assertTrue(isolatedLeader.state() == RaftState.IsolatedLeader);
-
- // if an append-entries reply is received by the isolated-leader, and that reply
- // has a term > than its own term, then IsolatedLeader switches to Follower
- // bowing itself to another leader in the cluster
- RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor,
+ public void testHandleMessageFromAnotherLeader() throws Exception {
+ String followerAddress1 = "akka://test/user/$a";
+ String followerAddress2 = "akka://test/user/$b";
+
+ MockRaftActorContext leaderActorContext = createActorContext();
+ Map<String, String> peerAddresses = new HashMap<>();
+ peerAddresses.put("follower-1", followerAddress1);
+ peerAddresses.put("follower-2", followerAddress2);
+ leaderActorContext.setPeerAddresses(peerAddresses);
+
+ isolatedLeader = new IsolatedLeader(leaderActorContext);
+ assertEquals("Raft state", RaftState.IsolatedLeader, isolatedLeader.state());
+
+ // if an append-entries reply is received by the isolated-leader, and that reply
+ // has a term > than its own term, then IsolatedLeader switches to Follower
+ // bowing itself to another leader in the cluster
+ RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor,
new AppendEntriesReply("follower-1", isolatedLeader.lastTerm() + 1, true,
- isolatedLeader.lastIndex() + 1, isolatedLeader.lastTerm() + 1));
+ isolatedLeader.lastIndex() + 1, isolatedLeader.lastTerm() + 1));
- assertEquals(RaftState.Follower, behavior.state());
- }};
+ assertEquals("Raft state", RaftState.Follower, behavior.state());
+ behavior.close();
}
}
import akka.testkit.JavaTestKit;
import akka.testkit.TestActorRef;
import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Uninterruptibles;
import com.google.protobuf.ByteString;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
import org.opendaylight.controller.cluster.raft.base.messages.ApplyLogEntries;
import org.opendaylight.controller.cluster.raft.base.messages.ApplyState;
import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot;
-import org.opendaylight.controller.cluster.raft.base.messages.InitiateInstallSnapshot;
import org.opendaylight.controller.cluster.raft.base.messages.IsolatedLeaderCheck;
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.behaviors.AbstractLeader.FollowerToSnapshot;
import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
import org.opendaylight.controller.cluster.raft.messages.InstallSnapshot;
import org.opendaylight.controller.cluster.raft.messages.InstallSnapshotReply;
+import org.opendaylight.controller.cluster.raft.messages.RaftRPC;
import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply;
-import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
import org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages;
import scala.concurrent.duration.FiniteDuration;
public class LeaderTest extends AbstractRaftActorBehaviorTest {
- private final ActorRef leaderActor =
- getSystem().actorOf(Props.create(DoNothingActor.class));
- private final ActorRef senderActor =
- getSystem().actorOf(Props.create(DoNothingActor.class));
+ static final String FOLLOWER_ID = "follower";
+
+ private final TestActorRef<ForwardMessageToBehaviorActor> leaderActor = actorFactory.createTestActor(
+ Props.create(ForwardMessageToBehaviorActor.class), actorFactory.generateActorId("leader"));
+
+ private final TestActorRef<ForwardMessageToBehaviorActor> followerActor = actorFactory.createTestActor(
+ Props.create(ForwardMessageToBehaviorActor.class), actorFactory.generateActorId("follower"));
+
+ private Leader leader;
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ if(leader != null) {
+ leader.close();
+ }
+
+ super.tearDown();
+ }
@Test
public void testHandleMessageForUnknownMessage() throws Exception {
- new JavaTestKit(getSystem()) {{
- Leader leader =
- new Leader(createActorContext());
+ logStart("testHandleMessageForUnknownMessage");
- // handle message should return the Leader state when it receives an
- // unknown message
- RaftActorBehavior behavior = leader.handleMessage(senderActor, "foo");
- Assert.assertTrue(behavior instanceof Leader);
- }};
+ leader = new Leader(createActorContext());
+
+ // handle message should return the Leader state when it receives an
+ // unknown message
+ RaftActorBehavior behavior = leader.handleMessage(followerActor, "foo");
+ Assert.assertTrue(behavior instanceof Leader);
}
@Test
- public void testThatLeaderSendsAHeartbeatMessageToAllFollowers() {
- new JavaTestKit(getSystem()) {{
- new Within(duration("1 seconds")) {
- @Override
- protected void run() {
- ActorRef followerActor = getTestActor();
+ public void testThatLeaderSendsAHeartbeatMessageToAllFollowers() throws Exception {
+ logStart("testThatLeaderSendsAHeartbeatMessageToAllFollowers");
- MockRaftActorContext actorContext = (MockRaftActorContext) createActorContext();
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- Map<String, String> peerAddresses = new HashMap<>();
+ long term = 1;
+ actorContext.getTermInformation().update(term, "");
- String followerId = "follower";
- peerAddresses.put(followerId, followerActor.path().toString());
+ leader = new Leader(actorContext);
- actorContext.setPeerAddresses(peerAddresses);
+ // Leader should send an immediate heartbeat with no entries as follower is inactive.
+ long lastIndex = actorContext.getReplicatedLog().lastIndex();
+ AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
+ assertEquals("getTerm", term, appendEntries.getTerm());
+ assertEquals("getPrevLogIndex", -1, appendEntries.getPrevLogIndex());
+ assertEquals("getPrevLogTerm", -1, appendEntries.getPrevLogTerm());
+ assertEquals("Entries size", 0, appendEntries.getEntries().size());
- long term = 1;
- actorContext.getTermInformation().update(term, "");
+ // The follower would normally reply - simulate that explicitly here.
+ leader.handleMessage(followerActor, new AppendEntriesReply(
+ FOLLOWER_ID, term, true, lastIndex - 1, term));
+ assertEquals("isFollowerActive", true, leader.getFollower(FOLLOWER_ID).isFollowerActive());
- Leader leader = new Leader(actorContext);
+ followerActor.underlyingActor().clear();
- // Leader should send an immediate heartbeat with no entries as follower is inactive.
- long lastIndex = actorContext.getReplicatedLog().lastIndex();
- AppendEntries appendEntries = expectMsgClass(duration("5 seconds"), AppendEntries.class);
- assertEquals("getTerm", term, appendEntries.getTerm());
- assertEquals("getPrevLogIndex", -1, appendEntries.getPrevLogIndex());
- assertEquals("getPrevLogTerm", -1, appendEntries.getPrevLogTerm());
- assertEquals("Entries size", 0, appendEntries.getEntries().size());
+ // Sleep for the heartbeat interval so AppendEntries is sent.
+ Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().
+ getHeartBeatInterval().toMillis(), TimeUnit.MILLISECONDS);
- // The follower would normally reply - simulate that explicitly here.
- leader.handleMessage(followerActor, new AppendEntriesReply(
- followerId, term, true, lastIndex - 1, term));
- assertEquals("isFollowerActive", true, leader.getFollower(followerId).isFollowerActive());
+ leader.handleMessage(leaderActor, new SendHeartBeat());
- // Sleep for the heartbeat interval so AppendEntries is sent.
- Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().
- getHeartBeatInterval().toMillis(), TimeUnit.MILLISECONDS);
+ appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
+ assertEquals("getPrevLogIndex", lastIndex - 1, appendEntries.getPrevLogIndex());
+ assertEquals("getPrevLogTerm", term, appendEntries.getPrevLogTerm());
+ assertEquals("Entries size", 1, appendEntries.getEntries().size());
+ assertEquals("Entry getIndex", lastIndex, appendEntries.getEntries().get(0).getIndex());
+ assertEquals("Entry getTerm", term, appendEntries.getEntries().get(0).getTerm());
+ }
- leader.handleMessage(senderActor, new SendHeartBeat());
+ @Test
+ public void testHandleReplicateMessageSendAppendEntriesToFollower() throws Exception {
+ logStart("testHandleReplicateMessageSendAppendEntriesToFollower");
- appendEntries = expectMsgClass(duration("5 seconds"), AppendEntries.class);
- assertEquals("getPrevLogIndex", lastIndex - 1, appendEntries.getPrevLogIndex());
- assertEquals("getPrevLogTerm", term, appendEntries.getPrevLogTerm());
- assertEquals("Entries size", 1, appendEntries.getEntries().size());
- assertEquals("Entry getIndex", lastIndex, appendEntries.getEntries().get(0).getIndex());
- assertEquals("Entry getTerm", term, appendEntries.getEntries().get(0).getTerm());
- }
- };
- }};
+ MockRaftActorContext actorContext = createActorContextWithFollower();
+
+ long term = 1;
+ actorContext.getTermInformation().update(term, "");
+
+ leader = new Leader(actorContext);
+
+ // Leader will send an immediate heartbeat - ignore it.
+ MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
+
+ // The follower would normally reply - simulate that explicitly here.
+ long lastIndex = actorContext.getReplicatedLog().lastIndex();
+ leader.handleMessage(followerActor, new AppendEntriesReply(
+ FOLLOWER_ID, term, true, lastIndex, term));
+ assertEquals("isFollowerActive", true, leader.getFollower(FOLLOWER_ID).isFollowerActive());
+
+ followerActor.underlyingActor().clear();
+
+ MockRaftActorContext.MockPayload payload = new MockRaftActorContext.MockPayload("foo");
+ MockRaftActorContext.MockReplicatedLogEntry newEntry = new MockRaftActorContext.MockReplicatedLogEntry(
+ 1, lastIndex + 1, payload);
+ actorContext.getReplicatedLog().append(newEntry);
+ RaftActorBehavior raftBehavior = leader.handleMessage(leaderActor,
+ new Replicate(null, null, newEntry));
+
+ // State should not change
+ assertTrue(raftBehavior instanceof Leader);
+
+ AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
+ assertEquals("getPrevLogIndex", lastIndex, appendEntries.getPrevLogIndex());
+ assertEquals("getPrevLogTerm", term, appendEntries.getPrevLogTerm());
+ assertEquals("Entries size", 1, appendEntries.getEntries().size());
+ assertEquals("Entry getIndex", lastIndex + 1, appendEntries.getEntries().get(0).getIndex());
+ assertEquals("Entry getTerm", term, appendEntries.getEntries().get(0).getTerm());
+ assertEquals("Entry payload", payload, appendEntries.getEntries().get(0).getData());
}
@Test
- public void testHandleReplicateMessageSendAppendEntriesToFollower() {
- new JavaTestKit(getSystem()) {{
- new Within(duration("1 seconds")) {
- @Override
- protected void run() {
- ActorRef followerActor = getTestActor();
+ public void testHandleReplicateMessageWhenThereAreNoFollowers() throws Exception {
+ logStart("testHandleReplicateMessageWhenThereAreNoFollowers");
- MockRaftActorContext actorContext = (MockRaftActorContext) createActorContext();
+ MockRaftActorContext actorContext = createActorContext();
- Map<String, String> peerAddresses = new HashMap<>();
+ leader = new Leader(actorContext);
- String followerId = "follower";
- peerAddresses.put(followerId, followerActor.path().toString());
+ actorContext.setLastApplied(0);
- actorContext.setPeerAddresses(peerAddresses);
+ long newLogIndex = actorContext.getReplicatedLog().lastIndex() + 1;
+ long term = actorContext.getTermInformation().getCurrentTerm();
+ MockRaftActorContext.MockReplicatedLogEntry newEntry = new MockRaftActorContext.MockReplicatedLogEntry(
+ term, newLogIndex, new MockRaftActorContext.MockPayload("foo"));
- long term = 1;
- actorContext.getTermInformation().update(term, "");
+ actorContext.getReplicatedLog().append(newEntry);
- Leader leader = new Leader(actorContext);
+ RaftActorBehavior raftBehavior = leader.handleMessage(leaderActor,
+ new Replicate(leaderActor, "state-id", newEntry));
- // Leader will send an immediate heartbeat - ignore it.
- expectMsgClass(duration("5 seconds"), AppendEntries.class);
+ // State should not change
+ assertTrue(raftBehavior instanceof Leader);
- // The follower would normally reply - simulate that explicitly here.
- long lastIndex = actorContext.getReplicatedLog().lastIndex();
- leader.handleMessage(followerActor, new AppendEntriesReply(
- followerId, term, true, lastIndex, term));
- assertEquals("isFollowerActive", true, leader.getFollower(followerId).isFollowerActive());
+ assertEquals("getCommitIndex", newLogIndex, actorContext.getCommitIndex());
- MockRaftActorContext.MockPayload payload = new MockRaftActorContext.MockPayload("foo");
- MockRaftActorContext.MockReplicatedLogEntry newEntry = new MockRaftActorContext.MockReplicatedLogEntry(
- 1, lastIndex + 1, payload);
- actorContext.getReplicatedLog().append(newEntry);
- RaftActorBehavior raftBehavior = leader.handleMessage(senderActor,
- new Replicate(null, null, newEntry));
+ // We should get 2 ApplyState messages - 1 for new log entry and 1 for the previous
+ // one since lastApplied state is 0.
+ List<ApplyState> applyStateList = MessageCollectorActor.getAllMatching(
+ leaderActor, ApplyState.class);
+ assertEquals("ApplyState count", newLogIndex, applyStateList.size());
- // State should not change
- assertTrue(raftBehavior instanceof Leader);
-
- AppendEntries appendEntries = expectMsgClass(duration("5 seconds"), AppendEntries.class);
- assertEquals("getPrevLogIndex", lastIndex, appendEntries.getPrevLogIndex());
- assertEquals("getPrevLogTerm", term, appendEntries.getPrevLogTerm());
- assertEquals("Entries size", 1, appendEntries.getEntries().size());
- assertEquals("Entry getIndex", lastIndex + 1, appendEntries.getEntries().get(0).getIndex());
- assertEquals("Entry getTerm", term, appendEntries.getEntries().get(0).getTerm());
- assertEquals("Entry payload", payload, appendEntries.getEntries().get(0).getData());
- }
- };
- }};
- }
+ for(int i = 0; i <= newLogIndex - 1; i++ ) {
+ ApplyState applyState = applyStateList.get(i);
+ assertEquals("getIndex", i + 1, applyState.getReplicatedLogEntry().getIndex());
+ assertEquals("getTerm", term, applyState.getReplicatedLogEntry().getTerm());
+ }
- @Test
- public void testHandleReplicateMessageWhenThereAreNoFollowers() {
- new JavaTestKit(getSystem()) {{
- new Within(duration("1 seconds")) {
- @Override
- protected void run() {
-
- ActorRef raftActor = getTestActor();
-
- MockRaftActorContext actorContext =
- new MockRaftActorContext("test", getSystem(), raftActor);
-
- actorContext.getReplicatedLog().removeFrom(0);
-
- actorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 2, 1)
- .build());
-
- Leader leader = new Leader(actorContext);
- RaftActorBehavior raftBehavior = leader
- .handleMessage(senderActor, new Replicate(null, "state-id",actorContext.getReplicatedLog().get(1)));
-
- // State should not change
- assertTrue(raftBehavior instanceof Leader);
-
- assertEquals(1, actorContext.getCommitIndex());
-
- final String out =
- new ExpectMsg<String>(duration("1 seconds"),
- "match hint") {
- // do not put code outside this method, will run afterwards
- @Override
- protected String match(Object in) {
- if (in instanceof ApplyState) {
- if (((ApplyState) in).getIdentifier().equals("state-id")) {
- return "match";
- }
- return null;
- } else {
- throw noMatch();
- }
- }
- }.get(); // this extracts the received message
-
- assertEquals("match", out);
-
- }
- };
- }};
+ ApplyState last = applyStateList.get((int) newLogIndex - 1);
+ assertEquals("getData", newEntry.getData(), last.getReplicatedLogEntry().getData());
+ assertEquals("getIdentifier", "state-id", last.getIdentifier());
}
@Test
public void testSendAppendEntriesOnAnInProgressInstallSnapshot() throws Exception {
- new JavaTestKit(getSystem()) {{
- ActorRef followerActor = getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ logStart("testSendAppendEntriesOnAnInProgressInstallSnapshot");
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(),
- followerActor.path().toString());
-
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext(leaderActor);
- actorContext.setPeerAddresses(peerAddresses);
-
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
-
- //clears leaders log
- actorContext.getReplicatedLog().removeFrom(0);
-
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int newEntryIndex = 4;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
-
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.setCommitIndex(followersLastIndex);
- //set follower timeout to 2 mins, helps during debugging
- actorContext.setConfigParams(new MockConfigParamsImpl(120000L, 10));
-
- MockLeader leader = new MockLeader(actorContext);
-
- // new entry
- ReplicatedLogImplEntry entry =
- new ReplicatedLogImplEntry(newEntryIndex, currentTerm,
- new MockRaftActorContext.MockPayload("D"));
+ MockRaftActorContext actorContext = createActorContextWithFollower();
+
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- //update follower timestamp
- leader.markFollowerActive(followerActor.path().toString());
+ //clears leaders log
+ actorContext.getReplicatedLog().removeFrom(0);
- ByteString bs = toByteString(leadersSnapshot);
- leader.setSnapshot(Optional.of(bs));
- leader.createFollowerToSnapshot(followerActor.path().toString(), bs);
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int newEntryIndex = 4;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- //send first chunk and no InstallSnapshotReply received yet
- leader.getFollowerToSnapshot().getNextChunk();
- leader.getFollowerToSnapshot().incrementChunkIndex();
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.setCommitIndex(followersLastIndex);
+ //set follower timeout to 2 mins, helps during debugging
+ actorContext.setConfigParams(new MockConfigParamsImpl(120000L, 10));
- Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().getHeartBeatInterval().toMillis(),
- TimeUnit.MILLISECONDS);
+ leader = new Leader(actorContext);
+
+ // new entry
+ ReplicatedLogImplEntry entry =
+ new ReplicatedLogImplEntry(newEntryIndex, currentTerm,
+ new MockRaftActorContext.MockPayload("D"));
- leader.handleMessage(leaderActor, new SendHeartBeat());
+ //update follower timestamp
+ leader.markFollowerActive(FOLLOWER_ID);
- AppendEntries aeproto = MessageCollectorActor.getFirstMatching(
- followerActor, AppendEntries.class);
+ ByteString bs = toByteString(leadersSnapshot);
+ leader.setSnapshot(Optional.of(bs));
+ FollowerToSnapshot fts = leader.new FollowerToSnapshot(bs);
+ leader.setFollowerSnapshot(FOLLOWER_ID, fts);
- assertNotNull("AppendEntries should be sent even if InstallSnapshotReply is not " +
- "received", aeproto);
+ //send first chunk and no InstallSnapshotReply received yet
+ fts.getNextChunk();
+ fts.incrementChunkIndex();
- AppendEntries ae = (AppendEntries) SerializationUtils.fromSerializable(aeproto);
+ Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().getHeartBeatInterval().toMillis(),
+ TimeUnit.MILLISECONDS);
- assertTrue("AppendEntries should be sent with empty entries", ae.getEntries().isEmpty());
+ leader.handleMessage(leaderActor, new SendHeartBeat());
- //InstallSnapshotReply received
- leader.getFollowerToSnapshot().markSendStatus(true);
+ AppendEntries aeproto = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- leader.handleMessage(senderActor, new SendHeartBeat());
+ AppendEntries ae = (AppendEntries) SerializationUtils.fromSerializable(aeproto);
- InstallSnapshotMessages.InstallSnapshot isproto = MessageCollectorActor.getFirstMatching(followerActor,
- InstallSnapshot.SERIALIZABLE_CLASS);
+ assertTrue("AppendEntries should be sent with empty entries", ae.getEntries().isEmpty());
- assertNotNull("Installsnapshot should get called for sending the next chunk of snapshot",
- isproto);
+ //InstallSnapshotReply received
+ fts.markSendStatus(true);
- InstallSnapshot is = (InstallSnapshot) SerializationUtils.fromSerializable(isproto);
+ leader.handleMessage(leaderActor, new SendHeartBeat());
- assertEquals(snapshotIndex, is.getLastIncludedIndex());
+ InstallSnapshotMessages.InstallSnapshot isproto = MessageCollectorActor.expectFirstMatching(followerActor,
+ InstallSnapshot.SERIALIZABLE_CLASS);
- }};
+ InstallSnapshot is = (InstallSnapshot) SerializationUtils.fromSerializable(isproto);
+
+ assertEquals(snapshotIndex, is.getLastIncludedIndex());
}
@Test
- public void testSendAppendEntriesSnapshotScenario() {
- new JavaTestKit(getSystem()) {{
-
- ActorRef followerActor = getTestActor();
+ public void testSendAppendEntriesSnapshotScenario() throws Exception {
+ logStart("testSendAppendEntriesSnapshotScenario");
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(),
- followerActor.path().toString());
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext(getRef());
- actorContext.setPeerAddresses(peerAddresses);
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
+ //clears leaders log
+ actorContext.getReplicatedLog().removeFrom(0);
- //clears leaders log
- actorContext.getReplicatedLog().removeFrom(0);
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int newEntryIndex = 4;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int newEntryIndex = 4;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.setCommitIndex(followersLastIndex);
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.setCommitIndex(followersLastIndex);
+ leader = new Leader(actorContext);
- Leader leader = new Leader(actorContext);
+ // Leader will send an immediate heartbeat - ignore it.
+ MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- // new entry
- ReplicatedLogImplEntry entry =
+ // new entry
+ ReplicatedLogImplEntry entry =
new ReplicatedLogImplEntry(newEntryIndex, currentTerm,
- new MockRaftActorContext.MockPayload("D"));
+ new MockRaftActorContext.MockPayload("D"));
- //update follower timestamp
- leader.markFollowerActive(followerActor.path().toString());
+ //update follower timestamp
+ leader.markFollowerActive(FOLLOWER_ID);
- Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().getHeartBeatInterval().toMillis(),
- TimeUnit.MILLISECONDS);
+ // this should invoke a sendinstallsnapshot as followersLastIndex < snapshotIndex
+ RaftActorBehavior raftBehavior = leader.handleMessage(
+ leaderActor, new Replicate(null, "state-id", entry));
- // this should invoke a sendinstallsnapshot as followersLastIndex < snapshotIndex
- RaftActorBehavior raftBehavior = leader.handleMessage(
- senderActor, new Replicate(null, "state-id", entry));
-
- assertTrue(raftBehavior instanceof Leader);
-
- // we might receive some heartbeat messages, so wait till we InitiateInstallSnapshot
- Boolean[] matches = new ReceiveWhile<Boolean>(Boolean.class, duration("2 seconds")) {
- @Override
- protected Boolean match(Object o) throws Exception {
- if (o instanceof InitiateInstallSnapshot) {
- return true;
- }
- return false;
- }
- }.get();
-
- boolean initiateInitiateInstallSnapshot = false;
- for (Boolean b: matches) {
- initiateInitiateInstallSnapshot = b | initiateInitiateInstallSnapshot;
- }
+ assertTrue(raftBehavior instanceof Leader);
- assertTrue(initiateInitiateInstallSnapshot);
- }};
+ MessageCollectorActor.expectFirstMatching(leaderActor, CaptureSnapshot.class);
}
@Test
public void testInitiateInstallSnapshot() throws Exception {
- new JavaTestKit(getSystem()) {{
-
- ActorRef leaderActor = getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ logStart("testInitiateInstallSnapshot");
- ActorRef followerActor = getTestActor();
-
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(),
- followerActor.path().toString());
+ MockRaftActorContext actorContext = createActorContextWithFollower();
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext(leaderActor);
- actorContext.setPeerAddresses(peerAddresses);
+ //clears leaders log
+ actorContext.getReplicatedLog().removeFrom(0);
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int newEntryIndex = 4;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- //clears leaders log
- actorContext.getReplicatedLog().removeFrom(0);
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.setLastApplied(3);
+ actorContext.setCommitIndex(followersLastIndex);
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int newEntryIndex = 4;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
+ leader = new Leader(actorContext);
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.setLastApplied(3);
- actorContext.setCommitIndex(followersLastIndex);
+ // Leader will send an immediate heartbeat - ignore it.
+ MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- Leader leader = new Leader(actorContext);
- // set the snapshot as absent and check if capture-snapshot is invoked.
- leader.setSnapshot(Optional.<ByteString>absent());
+ // set the snapshot as absent and check if capture-snapshot is invoked.
+ leader.setSnapshot(Optional.<ByteString>absent());
- // new entry
- ReplicatedLogImplEntry entry =
- new ReplicatedLogImplEntry(newEntryIndex, currentTerm,
- new MockRaftActorContext.MockPayload("D"));
+ // new entry
+ ReplicatedLogImplEntry entry = new ReplicatedLogImplEntry(newEntryIndex, currentTerm,
+ new MockRaftActorContext.MockPayload("D"));
- actorContext.getReplicatedLog().append(entry);
+ actorContext.getReplicatedLog().append(entry);
- // this should invoke a sendinstallsnapshot as followersLastIndex < snapshotIndex
- RaftActorBehavior raftBehavior = leader.handleMessage(
- leaderActor, new InitiateInstallSnapshot());
+ //update follower timestamp
+ leader.markFollowerActive(FOLLOWER_ID);
- CaptureSnapshot cs = MessageCollectorActor.
- getFirstMatching(leaderActor, CaptureSnapshot.class);
+ leader.handleMessage(leaderActor, new Replicate(null, "state-id", entry));
- assertNotNull(cs);
+ CaptureSnapshot cs = MessageCollectorActor.expectFirstMatching(leaderActor, CaptureSnapshot.class);
- assertTrue(cs.isInstallSnapshotInitiated());
- assertEquals(3, cs.getLastAppliedIndex());
- assertEquals(1, cs.getLastAppliedTerm());
- assertEquals(4, cs.getLastIndex());
- assertEquals(2, cs.getLastTerm());
+ assertTrue(cs.isInstallSnapshotInitiated());
+ assertEquals(3, cs.getLastAppliedIndex());
+ assertEquals(1, cs.getLastAppliedTerm());
+ assertEquals(4, cs.getLastIndex());
+ assertEquals(2, cs.getLastTerm());
- // if an initiate is started again when first is in progress, it shouldnt initiate Capture
- raftBehavior = leader.handleMessage(leaderActor, new InitiateInstallSnapshot());
- List<Object> captureSnapshots = MessageCollectorActor.getAllMatching(leaderActor, CaptureSnapshot.class);
- assertEquals("CaptureSnapshot should not get invoked when initiate is in progress", 1, captureSnapshots.size());
+ // if an initiate is started again when first is in progress, it shouldnt initiate Capture
+ leader.handleMessage(leaderActor, new Replicate(null, "state-id", entry));
- }};
+ List<CaptureSnapshot> captureSnapshots = MessageCollectorActor.getAllMatching(leaderActor, CaptureSnapshot.class);
+ assertEquals("CaptureSnapshot should not get invoked when initiate is in progress", 1, captureSnapshots.size());
}
@Test
- public void testInstallSnapshot() {
- new JavaTestKit(getSystem()) {{
+ public void testInstallSnapshot() throws Exception {
+ logStart("testInstallSnapshot");
- ActorRef followerActor = getTestActor();
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(),
- followerActor.path().toString());
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext();
- actorContext.setPeerAddresses(peerAddresses);
+ //clears leaders log
+ actorContext.getReplicatedLog().removeFrom(0);
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
+ actorContext.setCommitIndex(followersLastIndex);
- //clears leaders log
- actorContext.getReplicatedLog().removeFrom(0);
+ leader = new Leader(actorContext);
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int newEntryIndex = 4;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
+ // Ignore initial heartbeat.
+ MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
- actorContext.setCommitIndex(followersLastIndex);
+ RaftActorBehavior raftBehavior = leader.handleMessage(leaderActor,
+ new SendInstallSnapshot(toByteString(leadersSnapshot)));
- Leader leader = new Leader(actorContext);
+ assertTrue(raftBehavior instanceof Leader);
- // Ignore initial heartbeat.
- expectMsgClass(duration("5 seconds"), AppendEntries.class);
+ // check if installsnapshot gets called with the correct values.
- // new entry
- ReplicatedLogImplEntry entry =
- new ReplicatedLogImplEntry(newEntryIndex, currentTerm,
- new MockRaftActorContext.MockPayload("D"));
+ InstallSnapshot installSnapshot = (InstallSnapshot) SerializationUtils.fromSerializable(
+ MessageCollectorActor.expectFirstMatching(followerActor, InstallSnapshotMessages.InstallSnapshot.class));
- RaftActorBehavior raftBehavior = leader.handleMessage(senderActor,
- new SendInstallSnapshot(toByteString(leadersSnapshot)));
+ assertNotNull(installSnapshot.getData());
+ assertEquals(snapshotIndex, installSnapshot.getLastIncludedIndex());
+ assertEquals(snapshotTerm, installSnapshot.getLastIncludedTerm());
- assertTrue(raftBehavior instanceof Leader);
-
- // check if installsnapshot gets called with the correct values.
- final String out =
- new ExpectMsg<String>(duration("1 seconds"), "match hint") {
- // do not put code outside this method, will run afterwards
- @Override
- protected String match(Object in) {
- if (in instanceof InstallSnapshotMessages.InstallSnapshot) {
- InstallSnapshot is = (InstallSnapshot)
- SerializationUtils.fromSerializable(in);
- if (is.getData() == null) {
- return "InstallSnapshot data is null";
- }
- if (is.getLastIncludedIndex() != snapshotIndex) {
- return is.getLastIncludedIndex() + "!=" + snapshotIndex;
- }
- if (is.getLastIncludedTerm() != snapshotTerm) {
- return is.getLastIncludedTerm() + "!=" + snapshotTerm;
- }
- if (is.getTerm() == currentTerm) {
- return is.getTerm() + "!=" + currentTerm;
- }
-
- return "match";
-
- } else {
- return "message mismatch:" + in.getClass();
- }
- }
- }.get(); // this extracts the received message
-
- assertEquals("match", out);
- }};
+ assertEquals(currentTerm, installSnapshot.getTerm());
}
@Test
- public void testHandleInstallSnapshotReplyLastChunk() {
- new JavaTestKit(getSystem()) {{
+ public void testHandleInstallSnapshotReplyLastChunk() throws Exception {
+ logStart("testHandleInstallSnapshotReplyLastChunk");
- ActorRef followerActor = getTestActor();
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(),
- followerActor.path().toString());
-
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int newEntryIndex = 4;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
-
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext();
- actorContext.setPeerAddresses(peerAddresses);
- actorContext.setCommitIndex(followersLastIndex);
-
- MockLeader leader = new MockLeader(actorContext);
-
- // Ignore initial heartbeat.
- expectMsgClass(duration("5 seconds"), AppendEntries.class);
-
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
-
- // set the snapshot variables in replicatedlog
-
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
-
- ByteString bs = toByteString(leadersSnapshot);
- leader.setSnapshot(Optional.of(bs));
- leader.createFollowerToSnapshot(followerActor.path().toString(), bs);
- while(!leader.getFollowerToSnapshot().isLastChunk(leader.getFollowerToSnapshot().getChunkIndex())) {
- leader.getFollowerToSnapshot().getNextChunk();
- leader.getFollowerToSnapshot().incrementChunkIndex();
- }
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- //clears leaders log
- actorContext.getReplicatedLog().removeFrom(0);
+ actorContext.setCommitIndex(followersLastIndex);
- RaftActorBehavior raftBehavior = leader.handleMessage(senderActor,
- new InstallSnapshotReply(currentTerm, followerActor.path().toString(),
- leader.getFollowerToSnapshot().getChunkIndex(), true));
+ leader = new Leader(actorContext);
- assertTrue(raftBehavior instanceof Leader);
+ // Ignore initial heartbeat.
+ MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- assertEquals(0, leader.followerSnapshotSize());
- assertEquals(1, leader.followerLogSize());
- assertNotNull(leader.getFollower(followerActor.path().toString()));
- FollowerLogInformation fli = leader.getFollower(followerActor.path().toString());
- assertEquals(snapshotIndex, fli.getMatchIndex());
- assertEquals(snapshotIndex, fli.getMatchIndex());
- assertEquals(snapshotIndex + 1, fli.getNextIndex());
- }};
- }
- @Test
- public void testSendSnapshotfromInstallSnapshotReply() throws Exception {
- new JavaTestKit(getSystem()) {{
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- TestActorRef<MessageCollectorActor> followerActor =
- TestActorRef.create(getSystem(), Props.create(MessageCollectorActor.class), "follower-reply");
+ // set the snapshot variables in replicatedlog
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-reply",
- followerActor.path().toString());
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
+ ByteString bs = toByteString(leadersSnapshot);
+ leader.setSnapshot(Optional.of(bs));
+ FollowerToSnapshot fts = leader.new FollowerToSnapshot(bs);
+ leader.setFollowerSnapshot(FOLLOWER_ID, fts);
+ while(!fts.isLastChunk(fts.getChunkIndex())) {
+ fts.getNextChunk();
+ fts.incrementChunkIndex();
+ }
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext();
- DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl(){
- @Override
- public int getSnapshotChunkSize() {
- return 50;
- }
- };
- configParams.setHeartBeatInterval(new FiniteDuration(9, TimeUnit.SECONDS));
- configParams.setIsolatedLeaderCheckInterval(new FiniteDuration(10, TimeUnit.SECONDS));
+ //clears leaders log
+ actorContext.getReplicatedLog().removeFrom(0);
- actorContext.setConfigParams(configParams);
- actorContext.setPeerAddresses(peerAddresses);
- actorContext.setCommitIndex(followersLastIndex);
+ RaftActorBehavior raftBehavior = leader.handleMessage(followerActor,
+ new InstallSnapshotReply(currentTerm, FOLLOWER_ID, fts.getChunkIndex(), true));
- MockLeader leader = new MockLeader(actorContext);
+ assertTrue(raftBehavior instanceof Leader);
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
+ assertEquals(0, leader.followerSnapshotSize());
+ assertEquals(1, leader.followerLogSize());
+ FollowerLogInformation fli = leader.getFollower(FOLLOWER_ID);
+ assertNotNull(fli);
+ assertEquals(snapshotIndex, fli.getMatchIndex());
+ assertEquals(snapshotIndex, fli.getMatchIndex());
+ assertEquals(snapshotIndex + 1, fli.getNextIndex());
+ }
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
+ @Test
+ public void testSendSnapshotfromInstallSnapshotReply() throws Exception {
+ logStart("testSendSnapshotfromInstallSnapshotReply");
- ByteString bs = toByteString(leadersSnapshot);
- leader.setSnapshot(Optional.of(bs));
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- leader.handleMessage(leaderActor, new SendInstallSnapshot(bs));
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- List<Object> objectList = MessageCollectorActor.getAllMatching(followerActor,
- InstallSnapshotMessages.InstallSnapshot.class);
+ DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl(){
+ @Override
+ public int getSnapshotChunkSize() {
+ return 50;
+ }
+ };
+ configParams.setHeartBeatInterval(new FiniteDuration(9, TimeUnit.SECONDS));
+ configParams.setIsolatedLeaderCheckInterval(new FiniteDuration(10, TimeUnit.SECONDS));
+
+ actorContext.setConfigParams(configParams);
+ actorContext.setCommitIndex(followersLastIndex);
- assertEquals(1, objectList.size());
+ leader = new Leader(actorContext);
- Object o = objectList.get(0);
- assertTrue(o instanceof InstallSnapshotMessages.InstallSnapshot);
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- InstallSnapshotMessages.InstallSnapshot installSnapshot = (InstallSnapshotMessages.InstallSnapshot) o;
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
- assertEquals(1, installSnapshot.getChunkIndex());
- assertEquals(3, installSnapshot.getTotalChunks());
+ ByteString bs = toByteString(leadersSnapshot);
+ leader.setSnapshot(Optional.of(bs));
- leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
- "follower-reply", installSnapshot.getChunkIndex(), true));
+ leader.handleMessage(leaderActor, new SendInstallSnapshot(bs));
- objectList = MessageCollectorActor.getAllMatching(followerActor,
- InstallSnapshotMessages.InstallSnapshot.class);
+ InstallSnapshotMessages.InstallSnapshot installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- assertEquals(2, objectList.size());
+ assertEquals(1, installSnapshot.getChunkIndex());
+ assertEquals(3, installSnapshot.getTotalChunks());
- installSnapshot = (InstallSnapshotMessages.InstallSnapshot) objectList.get(1);
+ followerActor.underlyingActor().clear();
+ leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
+ FOLLOWER_ID, installSnapshot.getChunkIndex(), true));
- leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
- "follower-reply", installSnapshot.getChunkIndex(), true));
+ installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- objectList = MessageCollectorActor.getAllMatching(followerActor,
- InstallSnapshotMessages.InstallSnapshot.class);
+ assertEquals(2, installSnapshot.getChunkIndex());
+ assertEquals(3, installSnapshot.getTotalChunks());
- assertEquals(3, objectList.size());
+ followerActor.underlyingActor().clear();
+ leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
+ FOLLOWER_ID, installSnapshot.getChunkIndex(), true));
- installSnapshot = (InstallSnapshotMessages.InstallSnapshot) objectList.get(2);
+ installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- // Send snapshot reply one more time and make sure that a new snapshot message should not be sent to follower
- leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
- "follower-reply", installSnapshot.getChunkIndex(), true));
+ // Send snapshot reply one more time and make sure that a new snapshot message should not be sent to follower
+ followerActor.underlyingActor().clear();
+ leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
+ FOLLOWER_ID, installSnapshot.getChunkIndex(), true));
- objectList = MessageCollectorActor.getAllMatching(followerActor,
- InstallSnapshotMessages.InstallSnapshot.class);
+ installSnapshot = MessageCollectorActor.getFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- // Count should still stay at 3
- assertEquals(3, objectList.size());
- }};
+ Assert.assertNull(installSnapshot);
}
@Test
public void testHandleInstallSnapshotReplyWithInvalidChunkIndex() throws Exception{
- new JavaTestKit(getSystem()) {{
+ logStart("testHandleInstallSnapshotReplyWithInvalidChunkIndex");
- TestActorRef<MessageCollectorActor> followerActor =
- TestActorRef.create(getSystem(), Props.create(MessageCollectorActor.class), "follower");
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(),
- followerActor.path().toString());
-
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
-
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext();
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- actorContext.setConfigParams(new DefaultConfigParamsImpl(){
- @Override
- public int getSnapshotChunkSize() {
- return 50;
- }
- });
- actorContext.setPeerAddresses(peerAddresses);
- actorContext.setCommitIndex(followersLastIndex);
+ actorContext.setConfigParams(new DefaultConfigParamsImpl(){
+ @Override
+ public int getSnapshotChunkSize() {
+ return 50;
+ }
+ });
- MockLeader leader = new MockLeader(actorContext);
+ actorContext.setCommitIndex(followersLastIndex);
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
+ leader = new Leader(actorContext);
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- ByteString bs = toByteString(leadersSnapshot);
- leader.setSnapshot(Optional.of(bs));
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
- leader.handleMessage(leaderActor, new SendInstallSnapshot(bs));
+ ByteString bs = toByteString(leadersSnapshot);
+ leader.setSnapshot(Optional.of(bs));
- MessageCollectorActor.getAllMatching(followerActor,
- InstallSnapshotMessages.InstallSnapshot.class);
+ leader.handleMessage(leaderActor, new SendInstallSnapshot(bs));
- InstallSnapshotMessages.InstallSnapshot installSnapshot = MessageCollectorActor.getFirstMatching(
- followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- assertNotNull(installSnapshot);
+ InstallSnapshotMessages.InstallSnapshot installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- assertEquals(1, installSnapshot.getChunkIndex());
- assertEquals(3, installSnapshot.getTotalChunks());
+ assertEquals(1, installSnapshot.getChunkIndex());
+ assertEquals(3, installSnapshot.getTotalChunks());
- followerActor.underlyingActor().clear();
+ followerActor.underlyingActor().clear();
- leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
- followerActor.path().toString(), -1, false));
+ leader.handleMessage(followerActor, new InstallSnapshotReply(actorContext.getTermInformation().getCurrentTerm(),
+ FOLLOWER_ID, -1, false));
- Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().getHeartBeatInterval().toMillis(),
+ Uninterruptibles.sleepUninterruptibly(actorContext.getConfigParams().getHeartBeatInterval().toMillis(),
TimeUnit.MILLISECONDS);
- leader.handleMessage(leaderActor, new SendHeartBeat());
-
- installSnapshot = MessageCollectorActor.getFirstMatching(
- followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- assertNotNull(installSnapshot);
+ leader.handleMessage(leaderActor, new SendHeartBeat());
- assertEquals(1, installSnapshot.getChunkIndex());
- assertEquals(3, installSnapshot.getTotalChunks());
+ installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- followerActor.tell(PoisonPill.getInstance(), getRef());
- }};
+ assertEquals(1, installSnapshot.getChunkIndex());
+ assertEquals(3, installSnapshot.getTotalChunks());
}
@Test
public void testHandleSnapshotSendsPreviousChunksHashCodeWhenSendingNextChunk() throws Exception {
- new JavaTestKit(getSystem()) {
- {
- TestActorRef<MessageCollectorActor> followerActor =
- TestActorRef.create(getSystem(), Props.create(MessageCollectorActor.class), "follower-chunk");
+ logStart("testHandleSnapshotSendsPreviousChunksHashCodeWhenSendingNextChunk");
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put(followerActor.path().toString(),
- followerActor.path().toString());
+ MockRaftActorContext actorContext = createActorContextWithFollower();
- final int followersLastIndex = 2;
- final int snapshotIndex = 3;
- final int snapshotTerm = 1;
- final int currentTerm = 2;
+ final int followersLastIndex = 2;
+ final int snapshotIndex = 3;
+ final int snapshotTerm = 1;
+ final int currentTerm = 2;
- MockRaftActorContext actorContext =
- (MockRaftActorContext) createActorContext();
-
- actorContext.setConfigParams(new DefaultConfigParamsImpl() {
- @Override
- public int getSnapshotChunkSize() {
- return 50;
- }
- });
- actorContext.setPeerAddresses(peerAddresses);
- actorContext.setCommitIndex(followersLastIndex);
+ actorContext.setConfigParams(new DefaultConfigParamsImpl() {
+ @Override
+ public int getSnapshotChunkSize() {
+ return 50;
+ }
+ });
- MockLeader leader = new MockLeader(actorContext);
+ actorContext.setCommitIndex(followersLastIndex);
- Map<String, String> leadersSnapshot = new HashMap<>();
- leadersSnapshot.put("1", "A");
- leadersSnapshot.put("2", "B");
- leadersSnapshot.put("3", "C");
+ leader = new Leader(actorContext);
- // set the snapshot variables in replicatedlog
- actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
- actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
- actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
+ Map<String, String> leadersSnapshot = new HashMap<>();
+ leadersSnapshot.put("1", "A");
+ leadersSnapshot.put("2", "B");
+ leadersSnapshot.put("3", "C");
- ByteString bs = toByteString(leadersSnapshot);
- leader.setSnapshot(Optional.of(bs));
+ // set the snapshot variables in replicatedlog
+ actorContext.getReplicatedLog().setSnapshotIndex(snapshotIndex);
+ actorContext.getReplicatedLog().setSnapshotTerm(snapshotTerm);
+ actorContext.getTermInformation().update(currentTerm, leaderActor.path().toString());
- leader.handleMessage(leaderActor, new SendInstallSnapshot(bs));
+ ByteString bs = toByteString(leadersSnapshot);
+ leader.setSnapshot(Optional.of(bs));
- InstallSnapshotMessages.InstallSnapshot installSnapshot = MessageCollectorActor.getFirstMatching(
- followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- assertNotNull(installSnapshot);
+ leader.handleMessage(leaderActor, new SendInstallSnapshot(bs));
- assertEquals(1, installSnapshot.getChunkIndex());
- assertEquals(3, installSnapshot.getTotalChunks());
- assertEquals(AbstractLeader.INITIAL_LAST_CHUNK_HASH_CODE, installSnapshot.getLastChunkHashCode());
+ InstallSnapshotMessages.InstallSnapshot installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- int hashCode = installSnapshot.getData().hashCode();
+ assertEquals(1, installSnapshot.getChunkIndex());
+ assertEquals(3, installSnapshot.getTotalChunks());
+ assertEquals(AbstractLeader.INITIAL_LAST_CHUNK_HASH_CODE, installSnapshot.getLastChunkHashCode());
- followerActor.underlyingActor().clear();
+ int hashCode = installSnapshot.getData().hashCode();
- leader.handleMessage(followerActor, new InstallSnapshotReply(installSnapshot.getTerm(),followerActor.path().toString(),1,true ));
+ followerActor.underlyingActor().clear();
- installSnapshot = MessageCollectorActor.getFirstMatching(
- followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- assertNotNull(installSnapshot);
+ leader.handleMessage(followerActor, new InstallSnapshotReply(installSnapshot.getTerm(),
+ FOLLOWER_ID, 1, true));
- assertEquals(2, installSnapshot.getChunkIndex());
- assertEquals(3, installSnapshot.getTotalChunks());
- assertEquals(hashCode, installSnapshot.getLastChunkHashCode());
+ installSnapshot = MessageCollectorActor.expectFirstMatching(
+ followerActor, InstallSnapshotMessages.InstallSnapshot.class);
- followerActor.tell(PoisonPill.getInstance(), getRef());
- }};
+ assertEquals(2, installSnapshot.getChunkIndex());
+ assertEquals(3, installSnapshot.getTotalChunks());
+ assertEquals(hashCode, installSnapshot.getLastChunkHashCode());
}
@Test
public void testFollowerToSnapshotLogic() {
+ logStart("testFollowerToSnapshotLogic");
- MockRaftActorContext actorContext = (MockRaftActorContext) createActorContext();
+ MockRaftActorContext actorContext = createActorContext();
actorContext.setConfigParams(new DefaultConfigParamsImpl() {
@Override
}
});
- MockLeader leader = new MockLeader(actorContext);
+ leader = new Leader(actorContext);
Map<String, String> leadersSnapshot = new HashMap<>();
leadersSnapshot.put("1", "A");
ByteString bs = toByteString(leadersSnapshot);
byte[] barray = bs.toByteArray();
- leader.createFollowerToSnapshot("followerId", bs);
+ FollowerToSnapshot fts = leader.new FollowerToSnapshot(bs);
+ leader.setFollowerSnapshot(FOLLOWER_ID, fts);
+
assertEquals(bs.size(), barray.length);
int chunkIndex=0;
j = barray.length;
}
- ByteString chunk = leader.getFollowerToSnapshot().getNextChunk();
+ ByteString chunk = fts.getNextChunk();
assertEquals("bytestring size not matching for chunk:"+ chunkIndex, j-i, chunk.size());
- assertEquals("chunkindex not matching", chunkIndex, leader.getFollowerToSnapshot().getChunkIndex());
+ assertEquals("chunkindex not matching", chunkIndex, fts.getChunkIndex());
- leader.getFollowerToSnapshot().markSendStatus(true);
- if (!leader.getFollowerToSnapshot().isLastChunk(chunkIndex)) {
- leader.getFollowerToSnapshot().incrementChunkIndex();
+ fts.markSendStatus(true);
+ if (!fts.isLastChunk(chunkIndex)) {
+ fts.incrementChunkIndex();
}
}
- assertEquals("totalChunks not matching", chunkIndex, leader.getFollowerToSnapshot().getTotalChunks());
+ assertEquals("totalChunks not matching", chunkIndex, fts.getTotalChunks());
}
-
@Override protected RaftActorBehavior createBehavior(
RaftActorContext actorContext) {
return new Leader(actorContext);
}
- @Override protected RaftActorContext createActorContext() {
+ @Override
+ protected MockRaftActorContext createActorContext() {
return createActorContext(leaderActor);
}
@Override
- protected RaftActorContext createActorContext(ActorRef actorRef) {
+ protected MockRaftActorContext createActorContext(ActorRef actorRef) {
+ return createActorContext("leader", actorRef);
+ }
+
+ private MockRaftActorContext createActorContextWithFollower() {
+ MockRaftActorContext actorContext = createActorContext();
+ actorContext.setPeerAddresses(ImmutableMap.<String,String>builder().put(FOLLOWER_ID,
+ followerActor.path().toString()).build());
+ return actorContext;
+ }
+
+ private MockRaftActorContext createActorContext(String id, ActorRef actorRef) {
DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl();
configParams.setHeartBeatInterval(new FiniteDuration(50, TimeUnit.MILLISECONDS));
configParams.setElectionTimeoutFactor(100000);
- MockRaftActorContext context = new MockRaftActorContext("test", getSystem(), actorRef);
+ MockRaftActorContext context = new MockRaftActorContext(id, getSystem(), actorRef);
context.setConfigParams(configParams);
return context;
}
- private ByteString toByteString(Map<String, String> state) {
- ByteArrayOutputStream b = null;
- ObjectOutputStream o = null;
- try {
- try {
- b = new ByteArrayOutputStream();
- o = new ObjectOutputStream(b);
- o.writeObject(state);
- byte[] snapshotBytes = b.toByteArray();
- return ByteString.copyFrom(snapshotBytes);
- } finally {
- if (o != null) {
- o.flush();
- o.close();
- }
- if (b != null) {
- b.close();
- }
- }
- } catch (IOException e) {
- Assert.fail("IOException in converting Hashmap to Bytestring:" + e);
- }
- return null;
- }
-
public static class ForwardMessageToBehaviorActor extends MessageCollectorActor {
AbstractRaftActorBehavior behavior;
@Test
public void testLeaderCreatedWithCommitIndexLessThanLastIndex() throws Exception {
- new JavaTestKit(getSystem()) {{
- TestActorRef<ForwardMessageToBehaviorActor> leaderActor = TestActorRef.create(getSystem(),
- Props.create(ForwardMessageToBehaviorActor.class));
+ logStart("testLeaderCreatedWithCommitIndexLessThanLastIndex");
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ MockRaftActorContext leaderActorContext = createActorContextWithFollower();
- TestActorRef<ForwardMessageToBehaviorActor> followerActor = TestActorRef.create(getSystem(),
- ForwardMessageToBehaviorActor.props());
+ MockRaftActorContext followerActorContext = createActorContext(FOLLOWER_ID, followerActor);
- MockRaftActorContext followerActorContext =
- new MockRaftActorContext("follower", getSystem(), followerActor);
+ Follower follower = new Follower(followerActorContext);
+ followerActor.underlyingActor().behavior = follower;
- Follower follower = new Follower(followerActorContext);
- followerActor.underlyingActor().behavior = follower;
+ Map<String, String> peerAddresses = new HashMap<>();
+ peerAddresses.put(FOLLOWER_ID, followerActor.path().toString());
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower", followerActor.path().toString());
+ leaderActorContext.setPeerAddresses(peerAddresses);
- leaderActorContext.setPeerAddresses(peerAddresses);
+ leaderActorContext.getReplicatedLog().removeFrom(0);
- leaderActorContext.getReplicatedLog().removeFrom(0);
+ //create 3 entries
+ leaderActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
- //create 3 entries
- leaderActorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
+ leaderActorContext.setCommitIndex(1);
- leaderActorContext.setCommitIndex(1);
+ followerActorContext.getReplicatedLog().removeFrom(0);
- followerActorContext.getReplicatedLog().removeFrom(0);
+ // follower too has the exact same log entries and has the same commit index
+ followerActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
- // follower too has the exact same log entries and has the same commit index
- followerActorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
+ followerActorContext.setCommitIndex(1);
- followerActorContext.setCommitIndex(1);
+ leader = new Leader(leaderActorContext);
- Leader leader = new Leader(leaderActorContext);
+ AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- AppendEntries appendEntries = MessageCollectorActor.getFirstMatching(followerActor, AppendEntries.class);
- assertNotNull(appendEntries);
+ assertEquals(1, appendEntries.getLeaderCommit());
+ assertEquals(0, appendEntries.getEntries().size());
+ assertEquals(0, appendEntries.getPrevLogIndex());
- assertEquals(1, appendEntries.getLeaderCommit());
- assertEquals(0, appendEntries.getEntries().size());
- assertEquals(0, appendEntries.getPrevLogIndex());
+ AppendEntriesReply appendEntriesReply = MessageCollectorActor.expectFirstMatching(
+ leaderActor, AppendEntriesReply.class);
- AppendEntriesReply appendEntriesReply = MessageCollectorActor.getFirstMatching(
- leaderActor, AppendEntriesReply.class);
- assertNotNull(appendEntriesReply);
+ assertEquals(2, appendEntriesReply.getLogLastIndex());
+ assertEquals(1, appendEntriesReply.getLogLastTerm());
- assertEquals(2, appendEntriesReply.getLogLastIndex());
- assertEquals(1, appendEntriesReply.getLogLastTerm());
+ // follower returns its next index
+ assertEquals(2, appendEntriesReply.getLogLastIndex());
+ assertEquals(1, appendEntriesReply.getLogLastTerm());
- // follower returns its next index
- assertEquals(2, appendEntriesReply.getLogLastIndex());
- assertEquals(1, appendEntriesReply.getLogLastTerm());
- }};
+ follower.close();
}
-
@Test
public void testLeaderCreatedWithCommitIndexLessThanFollowersCommitIndex() throws Exception {
- new JavaTestKit(getSystem()) {{
- TestActorRef<ForwardMessageToBehaviorActor> leaderActor = TestActorRef.create(getSystem(),
- Props.create(ForwardMessageToBehaviorActor.class));
+ logStart("testLeaderCreatedWithCommitIndexLessThanFollowersCommitIndex");
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ MockRaftActorContext leaderActorContext = createActorContext();
- TestActorRef<ForwardMessageToBehaviorActor> followerActor = TestActorRef.create(getSystem(),
- ForwardMessageToBehaviorActor.props());
+ MockRaftActorContext followerActorContext = createActorContext(FOLLOWER_ID, followerActor);
- MockRaftActorContext followerActorContext =
- new MockRaftActorContext("follower", getSystem(), followerActor);
+ Follower follower = new Follower(followerActorContext);
+ followerActor.underlyingActor().behavior = follower;
- Follower follower = new Follower(followerActorContext);
- followerActor.underlyingActor().behavior = follower;
+ Map<String, String> peerAddresses = new HashMap<>();
+ peerAddresses.put(FOLLOWER_ID, followerActor.path().toString());
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower", followerActor.path().toString());
+ leaderActorContext.setPeerAddresses(peerAddresses);
- leaderActorContext.setPeerAddresses(peerAddresses);
+ leaderActorContext.getReplicatedLog().removeFrom(0);
- leaderActorContext.getReplicatedLog().removeFrom(0);
+ leaderActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
- leaderActorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
+ leaderActorContext.setCommitIndex(1);
- leaderActorContext.setCommitIndex(1);
+ followerActorContext.getReplicatedLog().removeFrom(0);
- followerActorContext.getReplicatedLog().removeFrom(0);
+ followerActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
- followerActorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
+ // follower has the same log entries but its commit index > leaders commit index
+ followerActorContext.setCommitIndex(2);
- // follower has the same log entries but its commit index > leaders commit index
- followerActorContext.setCommitIndex(2);
+ leader = new Leader(leaderActorContext);
- Leader leader = new Leader(leaderActorContext);
+ // Initial heartbeat
+ AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- // Initial heartbeat
- AppendEntries appendEntries = MessageCollectorActor.getFirstMatching(followerActor, AppendEntries.class);
- assertNotNull(appendEntries);
+ assertEquals(1, appendEntries.getLeaderCommit());
+ assertEquals(0, appendEntries.getEntries().size());
+ assertEquals(0, appendEntries.getPrevLogIndex());
- assertEquals(1, appendEntries.getLeaderCommit());
- assertEquals(0, appendEntries.getEntries().size());
- assertEquals(0, appendEntries.getPrevLogIndex());
+ AppendEntriesReply appendEntriesReply = MessageCollectorActor.expectFirstMatching(
+ leaderActor, AppendEntriesReply.class);
- AppendEntriesReply appendEntriesReply = MessageCollectorActor.getFirstMatching(
- leaderActor, AppendEntriesReply.class);
- assertNotNull(appendEntriesReply);
+ assertEquals(2, appendEntriesReply.getLogLastIndex());
+ assertEquals(1, appendEntriesReply.getLogLastTerm());
- assertEquals(2, appendEntriesReply.getLogLastIndex());
- assertEquals(1, appendEntriesReply.getLogLastTerm());
+ leaderActor.underlyingActor().behavior = leader;
+ leader.handleMessage(followerActor, appendEntriesReply);
- leaderActor.underlyingActor().behavior = leader;
- leader.handleMessage(followerActor, appendEntriesReply);
+ leaderActor.underlyingActor().clear();
+ followerActor.underlyingActor().clear();
- leaderActor.underlyingActor().clear();
- followerActor.underlyingActor().clear();
+ Uninterruptibles.sleepUninterruptibly(leaderActorContext.getConfigParams().getHeartBeatInterval().toMillis(),
+ TimeUnit.MILLISECONDS);
- Uninterruptibles.sleepUninterruptibly(leaderActorContext.getConfigParams().getHeartBeatInterval().toMillis(),
- TimeUnit.MILLISECONDS);
+ leader.handleMessage(leaderActor, new SendHeartBeat());
- leader.handleMessage(leaderActor, new SendHeartBeat());
+ appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- appendEntries = MessageCollectorActor.getFirstMatching(followerActor, AppendEntries.class);
- assertNotNull(appendEntries);
+ assertEquals(2, appendEntries.getLeaderCommit());
+ assertEquals(0, appendEntries.getEntries().size());
+ assertEquals(2, appendEntries.getPrevLogIndex());
- assertEquals(1, appendEntries.getLeaderCommit());
- assertEquals(0, appendEntries.getEntries().size());
- assertEquals(2, appendEntries.getPrevLogIndex());
+ appendEntriesReply = MessageCollectorActor.expectFirstMatching(leaderActor, AppendEntriesReply.class);
- appendEntriesReply = MessageCollectorActor.getFirstMatching(leaderActor, AppendEntriesReply.class);
- assertNotNull(appendEntriesReply);
+ assertEquals(2, appendEntriesReply.getLogLastIndex());
+ assertEquals(1, appendEntriesReply.getLogLastTerm());
- assertEquals(2, appendEntriesReply.getLogLastIndex());
- assertEquals(1, appendEntriesReply.getLogLastTerm());
+ assertEquals(2, followerActorContext.getCommitIndex());
- assertEquals(1, followerActorContext.getCommitIndex());
- }};
+ follower.close();
}
@Test
public void testHandleAppendEntriesReplyFailure(){
- new JavaTestKit(getSystem()) {
- {
+ logStart("testHandleAppendEntriesReplyFailure");
- ActorRef leaderActor =
- getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ MockRaftActorContext leaderActorContext = createActorContextWithFollower();
- ActorRef followerActor =
- getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ leader = new Leader(leaderActorContext);
+ // Send initial heartbeat reply with last index.
+ leader.handleAppendEntriesReply(followerActor, new AppendEntriesReply(FOLLOWER_ID, 1, true, 10, 1));
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ FollowerLogInformation followerInfo = leader.getFollower(FOLLOWER_ID);
+ assertEquals("getNextIndex", 11, followerInfo.getNextIndex());
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-1",
- followerActor.path().toString());
+ AppendEntriesReply reply = new AppendEntriesReply(FOLLOWER_ID, 1, false, 10, 1);
- leaderActorContext.setPeerAddresses(peerAddresses);
+ RaftActorBehavior raftActorBehavior = leader.handleAppendEntriesReply(followerActor, reply);
- Leader leader = new Leader(leaderActorContext);
+ assertEquals(RaftState.Leader, raftActorBehavior.state());
- AppendEntriesReply reply = new AppendEntriesReply("follower-1", 1, false, 10, 1);
-
- RaftActorBehavior raftActorBehavior = leader.handleAppendEntriesReply(followerActor, reply);
-
- assertEquals(RaftState.Leader, raftActorBehavior.state());
-
- }};
+ assertEquals("getNextIndex", 10, followerInfo.getNextIndex());
}
@Test
public void testHandleAppendEntriesReplySuccess() throws Exception {
- new JavaTestKit(getSystem()) {
- {
-
- ActorRef leaderActor =
- getSystem().actorOf(Props.create(MessageCollectorActor.class));
-
- ActorRef followerActor =
- getSystem().actorOf(Props.create(MessageCollectorActor.class));
-
+ logStart("testHandleAppendEntriesReplySuccess");
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ MockRaftActorContext leaderActorContext = createActorContextWithFollower();
- leaderActorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
+ leaderActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-1",
- followerActor.path().toString());
+ leaderActorContext.setCommitIndex(1);
+ leaderActorContext.setLastApplied(1);
+ leaderActorContext.getTermInformation().update(1, "leader");
- leaderActorContext.setPeerAddresses(peerAddresses);
- leaderActorContext.setCommitIndex(1);
- leaderActorContext.setLastApplied(1);
- leaderActorContext.getTermInformation().update(1, "leader");
+ leader = new Leader(leaderActorContext);
- Leader leader = new Leader(leaderActorContext);
+ AppendEntriesReply reply = new AppendEntriesReply(FOLLOWER_ID, 1, true, 2, 1);
- AppendEntriesReply reply = new AppendEntriesReply("follower-1", 1, true, 2, 1);
+ RaftActorBehavior raftActorBehavior = leader.handleAppendEntriesReply(followerActor, reply);
- RaftActorBehavior raftActorBehavior = leader.handleAppendEntriesReply(followerActor, reply);
+ assertEquals(RaftState.Leader, raftActorBehavior.state());
- assertEquals(RaftState.Leader, raftActorBehavior.state());
+ assertEquals(2, leaderActorContext.getCommitIndex());
- assertEquals(2, leaderActorContext.getCommitIndex());
+ ApplyLogEntries applyLogEntries = MessageCollectorActor.expectFirstMatching(
+ leaderActor, ApplyLogEntries.class);
- ApplyLogEntries applyLogEntries =
- MessageCollectorActor.getFirstMatching(leaderActor,
- ApplyLogEntries.class);
+ assertEquals(2, leaderActorContext.getLastApplied());
- assertNotNull(applyLogEntries);
+ assertEquals(2, applyLogEntries.getToIndex());
- assertEquals(2, leaderActorContext.getLastApplied());
+ List<ApplyState> applyStateList = MessageCollectorActor.getAllMatching(leaderActor,
+ ApplyState.class);
- assertEquals(2, applyLogEntries.getToIndex());
+ assertEquals(1,applyStateList.size());
- List<Object> applyStateList = MessageCollectorActor.getAllMatching(leaderActor,
- ApplyState.class);
+ ApplyState applyState = applyStateList.get(0);
- assertEquals(1,applyStateList.size());
-
- ApplyState applyState = (ApplyState) applyStateList.get(0);
-
- assertEquals(2, applyState.getReplicatedLogEntry().getIndex());
-
- }};
+ assertEquals(2, applyState.getReplicatedLogEntry().getIndex());
}
@Test
public void testHandleAppendEntriesReplyUnknownFollower(){
- new JavaTestKit(getSystem()) {
- {
+ logStart("testHandleAppendEntriesReplyUnknownFollower");
- ActorRef leaderActor =
- getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ MockRaftActorContext leaderActorContext = createActorContext();
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ leader = new Leader(leaderActorContext);
- Leader leader = new Leader(leaderActorContext);
+ AppendEntriesReply reply = new AppendEntriesReply("unkown-follower", 1, false, 10, 1);
- AppendEntriesReply reply = new AppendEntriesReply("follower-1", 1, false, 10, 1);
+ RaftActorBehavior raftActorBehavior = leader.handleAppendEntriesReply(followerActor, reply);
- RaftActorBehavior raftActorBehavior = leader.handleAppendEntriesReply(getRef(), reply);
-
- assertEquals(RaftState.Leader, raftActorBehavior.state());
-
- }};
+ assertEquals(RaftState.Leader, raftActorBehavior.state());
}
@Test
public void testHandleRequestVoteReply(){
- new JavaTestKit(getSystem()) {
- {
-
- ActorRef leaderActor =
- getSystem().actorOf(Props.create(MessageCollectorActor.class));
+ logStart("testHandleRequestVoteReply");
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ MockRaftActorContext leaderActorContext = createActorContext();
- Leader leader = new Leader(leaderActorContext);
+ leader = new Leader(leaderActorContext);
- RaftActorBehavior raftActorBehavior = leader.handleRequestVoteReply(getRef(), new RequestVoteReply(1, true));
+ // Should be a no-op.
+ RaftActorBehavior raftActorBehavior = leader.handleRequestVoteReply(followerActor,
+ new RequestVoteReply(1, true));
- assertEquals(RaftState.Leader, raftActorBehavior.state());
+ assertEquals(RaftState.Leader, raftActorBehavior.state());
- raftActorBehavior = leader.handleRequestVoteReply(getRef(), new RequestVoteReply(1, false));
+ raftActorBehavior = leader.handleRequestVoteReply(followerActor, new RequestVoteReply(1, false));
- assertEquals(RaftState.Leader, raftActorBehavior.state());
- }};
+ assertEquals(RaftState.Leader, raftActorBehavior.state());
}
@Test
public void testIsolatedLeaderCheckNoFollowers() {
- new JavaTestKit(getSystem()) {{
- ActorRef leaderActor = getTestActor();
-
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ logStart("testIsolatedLeaderCheckNoFollowers");
- Map<String, String> peerAddresses = new HashMap<>();
- leaderActorContext.setPeerAddresses(peerAddresses);
+ MockRaftActorContext leaderActorContext = createActorContext();
- Leader leader = new Leader(leaderActorContext);
- RaftActorBehavior behavior = leader.handleMessage(leaderActor, new IsolatedLeaderCheck());
- Assert.assertTrue(behavior instanceof Leader);
- }};
+ leader = new Leader(leaderActorContext);
+ RaftActorBehavior behavior = leader.handleMessage(leaderActor, new IsolatedLeaderCheck());
+ Assert.assertTrue(behavior instanceof Leader);
}
@Test
public void testIsolatedLeaderCheckTwoFollowers() throws Exception {
+ logStart("testIsolatedLeaderCheckTwoFollowers");
+
new JavaTestKit(getSystem()) {{
ActorRef followerActor1 = getTestActor();
ActorRef followerActor2 = getTestActor();
- MockRaftActorContext leaderActorContext = (MockRaftActorContext) createActorContext();
+ MockRaftActorContext leaderActorContext = createActorContext();
Map<String, String> peerAddresses = new HashMap<>();
peerAddresses.put("follower-1", followerActor1.path().toString());
leaderActorContext.setPeerAddresses(peerAddresses);
- Leader leader = new Leader(leaderActorContext);
- leader.stopIsolatedLeaderCheckSchedule();
+ leader = new Leader(leaderActorContext);
leader.markFollowerActive("follower-1");
leader.markFollowerActive("follower-2");
behavior = leader.handleMessage(leaderActor, new IsolatedLeaderCheck());
Assert.assertTrue("Behavior not instance of IsolatedLeader when majority followers are inactive",
behavior instanceof IsolatedLeader);
-
}};
}
@Test
public void testAppendEntryCallAtEndofAppendEntryReply() throws Exception {
- new JavaTestKit(getSystem()) {{
- TestActorRef<MessageCollectorActor> leaderActor = TestActorRef.create(getSystem(),
- Props.create(MessageCollectorActor.class));
+ logStart("testAppendEntryCallAtEndofAppendEntryReply");
- MockRaftActorContext leaderActorContext =
- new MockRaftActorContext("leader", getSystem(), leaderActor);
+ MockRaftActorContext leaderActorContext = createActorContextWithFollower();
- DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl();
- //configParams.setHeartBeatInterval(new FiniteDuration(9, TimeUnit.SECONDS));
- configParams.setIsolatedLeaderCheckInterval(new FiniteDuration(10, TimeUnit.SECONDS));
+ DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl();
+ //configParams.setHeartBeatInterval(new FiniteDuration(9, TimeUnit.SECONDS));
+ configParams.setIsolatedLeaderCheckInterval(new FiniteDuration(10, TimeUnit.SECONDS));
- leaderActorContext.setConfigParams(configParams);
+ leaderActorContext.setConfigParams(configParams);
- TestActorRef<ForwardMessageToBehaviorActor> followerActor = TestActorRef.create(getSystem(),
- ForwardMessageToBehaviorActor.props());
+ MockRaftActorContext followerActorContext = createActorContext(FOLLOWER_ID, followerActor);
- MockRaftActorContext followerActorContext =
- new MockRaftActorContext("follower-reply", getSystem(), followerActor);
+ followerActorContext.setConfigParams(configParams);
- followerActorContext.setConfigParams(configParams);
+ Follower follower = new Follower(followerActorContext);
+ followerActor.underlyingActor().behavior = follower;
- Follower follower = new Follower(followerActorContext);
- followerActor.underlyingActor().behavior = follower;
+ leaderActorContext.getReplicatedLog().removeFrom(0);
+ leaderActorContext.setCommitIndex(-1);
+ leaderActorContext.setLastApplied(-1);
- Map<String, String> peerAddresses = new HashMap<>();
- peerAddresses.put("follower-reply",
- followerActor.path().toString());
+ followerActorContext.getReplicatedLog().removeFrom(0);
+ followerActorContext.setCommitIndex(-1);
+ followerActorContext.setLastApplied(-1);
- leaderActorContext.setPeerAddresses(peerAddresses);
+ leader = new Leader(leaderActorContext);
- leaderActorContext.getReplicatedLog().removeFrom(0);
- leaderActorContext.setCommitIndex(-1);
- leaderActorContext.setLastApplied(-1);
+ AppendEntriesReply appendEntriesReply = MessageCollectorActor.expectFirstMatching(
+ leaderActor, AppendEntriesReply.class);
- followerActorContext.getReplicatedLog().removeFrom(0);
- followerActorContext.setCommitIndex(-1);
- followerActorContext.setLastApplied(-1);
+ leader.handleMessage(followerActor, appendEntriesReply);
- Leader leader = new Leader(leaderActorContext);
+ // Clear initial heartbeat messages
- AppendEntriesReply appendEntriesReply = MessageCollectorActor.getFirstMatching(
- leaderActor, AppendEntriesReply.class);
- assertNotNull(appendEntriesReply);
- System.out.println("appendEntriesReply: "+appendEntriesReply);
- leader.handleMessage(followerActor, appendEntriesReply);
+ leaderActor.underlyingActor().clear();
+ followerActor.underlyingActor().clear();
- // Clear initial heartbeat messages
+ // create 3 entries
+ leaderActorContext.setReplicatedLog(
+ new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
+ leaderActorContext.setCommitIndex(1);
+ leaderActorContext.setLastApplied(1);
- leaderActor.underlyingActor().clear();
- followerActor.underlyingActor().clear();
-
- // create 3 entries
- leaderActorContext.setReplicatedLog(
- new MockRaftActorContext.MockReplicatedLogBuilder().createEntries(0, 3, 1).build());
- leaderActorContext.setCommitIndex(1);
- leaderActorContext.setLastApplied(1);
+ Uninterruptibles.sleepUninterruptibly(leaderActorContext.getConfigParams().getHeartBeatInterval().toMillis(),
+ TimeUnit.MILLISECONDS);
- Uninterruptibles.sleepUninterruptibly(leaderActorContext.getConfigParams().getHeartBeatInterval().toMillis(),
- TimeUnit.MILLISECONDS);
+ leader.handleMessage(leaderActor, new SendHeartBeat());
- leader.handleMessage(leaderActor, new SendHeartBeat());
+ AppendEntries appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- AppendEntries appendEntries = MessageCollectorActor.getFirstMatching(followerActor, AppendEntries.class);
- assertNotNull(appendEntries);
+ // Should send first log entry
+ assertEquals(1, appendEntries.getLeaderCommit());
+ assertEquals(0, appendEntries.getEntries().get(0).getIndex());
+ assertEquals(-1, appendEntries.getPrevLogIndex());
- // Should send first log entry
- assertEquals(1, appendEntries.getLeaderCommit());
- assertEquals(0, appendEntries.getEntries().get(0).getIndex());
- assertEquals(-1, appendEntries.getPrevLogIndex());
+ appendEntriesReply = MessageCollectorActor.expectFirstMatching(leaderActor, AppendEntriesReply.class);
- appendEntriesReply = MessageCollectorActor.getFirstMatching(leaderActor, AppendEntriesReply.class);
- assertNotNull(appendEntriesReply);
+ assertEquals(1, appendEntriesReply.getLogLastTerm());
+ assertEquals(0, appendEntriesReply.getLogLastIndex());
- assertEquals(1, appendEntriesReply.getLogLastTerm());
- assertEquals(0, appendEntriesReply.getLogLastIndex());
+ followerActor.underlyingActor().clear();
- followerActor.underlyingActor().clear();
+ leader.handleAppendEntriesReply(followerActor, appendEntriesReply);
- leader.handleAppendEntriesReply(followerActor, appendEntriesReply);
+ appendEntries = MessageCollectorActor.expectFirstMatching(followerActor, AppendEntries.class);
- appendEntries = MessageCollectorActor.getFirstMatching(followerActor, AppendEntries.class);
- assertNotNull(appendEntries);
+ // Should send second log entry
+ assertEquals(1, appendEntries.getLeaderCommit());
+ assertEquals(1, appendEntries.getEntries().get(0).getIndex());
- // Should send second log entry
- assertEquals(1, appendEntries.getLeaderCommit());
- assertEquals(1, appendEntries.getEntries().get(0).getIndex());
- }};
+ follower.close();
}
- class MockLeader extends Leader {
-
- FollowerToSnapshot fts;
-
- public MockLeader(RaftActorContext context){
- super(context);
- }
-
- public FollowerToSnapshot getFollowerToSnapshot() {
- return fts;
- }
-
- public void createFollowerToSnapshot(String followerId, ByteString bs ) {
- fts = new FollowerToSnapshot(bs);
- setFollowerSnapshot(followerId, fts);
- }
+ @Override
+ protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext,
+ ActorRef actorRef, RaftRPC rpc) throws Exception {
+ super.assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, actorRef, rpc);
+ assertEquals("New votedFor", null, actorContext.getTermInformation().getVotedFor());
}
private class MockConfigParamsImpl extends DefaultConfigParamsImpl {
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import org.junit.Assert;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.Duration;
* @return
*/
public static <T> T getFirstMatching(ActorRef actor, Class<T> clazz) throws Exception {
- for(int i = 0; i < 50; i++) {
- List<Object> allMessages = getAllMessages(actor);
+ List<Object> allMessages = getAllMessages(actor);
- for(Object message : allMessages){
- if(message.getClass().equals(clazz)){
- return (T) message;
- }
+ for(Object message : allMessages){
+ if(message.getClass().equals(clazz)){
+ return (T) message;
}
+ }
+
+ return null;
+ }
+
+ public static <T> T expectFirstMatching(ActorRef actor, Class<T> clazz) {
+ return expectFirstMatching(actor, clazz, 5000);
+ }
+
+ public static <T> T expectFirstMatching(ActorRef actor, Class<T> clazz, long timeout) {
+ int count = (int) (timeout / 50);
+ for(int i = 0; i < count; i++) {
+ try {
+ T message = getFirstMatching(actor, clazz);
+ if(message != null) {
+ return message;
+ }
+ } catch (Exception e) {}
- Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
+ Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS);
}
+ Assert.fail("Did not receive message of type " + clazz);
return null;
}
- public static List<Object> getAllMatching(ActorRef actor, Class<?> clazz) throws Exception {
+ public static <T> List<T> getAllMatching(ActorRef actor, Class<T> clazz) throws Exception {
List<Object> allMessages = getAllMessages(actor);
- List<Object> output = Lists.newArrayList();
+ List<T> output = Lists.newArrayList();
for(Object message : allMessages){
if(message.getClass().equals(clazz)){
- output.add(message);
+ output.add((T) message);
}
}
org.slf4j.simpleLogger.logFile=System.out
org.slf4j.simpleLogger.showShortLogName=true
org.slf4j.simpleLogger.levelInBrackets=true
-org.slf4j.simpleLogger.org.opendaylight.controller.cluster.raft=trace
\ No newline at end of file
+org.slf4j.simpleLogger.log.org.opendaylight.controller.cluster.raft=trace
\ No newline at end of file
<type>test-jar</type>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-flow-service</artifactId>
- <scope>test</scope>
- </dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-native</artifactId>
<artifactId>yang-parser-impl</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-test-model</artifactId>
- </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-test-model</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>opendaylight-l2-types</artifactId>
+ </dependency>
</dependencies>
<build>
<plugins>
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
-/*
+/**
* FIXME: THis test should be moved to sal-binding-broker and rewriten
* to use new DataBroker API
*/
@SuppressWarnings("deprecation")
public class ConcurrentImplicitCreateTest extends AbstractDataServiceTest {
- private static final NodeKey NODE_FOO_KEY = new NodeKey(new NodeId("foo"));
- private static final NodeKey NODE_BAR_KEY = new NodeKey(new NodeId("foo"));
- private static InstanceIdentifier<Nodes> NODES_PATH = InstanceIdentifier.builder(Nodes.class).build();
- private static InstanceIdentifier<Node> NODE_FOO_PATH = NODES_PATH.child(Node.class, NODE_FOO_KEY);
- private static InstanceIdentifier<Node> NODE_BAR_PATH = NODES_PATH.child(Node.class, NODE_FOO_KEY);
+ private static final TopLevelListKey FOO_KEY = new TopLevelListKey("foo");
+ private static final TopLevelListKey BAR_KEY = new TopLevelListKey("bar");
+ private static InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.builder(Top.class).build();
+ private static InstanceIdentifier<TopLevelList> FOO_PATH = TOP_PATH.child(TopLevelList.class, FOO_KEY);
+ private static InstanceIdentifier<TopLevelList> BAR_PATH = TOP_PATH.child(TopLevelList.class, BAR_KEY);
@Test
public void testConcurrentCreate() throws InterruptedException, ExecutionException {
DataModificationTransaction fooTx = baDataService.beginTransaction();
DataModificationTransaction barTx = baDataService.beginTransaction();
- fooTx.putOperationalData(NODE_FOO_PATH, new NodeBuilder().setKey(NODE_FOO_KEY).build());
- barTx.putOperationalData(NODE_BAR_PATH, new NodeBuilder().setKey(NODE_BAR_KEY).build());
+ fooTx.putOperationalData(FOO_PATH, new TopLevelListBuilder().setKey(FOO_KEY).build());
+ barTx.putOperationalData(BAR_PATH, new TopLevelListBuilder().setKey(BAR_KEY).build());
Future<RpcResult<TransactionStatus>> fooFuture = fooTx.commit();
Future<RpcResult<TransactionStatus>> barFuture = barTx.commit();
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesKey;
+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.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ContainerWithUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUsesKey;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import com.google.common.util.concurrent.SettableFuture;
-/*
- * FIXME: THis test should be moved to compat test-suite and rewriten
- * to use sal-test-model
+/**
+ * FIXME: THis test should be moved to compat test-suite
*/
@SuppressWarnings("deprecation")
public class WildcardedDataChangeListenerTest extends AbstractDataServiceTest {
- private static final NodeKey NODE_0_KEY = new NodeKey(new NodeId("test:0"));
- private static final NodeKey NODE_1_KEY = new NodeKey(new NodeId("test:1"));
+ private static final TopLevelListKey TOP_LEVEL_LIST_0_KEY = new TopLevelListKey("test:0");
+ private static final TopLevelListKey TOP_LEVEL_LIST_1_KEY = new TopLevelListKey("test:1");
- public static final InstanceIdentifier<Flow> DEEP_WILDCARDED_PATH = InstanceIdentifier.builder(Nodes.class)
- .child(Node.class) //
- .augmentation(FlowCapableNode.class) //
- .child(Table.class) //
- .child(Flow.class) //
+ protected static final InstanceIdentifier<ListViaUses> DEEP_WILDCARDED_PATH = InstanceIdentifier
+ .builder(Top.class)
+ .child(TopLevelList.class) //
+ .augmentation(TreeComplexUsesAugment.class) //
+ .child(ListViaUses.class) //
.build();
- private static final TableKey TABLE_0_KEY = new TableKey((short) 0);
- private static final TableFeaturesKey TABLE_FEATURES_KEY = new TableFeaturesKey((short) 0);
-
- private static final InstanceIdentifier<Table> NODE_0_TABLE_PATH = InstanceIdentifier.builder(Nodes.class)
- .child(Node.class, NODE_0_KEY) //
- .augmentation(FlowCapableNode.class) //
- .child(Table.class, TABLE_0_KEY) //
+ private static final InstanceIdentifier<TreeComplexUsesAugment> NODE_0_TCU_PATH = InstanceIdentifier
+ .builder(Top.class)
+ .child(TopLevelList.class, TOP_LEVEL_LIST_0_KEY) //
+ .augmentation(TreeComplexUsesAugment.class) //
.build();
- private static final InstanceIdentifier<Table> NODE_1_TABLE_PATH = InstanceIdentifier.builder(Nodes.class)
- .child(Node.class, NODE_1_KEY) //
- .augmentation(FlowCapableNode.class) //
- .child(Table.class, TABLE_0_KEY) //
+ private static final InstanceIdentifier<TreeComplexUsesAugment> NODE_1_TCU_PATH = InstanceIdentifier
+ .builder(Top.class)
+ .child(TopLevelList.class, TOP_LEVEL_LIST_1_KEY) //
+ .augmentation(TreeComplexUsesAugment.class) //
.build();
- private static final FlowKey FLOW_KEY = new FlowKey(new FlowId("test"));
- private static final InstanceIdentifier<Flow> NODE_0_FLOW_PATH = NODE_0_TABLE_PATH.child(Flow.class, FLOW_KEY);
+ private static final ListViaUsesKey LIST_VIA_USES_KEY = new ListViaUsesKey("test");
+
+ private static final InstanceIdentifier<ListViaUses> NODE_0_LVU_PATH = NODE_0_TCU_PATH.child(ListViaUses.class, LIST_VIA_USES_KEY);
- private static final InstanceIdentifier<Flow> NODE_1_FLOW_PATH = NODE_1_TABLE_PATH.child(Flow.class, FLOW_KEY);
+ private static final InstanceIdentifier<ListViaUses> NODE_1_LVU_PATH = NODE_1_TCU_PATH.child(ListViaUses.class, LIST_VIA_USES_KEY);
- private static final InstanceIdentifier<TableFeatures> NODE_0_TABLE_FEATURES_PATH =
- NODE_0_TABLE_PATH.child(TableFeatures.class, TABLE_FEATURES_KEY);
+ private static final InstanceIdentifier<ContainerWithUses> NODE_0_CWU_PATH =
+ NODE_0_TCU_PATH.child(ContainerWithUses.class);
- private static final TableFeatures TABLE_FEATURES = new TableFeaturesBuilder()//
- .setKey(TABLE_FEATURES_KEY) //
- .setName("Foo") //
- .setMaxEntries(1000L) //
+ private static final ContainerWithUses CWU= new ContainerWithUsesBuilder()//
+ .setLeafFromGrouping("some container value") //
.build();
- private static final Flow FLOW = new FlowBuilder() //
- .setKey(FLOW_KEY) //
- .setBarrier(true) //
- .setStrict(true) //
+ private static final ListViaUses LVU = new ListViaUsesBuilder() //
+ .setKey(LIST_VIA_USES_KEY) //
+ .setName("john")
.build();
@Test
- public void testSepareteWrites() throws InterruptedException, TimeoutException, ExecutionException {
+ public void testSeparateWrites() throws InterruptedException, TimeoutException, ExecutionException {
DataProviderService dataBroker = testContext.getBindingDataBroker();
final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> eventFuture = SettableFuture.create();
dataBroker.registerDataChangeListener(DEEP_WILDCARDED_PATH, new DataChangeListener() {
-
@Override
public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> dataChangeEvent) {
eventFuture.set(dataChangeEvent);
});
DataModificationTransaction transaction = dataBroker.beginTransaction();
- transaction.putOperationalData(NODE_0_TABLE_FEATURES_PATH, TABLE_FEATURES);
- transaction.putOperationalData(NODE_0_FLOW_PATH, FLOW);
- transaction.putOperationalData(NODE_1_FLOW_PATH, FLOW);
+ transaction.putOperationalData(NODE_0_CWU_PATH, CWU);
+ transaction.putOperationalData(NODE_0_LVU_PATH, LVU);
+ transaction.putOperationalData(NODE_1_LVU_PATH, LVU);
transaction.commit().get();
DataChangeEvent<InstanceIdentifier<?>, DataObject> event = eventFuture.get(1000, TimeUnit.MILLISECONDS);
final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> eventFuture = SettableFuture.create();
dataBroker.registerDataChangeListener(DEEP_WILDCARDED_PATH, new DataChangeListener() {
-
@Override
public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> dataChangeEvent) {
eventFuture.set(dataChangeEvent);
}
});
- DataModificationTransaction tableTx = dataBroker.beginTransaction();
- tableTx.putOperationalData(NODE_0_TABLE_FEATURES_PATH, TABLE_FEATURES);
- tableTx.commit().get();
+ DataModificationTransaction cwuTx = dataBroker.beginTransaction();
+ cwuTx.putOperationalData(NODE_0_CWU_PATH, CWU);
+ cwuTx.commit().get();
assertFalse(eventFuture.isDone());
- DataModificationTransaction flowTx = dataBroker.beginTransaction();
+ DataModificationTransaction lvuTx = dataBroker.beginTransaction();
- Table table = new TableBuilder() //
- .setKey(TABLE_0_KEY) //
- .setFlow(Collections.singletonList(FLOW)) //
- .build();
+ TreeComplexUsesAugment tcua = new TreeComplexUsesAugmentBuilder()
+ .setListViaUses(Collections.singletonList(LVU)).build();
- flowTx.putOperationalData(NODE_0_TABLE_PATH, table);
- flowTx.putOperationalData(NODE_1_FLOW_PATH, FLOW);
- flowTx.commit().get();
+ lvuTx.putOperationalData(NODE_0_TCU_PATH, tcua);
+ lvuTx.putOperationalData(NODE_1_LVU_PATH, LVU);
+ lvuTx.commit().get();
validateEvent(eventFuture.get(1000, TimeUnit.MILLISECONDS));
}
// We wrote initial state NODE_0_FLOW
DataModificationTransaction transaction = dataBroker.beginTransaction();
- transaction.putOperationalData(NODE_0_FLOW_PATH, FLOW);
+ transaction.putOperationalData(NODE_0_LVU_PATH, LVU);
transaction.commit().get();
// We registered DataChangeListener
assertFalse(eventFuture.isDone());
DataModificationTransaction secondTx = dataBroker.beginTransaction();
- secondTx.putOperationalData(NODE_0_FLOW_PATH, FLOW);
- secondTx.putOperationalData(NODE_1_FLOW_PATH, FLOW);
+ secondTx.putOperationalData(NODE_0_LVU_PATH, LVU);
+ secondTx.putOperationalData(NODE_1_LVU_PATH, LVU);
secondTx.commit().get();
DataChangeEvent<InstanceIdentifier<?>, DataObject> event = (eventFuture.get(1000, TimeUnit.MILLISECONDS));
assertNotNull(event);
// Data change should contains NODE_1 Flow - which was added
- assertTrue(event.getCreatedOperationalData().containsKey(NODE_1_FLOW_PATH));
+ assertTrue(event.getCreatedOperationalData().containsKey(NODE_1_LVU_PATH));
// Data change must not containe NODE_0 Flow which was replaced with same value.
- assertFalse(event.getUpdatedOperationalData().containsKey(NODE_0_FLOW_PATH));
+ assertFalse(event.getUpdatedOperationalData().containsKey(NODE_0_LVU_PATH));
}
private static void validateEvent(final DataChangeEvent<InstanceIdentifier<?>, DataObject> event) {
assertNotNull(event);
- assertTrue(event.getCreatedOperationalData().containsKey(NODE_1_FLOW_PATH));
- assertTrue(event.getCreatedOperationalData().containsKey(NODE_0_FLOW_PATH));
- assertFalse(event.getCreatedOperationalData().containsKey(NODE_0_TABLE_FEATURES_PATH));
+ assertTrue(event.getCreatedOperationalData().containsKey(NODE_1_LVU_PATH));
+ assertTrue(event.getCreatedOperationalData().containsKey(NODE_0_LVU_PATH));
+ assertFalse(event.getCreatedOperationalData().containsKey(NODE_0_CWU_PATH));
}
}
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
import org.opendaylight.controller.sal.binding.test.util.BindingBrokerTestFactory;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.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.TopBuilder;
+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.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
@SuppressWarnings("deprecation")
public class DOMCodecBug02Test extends AbstractDataServiceTest {
- private static final InstanceIdentifier<Nodes> NODES_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
+ private static final InstanceIdentifier<Top> TOP_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
.toInstance();
/**
.submit(new Callable<Future<RpcResult<TransactionStatus>>>() {
@Override
public Future<RpcResult<TransactionStatus>> call() throws Exception {
- NodesBuilder nodesBuilder = new NodesBuilder();
- nodesBuilder.setNode(Collections.<Node> emptyList());
+ TopBuilder topBuilder = new TopBuilder();
+ topBuilder.setTopLevelList(Collections.<TopLevelList> emptyList());
DataModificationTransaction transaction = baDataService.beginTransaction();
- transaction.putOperationalData(NODES_INSTANCE_ID_BA, nodesBuilder.build());
+ transaction.putOperationalData(TOP_INSTANCE_ID_BA, topBuilder.build());
return transaction.commit();
}
});
RpcResult<TransactionStatus> result = future.get().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Nodes nodes = checkForNodes();
- assertNotNull(nodes);
+ Top top = checkForTop();
+ assertNotNull(top);
}
- private Nodes checkForNodes() {
- return (Nodes) baDataService.readOperationalData(NODES_INSTANCE_ID_BA);
+ private Top checkForTop() {
+ return (Top) baDataService.readOperationalData(TOP_INSTANCE_ID_BA);
}
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.SupportedActions;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.SupportedActionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.supported.actions.ActionType;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.supported.actions.ActionTypeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.SupportType;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.CustomEnum;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.Cont2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.Cont2Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.cont2.Contlist1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.cont2.Contlist1Builder;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
@SuppressWarnings("deprecation")
public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataChangeListener {
- private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
- private static final String NODE_ID = "openflow:1";
+ private static final QName TOP_LEVEL_LIST_NAME_QNAME = QName.create(TopLevelList.QNAME, "name");
+ private static final String TOP_LEVEL_LIST_NAME = "tll:foo";
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey(TOP_LEVEL_LIST_NAME);
- private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
- NODE_ID);
+ private static final Map<QName, Object> TLL_KEY_BI = Collections.<QName, Object> singletonMap(TOP_LEVEL_LIST_NAME_QNAME,
+ TOP_LEVEL_LIST_NAME);
- private static final InstanceIdentifier<Nodes> NODES_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
+ private static final InstanceIdentifier<Top> TOP_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
.toInstance();
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = NODES_INSTANCE_ID_BA.child(Node.class, NODE_KEY);
+ private static final InstanceIdentifier<TopLevelList> TLL_INSTANCE_ID_BA = TOP_INSTANCE_ID_BA.child(TopLevelList.class, TLL_KEY);
- private static final InstanceIdentifier<SupportedActions> SUPPORTED_ACTIONS_INSTANCE_ID_BA = //
- NODES_INSTANCE_ID_BA.builder() //
- .child(Node.class, NODE_KEY) //
- .augmentation(FlowCapableNode.class) //
- .child(SupportedActions.class)
+ private static final InstanceIdentifier<Cont2> CONT2_INSTANCE_ID_BA = //
+ TOP_INSTANCE_ID_BA.builder() //
+ .child(TopLevelList.class, TLL_KEY) //
+ .augmentation(TllComplexAugment.class) //
+ .child(Cont2.class)
.toInstance();
- private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier NODE_INSTANCE_ID_BI = //
+ private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier TLL_INSTANCE_ID_BI = //
org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder() //
- .node(Nodes.QNAME) //
- .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
+ .node(Top.QNAME) //
+ .nodeWithKey(TopLevelList.QNAME, TLL_KEY_BI) //
.toInstance();
- private static final QName SUPPORTED_ACTIONS_QNAME = QName.create(FlowCapableNode.QNAME, SupportedActions.QNAME.getLocalName());
+ private static final QName CONT2_QNAME = QName.create(TllComplexAugment.QNAME, Cont2.QNAME.getLocalName());
- private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier SUPPORTED_ACTIONS_INSTANCE_ID_BI = //
+ private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier CONT2_INSTANCE_ID_BI = //
org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder() //
- .node(Nodes.QNAME) //
- .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
- .node(SUPPORTED_ACTIONS_QNAME) //
+ .node(Top.QNAME) //
+ .nodeWithKey(TopLevelList.QNAME, TLL_KEY_BI) //
+ .node(CONT2_QNAME) //
.toInstance();
private final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> receivedChangeEvent = SettableFuture.create();
public void testAugmentSerialization() throws Exception {
- baDataService.registerDataChangeListener(NODES_INSTANCE_ID_BA, this);
+ baDataService.registerDataChangeListener(TOP_INSTANCE_ID_BA, this);
- NodeBuilder nodeBuilder = new NodeBuilder();
- nodeBuilder.setId(new NodeId(NODE_ID));
- nodeBuilder.setKey(NODE_KEY);
+ TopLevelListBuilder tllBuilder = new TopLevelListBuilder();
+ tllBuilder.setKey(TLL_KEY);
DataModificationTransaction transaction = baDataService.beginTransaction();
- FlowCapableNodeBuilder fnub = new FlowCapableNodeBuilder();
- fnub.setHardware("Hardware Foo");
- fnub.setManufacturer("Manufacturer Foo");
- fnub.setSerialNumber("Serial Foo");
- fnub.setDescription("Description Foo");
- fnub.setSoftware("JUnit emulated");
- FlowCapableNode fnu = fnub.build();
- nodeBuilder.addAugmentation(FlowCapableNode.class, fnu);
- Node original = nodeBuilder.build();
- transaction.putOperationalData(NODE_INSTANCE_ID_BA, original);
+ TllComplexAugmentBuilder tllcab = new TllComplexAugmentBuilder();
+ tllcab.setAttrStr1("Hardware Foo");
+ tllcab.setAttrStr2("Manufacturer Foo");
+ tllcab.setAttrStr3("Serial Foo");
+ tllcab.setAttrStr4("Description Foo");
+ TllComplexAugment tlca = tllcab.build();
+ tllBuilder.addAugmentation(TllComplexAugment.class, tlca);
+ TopLevelList original = tllBuilder.build();
+ transaction.putOperationalData(TLL_INSTANCE_ID_BA, original);
RpcResult<TransactionStatus> result = transaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
DataChangeEvent<InstanceIdentifier<?>, DataObject> potential = receivedChangeEvent.get(1000,TimeUnit.MILLISECONDS);
assertNotNull(potential);
- verifyNodes((Nodes) potential.getUpdatedOperationalSubtree(),original);
- assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
- Nodes nodes = checkForNodes();
- verifyNodes(nodes,original);
+ verifyTll((Top) potential.getUpdatedOperationalSubtree(),original);
+ assertBindingIndependentVersion(TLL_INSTANCE_ID_BI);
+ Top top = checkForTop();
+ verifyTll(top,original);
testAddingNodeConnector();
- testNodeRemove();
+ testTllRemove();
}
public void testAugmentNestedSerialization() throws Exception {
DataModificationTransaction transaction = baDataService.beginTransaction();
- SupportedActionsBuilder actions = new SupportedActionsBuilder();
- ActionTypeBuilder action = new ActionTypeBuilder();
- action.setAction("foo-action");
- action.setSupportState(SupportType.Native);
- List<ActionType> actionTypes = Collections.singletonList(action.build());
- actions.setActionType(actionTypes );
+ Cont2Builder cont2b = new Cont2Builder();
+ Contlist1Builder cl1b = new Contlist1Builder();
+ cl1b.setAttrStr("foo-action");
+ cl1b.setAttrEnum(CustomEnum.Type1);
+ List<Contlist1> contlists = Collections.singletonList(cl1b.build());
+ cont2b.setContlist1(contlists);
- transaction.putOperationalData(SUPPORTED_ACTIONS_INSTANCE_ID_BA, actions.build());
+ transaction.putOperationalData(CONT2_INSTANCE_ID_BA, cont2b.build());
RpcResult<TransactionStatus> putResult = transaction.commit().get();
assertNotNull(putResult);
assertEquals(TransactionStatus.COMMITED, putResult.getResult());
- SupportedActions readedTable = (SupportedActions) baDataService.readOperationalData(SUPPORTED_ACTIONS_INSTANCE_ID_BA);
+ Cont2 readedTable = (Cont2) baDataService.readOperationalData(CONT2_INSTANCE_ID_BA);
assertNotNull(readedTable);
- CompositeNode biSupportedActions = biDataService.readOperationalData(SUPPORTED_ACTIONS_INSTANCE_ID_BI);
+ CompositeNode biSupportedActions = biDataService.readOperationalData(CONT2_INSTANCE_ID_BI);
assertNotNull(biSupportedActions);
}
private void testAddingNodeConnector() throws Exception {
-
- NodeConnectorId ncId = new NodeConnectorId("openflow:1:bar");
- NodeConnectorKey nodeKey = new NodeConnectorKey(ncId );
- InstanceIdentifier<NodeConnector> ncInstanceId = NODE_INSTANCE_ID_BA.child(NodeConnector.class, nodeKey);
- NodeConnectorBuilder ncBuilder = new NodeConnectorBuilder();
- ncBuilder.setId(ncId);
- ncBuilder.setKey(nodeKey);
- NodeConnector connector = ncBuilder.build();
+ NestedListKey nlKey = new NestedListKey("test:0:0");
+ InstanceIdentifier<NestedList> ncInstanceId = TLL_INSTANCE_ID_BA.child(NestedList.class, nlKey);
+ NestedListBuilder nlBuilder = new NestedListBuilder();
+ nlBuilder.setKey(nlKey);
+ NestedList nestedList = nlBuilder.build();
DataModificationTransaction transaction = baDataService.beginTransaction();
- transaction.putOperationalData(ncInstanceId, connector);
+ transaction.putOperationalData(ncInstanceId, nestedList);
RpcResult<TransactionStatus> result = transaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Node node = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
- assertNotNull(node);
- assertNotNull(node.getNodeConnector());
- assertFalse(node.getNodeConnector().isEmpty());
- NodeConnector readedNc = node.getNodeConnector().get(0);
- assertNotNull(readedNc);
+ TopLevelList tll = (TopLevelList) baDataService.readOperationalData(TLL_INSTANCE_ID_BA);
+ assertNotNull(tll);
+ assertNotNull(tll.getNestedList());
+ assertFalse(tll.getNestedList().isEmpty());
+ NestedList readedNl = tll.getNestedList().get(0);
+ assertNotNull(readedNl);
}
- private void testNodeRemove() throws Exception {
+ private void testTllRemove() throws Exception {
DataModificationTransaction transaction = baDataService.beginTransaction();
- transaction.removeOperationalData(NODE_INSTANCE_ID_BA);
+ transaction.removeOperationalData(TLL_INSTANCE_ID_BA);
RpcResult<TransactionStatus> result = transaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Node node = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
- assertNull(node);
+ TopLevelList tll = (TopLevelList) baDataService.readOperationalData(TLL_INSTANCE_ID_BA);
+ assertNull(tll);
}
- private void verifyNodes(final Nodes nodes,final Node original) {
- assertNotNull(nodes);
- assertNotNull(nodes.getNode());
- assertEquals(1, nodes.getNode().size());
- Node readedNode = nodes.getNode().get(0);
- assertEquals(original.getId(), readedNode.getId());
+ private void verifyTll(final Top top,final TopLevelList original) {
+ assertNotNull(top);
+ assertNotNull(top.getTopLevelList());
+ assertEquals(1, top.getTopLevelList().size());
+ TopLevelList readedNode = top.getTopLevelList().get(0);
+ assertEquals(original.getName(), readedNode.getName());
assertEquals(original.getKey(), readedNode.getKey());
- FlowCapableNode fnu = original.getAugmentation(FlowCapableNode.class);
- FlowCapableNode readedAugment = readedNode.getAugmentation(FlowCapableNode.class);
+ TllComplexAugment fnu = original.getAugmentation(TllComplexAugment.class);
+ TllComplexAugment readedAugment = readedNode.getAugmentation(TllComplexAugment.class);
assertNotNull(fnu);
- assertEquals(fnu.getDescription(), readedAugment.getDescription());
- assertEquals(fnu.getSerialNumber(), readedAugment.getSerialNumber());
+ assertEquals(fnu.getAttrStr2(), readedAugment.getAttrStr2());
+ assertEquals(fnu.getAttrStr3(), readedAugment.getAttrStr3());
}
assertNotNull(node);
}
- private Nodes checkForNodes() {
- return (Nodes) baDataService.readOperationalData(NODES_INSTANCE_ID_BA);
+ private Top checkForTop() {
+ return (Top) baDataService.readOperationalData(TOP_INSTANCE_ID_BA);
}
@Override
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsData;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsDataBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.statistics.FlowStatisticsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.List11SimpleAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.List11SimpleAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Key;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@SuppressWarnings("deprecation")
public class DeleteNestedAugmentationListenParentTest extends AbstractDataServiceTest {
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId("foo"));
+ private static final TopLevelListKey FOO_KEY = new TopLevelListKey("foo");
- private static final TableKey TABLE_KEY = new TableKey((short) 0);
+ private static final List1Key LIST1_KEY = new List1Key("one");
- private static final FlowKey FLOW_KEY = new FlowKey(new FlowId("100"));
+ private static final List11Key LIST11_KEY = new List11Key(100);
- private static final InstanceIdentifier<FlowCapableNode> NODE_AUGMENT_PATH = InstanceIdentifier.builder(Nodes.class)
- .child(Node.class,NODE_KEY)
- .augmentation(FlowCapableNode.class)
+ private static final InstanceIdentifier<TllComplexAugment> TLL_COMPLEX_AUGMENT_PATH = InstanceIdentifier.builder(Top.class)
+ .child(TopLevelList.class,FOO_KEY)
+ .augmentation(TllComplexAugment.class)
.build();
- private static final InstanceIdentifier<Flow> FLOW_PATH = NODE_AUGMENT_PATH.builder()
- .child(Table.class,TABLE_KEY)
- .child(Flow.class,FLOW_KEY)
+ private static final InstanceIdentifier<List11> LIST11_PATH = TLL_COMPLEX_AUGMENT_PATH.builder()
+ .child(List1.class,LIST1_KEY)
+ .child(List11.class,LIST11_KEY)
.build();
public void deleteChildListenParent() throws InterruptedException, ExecutionException {
DataModificationTransaction initTx = baDataService.beginTransaction();
- initTx.putOperationalData(FLOW_PATH, flow());
+ initTx.putOperationalData(LIST11_PATH, createList11());
initTx.commit().get();
final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> event = SettableFuture.create();
- baDataService.registerDataChangeListener(FLOW_PATH, new DataChangeListener() {
+ baDataService.registerDataChangeListener(LIST11_PATH, new DataChangeListener() {
@Override
public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
});
DataModificationTransaction deleteTx = baDataService.beginTransaction();
- deleteTx.removeOperationalData(FLOW_PATH.augmentation(FlowStatisticsData.class));
+ deleteTx.removeOperationalData(LIST11_PATH.augmentation(List11SimpleAugment.class));
deleteTx.commit().get();
DataChangeEvent<InstanceIdentifier<?>, DataObject> receivedEvent = event.get();
- assertFalse(receivedEvent.getRemovedOperationalData().contains(NODE_AUGMENT_PATH));
+ assertFalse(receivedEvent.getRemovedOperationalData().contains(TLL_COMPLEX_AUGMENT_PATH));
}
- private Flow flow() {
- FlowBuilder builder = new FlowBuilder()
- .setKey(FLOW_KEY)
- .addAugmentation(FlowStatisticsData.class,new FlowStatisticsDataBuilder()
- .setFlowStatistics(new FlowStatisticsBuilder().build())
- .build())
- .setBarrier(true)
- .setMatch(new MatchBuilder()
- .build())
- ;
+ private List11 createList11() {
+ List11Builder builder = new List11Builder()
+ .setKey(LIST11_KEY)
+ .addAugmentation(List11SimpleAugment.class,new List11SimpleAugmentBuilder()
+ .setAttrStr2("bad").build())
+ .setAttrStr("good");
return builder.build();
}
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
import org.junit.Test;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PopMplsActionCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.pop.mpls.action._case.PopMplsActionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanIdBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.BitFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Key;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
@SuppressWarnings("deprecation")
public class FlagsSerializationTest extends AbstractDataServiceTest {
-
- private static final String FLOW_ID = "1234";
- private static final short TABLE_ID = (short)0;
- private static final String NODE_ID = "node:1";
-
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
- private static final FlowKey FLOW_KEY = new FlowKey(new FlowId(FLOW_ID));
- private static final TableKey TABLE_KEY = new TableKey(TABLE_ID);
-
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
- .child(Node.class, NODE_KEY).toInstance();
-
- private static final InstanceIdentifier<? extends DataObject> FLOW_INSTANCE_ID_BA = //
- NODE_INSTANCE_ID_BA.builder() //
- .augmentation(FlowCapableNode.class)
- .child(Table.class,TABLE_KEY)
- .child(Flow.class, FLOW_KEY) //
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey("foo");
+ private static final List11Key LIST11_KEY = new List11Key(1234);
+ private static final List1Key LIST1_KEY = new List1Key("1");
+
+ private static final InstanceIdentifier<TopLevelList> TLL_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
+ .child(TopLevelList.class, TLL_KEY).toInstance();
+
+ private static final InstanceIdentifier<? extends DataObject> LIST11_INSTANCE_ID_BA = //
+ TLL_INSTANCE_ID_BA.builder() //
+ .augmentation(TllComplexAugment.class)
+ .child(List1.class,LIST1_KEY)
+ .child(List11.class, LIST11_KEY) //
.toInstance();
- private static final QName FLOW_FLAGS_QNAME = QName.create(Flow.QNAME, "flags");
+ private static final QName LIST11_FLAGS_QNAME = QName.create(List11.QNAME, "flags");
@Test
public void testIndirectGeneration() throws Exception {
- FlowModFlags checkOverlapFlags = new FlowModFlags(true,false,false,false,false);
- ImmutableSet<String> domCheckOverlapFlags = ImmutableSet.<String>of("CHECK_OVERLAP");
+ BitFlags checkOverlapFlags = new BitFlags(true,false,false,false,false);
+ ImmutableSet<String> domCheckOverlapFlags = ImmutableSet.<String>of("FLAG_FIVE");
testFlags(checkOverlapFlags,domCheckOverlapFlags);
- FlowModFlags allFalseFlags = new FlowModFlags(false,false,false,false,false);
+ BitFlags allFalseFlags = new BitFlags(false,false,false,false,false);
ImmutableSet<String> domAllFalseFlags = ImmutableSet.<String>of();
testFlags(allFalseFlags,domAllFalseFlags);
- FlowModFlags allTrueFlags = new FlowModFlags(true,true,true,true,true);
- ImmutableSet<String> domAllTrueFlags = ImmutableSet.<String>of("CHECK_OVERLAP","NO_BYT_COUNTS", "NO_PKT_COUNTS", "RESET_COUNTS", "SEND_FLOW_REM");
+ BitFlags allTrueFlags = new BitFlags(true,true,true,true,true);
+ ImmutableSet<String> domAllTrueFlags = ImmutableSet.<String>of("FLAG_ONE","FLAG_TWO","FLAG_THREE","FLAG_FOUR","FLAG_FIVE");
testFlags(allTrueFlags,domAllTrueFlags);
testFlags(null,null);
}
- private void testFlags(final FlowModFlags flagsToTest, final ImmutableSet<String> domFlags) throws Exception {
- Flow flow = createFlow(flagsToTest);
- assertNotNull(flow);
+ private void testFlags(final BitFlags flagsToTest, final ImmutableSet<String> domFlags) throws Exception {
+ List11 list11 = createList11(flagsToTest);
+ assertNotNull(list11);
- CompositeNode domFlow = biDataService.readConfigurationData(mappingService.toDataDom(FLOW_INSTANCE_ID_BA));
+ CompositeNode domList11 = biDataService.readConfigurationData(mappingService.toDataDom(LIST11_INSTANCE_ID_BA));
- assertNotNull(domFlow);
- org.opendaylight.yangtools.yang.data.api.Node<?> readedFlags = domFlow.getFirstSimpleByName(FLOW_FLAGS_QNAME);
+ assertNotNull(domList11);
+ org.opendaylight.yangtools.yang.data.api.Node<?> readedFlags = domList11.getFirstSimpleByName(LIST11_FLAGS_QNAME);
if(domFlags != null) {
assertNotNull(readedFlags);
} else {
assertNull(readedFlags);
}
- assertEquals(flagsToTest, flow.getFlags());
+ assertEquals(flagsToTest, list11.getFlags());
DataModificationTransaction transaction = baDataService.beginTransaction();
- transaction.removeConfigurationData(FLOW_INSTANCE_ID_BA);
+ transaction.removeConfigurationData(LIST11_INSTANCE_ID_BA);
RpcResult<TransactionStatus> result = transaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
}
- private Flow createFlow(final FlowModFlags flagsToTest) throws Exception {
+ private List11 createList11(final BitFlags flagsToTest) throws Exception {
DataModificationTransaction modification = baDataService.beginTransaction();
- FlowBuilder flow = new FlowBuilder();
- MatchBuilder match = new MatchBuilder();
- VlanMatchBuilder vlanBuilder = new VlanMatchBuilder();
- VlanIdBuilder vlanIdBuilder = new VlanIdBuilder();
- VlanId vlanId = new VlanId(10);
- vlanBuilder.setVlanId(vlanIdBuilder.setVlanId(vlanId).build());
- match.setVlanMatch(vlanBuilder.build());
-
- flow.setKey(FLOW_KEY);
- flow.setMatch(match.build());
-
- flow.setFlags(flagsToTest);
-
- InstructionsBuilder instructions = new InstructionsBuilder();
- InstructionBuilder instruction = new InstructionBuilder();
-
- instruction.setOrder(10);
- ApplyActionsBuilder applyActions = new ApplyActionsBuilder();
- List<Action> actionList = new ArrayList<>();
- PopMplsActionBuilder popMplsAction = new PopMplsActionBuilder();
- popMplsAction.setEthernetType(34);
- actionList.add(new ActionBuilder().setAction(new PopMplsActionCaseBuilder().setPopMplsAction(popMplsAction.build()).build()).setOrder(10).build());
-
- applyActions.setAction(actionList );
-
- instruction.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(applyActions.build()).build());
+ List11Builder list11b = new List11Builder();
+ list11b.setKey(LIST11_KEY);
+ list11b.setAttrStr("list:1:1");
- List<Instruction> instructionList = Collections.<Instruction>singletonList(instruction.build());
- instructions.setInstruction(instructionList );
+ list11b.setFlags(flagsToTest);
- flow.setInstructions(instructions.build());
- modification.putConfigurationData(FLOW_INSTANCE_ID_BA, flow.build());
+ modification.putConfigurationData(LIST11_INSTANCE_ID_BA, list11b.build());
RpcResult<TransactionStatus> ret = modification.commit().get();
assertNotNull(ret);
assertEquals(TransactionStatus.COMMITED, ret.getResult());
- return (Flow) baDataService.readConfigurationData(FLOW_INSTANCE_ID_BA);
+ return (List11) baDataService.readConfigurationData(LIST11_INSTANCE_ID_BA);
}
}
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.NestedListSimpleAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.NestedListSimpleAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugmentBuilder;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
@SuppressWarnings("deprecation")
public class PutAugmentationTest extends AbstractDataServiceTest implements DataChangeListener {
- private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
- private static final String NODE_ID = "openflow:1";
+ private static final QName TLL_NAME_QNAME = QName.create(TopLevelList.QNAME, "name");
+ private static final String TLL_NAME = "foo";
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey(TLL_NAME);
- private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
- NODE_ID);
+ private static final Map<QName, Object> TLL_KEY_BI = Collections.<QName, Object> singletonMap(TLL_NAME_QNAME,
+ TLL_NAME);
- private static final InstanceIdentifier<Nodes> NODES_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
+ private static final InstanceIdentifier<Top> TOP_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
.toInstance();
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = //
- NODES_INSTANCE_ID_BA.builder() //
- .child(Node.class, NODE_KEY).toInstance();
+ private static final InstanceIdentifier<TopLevelList> TLL_INSTANCE_ID_BA = //
+ TOP_INSTANCE_ID_BA.builder() //
+ .child(TopLevelList.class, TLL_KEY).toInstance();
- private static final InstanceIdentifier<FlowCapableNode> ALL_FLOW_CAPABLE_NODES = //
- NODES_INSTANCE_ID_BA.builder() //
- .child(Node.class) //
- .augmentation(FlowCapableNode.class) //
+ private static final InstanceIdentifier<TllComplexAugment> ALL_TCA = //
+ TOP_INSTANCE_ID_BA.builder() //
+ .child(TopLevelList.class) //
+ .augmentation(TllComplexAugment.class) //
.build();
- private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier NODE_INSTANCE_ID_BI = //
+ private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier TLL_INSTANCE_ID_BI = //
org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder() //
- .node(Nodes.QNAME) //
- .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
+ .node(Top.QNAME) //
+ .nodeWithKey(TopLevelList.QNAME, TLL_KEY_BI) //
.toInstance();
- private static final InstanceIdentifier<FlowCapableNode> FLOW_AUGMENTATION_PATH =
- NODE_INSTANCE_ID_BA.builder() //
- .augmentation(FlowCapableNode.class) //
+ private static final InstanceIdentifier<TllComplexAugment> TCA_AUGMENTATION_PATH =
+ TLL_INSTANCE_ID_BA.builder() //
+ .augmentation(TllComplexAugment.class) //
.build();
private SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> lastReceivedChangeEvent;
@Ignore
public void putNodeAndAugmentation() throws Exception {
lastReceivedChangeEvent = SettableFuture.create();
- baDataService.registerDataChangeListener(ALL_FLOW_CAPABLE_NODES, this);
+ baDataService.registerDataChangeListener(ALL_TCA, this);
- NodeBuilder nodeBuilder = new NodeBuilder();
- nodeBuilder.setId(new NodeId(NODE_ID));
- nodeBuilder.setKey(NODE_KEY);
+ TopLevelListBuilder nodeBuilder = new TopLevelListBuilder();
+ nodeBuilder.setKey(TLL_KEY);
DataModificationTransaction baseTransaction = baDataService.beginTransaction();
- baseTransaction.putOperationalData(NODE_INSTANCE_ID_BA, nodeBuilder.build());
+ baseTransaction.putOperationalData(TLL_INSTANCE_ID_BA, nodeBuilder.build());
RpcResult<TransactionStatus> result = baseTransaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Node node = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
- assertNotNull(node);
- assertEquals(NODE_KEY, node.getKey());
-
- FlowCapableNodeBuilder fnub = new FlowCapableNodeBuilder();
- fnub.setHardware("Hardware Foo");
- fnub.setManufacturer("Manufacturer Foo");
- fnub.setSerialNumber("Serial Foo");
- fnub.setDescription("Description Foo");
- fnub.setSoftware("JUnit emulated");
- FlowCapableNode fnu = fnub.build();
- InstanceIdentifier<FlowCapableNode> augmentIdentifier = NODE_INSTANCE_ID_BA
- .augmentation(FlowCapableNode.class);
+ TopLevelList tll = (TopLevelList) baDataService.readOperationalData(TLL_INSTANCE_ID_BA);
+ assertNotNull(tll);
+ assertEquals(TLL_KEY, tll.getKey());
+
+ TllComplexAugmentBuilder tcab = new TllComplexAugmentBuilder();
+ tcab.setAttrStr1("FooFoo");
+ tcab.setAttrStr2("BarBar");
+ TllComplexAugment tca = tcab.build();
+ InstanceIdentifier<TreeComplexUsesAugment> augmentIdentifier = TLL_INSTANCE_ID_BA
+ .augmentation(TreeComplexUsesAugment.class);
DataModificationTransaction augmentedTransaction = baDataService.beginTransaction();
- augmentedTransaction.putOperationalData(augmentIdentifier, fnu);
+ augmentedTransaction.putOperationalData(augmentIdentifier, tca);
lastReceivedChangeEvent = SettableFuture.create();
DataChangeEvent<InstanceIdentifier<?>, DataObject> potential = lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS);
assertNotNull(potential);
- assertTrue(potential.getCreatedOperationalData().containsKey(FLOW_AUGMENTATION_PATH));
+ assertTrue(potential.getCreatedOperationalData().containsKey(TCA_AUGMENTATION_PATH));
lastReceivedChangeEvent = SettableFuture.create();
- Node augmentedNode = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
- assertNotNull(node);
- assertEquals(NODE_KEY, augmentedNode.getKey());
+ TopLevelList augmentedTll = (TopLevelList) baDataService.readOperationalData(TLL_INSTANCE_ID_BA);
+ assertNotNull(tll);
+ assertEquals(TLL_KEY, augmentedTll.getKey());
System.out.println("Before assertion");
- assertNotNull(augmentedNode.getAugmentation(FlowCapableNode.class));
- FlowCapableNode readedAugmentation = augmentedNode.getAugmentation(FlowCapableNode.class);
- assertEquals(fnu.getDescription(), readedAugmentation.getDescription());
- assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
- testNodeRemove();
- assertTrue(lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS).getRemovedOperationalData().contains(FLOW_AUGMENTATION_PATH));
+ assertNotNull(augmentedTll.getAugmentation(TllComplexAugment.class));
+ TllComplexAugment readedAugmentation = augmentedTll.getAugmentation(TllComplexAugment.class);
+ assertEquals(tca.getAttrStr2(), readedAugmentation.getAttrStr2());
+ assertBindingIndependentVersion(TLL_INSTANCE_ID_BI);
+ testTllRemove();
+ assertTrue(lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS).getRemovedOperationalData().contains(TCA_AUGMENTATION_PATH));
}
@Test
@Ignore
public void putNodeWithAugmentation() throws Exception {
lastReceivedChangeEvent = SettableFuture.create();
- baDataService.registerDataChangeListener(ALL_FLOW_CAPABLE_NODES, this);
-
- NodeBuilder nodeBuilder = new NodeBuilder();
- nodeBuilder.setId(new NodeId(NODE_ID));
- nodeBuilder.setKey(NODE_KEY);
- FlowCapableNodeBuilder fnub = new FlowCapableNodeBuilder();
- fnub.setHardware("Hardware Foo");
- fnub.setManufacturer("Manufacturer Foo");
- fnub.setSerialNumber("Serial Foo");
- fnub.setDescription("Description Foo");
- fnub.setSoftware("JUnit emulated");
- FlowCapableNode fnu = fnub.build();
-
- nodeBuilder.addAugmentation(FlowCapableNode.class, fnu);
+ baDataService.registerDataChangeListener(ALL_TCA, this);
+
+ TopLevelListBuilder nodeBuilder = new TopLevelListBuilder();
+ nodeBuilder.setKey(TLL_KEY);
+ TllComplexAugmentBuilder tcab = new TllComplexAugmentBuilder();
+ tcab.setAttrStr1("FooFoo");
+ tcab.setAttrStr2("BarBar");
+ TllComplexAugment tca = tcab.build();
+
+ nodeBuilder.addAugmentation(TreeComplexUsesAugment.class, tca);
DataModificationTransaction baseTransaction = baDataService.beginTransaction();
- baseTransaction.putOperationalData(NODE_INSTANCE_ID_BA, nodeBuilder.build());
+ baseTransaction.putOperationalData(TLL_INSTANCE_ID_BA, nodeBuilder.build());
RpcResult<TransactionStatus> result = baseTransaction.commit().get();
DataChangeEvent<InstanceIdentifier<?>, DataObject> potential = lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS);
assertNotNull(potential);
- assertTrue(potential.getCreatedOperationalData().containsKey(FLOW_AUGMENTATION_PATH));
+ assertTrue(potential.getCreatedOperationalData().containsKey(TCA_AUGMENTATION_PATH));
lastReceivedChangeEvent = SettableFuture.create();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- FlowCapableNode readedAugmentation = (FlowCapableNode) baDataService.readOperationalData(
- NODE_INSTANCE_ID_BA.augmentation(FlowCapableNode.class));
+ TllComplexAugment readedAugmentation = (TllComplexAugment) baDataService.readOperationalData(
+ TLL_INSTANCE_ID_BA.augmentation(TllComplexAugment.class));
assertNotNull(readedAugmentation);
- assertEquals(fnu.getHardware(), readedAugmentation.getHardware());
+ assertEquals(tca.getAttrStr1(), readedAugmentation.getAttrStr1());
testPutNodeConnectorWithAugmentation();
lastReceivedChangeEvent = SettableFuture.create();
- testNodeRemove();
+ testTllRemove();
- assertTrue(lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS).getRemovedOperationalData().contains(FLOW_AUGMENTATION_PATH));
+ assertTrue(lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS).getRemovedOperationalData().contains(TCA_AUGMENTATION_PATH));
}
private void testPutNodeConnectorWithAugmentation() throws Exception {
- NodeConnectorKey ncKey = new NodeConnectorKey(new NodeConnectorId("test:0:0"));
- InstanceIdentifier<NodeConnector> ncPath = NODE_INSTANCE_ID_BA
- .child(NodeConnector.class, ncKey);
- InstanceIdentifier<FlowCapableNodeConnector> ncAugmentPath = ncPath
- .augmentation(FlowCapableNodeConnector.class);
+ NestedListKey ncKey = new NestedListKey("test:0:0");
+ InstanceIdentifier<NestedList> ncPath = TLL_INSTANCE_ID_BA
+ .child(NestedList.class, ncKey);
+ InstanceIdentifier<NestedListSimpleAugment> ncAugmentPath = ncPath
+ .augmentation(NestedListSimpleAugment.class);
- NodeConnectorBuilder nc = new NodeConnectorBuilder();
+ NestedListBuilder nc = new NestedListBuilder();
nc.setKey(ncKey);
- FlowCapableNodeConnectorBuilder fncb = new FlowCapableNodeConnectorBuilder();
- fncb.setName("Baz");
- nc.addAugmentation(FlowCapableNodeConnector.class, fncb.build());
+ NestedListSimpleAugmentBuilder fncb = new NestedListSimpleAugmentBuilder();
+ fncb.setType("Baz");
+ nc.addAugmentation(NestedListSimpleAugment.class, fncb.build());
DataModificationTransaction baseTransaction = baDataService.beginTransaction();
baseTransaction.putOperationalData(ncPath, nc.build());
RpcResult<TransactionStatus> result = baseTransaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- FlowCapableNodeConnector readedAugmentation = (FlowCapableNodeConnector) baDataService
+ NestedListSimpleAugment readedAugmentation = (NestedListSimpleAugment) baDataService
.readOperationalData(ncAugmentPath);
assertNotNull(readedAugmentation);
- assertEquals(fncb.getName(), readedAugmentation.getName());
+ assertEquals(fncb.getType(), readedAugmentation.getType());
}
- private void testNodeRemove() throws Exception {
+ private void testTllRemove() throws Exception {
DataModificationTransaction transaction = baDataService.beginTransaction();
- transaction.removeOperationalData(NODE_INSTANCE_ID_BA);
+ transaction.removeOperationalData(TLL_INSTANCE_ID_BA);
RpcResult<TransactionStatus> result = transaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Node node = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
- assertNull(node);
+ TopLevelList tll = (TopLevelList) baDataService.readOperationalData(TLL_INSTANCE_ID_BA);
+ assertNull(tll);
}
- private void assertBindingIndependentVersion(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier nodeId) {
- CompositeNode node = biDataService.readOperationalData(nodeId);
- assertNotNull(node);
+ private void assertBindingIndependentVersion(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier tllId) {
+ CompositeNode tll = biDataService.readOperationalData(tllId);
+ assertNotNull(tll);
}
@Override
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+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.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@SuppressWarnings("deprecation")
public class WriteParentListenAugmentTest extends AbstractDataServiceTest {
- private static final String NODE_ID = "node:1";
+ private static final String TLL_NAME = "foo";
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
- .child(Node.class, NODE_KEY).toInstance();
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey(TLL_NAME);
+ private static final InstanceIdentifier<TopLevelList> TLL_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
+ .child(TopLevelList.class, TLL_KEY).toInstance();
- private static final InstanceIdentifier<FlowCapableNode> AUGMENT_WILDCARDED_PATH = InstanceIdentifier
- .builder(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class).toInstance();
+ private static final InstanceIdentifier<TreeComplexUsesAugment> AUGMENT_WILDCARDED_PATH = InstanceIdentifier
+ .builder(Top.class).child(TopLevelList.class).augmentation(TreeComplexUsesAugment.class).toInstance();
- private static final InstanceIdentifier<FlowCapableNode> AUGMENT_NODE_PATH = InstanceIdentifier
- .builder(Nodes.class).child(Node.class, NODE_KEY).augmentation(FlowCapableNode.class).toInstance();
+ private static final InstanceIdentifier<TreeComplexUsesAugment> AUGMENT_TLL_PATH = InstanceIdentifier
+ .builder(Top.class).child(TopLevelList.class, TLL_KEY).augmentation(TreeComplexUsesAugment.class).toInstance();
@Test
public void writeNodeListenAugment() throws Exception {
DataModificationTransaction modification = baDataService.beginTransaction();
- Node node = new NodeBuilder() //
- .setKey(NODE_KEY) //
- .addAugmentation(FlowCapableNode.class, flowCapableNode("one")).build();
- modification.putOperationalData(NODE_INSTANCE_ID_BA, node);
+ TopLevelList tll = new TopLevelListBuilder() //
+ .setKey(TLL_KEY) //
+ .addAugmentation(TreeComplexUsesAugment.class, treeComplexUsesAugment("one")).build();
+ modification.putOperationalData(TLL_INSTANCE_ID_BA, tll);
modification.commit().get();
DataChangeEvent<InstanceIdentifier<?>, DataObject> receivedEvent = event.get(1000, TimeUnit.MILLISECONDS);
- assertTrue(receivedEvent.getCreatedOperationalData().containsKey(AUGMENT_NODE_PATH));
+ assertTrue(receivedEvent.getCreatedOperationalData().containsKey(AUGMENT_TLL_PATH));
dclRegistration.close();
DataModificationTransaction mod2 = baDataService.beginTransaction();
- mod2.putOperationalData(AUGMENT_NODE_PATH, flowCapableNode("two"));
+ mod2.putOperationalData(AUGMENT_TLL_PATH, treeComplexUsesAugment("two"));
mod2.commit().get();
- FlowCapableNode readedAug = (FlowCapableNode) baDataService.readOperationalData(AUGMENT_NODE_PATH);
- assertEquals("two", readedAug.getDescription());
+ TreeComplexUsesAugment readedAug = (TreeComplexUsesAugment) baDataService.readOperationalData(AUGMENT_TLL_PATH);
+ assertEquals("two", readedAug.getContainerWithUses().getLeafFromGrouping());
}
- private FlowCapableNode flowCapableNode(final String description) {
- return new FlowCapableNodeBuilder() //
- .setDescription(description) //
+ private TreeComplexUsesAugment treeComplexUsesAugment(final String value) {
+ return new TreeComplexUsesAugmentBuilder() //
+ .setContainerWithUses(new ContainerWithUsesBuilder().setLeafFromGrouping(value).build()) //
.build();
}
}
\ No newline at end of file
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanIdBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Key;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
@SuppressWarnings("deprecation")
public class WriteParentReadChildTest extends AbstractDataServiceTest {
- private static final String FLOW_ID = "1234";
- private static final short TABLE_ID = (short) 0;
- private static final String NODE_ID = "node:1";
+ private static final int LIST11_ID = 1234;
+ private static final String LIST1_NAME = "bar";
+ private static final String TLL_NAME = "foo";
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
- private static final FlowKey FLOW_KEY = new FlowKey(new FlowId(FLOW_ID));
- private static final TableKey TABLE_KEY = new TableKey(TABLE_ID);
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey(TLL_NAME);
+ private static final List11Key LIST11_KEY = new List11Key(LIST11_ID);
+ private static final List1Key LIST1_KEY = new List1Key(LIST1_NAME);
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
- .child(Node.class, NODE_KEY).toInstance();
+ private static final InstanceIdentifier<TopLevelList> TLL_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
+ .child(TopLevelList.class, TLL_KEY).toInstance();
- private static final InstanceIdentifier<Table> TABLE_INSTANCE_ID_BA = //
- NODE_INSTANCE_ID_BA.builder() //
- .augmentation(FlowCapableNode.class).child(Table.class, TABLE_KEY).build();
+ private static final InstanceIdentifier<List1> LIST1_INSTANCE_ID_BA = //
+ TLL_INSTANCE_ID_BA.builder() //
+ .augmentation(TllComplexAugment.class).child(List1.class, LIST1_KEY).build();
- private static final InstanceIdentifier<? extends DataObject> FLOW_INSTANCE_ID_BA = //
- TABLE_INSTANCE_ID_BA.child(Flow.class, FLOW_KEY);
+ private static final InstanceIdentifier<? extends DataObject> LIST11_INSTANCE_ID_BA = //
+ LIST1_INSTANCE_ID_BA.child(List11.class, LIST11_KEY);
/**
*
* The scenario tests writing parent node, which also contains child items
* @throws Exception
*/
@Test
- public void writeTableReadFlow() throws Exception {
+ public void writeParentReadChild() throws Exception {
DataModificationTransaction modification = baDataService.beginTransaction();
- Flow flow = new FlowBuilder() //
- .setKey(FLOW_KEY) //
- .setMatch(new MatchBuilder() //
- .setVlanMatch(new VlanMatchBuilder() //
- .setVlanId(new VlanIdBuilder() //
- .setVlanId(new VlanId(10)) //
- .build()) //
- .build()) //
- .build()) //
- .setInstructions(new InstructionsBuilder() //
- .setInstruction(ImmutableList.<Instruction>builder() //
- .build()) //
- .build()) //
+ List11 list11 = new List11Builder() //
+ .setKey(LIST11_KEY) //
+ .setAttrStr("primary")
.build();
- Table table = new TableBuilder()
- .setKey(TABLE_KEY)
- .setFlow(ImmutableList.of(flow))
+ List1 list1 = new List1Builder()
+ .setKey(LIST1_KEY)
+ .setList11(ImmutableList.of(list11))
.build();
- modification.putConfigurationData(TABLE_INSTANCE_ID_BA, table);
+ modification.putConfigurationData(LIST1_INSTANCE_ID_BA, list1);
RpcResult<TransactionStatus> ret = modification.commit().get();
assertNotNull(ret);
assertEquals(TransactionStatus.COMMITED, ret.getResult());
- DataObject readedTable = baDataService.readConfigurationData(TABLE_INSTANCE_ID_BA);
- assertNotNull("Readed table should not be nul.", readedTable);
- assertTrue(readedTable instanceof Table);
+ DataObject readList1 = baDataService.readConfigurationData(LIST1_INSTANCE_ID_BA);
+ assertNotNull("Readed table should not be nul.", readList1);
+ assertTrue(readList1 instanceof List1);
- DataObject readedFlow = baDataService.readConfigurationData(FLOW_INSTANCE_ID_BA);
- assertNotNull("Readed flow should not be null.",readedFlow);
- assertTrue(readedFlow instanceof Flow);
- assertEquals(flow, readedFlow);
+ DataObject readList11 = baDataService.readConfigurationData(LIST11_INSTANCE_ID_BA);
+ assertNotNull("Readed flow should not be null.",readList11);
+ assertTrue(readList11 instanceof List11);
+ assertEquals(list11, readList11);
}
}
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
-// FIXME: Migrate to use new Data Broker APIs
+/**
+ * FIXME: Migrate to use new Data Broker APIs
+ */
@SuppressWarnings("deprecation")
public class BrokerIntegrationTest extends AbstractDataServiceTest {
+ private static final TopLevelListKey TLL_FOO_KEY = new TopLevelListKey("foo");
+ private static final TopLevelListKey TLL_BAR_KEY = new TopLevelListKey("bar");
+ private static final TopLevelListKey TLL_BAZ_KEY = new TopLevelListKey("baz");
+ private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.builder(Top.class).build();
+ private static final InstanceIdentifier<TopLevelList> FOO_PATH = TOP_PATH.child(TopLevelList.class, TLL_FOO_KEY);
+ private static final InstanceIdentifier<TopLevelList> BAR_PATH = TOP_PATH.child(TopLevelList.class, TLL_BAR_KEY);
+ private static final InstanceIdentifier<TopLevelList> BAZ_PATH = TOP_PATH.child(TopLevelList.class, TLL_BAZ_KEY);
+
@Test
public void simpleModifyOperation() throws Exception {
- NodeRef node1 = createNodeRef("0");
- DataObject node = baDataService.readConfigurationData(node1.getValue());
- assertNull(node);
- Node nodeData1 = createNode("0");
+ DataObject tllFoo = baDataService.readConfigurationData(FOO_PATH);
+ assertNull(tllFoo);
+ TopLevelList tllFooData = createTll(TLL_FOO_KEY);
DataModificationTransaction transaction = baDataService.beginTransaction();
- transaction.putConfigurationData(node1.getValue(), nodeData1);
+ transaction.putConfigurationData(FOO_PATH, tllFooData);
Future<RpcResult<TransactionStatus>> commitResult = transaction.commit();
assertNotNull(commitResult);
assertNotNull(result.getResult());
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Node readedData = (Node) baDataService.readConfigurationData(node1.getValue());
+ TopLevelList readedData = (TopLevelList) baDataService.readConfigurationData(FOO_PATH);
assertNotNull(readedData);
- assertEquals(nodeData1.getKey(), readedData.getKey());
+ assertEquals(tllFooData.getKey(), readedData.getKey());
- NodeRef nodeFoo = createNodeRef("foo");
- NodeRef nodeBar = createNodeRef("bar");
- Node nodeFooData = createNode("foo");
- Node nodeBarData = createNode("bar");
+ TopLevelList nodeBarData = createTll(TLL_BAR_KEY);
+ TopLevelList nodeBazData = createTll(TLL_BAZ_KEY);
DataModificationTransaction insertMoreTr = baDataService.beginTransaction();
- insertMoreTr.putConfigurationData(nodeFoo.getValue(), nodeFooData);
- insertMoreTr.putConfigurationData(nodeBar.getValue(), nodeBarData);
+ insertMoreTr.putConfigurationData(BAR_PATH, nodeBarData);
+ insertMoreTr.putConfigurationData(BAZ_PATH, nodeBazData);
RpcResult<TransactionStatus> result2 = insertMoreTr.commit().get();
assertNotNull(result2);
assertNotNull(result2.getResult());
assertEquals(TransactionStatus.COMMITED, result.getResult());
- Nodes allNodes = (Nodes) baDataService.readConfigurationData(InstanceIdentifier.builder(Nodes.class)
- .toInstance());
- assertNotNull(allNodes);
- assertNotNull(allNodes.getNode());
- assertEquals(3, allNodes.getNode().size());
+ Top top = (Top) baDataService.readConfigurationData(TOP_PATH);
+ assertNotNull(top);
+ assertNotNull(top.getTopLevelList());
+ assertEquals(3, top.getTopLevelList().size());
/**
* We create transaction no 2
* We remove node 1
*
*/
- removalTransaction.removeConfigurationData(node1.getValue());
+ removalTransaction.removeConfigurationData(BAR_PATH);
/**
* We commit transaction
assertNotNull(result3.getResult());
assertEquals(TransactionStatus.COMMITED, result2.getResult());
- DataObject readedData2 = baDataService.readConfigurationData(node1.getValue());
+ DataObject readedData2 = baDataService.readConfigurationData(BAR_PATH);
assertNull(readedData2);
}
- private static NodeRef createNodeRef(final String string) {
- NodeKey key = new NodeKey(new NodeId(string));
- InstanceIdentifier<Node> path = InstanceIdentifier.builder(Nodes.class).child(Node.class, key)
- .toInstance();
- return new NodeRef(path);
- }
-
- private static Node createNode(final String string) {
- NodeBuilder ret = new NodeBuilder();
- ret.setId(new NodeId(string));
- ret.setKey(new NodeKey(ret.getId()));
+ private static TopLevelList createTll(final TopLevelListKey key) {
+ TopLevelListBuilder ret = new TopLevelListBuilder();
+ ret.setKey(key);
return ret.build();
}
}
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpVersion;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.DecNwTtlCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.dec.nw.ttl._case.DecNwTtl;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.dec.nw.ttl._case.DecNwTtlBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.IpMatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List12Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List12Key;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.SettableFuture;
-// FIXME: Migrate to use new Data Broker APIs
+/**
+ * FIXME: Migrate to use new Data Broker APIs
+ */
@SuppressWarnings("deprecation")
public class ChangeOriginatedInDomBrokerTest extends AbstractDataServiceTest {
- private static final Logger LOG = LoggerFactory.getLogger(ChangeOriginatedInDomBrokerTest.class);
-
- private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
- private static final QName FLOW_ID_QNAME = QName.create(Flow.QNAME, "id");
- private static final QName TABLE_ID_QNAME = QName.create(Table.QNAME, "id");
+ protected static final Logger LOG = LoggerFactory.getLogger(ChangeOriginatedInDomBrokerTest.class);
- private static final String NODE_ID = "node:1";
- private static final FlowId FLOW_ID = new FlowId("1234");
- private static final Short TABLE_ID = Short.valueOf((short) 0);
+ private static final QName TLL_NAME_QNAME = QName.create(TopLevelList.QNAME, "name");
+ private static final QName LIST1_ATTR_STR_QNAME = QName.create(List1.QNAME, "attr-str");
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
- private static final FlowKey FLOW_KEY = new FlowKey(FLOW_ID);
+ private static final String TLL_NAME = "1";
+ private static final int LIST11_ATTR_INT = 1234;
+ private static final String LIST1_ATTR_STR = "foo:foo";
- private final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> modificationCapture = SettableFuture.create();
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey(TLL_NAME);
+ private static final List1Key LIST1_KEY = new List1Key(LIST1_ATTR_STR);
+ private static final List11Key LIST11_KEY = new List11Key(LIST11_ATTR_INT);
- private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
- NODE_ID);
+ protected final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> modificationCapture = SettableFuture.create();
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
- .child(Node.class, NODE_KEY).toInstance();
+ private static final Map<QName, Object> TLL_KEY_BI = Collections.<QName, Object> singletonMap(TLL_NAME_QNAME,
+ TLL_NAME);
- private static final Map<QName, Object> FLOW_KEY_BI = //
- ImmutableMap.<QName, Object> of(FLOW_ID_QNAME, FLOW_ID.getValue());
+ private static final InstanceIdentifier<TopLevelList> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
+ .child(TopLevelList.class, TLL_KEY).toInstance();
- private static final Map<QName, Object> TABLE_KEY_BI = //
- ImmutableMap.<QName, Object> of(TABLE_ID_QNAME, TABLE_ID);;
+ private static final Map<QName, Object> LIST1_KEY_BI = //
+ ImmutableMap.<QName, Object> of(LIST1_ATTR_STR_QNAME, LIST1_ATTR_STR);;
- private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier FLOW_INSTANCE_ID_BI = //
+ private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier LIST1_INSTANCE_ID_BI = //
org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder() //
- .node(Nodes.QNAME) //
- .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
- .nodeWithKey(Table.QNAME, TABLE_KEY_BI) //
- .nodeWithKey(Flow.QNAME, FLOW_KEY_BI) //
+ .node(Top.QNAME) //
+ .nodeWithKey(TopLevelList.QNAME, TLL_KEY_BI) //
+ .nodeWithKey(List1.QNAME, LIST1_KEY_BI) //
.toInstance();
- private static final TableKey TABLE_KEY_BA = new TableKey((short) 0);
- private static final InstanceIdentifier<Flow> FLOWS_PATH_BA = //
+ private static final InstanceIdentifier<List1> LIST1_PATH_BA = //
NODE_INSTANCE_ID_BA.builder() //
- .augmentation(FlowCapableNode.class) //
- .child(Table.class, TABLE_KEY_BA) //
- .child(Flow.class) //
+ .augmentation(TllComplexAugment.class) //
+ .child(List1.class, LIST1_KEY) //
.toInstance();
- private static final InstanceIdentifier<Flow> FLOW_INSTANCE_ID_BA = //
- FLOWS_PATH_BA.firstIdentifierOf(Table.class).child(Flow.class, FLOW_KEY);
-
@Test
public void simpleModifyOperation() throws Exception {
- assertNull(biDataService.readConfigurationData(FLOW_INSTANCE_ID_BI));
+ assertNull(biDataService.readConfigurationData(LIST1_INSTANCE_ID_BI));
registerChangeListener();
- CompositeNode domflow = createTestFlow();
+ CompositeNode domflow = createTestList1();
DataModificationTransaction biTransaction = biDataService.beginTransaction();
- biTransaction.putConfigurationData(FLOW_INSTANCE_ID_BI, domflow);
+ biTransaction.putConfigurationData(LIST1_INSTANCE_ID_BI, domflow);
RpcResult<TransactionStatus> biResult = biTransaction.commit().get();
assertEquals(TransactionStatus.COMMITED, biResult.getResult());
DataChangeEvent<InstanceIdentifier<?>, DataObject> event = modificationCapture.get(1000,TimeUnit.MILLISECONDS);
assertNotNull(event);
LOG.info("Created Configuration :{}",event.getCreatedConfigurationData());
- Flow flow = (Flow) event.getCreatedConfigurationData().get(FLOW_INSTANCE_ID_BA);
- assertNotNull(flow);
- assertNotNull(flow.getMatch());
+ List1 list1 = (List1) event.getCreatedConfigurationData().get(LIST1_PATH_BA);
+ assertNotNull(list1);
+ assertNotNull(list1.getAttrStr());
+ assertNotNull(list1.getList11());
+ assertNotNull(list1.getList12());
assertEquals(TransactionStatus.COMMITED, biResult.getResult());
}
private void registerChangeListener() {
- baDataService.registerDataChangeListener(FLOWS_PATH_BA, new DataChangeListener() {
+ baDataService.registerDataChangeListener(LIST1_PATH_BA, new DataChangeListener() {
@Override
public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
});
}
- private CompositeNode createTestFlow() {
- FlowBuilder flow = new FlowBuilder();
- flow.setKey(FLOW_KEY);
- Short tableId = 0;
- flow.setTableId(tableId);
- MatchBuilder match = new MatchBuilder();
- match.setIpMatch(new IpMatchBuilder().setIpProto(IpVersion.Ipv4).build());
- Ipv4MatchBuilder ipv4Match = new Ipv4MatchBuilder();
- // ipv4Match.setIpv4Destination(new Ipv4Prefix(cliInput.get(4)));
- Ipv4Prefix prefix = new Ipv4Prefix("10.0.0.1/24");
- ipv4Match.setIpv4Destination(prefix);
- Ipv4Match i4m = ipv4Match.build();
- match.setLayer3Match(i4m);
- flow.setMatch(match.build());
-
-
-
- // Create a drop action
- /*
- * Note: We are mishandling drop actions DropAction dropAction = new
- * DropActionBuilder().build(); ActionBuilder ab = new ActionBuilder();
- * ab.setAction(dropAction);
- */
-
- DecNwTtl decNwTtl = new DecNwTtlBuilder().build();
- ActionBuilder ab = new ActionBuilder();
- ActionKey actionKey = new ActionKey(0);
- ab.setKey(actionKey );
- ab.setAction(new DecNwTtlCaseBuilder().setDecNwTtl(decNwTtl).build());
-
- // Add our drop action to a list
- List<Action> actionList = new ArrayList<Action>();
- actionList.add(ab.build());
-
- // Create an Apply Action
- ApplyActionsBuilder aab = new ApplyActionsBuilder();
- aab.setAction(actionList);
-
- // Wrap our Apply Action in an Instruction
- InstructionBuilder ib = new InstructionBuilder();
- ib.setOrder(0);
- ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
-
- // Put our Instruction in a list of Instructions
- InstructionsBuilder isb = new InstructionsBuilder();
- List<Instruction> instructions = new ArrayList<Instruction>();
- instructions.add(ib.build());
- isb.setInstruction(instructions);
-
- // Add our instructions to the flow
- flow.setInstructions(isb.build());
-
- flow.setPriority(2);
- flow.setFlowName("Foo Name");
- CompositeNode domFlow = mappingService.toDataDom(flow.build());
- return domFlow;
+ private CompositeNode createTestList1() {
+ List1Builder l1b = new List1Builder();
+ List11Builder l11b = new List11Builder();
+ List12Builder l12b = new List12Builder();
+ l11b.setKey(LIST11_KEY);
+ l11b.setAttrStr("foo:foo:foo");
+ l12b.setKey(new List12Key(321));
+ l12b.setAttrStr("foo:foo:bar");
+ l1b.setKey(LIST1_KEY);
+ l1b.setList11(ImmutableList.of(l11b.build()));
+ l1b.setList12(ImmutableList.of(l12b.build()));
+ CompositeNode domList1 = mappingService.toDataDom(l1b.build());
+ return domList1;
}
}
*/
package org.opendaylight.controller.sal.binding.test.connect.dom;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
-import java.math.BigInteger;
import java.util.Collections;
import java.util.Map;
import org.opendaylight.controller.sal.binding.test.util.BindingTestContext;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupStatistics;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.statistics.GroupStatistics;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.List11SimpleAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.TllComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.List1Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.aug.grouping.list1.List11Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.top.top.level.list.list1.list1._1.Cont;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
@SuppressWarnings("deprecation")
public class CrossBrokerMountPointTest {
- private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
- private static final String NODE_ID = "node:1";
+ private static final QName TLL_NAME_QNAME = QName.create(TopLevelList.QNAME, "name");
+ private static final String TLL_NAME = "foo:1";
+
+ private static final TopLevelListKey TLL_KEY = new TopLevelListKey(TLL_NAME);
- private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
+ private static final Map<QName, Object> TLL_KEY_BI = Collections.<QName, Object> singletonMap(TLL_NAME_QNAME,
+ TLL_NAME);
- private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
- NODE_ID);
+ private static final InstanceIdentifier<TopLevelList> TLL_INSTANCE_ID_BA = InstanceIdentifier.builder(Top.class) //
+ .child(TopLevelList.class, TLL_KEY).build();
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
- .child(Node.class, NODE_KEY).toInstance();
- private static GroupKey GROUP_KEY = new GroupKey(new GroupId(0L));
+ private static final List1Key LIST1_KEY = new List1Key("foo");
+ private static final List11Key LIST11_KEY = new List11Key(1);
- private static final InstanceIdentifier<GroupStatistics> GROUP_STATISTICS_ID_BA = NODE_INSTANCE_ID_BA
- .builder().augmentation(FlowCapableNode.class) //
- .child(Group.class, GROUP_KEY) //
- .augmentation(NodeGroupStatistics.class) //
- .child(GroupStatistics.class) //
- .toInstance();
+ private static final InstanceIdentifier<Cont> AUG_CONT_ID_BA = TLL_INSTANCE_ID_BA
+ .builder().augmentation(TllComplexAugment.class) //
+ .child(List1.class, LIST1_KEY) //
+ .child(List11.class, LIST11_KEY) //
+ .augmentation(List11SimpleAugment.class) //
+ .child(Cont.class) //
+ .build();
- private static final QName AUGMENTED_GROUP_STATISTICS = QName.create(NodeGroupStatistics.QNAME,
- GroupStatistics.QNAME.getLocalName());
+ private static final QName AUG_CONT = QName.create(List11.QNAME,
+ Cont.QNAME.getLocalName());
- private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier NODE_INSTANCE_ID_BI = //
+ private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier TLL_INSTANCE_ID_BI = //
org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder() //
- .node(Nodes.QNAME) //
- .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
- .toInstance();
+ .node(Top.QNAME) //
+ .nodeWithKey(TopLevelList.QNAME, TLL_KEY_BI) //
+ .build();
private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier GROUP_STATISTICS_ID_BI = org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
//
- .builder(NODE_INSTANCE_ID_BI)
- .nodeWithKey(QName.create(FlowCapableNode.QNAME, "group"), QName.create(FlowCapableNode.QNAME, "group-id"),
- 0L).node(AUGMENTED_GROUP_STATISTICS).toInstance();
+ .builder(TLL_INSTANCE_ID_BI)
+ .nodeWithKey(QName.create(TllComplexAugment.QNAME, "list1"), QName.create(TllComplexAugment.QNAME, "attr-str"),
+ LIST1_KEY.getAttrStr())
+ .nodeWithKey(QName.create(TllComplexAugment.QNAME, "list1-1"), QName.create(TllComplexAugment.QNAME, "attr-int"),
+ LIST11_KEY.getAttrInt())
+ .node(AUG_CONT).build();
private BindingTestContext testContext;
private MountProviderService bindingMountPointService;
@Test
public void testMountPoint() {
- testContext.getBindingDataBroker().readOperationalData(NODE_INSTANCE_ID_BA);
+ testContext.getBindingDataBroker().readOperationalData(TLL_INSTANCE_ID_BA);
- MountProvisionInstance domMountPoint = domMountPointService.createMountPoint(NODE_INSTANCE_ID_BI);
+ MountProvisionInstance domMountPoint = domMountPointService.createMountPoint(TLL_INSTANCE_ID_BI);
assertNotNull(domMountPoint);
- MountProviderInstance bindingMountPoint = bindingMountPointService.getMountPoint(NODE_INSTANCE_ID_BA);
+ MountProviderInstance bindingMountPoint = bindingMountPointService.getMountPoint(TLL_INSTANCE_ID_BA);
assertNotNull(bindingMountPoint);
- final BigInteger packetCount = BigInteger.valueOf(500L);
+ final Integer attrIntalue = 500;
DataReader<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, CompositeNode> simpleReader = new DataReader<org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, CompositeNode>() {
if (arg0.equals(GROUP_STATISTICS_ID_BI)) {
ImmutableCompositeNode data = ImmutableCompositeNode
.builder()
- .setQName(AUGMENTED_GROUP_STATISTICS)
- .addLeaf(QName.create(AUGMENTED_GROUP_STATISTICS, "packet-count"), packetCount) //
- .toInstance();
+ .setQName(AUG_CONT)
+ .addLeaf(QName.create(AUG_CONT, "attr-int"), attrIntalue) //
+ .build();
return data;
}
}
};
- domMountPoint.registerOperationalReader(NODE_INSTANCE_ID_BI, simpleReader);
+ domMountPoint.registerOperationalReader(TLL_INSTANCE_ID_BI, simpleReader);
- GroupStatistics data = (GroupStatistics) bindingMountPoint.readOperationalData(GROUP_STATISTICS_ID_BA);
+ Cont data = (Cont) bindingMountPoint.readOperationalData(AUG_CONT_ID_BA);
assertNotNull(data);
- assertEquals(packetCount,data.getPacketCount().getValue());
+ assertEquals(attrIntalue ,data.getAttrInt());
}
}
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
-import java.math.BigInteger;
-import java.util.Collections;
-import java.util.Set;
-import java.util.concurrent.Future;
-
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.sal.binding.test.util.BindingTestContext;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeFlowRemoved;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev131103.TransactionId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeContext;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.OpendaylightOfMigrationTestModelService;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.TestContext;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.Future;
public class CrossBrokerRpcTest {
- protected RpcProviderRegistry baRpcRegistry;
- protected RpcProvisionRegistry biRpcRegistry;
+ protected RpcProviderRegistry providerRegistry;
+ protected RpcProvisionRegistry provisionRegistry;
private BindingTestContext testContext;
private RpcImplementation biRpcInvoker;
- private MessageCapturingFlowService flowService;
+ private MessageCapturingFlowService knockService;
- public static final NodeId NODE_A = new NodeId("a");
- public static final NodeId NODE_B = new NodeId("b");
- public static final NodeId NODE_C = new NodeId("c");
- public static final NodeId NODE_D = new NodeId("d");
+ public static final TopLevelListKey NODE_A = new TopLevelListKey("a");
+ public static final TopLevelListKey NODE_B = new TopLevelListKey("b");
+ public static final TopLevelListKey NODE_C = new TopLevelListKey("c");
- private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
- private static final QName ADD_FLOW_QNAME = QName.create(NodeFlowRemoved.QNAME, "add-flow");
+ private static final QName NODE_ID_QNAME = QName.create(TopLevelList.QNAME, "name");
+ private static final QName KNOCK_KNOCK_QNAME = QName.create(KnockKnockOutput.QNAME, "knock-knock");
- public static final InstanceIdentifier<Node> BA_NODE_A_ID = createBANodeIdentifier(NODE_A);
- public static final InstanceIdentifier<Node> BA_NODE_B_ID = createBANodeIdentifier(NODE_B);
- public static final InstanceIdentifier<Node> BA_NODE_C_ID = createBANodeIdentifier(NODE_C);
- public static final InstanceIdentifier<Node> BA_NODE_D_ID = createBANodeIdentifier(NODE_D);
+ public static final InstanceIdentifier<Top> NODES_PATH = InstanceIdentifier.builder(Top.class).build();
+ public static final InstanceIdentifier<TopLevelList> BA_NODE_A_ID = NODES_PATH.child(TopLevelList.class, NODE_A);
+ public static final InstanceIdentifier<TopLevelList> BA_NODE_B_ID = NODES_PATH.child(TopLevelList.class, NODE_B);
+ public static final InstanceIdentifier<TopLevelList> BA_NODE_C_ID = NODES_PATH.child(TopLevelList.class, NODE_C);
- public static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier BI_NODE_A_ID = createBINodeIdentifier(NODE_A);
- public static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier BI_NODE_B_ID = createBINodeIdentifier(NODE_B);
public static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier BI_NODE_C_ID = createBINodeIdentifier(NODE_C);
- public static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier BI_NODE_D_ID = createBINodeIdentifier(NODE_D);
-
@Before
testContext = testFactory.getTestContext();
testContext.start();
- baRpcRegistry = testContext.getBindingRpcRegistry();
- biRpcRegistry = testContext.getDomRpcRegistry();
+ providerRegistry = testContext.getBindingRpcRegistry();
+ provisionRegistry = testContext.getDomRpcRegistry();
biRpcInvoker = testContext.getDomRpcInvoker();
- assertNotNull(baRpcRegistry);
- assertNotNull(biRpcRegistry);
+ assertNotNull(providerRegistry);
+ assertNotNull(provisionRegistry);
- flowService = MessageCapturingFlowService.create(baRpcRegistry);
+ knockService = MessageCapturingFlowService.create(providerRegistry);
}
@Test
public void bindingRoutedRpcProvider_DomInvokerTest() throws Exception {
- flowService//
- .registerPath(NodeContext.class, BA_NODE_A_ID) //
- .registerPath(NodeContext.class, BA_NODE_B_ID) //
- .setAddFlowResult(addFlowResult(true, 10));
+ knockService//
+ .registerPath(TestContext.class, BA_NODE_A_ID) //
+ .registerPath(TestContext.class, BA_NODE_B_ID) //
+ .setKnockKnockResult(knockResult(true, "open"));
- SalFlowService baFlowInvoker = baRpcRegistry.getRpcService(SalFlowService.class);
- assertNotSame(flowService, baFlowInvoker);
+ OpendaylightOfMigrationTestModelService baKnockInvoker =
+ providerRegistry.getRpcService(OpendaylightOfMigrationTestModelService.class);
+ assertNotSame(knockService, baKnockInvoker);
- AddFlowInput addFlowA = addFlow(BA_NODE_A_ID) //
- .setPriority(100).setBarrier(true).build();
+ KnockKnockInput knockKnockA = knockKnock(BA_NODE_A_ID) //
+ .setQuestion("who's there?").build();
- CompositeNode addFlowDom = toDomRpc(ADD_FLOW_QNAME, addFlowA);
- assertNotNull(addFlowDom);
- RpcResult<CompositeNode> domResult = biRpcInvoker.invokeRpc(ADD_FLOW_QNAME, addFlowDom).get();
+ CompositeNode knockKnockDom = toDomRpc(KNOCK_KNOCK_QNAME, knockKnockA);
+ assertNotNull(knockKnockDom);
+ RpcResult<CompositeNode> domResult = biRpcInvoker.invokeRpc(KNOCK_KNOCK_QNAME, knockKnockDom).get();
assertNotNull(domResult);
assertTrue("DOM result is successful.", domResult.isSuccessful());
- assertTrue("Bidning Add Flow RPC was captured.", flowService.getReceivedAddFlows().containsKey(BA_NODE_A_ID));
- assertEquals(addFlowA, flowService.getReceivedAddFlows().get(BA_NODE_A_ID).iterator().next());
+ assertTrue("Bidning Add Flow RPC was captured.", knockService.getReceivedKnocks().containsKey(BA_NODE_A_ID));
+ assertEquals(knockKnockA, knockService.getReceivedKnocks().get(BA_NODE_A_ID).iterator().next());
}
@Test
public void bindingRpcInvoker_DomRoutedProviderTest() throws Exception {
- AddFlowOutputBuilder builder = new AddFlowOutputBuilder();
- builder.setTransactionId(new TransactionId(BigInteger.valueOf(10)));
- final AddFlowOutput output = builder.build();
- org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration registration = biRpcRegistry.addRoutedRpcImplementation(ADD_FLOW_QNAME, new RpcImplementation() {
+ KnockKnockOutputBuilder builder = new KnockKnockOutputBuilder();
+ builder.setAnswer("open");
+ final KnockKnockOutput output = builder.build();
+ org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration registration = provisionRegistry.addRoutedRpcImplementation(KNOCK_KNOCK_QNAME, new RpcImplementation() {
@Override
public Set<QName> getSupportedRpcs() {
- return ImmutableSet.of(ADD_FLOW_QNAME);
+ return ImmutableSet.of(KNOCK_KNOCK_QNAME);
}
@Override
return Futures.immediateFuture(RpcResultBuilder.<CompositeNode>success(result).build());
}
});
- registration.registerPath(NodeContext.QNAME, BI_NODE_C_ID);
+ registration.registerPath(TestContext.QNAME, BI_NODE_C_ID);
+
- SalFlowService baFlowInvoker = baRpcRegistry.getRpcService(SalFlowService.class);
- Future<RpcResult<AddFlowOutput>> baResult = baFlowInvoker.addFlow(addFlow(BA_NODE_C_ID).setPriority(500).build());
+ OpendaylightOfMigrationTestModelService baKnockInvoker =
+ providerRegistry.getRpcService(OpendaylightOfMigrationTestModelService.class);
+ Future<RpcResult<KnockKnockOutput>> baResult = baKnockInvoker.knockKnock((knockKnock(BA_NODE_C_ID).setQuestion("Who's there?").build()));
assertNotNull(baResult);
- assertEquals(output,baResult.get().getResult());
+ assertEquals(output, baResult.get().getResult());
}
private CompositeNode toDomRpcInput(DataObject addFlowA) {
testContext.close();
}
- private static InstanceIdentifier<Node> createBANodeIdentifier(NodeId node) {
- return InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(node)).toInstance();
- }
-
- private static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier createBINodeIdentifier(NodeId node) {
- return org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder().node(Nodes.QNAME)
- .nodeWithKey(Node.QNAME, NODE_ID_QNAME, node.getValue()).toInstance();
+ private static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier createBINodeIdentifier(TopLevelListKey listKey) {
+ return org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder().node(Top.QNAME)
+ .nodeWithKey(TopLevelList.QNAME, NODE_ID_QNAME, listKey.getName()).toInstance();
}
- private Future<RpcResult<AddFlowOutput>> addFlowResult(boolean success, long xid) {
- AddFlowOutput output = new AddFlowOutputBuilder() //
- .setTransactionId(new TransactionId(BigInteger.valueOf(xid))).build();
- RpcResult<AddFlowOutput> result = RpcResultBuilder.<AddFlowOutput>status(success).withResult(output).build();
+ private Future<RpcResult<KnockKnockOutput>> knockResult(boolean success, String answer) {
+ KnockKnockOutput output = new KnockKnockOutputBuilder() //
+ .setAnswer(answer).build();
+ RpcResult<KnockKnockOutput> result = RpcResultBuilder.<KnockKnockOutput>status(success).withResult(output).build();
return Futures.immediateFuture(result);
}
- private static AddFlowInputBuilder addFlow(InstanceIdentifier<Node> nodeId) {
- AddFlowInputBuilder builder = new AddFlowInputBuilder();
- builder.setNode(new NodeRef(nodeId));
+ private static KnockKnockInputBuilder knockKnock(InstanceIdentifier<TopLevelList> listId) {
+ KnockKnockInputBuilder builder = new KnockKnockInputBuilder();
+ builder.setKnockerId(listId);
return builder;
}
- private CompositeNode toDomRpc(QName rpcName, AddFlowInput addFlowA) {
+ private CompositeNode toDomRpc(QName rpcName, KnockKnockInput knockInput) {
return new CompositeNodeTOImpl(rpcName, null,
- Collections.<org.opendaylight.yangtools.yang.data.api.Node<?>> singletonList(toDomRpcInput(addFlowA)));
+ Collections.<org.opendaylight.yangtools.yang.data.api.Node<?>>singletonList(toDomRpcInput(knockInput)));
}
}
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.OpendaylightTestRpcServiceService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.RockTheHouseInputBuilder;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
import org.opendaylight.yangtools.yang.common.QName;
private final static QName RPC_NAME = QName.create(RPC_SERVICE_NAMESPACE,
REVISION_DATE, "rock-the-house");
- private static final NodeId MOUNT_NODE = new NodeId("id");
- private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
+ private static final String TLL_NAME = "id";
+ private static final QName TLL_NAME_QNAME = QName.create(TopLevelList.QNAME, "name");
- private static final InstanceIdentifier<Node> BA_MOUNT_ID = createBANodeIdentifier(MOUNT_NODE);
- private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier BI_MOUNT_ID = createBINodeIdentifier(MOUNT_NODE);
+ private static final InstanceIdentifier<TopLevelList> BA_MOUNT_ID = createBATllIdentifier(TLL_NAME);
+ private static final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier BI_MOUNT_ID = createBITllIdentifier(TLL_NAME);
private BindingTestContext testContext;
private MountProvisionService domMountPointService;
schemaContext = mountSchemaContext;
}
- private static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier createBINodeIdentifier(
- final NodeId mountNode) {
+ private static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier createBITllIdentifier(
+ final String mount) {
return org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
- .builder().node(Nodes.QNAME)
- .nodeWithKey(Node.QNAME, NODE_ID_QNAME, mountNode.getValue())
+ .builder().node(Top.QNAME)
+ .nodeWithKey(TopLevelList.QNAME, TLL_NAME_QNAME, mount)
.toInstance();
}
- private static InstanceIdentifier<Node> createBANodeIdentifier(
- final NodeId mountNode) {
- return InstanceIdentifier.builder(Nodes.class)
- .child(Node.class, new NodeKey(mountNode)).toInstance();
+ private static InstanceIdentifier<TopLevelList> createBATllIdentifier(
+ final String mount) {
+ return InstanceIdentifier.builder(Top.class)
+ .child(TopLevelList.class, new TopLevelListKey(mount)).toInstance();
}
@SuppressWarnings("deprecation")
import java.util.concurrent.Future;
-import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.KnockKnockOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.of.migration.test.model.rev150210.OpendaylightOfMigrationTestModelService;
import org.opendaylight.yangtools.yang.binding.BaseIdentity;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
-public class MessageCapturingFlowService implements SalFlowService, AutoCloseable {
-
- private Future<RpcResult<AddFlowOutput>> addFlowResult;
- private Future<RpcResult<RemoveFlowOutput>> removeFlowResult;
- private Future<RpcResult<UpdateFlowOutput>> updateFlowResult;
-
- private final Multimap<InstanceIdentifier<?>, AddFlowInput> receivedAddFlows = HashMultimap.create();
- private final Multimap<InstanceIdentifier<?>, RemoveFlowInput> receivedRemoveFlows = HashMultimap.create();
- private final Multimap<InstanceIdentifier<?>, UpdateFlowInput> receivedUpdateFlows = HashMultimap.create();
- private RoutedRpcRegistration<SalFlowService> registration;
-
- @Override
- public Future<RpcResult<AddFlowOutput>> addFlow(AddFlowInput arg0) {
- receivedAddFlows.put(arg0.getNode().getValue(), arg0);
- return addFlowResult;
- }
-
- @Override
- public Future<RpcResult<RemoveFlowOutput>> removeFlow(RemoveFlowInput arg0) {
- receivedRemoveFlows.put(arg0.getNode().getValue(), arg0);
- return removeFlowResult;
- }
-
- @Override
- public Future<RpcResult<UpdateFlowOutput>> updateFlow(UpdateFlowInput arg0) {
- receivedUpdateFlows.put(arg0.getNode().getValue(), arg0);
- return updateFlowResult;
- }
-
- public Future<RpcResult<AddFlowOutput>> getAddFlowResult() {
- return addFlowResult;
- }
+public class MessageCapturingFlowService implements OpendaylightOfMigrationTestModelService, AutoCloseable {
- public MessageCapturingFlowService setAddFlowResult(Future<RpcResult<AddFlowOutput>> addFlowResult) {
- this.addFlowResult = addFlowResult;
- return this;
- }
+ private Future<RpcResult<KnockKnockOutput>> knockKnockResult;
- public Future<RpcResult<RemoveFlowOutput>> getRemoveFlowResult() {
- return removeFlowResult;
- }
-
- public MessageCapturingFlowService setRemoveFlowResult(Future<RpcResult<RemoveFlowOutput>> removeFlowResult) {
- this.removeFlowResult = removeFlowResult;
- return this;
- }
+ private final Multimap<InstanceIdentifier<?>, KnockKnockInput> receivedKnocks = HashMultimap.create();
+ private RoutedRpcRegistration<OpendaylightOfMigrationTestModelService> registration;
- public Future<RpcResult<UpdateFlowOutput>> getUpdateFlowResult() {
- return updateFlowResult;
+ public Future<RpcResult<KnockKnockOutput>> getKnockKnockResult() {
+ return knockKnockResult;
}
- public MessageCapturingFlowService setUpdateFlowResult(Future<RpcResult<UpdateFlowOutput>> updateFlowResult) {
- this.updateFlowResult = updateFlowResult;
+ public MessageCapturingFlowService setKnockKnockResult(Future<RpcResult<KnockKnockOutput>> kkOutput) {
+ this.knockKnockResult = kkOutput;
return this;
}
- public Multimap<InstanceIdentifier<?>, AddFlowInput> getReceivedAddFlows() {
- return receivedAddFlows;
- }
-
- public Multimap<InstanceIdentifier<?>, RemoveFlowInput> getReceivedRemoveFlows() {
- return receivedRemoveFlows;
- }
-
- public Multimap<InstanceIdentifier<?>, UpdateFlowInput> getReceivedUpdateFlows() {
- return receivedUpdateFlows;
+ public Multimap<InstanceIdentifier<?>, KnockKnockInput> getReceivedKnocks() {
+ return receivedKnocks;
}
public MessageCapturingFlowService registerTo(RpcProviderRegistry registry) {
- registration = registry.addRoutedRpcImplementation(SalFlowService.class, this);
+ registration = registry.addRoutedRpcImplementation(OpendaylightOfMigrationTestModelService.class, this);
assertNotNull(registration);
return this;
}
return ret;
}
+ @Override
+ public Future<RpcResult<KnockKnockOutput>> knockKnock(KnockKnockInput input) {
+ receivedKnocks.put(input.getKnockerId(), input);
+ return knockKnockResult;
+ }
+
}
protected final Logger LOG = LoggerFactory.getLogger(getClass());
public AbstractUntypedPersistentActor() {
- if(LOG.isDebugEnabled()) {
- LOG.debug("Actor created {}", getSelf());
+ if(LOG.isTraceEnabled()) {
+ LOG.trace("Actor created {}", getSelf());
}
getContext().
system().
@Override public void onReceiveCommand(Object message) throws Exception {
final String messageType = message.getClass().getSimpleName();
- if(LOG.isDebugEnabled()) {
- LOG.debug("Received message {}", messageType);
+ if(LOG.isTraceEnabled()) {
+ LOG.trace("Received message {}", messageType);
}
handleCommand(message);
- if(LOG.isDebugEnabled()) {
- LOG.debug("Done handling message {}", messageType);
+ if(LOG.isTraceEnabled()) {
+ LOG.trace("Done handling message {}", messageType);
}
}
@Override public void onReceiveRecover(Object message) throws Exception {
final String messageType = message.getClass().getSimpleName();
- if(LOG.isDebugEnabled()) {
- LOG.debug("Received message {}", messageType);
+ if(LOG.isTraceEnabled()) {
+ LOG.trace("Received message {}", messageType);
}
handleRecover(message);
- if(LOG.isDebugEnabled()) {
- LOG.debug("Done handling message {}", messageType);
+ if(LOG.isTraceEnabled()) {
+ LOG.trace("Done handling message {}", messageType);
}
}
loggers = ["akka.event.slf4j.Slf4jLogger"]
actor {
-
provider = "akka.cluster.ClusterActorRefProvider"
serializers {
- java = "akka.serialization.JavaSerializer"
- proto = "akka.remote.serialization.ProtobufSerializer"
- }
+ java = "akka.serialization.JavaSerializer"
+ proto = "akka.remote.serialization.ProtobufSerializer"
+ }
+
+ serialization-bindings {
+ "com.google.protobuf.Message" = proto
+ }
- serialization-bindings {
- "com.google.protobuf.Message" = proto
+ default-dispatcher {
+ # Setting throughput to 1 makes the dispatcher fair. It processes 1 message from
+ # the mailbox before moving on to the next mailbox
+ throughput = 1
+ }
- }
+ default-mailbox {
+ # When not using a BalancingDispatcher it is recommended that we use the SingleConsumerOnlyUnboundedMailbox
+ # as it is the most efficient for multiple producer/single consumer use cases
+ mailbox-type="akka.dispatch.SingleConsumerOnlyUnboundedMailbox"
+ }
}
remote {
log-remote-lifecycle-events = off
import java.util.Iterator;
import java.util.Map;
-import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.api.SimpleNode;
normalizedPath), e);
}
}
-
- // Write Augmentation data resolution
- if (legacyData.getValue().size() == 1) {
- final DataNormalizationOperation<?> potentialOp;
-
- try {
- final QName childType = legacyData.getValue().get(0).getNodeType();
- potentialOp = currentOp.getChild(childType);
- } catch (DataNormalizationException e) {
- throw new IllegalArgumentException(String.format("Failed to get child operation for %s", legacyData), e);
- }
-
- if (potentialOp.getIdentifier() instanceof AugmentationIdentifier) {
- currentOp = potentialOp;
- normalizedPath = normalizedPath.node(potentialOp.getIdentifier());
- }
- }
-
Preconditions.checkArgument(currentOp != null,
"Instance Identifier %s does not reference correct schema Node.", normalizedPath);
return new AbstractMap.SimpleEntry<YangInstanceIdentifier, NormalizedNode<?, ?>>(normalizedPath,
YangInstanceIdentifier.create(Lists.newArrayList(new NodeIdentifier(TEST_QNAME), new NodeIdentifier(
OUTER_CONTAINER_QNAME))), outerContBuilder.toInstance()));
- verifyNormalizedInstanceIdentifier(normalizedNodeEntry.getKey(), TEST_QNAME, OUTER_CONTAINER_QNAME,
- Sets.newHashSet(AUGMENTED_LEAF_QNAME));
-
- verifyNormalizedNode(normalizedNodeEntry.getValue(), expAugmentation);
}
@Test
@Override
public void onReceiveCommand(final Object message) throws Exception {
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: onReceiveCommand: Received message {} from {}", persistenceId(), message, getSender());
- }
-
if (message.getClass().equals(CreateTransaction.SERIALIZABLE_CLASS)) {
handleCreateTransaction(message);
} else if(message instanceof ForwardedReadyTransaction) {
sender().tell((returnSerialized ? readDataReply.toSerializable(clientTxVersion): readDataReply), self());
} catch (Exception e) {
- LOG.error(String.format("Unexpected error reading path %s", path), e);
+ LOG.debug(String.format("Unexpected error reading path %s", path), e);
shardStats.incrementFailedReadTransactionsCount();
sender().tell(new akka.actor.Status.Failure(e), self());
}
timerContext.stop();
Snapshot timerSnapshot = commitTimer.getSnapshot();
- double allowedLatencyInNanos = timerSnapshot.get98thPercentile();
+ double allowedLatencyInNanos = timerSnapshot.get95thPercentile();
long commitTimeoutInSeconds = actorContext.getDatastoreContext()
.getShardTransactionCommitTimeoutInSeconds();
assertEquals(DatastoreContext.DEFAULT_SHARD_LEADER_ELECTION_TIMEOUT, build.getShardLeaderElectionTimeout());
assertEquals(DatastoreContext.DEFAULT_PERSISTENT, build.isPersistent());
assertEquals(DatastoreContext.DEFAULT_CONFIGURATION_READER, build.getConfigurationReader());
- assertEquals(DatastoreContext.DEFAULT_ISOLATED_LEADER_CHECK_INTERVAL_IN_MILLIS, build.getShardRaftConfig().getIsolatedCheckInterval().length());
+ assertEquals(DatastoreContext.DEFAULT_ISOLATED_LEADER_CHECK_INTERVAL_IN_MILLIS, build.getShardRaftConfig().getIsolatedCheckIntervalInMillis());
assertEquals(DatastoreContext.DEFAULT_SHARD_SNAPSHOT_DATA_THRESHOLD_PERCENTAGE, build.getShardRaftConfig().getSnapshotDataThresholdPercentage());
assertEquals(DatastoreContext.DEFAULT_SHARD_ELECTION_TIMEOUT_FACTOR, build.getShardRaftConfig().getElectionTimeoutFactor());
assertEquals(DatastoreContext.DEFAULT_TX_CREATION_INITIAL_RATE_LIMIT, build.getTransactionCreationInitialRateLimit());
NormalizedNode<?,?> expectedRoot = readStore(shard, YangInstanceIdentifier.builder().build());
- CaptureSnapshot capture = new CaptureSnapshot(-1, -1, -1, -1);
+ CaptureSnapshot capture = new CaptureSnapshot(-1, -1, -1, -1, -1, -1);
shard.tell(capture, getRef());
assertEquals("Snapshot saved", true, latch.get().await(5, TimeUnit.SECONDS));
doReturn(commitTimer).when(actorContext).getOperationTimer("commit");
doReturn(commitTimerContext).when(commitTimer).time();
doReturn(commitSnapshot).when(commitTimer).getSnapshot();
- doReturn(TimeUnit.MILLISECONDS.toNanos(2000) * 1.0).when(commitSnapshot).get98thPercentile();
+ doReturn(TimeUnit.MILLISECONDS.toNanos(2000) * 1.0).when(commitSnapshot).get95thPercentile();
doReturn(10.0).when(actorContext).getTxCreationLimit();
}
*/
package org.opendaylight.controller.md.sal.dom.api;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import java.util.Objects;
import javax.annotation.Nonnull;
@Override
public final String toString() {
- return com.google.common.base.Objects.toStringHelper(this).omitNullValues().add("type", type).add("contextReference", getContextReference()).toString();
+ return MoreObjects.toStringHelper(this).omitNullValues().add("type", type).add("contextReference", getContextReference()).toString();
}
}
package org.opendaylight.controller.sal.core.api;
import java.util.concurrent.Future;
-
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+/**
+ * @deprecated Use {@link org.opendaylight.controller.md.sal.dom.api.DOMRpcService} instead.
+ */
+@Deprecated
public interface RpcConsumptionRegistry {
/**
* Sends an RPC to other components registered to the broker.
*/
package org.opendaylight.controller.sal.core.api;
+import com.google.common.util.concurrent.ListenableFuture;
import java.util.Set;
-
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import com.google.common.util.concurrent.ListenableFuture;
-
/**
* {@link Provider}'s implementation of an RPC.
*
* {@link RpcResult}
* <li> {@link Broker} returns the {@link RpcResult} to {@link Consumer}
* </ol>
+ *
+ * @deprecated Use {@link org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation} instead.
*/
+@Deprecated
public interface RpcImplementation extends Provider.ProviderFunctionality {
/**
/**
* Exception reported when no RPC implementation is found in the system.
+ *
+ * @deprecated Use {@link org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException} instead.
*/
+@Deprecated
public class RpcImplementationUnavailableException extends RuntimeException {
private static final long serialVersionUID = 1L;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+/**
+ * @deprecated Use {@link org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService} and {@link org.opendaylight.controller.md.sal.dom.api.DOMRpcService} instead.
+ */
+@Deprecated
public interface RpcProvisionRegistry extends RpcImplementation, BrokerService, RouteChangePublisher<RpcRoutingContext, YangInstanceIdentifier>, DOMService {
/**
package org.opendaylight.controller.sal.core.api;
import java.util.EventListener;
-
import org.opendaylight.yangtools.yang.common.QName;
+/**
+ * @deprecated Use {@link org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener} instead.
+ */
+@Deprecated
public interface RpcRegistrationListener extends EventListener {
public void onRpcImplementationAdded(QName name);
*/
package org.opendaylight.controller.md.sal.dom.broker.impl;
-import com.google.common.base.Objects;
-import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.FutureCallback;
@Override
public String toString() {
- return addToStringAttributes(Objects.toStringHelper(this)).toString();
+ return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
}
protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.japi.Creator;
+import com.google.common.base.Stopwatch;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
import org.opendaylight.controller.cluster.raft.messages.InstallSnapshot;
private final Configuration configuration;
private final String followerId;
private final Logger LOG = LoggerFactory.getLogger(DummyShard.class);
+ private long lastMessageIndex = -1;
+ private long lastMessageSize = 0;
+ private Stopwatch appendEntriesWatch;
public DummyShard(Configuration configuration, String followerId) {
this.configuration = configuration;
}
protected void handleAppendEntries(AppendEntries req) throws InterruptedException {
- LOG.info("{} - Received AppendEntries message : leader term, index, size = {}, {}, {}", followerId, req.getTerm(),req.getLeaderCommit(), req.getEntries().size());
+ LOG.info("{} - Received AppendEntries message : leader term = {}, index = {}, prevLogIndex = {}, size = {}",
+ followerId, req.getTerm(),req.getLeaderCommit(), req.getPrevLogIndex(), req.getEntries().size());
+
+ if(appendEntriesWatch != null){
+ long elapsed = appendEntriesWatch.elapsed(TimeUnit.SECONDS);
+ if(elapsed >= 5){
+ LOG.error("More than 5 seconds since last append entry, elapsed Time = {} seconds" +
+ ", leaderCommit = {}, prevLogIndex = {}, size = {}",
+ elapsed, req.getLeaderCommit(), req.getPrevLogIndex(), req.getEntries().size());
+ }
+ appendEntriesWatch.reset().start();
+ } else {
+ appendEntriesWatch = Stopwatch.createStarted();
+ }
+
+ if(lastMessageIndex == req.getLeaderCommit() && req.getEntries().size() > 0 && lastMessageSize > 0){
+ LOG.error("{} - Duplicate message with leaderCommit = {} prevLogIndex = {} received", followerId, req.getLeaderCommit(), req.getPrevLogIndex());
+ }
+
+ lastMessageIndex = req.getLeaderCommit();
+ lastMessageSize = req.getEntries().size();
+
long lastIndex = req.getLeaderCommit();
- if (req.getEntries().size() > 0)
- lastIndex = req.getEntries().get(0).getIndex();
+ if (req.getEntries().size() > 0) {
+ for(ReplicatedLogEntry entry : req.getEntries()) {
+ lastIndex = entry.getIndex();
+ }
+ }
- if (configuration.shouldCauseTrouble()) {
+ if (configuration.shouldCauseTrouble() && req.getEntries().size() > 0) {
boolean ignore = false;
if (configuration.shouldDropReplies()) {
--- /dev/null
+org.slf4j.simpleLogger.showDateTime=true
+org.slf4j.simpleLogger.dateTimeFormat=hh:mm:ss,S a
+org.slf4j.simpleLogger.logFile=System.out
+org.slf4j.simpleLogger.showShortLogName=true
+org.slf4j.simpleLogger.levelInBrackets=true
+org.slf4j.simpleLogger.defaultLogLevel=error
\ No newline at end of file
*/
package org.opendaylight.controller.md.sal.dom.store.impl;
-import com.google.common.base.Objects;
-import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Preconditions;
-
import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction;
import org.slf4j.Logger;
@Override
public final String toString() {
- return addToStringAttributes(Objects.toStringHelper(this)).toString();
+ return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
}
/**
package org.opendaylight.controller.md.sal.dom.store.impl;
import static com.google.common.base.Preconditions.checkState;
-import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
--- /dev/null
+module opendaylight-of-migration-test-model {
+
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:of-migration-test-model";
+ prefix of-migration-test;
+
+ import opendaylight-mdsal-list-test {prefix test;}
+ import yang-ext {prefix ext;}
+ import opendaylight-mdsal-augment-test {prefix aug;}
+ import opendaylight-test-routed-rpc {prefix routed;}
+
+ description
+ "This module contains a collection of YANG definitions used for
+ test cases that used to depend on flow model.";
+
+ revision 2015-02-10 {
+ }
+
+ typedef bit-flags {
+ type bits {
+ bit FLAG_ONE;
+ bit FLAG_TWO;
+ bit FLAG_THREE;
+ bit FLAG_FOUR;
+ bit FLAG_FIVE;
+ }
+ }
+
+ typedef custom-enum {
+ type enumeration {
+ enum type1;
+ enum type2;
+ enum type3;
+ }
+ }
+
+ grouping enum-grouping {
+ leaf attr-enum {
+ type custom-enum;
+ }
+ }
+
+ grouping aug-grouping {
+ container cont1 {
+ leaf attr-str {
+ type string;
+ }
+ }
+
+ container cont2 {
+ list contlist1 {
+ key "attr-str";
+
+ leaf attr-str {
+ type string;
+ }
+
+ uses enum-grouping;
+ }
+ }
+
+ leaf attr-str1 {
+ type string;
+ }
+
+ leaf attr-str2 {
+ type string;
+ }
+
+ leaf attr-str3 {
+ type string;
+ }
+
+ leaf attr-str4 {
+ type string;
+ }
+
+ list list1 {
+ key "attr-str";
+ leaf attr-str {
+ type string;
+ }
+
+ list list1-1 {
+ key "attr-int";
+ leaf attr-int {
+ type int32;
+ }
+
+ leaf attr-str {
+ type string;
+ }
+
+ leaf flags {
+ type bit-flags;
+ }
+ }
+
+ list list1-2 {
+ key "attr-int";
+ leaf attr-int {
+ type int32;
+ }
+
+ leaf attr-str {
+ type string;
+ }
+ }
+ }
+ }
+
+ augment "/test:top/test:top-level-list" {
+ ext:augment-identifier tll-complex-augment;
+ uses aug-grouping;
+ }
+
+ augment "/test:top/test:top-level-list/list1/list1-1" {
+ ext:augment-identifier list11-simple-augment;
+
+ leaf attr-str2 {
+ type string;
+ }
+
+ container cont {
+ leaf attr-int {
+ type int32;
+ }
+ }
+ }
+
+ augment "/test:top/test:top-level-list/test:nested-list/" {
+ ext:augment-identifier nested-list-simple-augment;
+
+ leaf type {
+ type string;
+ }
+ }
+
+ rpc knock-knock {
+ input {
+ leaf knocker-id {
+ ext:context-reference routed:test-context;
+ type instance-identifier;
+ }
+
+ leaf question {
+ type string;
+ }
+ }
+
+ output {
+ leaf answer {
+ type string;
+ }
+ }
+ }
+}
import java.util.Dictionary;
import java.util.Hashtable;
+import org.opendaylight.controller.netconf.api.util.NetconfConstants;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
import org.osgi.framework.BundleActivator;
NetconfOperationServiceFactoryImpl factory = new NetconfOperationServiceFactoryImpl(yangStoreService);
LOG.debug("Registering into OSGi");
Dictionary<String, String> properties = new Hashtable<>();
- properties.put("name", "config-netconf-connector");
+ properties.put(NetconfConstants.SERVICE_NAME, NetconfConstants.CONFIG_NETCONF_CONNECTOR);
osgiRegistration = context.registerService(NetconfOperationServiceFactory.class, factory, properties);
}
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-subsystem</artifactId>
+ <version>0.3.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>mdsal-netconf-connector</artifactId>
+ <packaging>bundle</packaging>
+ <name>${project.artifactId}</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-mapping-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.logback_settings</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-operations</artifactId>
+ <version>0.7.0-SNAPSHOT</version>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Import-Package>*</Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <!--FIXME extract yang plugin definition into parent-->
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.config.yang.netconf.mdsal.mapper;
+
+import org.opendaylight.controller.netconf.mdsal.connector.MdsalNetconfOperationServiceFactory;
+
+public class NetconfMdsalMapperModule extends org.opendaylight.controller.config.yang.netconf.mdsal.mapper.AbstractNetconfMdsalMapperModule {
+ public NetconfMdsalMapperModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NetconfMdsalMapperModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.netconf.mdsal.mapper.NetconfMdsalMapperModule oldModule, java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ return new MdsalNetconfOperationServiceFactory(getRootSchemaServiceDependency(), getDomBrokerDependency());
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+/*
+* Generated file
+*
+* Generated from: yang module name: netconf-mdsal-mapper yang module local name: netconf-mdsal-mapper
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Jan 14 14:58:42 CET 2015
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.netconf.mdsal.mapper;
+public class NetconfMdsalMapperModuleFactory extends org.opendaylight.controller.config.yang.netconf.mdsal.mapper.AbstractNetconfMdsalMapperModuleFactory {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mdsal.connector;
+
+import com.google.common.base.Preconditions;
+import java.util.concurrent.atomic.AtomicReference;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+
+public class CurrentSchemaContext implements SchemaContextListener, AutoCloseable {
+ final AtomicReference<SchemaContext> currentContext = new AtomicReference<SchemaContext>();
+ private final ListenerRegistration<SchemaContextListener> schemaContextListenerListenerRegistration;
+
+ public SchemaContext getCurrentContext() {
+ Preconditions.checkState(currentContext.get() != null, "Current context not received");
+ return currentContext.get();
+ }
+
+ public CurrentSchemaContext(final SchemaService schemaService) {
+ schemaContextListenerListenerRegistration = schemaService.registerSchemaContextListener(this);
+ }
+
+ @Override
+ public void onGlobalContextUpdated(final SchemaContext schemaContext) {
+ currentContext.set(schemaContext);
+ }
+
+ @Override
+ public void close() throws Exception {
+ schemaContextListenerListenerRegistration.close();
+ currentContext.set(null);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.netconf.mdsal.connector;
+
+import com.google.common.base.Optional;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MdsalNetconfOperationService implements NetconfOperationService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MdsalNetconfOperationService.class);
+
+ private final CurrentSchemaContext schemaContext;
+ private final String netconfSessionIdForReporting;
+ private final OperationProvider operationProvider;
+
+ public MdsalNetconfOperationService(final CurrentSchemaContext schemaContext, final String netconfSessionIdForReporting,
+ final DOMDataBroker dataBroker) {
+ this.schemaContext = schemaContext;
+ // TODO schema contexts are different in data broker and the one we receive here ... the one received here should be updated same way as broker is
+ this.netconfSessionIdForReporting = netconfSessionIdForReporting;
+ this.operationProvider = new OperationProvider(netconfSessionIdForReporting, schemaContext, dataBroker);
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ // TODO does this get called dynamically ?
+ @Override
+ public Set<Capability> getCapabilities() {
+ final Set<Capability> capabilities = new HashSet<>();
+ // [RFC6241] 8.3. Candidate Configuration Capability
+ capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
+
+ final SchemaContext currentContext = schemaContext.getCurrentContext();
+ final Set<Module> modules = currentContext.getModules();
+ for (final Module module : modules) {
+ if(currentContext.getModuleSource(module).isPresent()) {
+ capabilities.add(new YangStoreCapability(module, currentContext.getModuleSource(module).get()));
+ } else {
+ LOG.warn("Missing source for module {}. This module will not be available from netconf server for session {}",
+ module, netconfSessionIdForReporting);
+ }
+ }
+
+ return capabilities;
+ }
+
+ @Override
+ public Set<NetconfOperation> getNetconfOperations() {
+ return operationProvider.getOperations();
+ }
+
+ // TODO reuse from netconf impl
+ private static class BasicCapability implements Capability {
+
+ private final String capability;
+
+ private BasicCapability(final String capability) {
+ this.capability = capability;
+ }
+
+ @Override
+ public String getCapabilityUri() {
+ return capability;
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Collection<String> getLocation() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return capability;
+ }
+ }
+
+ private static final class YangStoreCapability extends BasicCapability {
+
+ private final String content;
+ private final String revision;
+ private final String moduleName;
+ private final String moduleNamespace;
+
+ public YangStoreCapability(final Module module, final String moduleContent) {
+ super(toCapabilityURI(module));
+ this.content = moduleContent;
+ this.moduleName = module.getName();
+ this.moduleNamespace = module.getNamespace().toString();
+ this.revision = SimpleDateFormatUtil.getRevisionFormat().format(module.getRevision());
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.of(content);
+ }
+
+ private static String toCapabilityURI(final Module module) {
+ return String.valueOf(module.getNamespace()) + "?module="
+ + module.getName() + "&revision=" + SimpleDateFormatUtil.getRevisionFormat().format(module.getRevision());
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.of(moduleName);
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of(moduleNamespace);
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.of(revision);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.netconf.mdsal.connector;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+
+public class MdsalNetconfOperationServiceFactory implements NetconfOperationServiceFactory, AutoCloseable {
+
+ private final DOMDataBroker dataBroker;
+ private final CurrentSchemaContext currentSchemaContext;
+
+ public MdsalNetconfOperationServiceFactory(final SchemaService schemaService, final DOMDataBroker domDataBroker) {
+ this.currentSchemaContext = new CurrentSchemaContext(Preconditions.checkNotNull(schemaService));
+ this.dataBroker = Preconditions.checkNotNull(domDataBroker);
+ }
+
+ @Override
+ public MdsalNetconfOperationService createService(final String netconfSessionIdForReporting) {
+ return new MdsalNetconfOperationService(currentSchemaContext, netconfSessionIdForReporting, dataBroker);
+ }
+
+ @Override
+ public void close() throws Exception {
+ currentSchemaContext.close();
+ }
+}
--- /dev/null
+/*
+ * 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.netconf.mdsal.connector;
+
+import com.google.common.collect.Sets;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mdsal.connector.ops.Commit;
+import org.opendaylight.controller.netconf.mdsal.connector.ops.DiscardChanges;
+import org.opendaylight.controller.netconf.mdsal.connector.ops.EditConfig;
+import org.opendaylight.controller.netconf.mdsal.connector.ops.Lock;
+import org.opendaylight.controller.netconf.mdsal.connector.ops.Unlock;
+import org.opendaylight.controller.netconf.mdsal.connector.ops.get.Get;
+import org.opendaylight.controller.netconf.mdsal.connector.ops.get.GetConfig;
+
+final class OperationProvider {
+
+ private final String netconfSessionIdForReporting;
+ private final CurrentSchemaContext schemaContext;
+ private final DOMDataBroker dataBroker;
+ private final TransactionProvider transactionProvider;
+
+ public OperationProvider(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext, final DOMDataBroker dataBroker) {
+ this.netconfSessionIdForReporting = netconfSessionIdForReporting;
+ this.schemaContext = schemaContext;
+ this.dataBroker = dataBroker;
+ this.transactionProvider = new TransactionProvider(dataBroker, netconfSessionIdForReporting);
+
+ }
+
+ Set<NetconfOperation> getOperations() {
+ return Sets.<NetconfOperation>newHashSet(
+ new Commit(netconfSessionIdForReporting, transactionProvider),
+ new DiscardChanges(netconfSessionIdForReporting, transactionProvider),
+ new EditConfig(netconfSessionIdForReporting, schemaContext, transactionProvider),
+ new Get(netconfSessionIdForReporting, schemaContext, transactionProvider),
+ new GetConfig(netconfSessionIdForReporting, schemaContext, transactionProvider),
+ new Lock(netconfSessionIdForReporting),
+ new Unlock(netconfSessionIdForReporting)
+ );
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mdsal.connector;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+//TODO make a global TransactionProvider for all Netconf sessions instead of each session having one.
+public class TransactionProvider implements AutoCloseable{
+
+ private static final Logger LOG = LoggerFactory.getLogger(TransactionProvider.class);
+
+ private final DOMDataBroker dataBroker;
+
+ private DOMDataReadWriteTransaction candidateTransaction = null;
+ private DOMDataReadWriteTransaction runningTransaction = null;
+ private final List<DOMDataReadWriteTransaction> allOpenReadWriteTransactions = new ArrayList<>();
+
+ private final String netconfSessionIdForReporting;
+
+ private static final String NO_TRANSACTION_FOUND_FOR_SESSION = "No candidateTransaction found for session ";
+
+
+ public TransactionProvider(DOMDataBroker dataBroker, String netconfSessionIdForReporting) {
+ this.dataBroker = dataBroker;
+ this.netconfSessionIdForReporting = netconfSessionIdForReporting;
+ }
+
+ @Override
+ public synchronized void close() throws Exception {
+ for (DOMDataReadWriteTransaction rwt : allOpenReadWriteTransactions) {
+ rwt.cancel();
+ }
+
+ allOpenReadWriteTransactions.clear();
+ }
+
+ public synchronized Optional<DOMDataReadWriteTransaction> getCandidateTransaction() {
+ if (candidateTransaction == null) {
+ return Optional.absent();
+ }
+
+ return Optional.of(candidateTransaction);
+ }
+
+ public synchronized DOMDataReadWriteTransaction getOrCreateTransaction() {
+ if (getCandidateTransaction().isPresent()) {
+ return getCandidateTransaction().get();
+ }
+
+ candidateTransaction = dataBroker.newReadWriteTransaction();
+ allOpenReadWriteTransactions.add(candidateTransaction);
+ return candidateTransaction;
+ }
+
+ public synchronized boolean commitTransaction() throws NetconfDocumentedException {
+ if (!getCandidateTransaction().isPresent()) {
+ throw new NetconfDocumentedException(NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting,
+ ErrorType.application, ErrorTag.operation_failed, ErrorSeverity.error);
+ }
+
+ CheckedFuture<Void, TransactionCommitFailedException> future = candidateTransaction.submit();
+ try {
+ future.checkedGet();
+ } catch (TransactionCommitFailedException e) {
+ LOG.debug("Transaction {} failed on", candidateTransaction, e);
+ throw new NetconfDocumentedException("Transaction commit failed on " + e.getMessage() + " " + netconfSessionIdForReporting,
+ ErrorType.application, ErrorTag.operation_failed, ErrorSeverity.error);
+ }
+ allOpenReadWriteTransactions.remove(candidateTransaction);
+ candidateTransaction = null;
+
+ return true;
+ }
+
+ public synchronized void abortTransaction() {
+ LOG.debug("Aborting current candidateTransaction");
+ Optional<DOMDataReadWriteTransaction> otx = getCandidateTransaction();
+ Preconditions.checkState(otx.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting);
+ candidateTransaction.cancel();
+ allOpenReadWriteTransactions.remove(candidateTransaction);
+ candidateTransaction = null;
+ }
+
+ public synchronized DOMDataReadWriteTransaction createRunningTransaction() {
+ runningTransaction = dataBroker.newReadWriteTransaction();
+ allOpenReadWriteTransactions.add(runningTransaction);
+ return runningTransaction;
+ }
+
+ public synchronized boolean commitRunningTransaction(DOMDataReadWriteTransaction tx) throws NetconfDocumentedException {
+ allOpenReadWriteTransactions.remove(tx);
+
+ CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
+ try {
+ future.checkedGet();
+ } catch (TransactionCommitFailedException e) {
+ LOG.debug("Transaction {} failed on", tx, e);
+ throw new NetconfDocumentedException("Transaction commit failed on " + e.getMessage() + " " + netconfSessionIdForReporting,
+ ErrorType.application, ErrorTag.operation_failed, ErrorSeverity.error);
+ }
+
+ return true;
+ }
+
+ public synchronized void abortRunningTransaction(DOMDataReadWriteTransaction tx) {
+ LOG.debug("Aborting current running Transaction");
+ Preconditions.checkState(runningTransaction != null, NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting);
+ tx.cancel();
+ allOpenReadWriteTransactions.remove(tx);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mdsal.connector.ops;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.mdsal.connector.TransactionProvider;
+import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class Commit extends AbstractLastNetconfOperation{
+
+ private static final Logger LOG = LoggerFactory.getLogger(Commit.class);
+
+ private static final String OPERATION_NAME = "commit";
+ private final TransactionProvider transactionProvider;
+
+ public Commit(final String netconfSessionIdForReporting, final TransactionProvider transactionProvider) {
+ super(netconfSessionIdForReporting);
+ this.transactionProvider = transactionProvider;
+
+ }
+
+ @Override
+ protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException {
+
+ boolean commitStatus = transactionProvider.commitTransaction();
+ LOG.trace("Transaction commited succesfuly", commitStatus);
+
+ return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.<String>absent());
+ }
+
+ @Override
+ protected String getOperationName() {
+ return OPERATION_NAME;
+ }
+
+}
/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.cluster.raft.base.messages;
+package org.opendaylight.controller.netconf.mdsal.connector.ops;
-/**
- * Internal message by Leader to initiate an install snapshot
- */
-public class InitiateInstallSnapshot {
+public enum Datastore {
+ candidate, running
}
-
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mdsal.connector.ops;
+
+import com.google.common.base.Optional;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.mdsal.connector.TransactionProvider;
+import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class DiscardChanges extends AbstractLastNetconfOperation{
+
+ private static final Logger LOG = LoggerFactory.getLogger(DiscardChanges.class);
+
+ private static final String OPERATION_NAME = "discard-changes";
+
+ private final TransactionProvider transactionProvider;
+
+ public DiscardChanges(final String netconfSessionIdForReporting, final TransactionProvider transactionProvider) {
+ super(netconfSessionIdForReporting);
+ this.transactionProvider = transactionProvider;
+ }
+
+ @Override
+ protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException {
+ operationElement.getOnlyChildElement(OPERATION_NAME);
+
+ try {
+ transactionProvider.abortTransaction();
+ } catch (IllegalStateException e) {
+ LOG.warn("Abort failed ", e);
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo
+ .put(ErrorTag.operation_failed.name(),
+ "Operation failed. Use 'get-config' or 'edit-config' before triggering 'discard-changes' operation");
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed,
+ ErrorSeverity.error, errorInfo);
+ }
+ return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.<String>absent());
+ }
+
+ @Override
+ protected String getOperationName() {
+ return OPERATION_NAME;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mdsal.connector.ops;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.mdsal.connector.CurrentSchemaContext;
+import org.opendaylight.controller.netconf.mdsal.connector.TransactionProvider;
+import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.data.operations.DataModificationException;
+import org.opendaylight.yangtools.yang.data.operations.DataModificationException.DataExistsException;
+import org.opendaylight.yangtools.yang.data.operations.DataModificationException.DataMissingException;
+import org.opendaylight.yangtools.yang.data.operations.DataOperations;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class EditConfig extends AbstractLastNetconfOperation {
+
+ private static final Logger LOG = LoggerFactory.getLogger(EditConfig.class);
+
+ private static final String OPERATION_NAME = "edit-config";
+ private static final String CONFIG_KEY = "config";
+
+ private final CurrentSchemaContext schemaContext;
+ private final TransactionProvider transactionProvider;
+
+ public EditConfig(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext, final TransactionProvider transactionProvider) {
+ super(netconfSessionIdForReporting);
+ this.schemaContext = schemaContext;
+ this.transactionProvider = transactionProvider;
+ }
+
+ @Override
+ protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException {
+ final XmlElement configElement = getConfigElement(operationElement);
+
+ for (XmlElement element : configElement.getChildElements()) {
+ final String ns = element.getNamespace();
+ final DataSchemaNode schemaNode = getSchemaNodeFromNamespace(ns, element).get();
+ YangInstanceIdentifier ident = YangInstanceIdentifier.of(schemaNode.getQName());
+
+ final NormalizedNode storedNode = readStoredNode(LogicalDatastoreType.CONFIGURATION, ident);
+ try {
+ final Optional<NormalizedNode<?,?>> newNode = modifyNode(schemaNode, element, storedNode);
+ final DOMDataReadWriteTransaction rwTx = transactionProvider.getOrCreateTransaction();
+ if (newNode.isPresent()) {
+ rwTx.put(LogicalDatastoreType.CONFIGURATION, ident, newNode.get());
+ } else {
+ rwTx.delete(LogicalDatastoreType.CONFIGURATION, ident);
+ }
+ } catch (final DataModificationException e) {
+ if (e instanceof DataExistsException) {
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.protocol, ErrorTag.data_exists, ErrorSeverity.error);
+ } else if (e instanceof DataMissingException) {
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.protocol, ErrorTag.data_missing, ErrorSeverity.error);
+ } else {
+ //should never happen, since in edit-config only the 2 previous cases can happen
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.protocol, ErrorTag.operation_failed, ErrorSeverity.error);
+ }
+ }
+ }
+
+ return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.<String>absent());
+ }
+
+ private NormalizedNode readStoredNode(final LogicalDatastoreType logicalDatastoreType, final YangInstanceIdentifier path) throws NetconfDocumentedException{
+ final DOMDataReadWriteTransaction rwTx = transactionProvider.getOrCreateTransaction();
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readFuture = rwTx.read(logicalDatastoreType, path);
+ try {
+ if (readFuture.checkedGet().isPresent()) {
+ final NormalizedNode node = readFuture.checkedGet().get();
+ return node;
+ } else {
+ LOG.warn("Unable to read node : {} from {} datastore", path, logicalDatastoreType);
+ }
+ } catch (final ReadFailedException e) {
+ //only log this since DataOperations.modify will handle throwing an exception or writing the node.
+ LOG.warn("Unable to read stored data: {}", path, e);
+ }
+
+ //we can return null here since DataOperations.modify handles null as input
+ return null;
+ }
+
+ private Optional<DataSchemaNode> getSchemaNodeFromNamespace(final String namespace, final XmlElement element){
+ Optional<DataSchemaNode> dataSchemaNode = Optional.absent();
+ try {
+ //returns module with newest revision since findModuleByNamespace returns a set of modules and we only need the newest one
+ final Module module = schemaContext.getCurrentContext().findModuleByNamespaceAndRevision(new URI(namespace), null);
+ dataSchemaNode = Optional.of(module.getDataChildByName(element.getName()));
+ } catch (URISyntaxException e) {
+ LOG.debug("Unable to create URI for namespace : {}", namespace);
+ }
+
+ return dataSchemaNode;
+ }
+
+ private Optional<NormalizedNode<?, ?>> modifyNode(final DataSchemaNode schemaNode, final XmlElement element, final NormalizedNode storedNode) throws DataModificationException{
+ if (schemaNode instanceof ContainerSchemaNode) {
+ final ContainerNode modifiedNode =
+ DomToNormalizedNodeParserFactory
+ .getInstance(DomUtils.defaultValueCodecProvider())
+ .getContainerNodeParser()
+ .parse(Collections.singletonList(element.getDomElement()), (ContainerSchemaNode) schemaNode);
+
+ final Optional<ContainerNode> oNode = DataOperations.modify((ContainerSchemaNode) schemaNode, (ContainerNode) storedNode, modifiedNode);
+ if (!oNode.isPresent()) {
+ return Optional.absent();
+ }
+
+ final NormalizedNode<?,?> node = oNode.get();
+ return Optional.<NormalizedNode<?,?>>of(node);
+ } else if (schemaNode instanceof ListSchemaNode) {
+ final MapNode modifiedNode =
+ DomToNormalizedNodeParserFactory
+ .getInstance(DomUtils.defaultValueCodecProvider())
+ .getMapNodeParser()
+ .parse(Collections.singletonList(element.getDomElement()), (ListSchemaNode) schemaNode);
+
+ final Optional<MapNode> oNode = DataOperations.modify((ListSchemaNode) schemaNode, (MapNode) storedNode, modifiedNode);
+ if (!oNode.isPresent()) {
+ return Optional.absent();
+ }
+
+ final NormalizedNode<?, ?> node = oNode.get();
+ return Optional.<NormalizedNode<?,?>>of(node);
+ } else {
+ //this should never happen since edit-config on any other node type should not be possible nor makes sense
+ LOG.debug("DataNode from module is not ContainerSchemaNode nor ListSchemaNode, aborting..");
+ return Optional.absent();
+ }
+
+ }
+
+ private XmlElement getConfigElement(final XmlElement operationElement) throws NetconfDocumentedException{
+ final Optional<XmlElement> configChildNode = operationElement.getOnlyChildElementOptionally(CONFIG_KEY);
+ if (!configChildNode.isPresent()) {
+ throw new NetconfDocumentedException("Can't get child element with name: " + CONFIG_KEY,
+ ErrorType.application,
+ ErrorTag.unknown_element,
+ ErrorSeverity.error);
+ }
+
+ return configChildNode.get();
+ }
+
+ @Override
+ protected String getOperationName() {
+ return OPERATION_NAME;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mdsal.connector.ops;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException;
+import org.opendaylight.controller.netconf.util.exception.UnexpectedNamespaceException;
+import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class Lock extends AbstractLastNetconfOperation{
+
+ private static final Logger LOG = LoggerFactory.getLogger(Lock.class);
+
+ private static final String OPERATION_NAME = "lock";
+ private static final String TARGET_KEY = "target";
+
+ public Lock(final String netconfSessionIdForReporting) {
+ super(netconfSessionIdForReporting);
+ }
+
+ @Override
+ protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException {
+ final Datastore targetDatastore = extractTargetParameter(operationElement);
+ if (targetDatastore == Datastore.candidate) {
+ LOG.debug("Locking candidate datastore on session: {}", getNetconfSessionIdForReporting());
+ return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.<String>absent());
+ }
+
+ throw new NetconfDocumentedException("Unable to lock " + targetDatastore + " datastore", NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.operation_not_supported, NetconfDocumentedException.ErrorSeverity.error);
+ }
+
+ static Datastore extractTargetParameter(final XmlElement operationElement) throws NetconfDocumentedException {
+ final XmlElement targetChildNode;
+ try {
+ final XmlElement targetElement = operationElement.getOnlyChildElementWithSameNamespace(TARGET_KEY);
+ targetChildNode = targetElement.getOnlyChildElementWithSameNamespace();
+ } catch (final MissingNameSpaceException | UnexpectedNamespaceException e) {
+ LOG.trace("Can't get only child element with same namespace", e);
+ throw NetconfDocumentedException.wrap(e);
+ }
+
+ return Datastore.valueOf(targetChildNode.getName());
+ }
+
+ @Override
+ protected String getOperationName() {
+ return OPERATION_NAME;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mdsal.connector.ops;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class Unlock extends AbstractLastNetconfOperation{
+
+ private static final Logger LOG = LoggerFactory.getLogger(Unlock.class);
+
+ private static final String OPERATION_NAME = "unlock";
+ private static final String TARGET_KEY = "target";
+
+ public Unlock(final String netconfSessionIdForReporting) {
+ super(netconfSessionIdForReporting);
+ }
+
+ @Override
+ protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException {
+ final Datastore targetDatastore = Lock.extractTargetParameter(operationElement);
+ if (targetDatastore == Datastore.candidate) {
+ LOG.debug("Unlocking candidate datastore on session: {}", getNetconfSessionIdForReporting());
+ return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.<String>absent());
+ }
+
+ throw new NetconfDocumentedException("Unable to unlock " + targetDatastore + " datastore", NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.operation_not_supported, NetconfDocumentedException.ErrorSeverity.error);
+ }
+
+ @Override
+ protected String getOperationName() {
+ return OPERATION_NAME;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mdsal.connector.ops.get;
+
+import com.google.common.base.Function;
+import com.google.common.base.Throwables;
+import com.google.common.collect.Iterables;
+import java.io.IOException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.dom.DOMResult;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.mdsal.connector.CurrentSchemaContext;
+import org.opendaylight.controller.netconf.mdsal.connector.ops.Datastore;
+import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+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.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+public abstract class AbstractGet extends AbstractLastNetconfOperation {
+
+ protected static final YangInstanceIdentifier ROOT = YangInstanceIdentifier.builder().build();
+
+ protected final CurrentSchemaContext schemaContext;
+
+
+ public AbstractGet(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext) {
+ super(netconfSessionIdForReporting);
+ this.schemaContext = schemaContext;
+ }
+
+ private static final XMLOutputFactory XML_OUTPUT_FACTORY;
+
+ static {
+ XML_OUTPUT_FACTORY = XMLOutputFactory.newFactory();
+ XML_OUTPUT_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
+ }
+
+ protected Node transformNormalizedNode(final Document document, final NormalizedNode<?, ?> data, final YangInstanceIdentifier dataRoot) {
+// boolean isDataRoot = true;
+
+ final DOMResult result = new DOMResult(document.createElement(XmlNetconfConstants.DATA_KEY));
+
+ final XMLStreamWriter xmlWriter = getXmlStreamWriter(result);
+
+ final NormalizedNodeStreamWriter nnStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter,
+ schemaContext.getCurrentContext(), getSchemaPath(dataRoot));
+
+ final NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(nnStreamWriter);
+
+// if (isDataRoot) {
+ writeRootElement(xmlWriter, nnWriter, (ContainerNode) data);
+// } else {
+// if (data instanceof MapEntryNode) {
+// // Restconf allows returning one list item. We need to wrap it
+// // in map node in order to serialize it properly
+// data = ImmutableNodes.mapNodeBuilder(data.getNodeType()).addChild((MapEntryNode) data).build();
+// }
+// nnWriter.write(data);
+// nnWriter.flush();
+// }
+ return result.getNode();
+ }
+
+ private XMLStreamWriter getXmlStreamWriter(final DOMResult result) {
+ try {
+ return XML_OUTPUT_FACTORY.createXMLStreamWriter(result);
+ } catch (final XMLStreamException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static final Function<PathArgument, QName> PATH_ARG_TO_QNAME = new Function<YangInstanceIdentifier.PathArgument, QName>() {
+ @Override
+ public QName apply(final YangInstanceIdentifier.PathArgument input) {
+ return input.getNodeType();
+ }
+ };
+
+ private SchemaPath getSchemaPath(final YangInstanceIdentifier dataRoot) {
+ return SchemaPath.create(Iterables.transform(dataRoot.getPathArguments(), PATH_ARG_TO_QNAME), dataRoot.equals(ROOT));
+ }
+
+ // TODO this code is located in Restconf already
+ private void writeRootElement(final XMLStreamWriter xmlWriter, final NormalizedNodeWriter nnWriter, final ContainerNode data) {
+ try {
+ final QName name = SchemaContext.NAME;
+ for (final DataContainerChild<? extends PathArgument, ?> child : data.getValue()) {
+ nnWriter.write(child);
+ }
+ nnWriter.flush();
+ xmlWriter.flush();
+ } catch (XMLStreamException | IOException e) {
+ Throwables.propagate(e);
+ }
+ }
+
+ protected static final class GetConfigExecution {
+ private final Datastore datastore;
+
+ public GetConfigExecution(final Datastore datastore) {
+ this.datastore = datastore;
+ }
+
+ public Datastore getDatastore() {
+ return datastore;
+ }
+
+ static GetConfigExecution fromXml(final XmlElement xml, final String operationName) throws NetconfDocumentedException {
+ try {
+ validateInputRpc(xml, operationName);
+ } catch (final NetconfDocumentedException e) {
+ throw new NetconfDocumentedException("Incorrect RPC: " + e.getMessage(), e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo());
+ }
+
+ final Datastore sourceDatastore;
+ try {
+ sourceDatastore = parseSource(xml);
+ } catch (final NetconfDocumentedException e) {
+ throw new NetconfDocumentedException("Get-config source attribute error: " + e.getMessage(), e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo());
+ }
+
+ // Add filter
+
+ return new GetConfigExecution(sourceDatastore);
+ }
+
+ private static Datastore parseSource(final XmlElement xml) throws NetconfDocumentedException {
+ final Datastore sourceDatastore;
+ final XmlElement sourceElement = xml.getOnlyChildElement(XmlNetconfConstants.SOURCE_KEY,
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+
+ final String sourceParsed = sourceElement.getOnlyChildElement().getName();
+ sourceDatastore = Datastore.valueOf(sourceParsed);
+ return sourceDatastore;
+ }
+
+ private static void validateInputRpc(final XmlElement xml, String operationName) throws NetconfDocumentedException{
+ xml.checkName(operationName);
+ xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mdsal.connector.ops.get;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.mdsal.connector.CurrentSchemaContext;
+import org.opendaylight.controller.netconf.mdsal.connector.TransactionProvider;
+import org.opendaylight.controller.netconf.mdsal.connector.ops.Datastore;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class Get extends AbstractGet {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Get.class);
+
+ private static final String OPERATION_NAME = "get";
+
+ private final TransactionProvider transactionProvider;
+
+ public Get(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext, final TransactionProvider transactionProvider) {
+ super(netconfSessionIdForReporting, schemaContext);
+ this.transactionProvider = transactionProvider;
+ }
+
+ @Override
+ protected Element handleWithNoSubsequentOperations(Document document, XmlElement operationElement) throws NetconfDocumentedException {
+ GetConfigExecution getConfigExecution = null;
+ try {
+ getConfigExecution = GetConfigExecution.fromXml(operationElement, OPERATION_NAME);
+
+ } catch (final NetconfDocumentedException e) {
+ LOG.warn("Get request processing failed on session: {}", getNetconfSessionIdForReporting(), e);
+ throw e;
+ }
+
+ final YangInstanceIdentifier dataRoot = ROOT;
+ DOMDataReadWriteTransaction rwTx = getTransaction(getConfigExecution.getDatastore());
+ try {
+ final Optional<NormalizedNode<?, ?>> normalizedNodeOptional = rwTx.read(LogicalDatastoreType.OPERATIONAL, dataRoot).checkedGet();
+ if (getConfigExecution.getDatastore() == Datastore.running) {
+ transactionProvider.abortRunningTransaction(rwTx);
+ rwTx = null;
+ }
+ return (Element) transformNormalizedNode(document, normalizedNodeOptional.get(), dataRoot);
+ } catch (ReadFailedException e) {
+ LOG.warn("Unable to read data: {}", dataRoot, e);
+ throw new IllegalStateException("Unable to read data " + dataRoot, e);
+ }
+ }
+
+ private DOMDataReadWriteTransaction getTransaction(Datastore datastore) throws NetconfDocumentedException{
+ if (datastore == Datastore.candidate) {
+ return transactionProvider.getOrCreateTransaction();
+ } else if (datastore == Datastore.running) {
+ return transactionProvider.createRunningTransaction();
+ }
+ throw new NetconfDocumentedException("Incorrect Datastore: ", ErrorType.protocol, ErrorTag.bad_element, ErrorSeverity.error);
+ }
+
+ @Override
+ protected String getOperationName() {
+ return OPERATION_NAME;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.mdsal.connector.ops.get;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.mdsal.connector.CurrentSchemaContext;
+import org.opendaylight.controller.netconf.mdsal.connector.TransactionProvider;
+import org.opendaylight.controller.netconf.mdsal.connector.ops.Datastore;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class GetConfig extends AbstractGet {
+
+ private static final Logger LOG = LoggerFactory.getLogger(GetConfig.class);
+
+ private static final String OPERATION_NAME = "get-config";
+
+ private final TransactionProvider transactionProvider;
+
+ public GetConfig(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext, final TransactionProvider transactionProvider) {
+ super(netconfSessionIdForReporting, schemaContext);
+ this.transactionProvider = transactionProvider;
+ }
+
+ @Override
+ protected Element handleWithNoSubsequentOperations(Document document, XmlElement operationElement) throws NetconfDocumentedException {
+ GetConfigExecution getConfigExecution = null;
+ try {
+ getConfigExecution = GetConfigExecution.fromXml(operationElement, OPERATION_NAME);
+
+ } catch (final NetconfDocumentedException e) {
+ LOG.warn("Get request processing failed on session: {}", getNetconfSessionIdForReporting(), e);
+ throw e;
+ }
+
+ final YangInstanceIdentifier dataRoot = ROOT;
+ DOMDataReadWriteTransaction rwTx = getTransaction(getConfigExecution.getDatastore());
+ try {
+ final Optional<NormalizedNode<?, ?>> normalizedNodeOptional = rwTx.read(LogicalDatastoreType.CONFIGURATION, dataRoot).checkedGet();
+ if (getConfigExecution.getDatastore() == Datastore.running) {
+ transactionProvider.abortRunningTransaction(rwTx);
+ rwTx = null;
+ }
+ return (Element) transformNormalizedNode(document, normalizedNodeOptional.get(), dataRoot);
+ } catch (ReadFailedException e) {
+ LOG.warn("Unable to read data: {}", dataRoot, e);
+ throw new IllegalStateException("Unable to read data " + dataRoot, e);
+ }
+ }
+
+ private DOMDataReadWriteTransaction getTransaction(Datastore datastore) throws NetconfDocumentedException{
+ if (datastore == Datastore.candidate) {
+ return transactionProvider.getOrCreateTransaction();
+ } else if (datastore == Datastore.running) {
+ return transactionProvider.createRunningTransaction();
+ }
+ throw new NetconfDocumentedException("Incorrect Datastore: ", ErrorType.protocol, ErrorTag.bad_element, ErrorSeverity.error);
+ }
+
+ @Override
+ protected String getOperationName() {
+ return OPERATION_NAME;
+ }
+
+}
--- /dev/null
+module netconf-mdsal-mapper {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:mapper";
+ prefix "nmm";
+
+ import netconf-northbound-mapper { prefix nnm; revision-date 2015-01-14; }
+ import opendaylight-md-sal-dom { prefix md-sal-dom; revision-date 2013-10-28; }
+ import config { prefix config; revision-date 2013-04-05; }
+
+ organization "Cisco Systems, Inc.";
+
+ description
+ "This module contains the base YANG definitions for
+ an MD-SAL mapper implementation";
+
+ revision "2015-01-14" {
+ description
+ "Initial revision.";
+ }
+
+ identity netconf-mdsal-mapper {
+ base config:module-type;
+ config:provided-service nnm:netconf-northbound-mapper;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case netconf-mdsal-mapper {
+ when "/config:modules/config:module/config:type = 'netconf-mdsal-mapper'";
+
+ container root-schema-service {
+ uses config:service-ref {
+ refine type {
+ mandatory false;
+ config:required-identity md-sal-dom:schema-service;
+ }
+ }
+ }
+
+ container dom-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory false;
+ config:required-identity md-sal-dom:dom-async-data-broker;
+ }
+ }
+ }
+ }
+ }
+
+}
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <Export-Package>org.opendaylight.controller.netconf.api,
- org.opendaylight.controller.netconf.api.jmx,
- org.opendaylight.controller.netconf.api.xml,
- org.opendaylight.controller.netconf.api.monitoring,</Export-Package>
- </instructions>
- </configuration>
</plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
</plugins>
</build>
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.api;
+
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.local.LocalAddress;
+import java.net.InetSocketAddress;
+
+public interface NetconfServerDispatcher {
+
+ ChannelFuture createServer(InetSocketAddress address);
+
+ ChannelFuture createLocalServer(LocalAddress address);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.api.util;
+
+public final class NetconfConstants {
+ /*
+ * TODO define marker interface in mapping-api that the serviceFactories in cofing subsystem
+ * will implement so we can check for services with instanceof instead of constants
+ */
+ public static final String SERVICE_NAME = "name";
+ public static final String CONFIG_NETCONF_CONNECTOR = "config-netconf-connector";
+ public static final String NETCONF_MONITORING = "ietf-netconf-monitoring";
+}
--- /dev/null
+module netconf-northbound {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound";
+ prefix "nn";
+
+ import config { prefix config; revision-date 2013-04-05; }
+
+ description
+ "This module contains the base YANG definitions for
+ netconf northbound server API";
+
+ revision "2015-01-14" {
+ description
+ "Initial revision.";
+ }
+
+ identity netconf-server-dispatcher {
+ base "config:service-type";
+ config:java-class "org.opendaylight.controller.netconf.api.NetconfServerDispatcher";
+ }
+
+}
\ No newline at end of file
<artifactId>netconf-connector-config</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-mdsal-config</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>mdsal-netconf-connector</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-impl</artifactId>
<name>global-netconf-processing-executor-threadfactory</name>
</threadFactory>
</module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled">prefix:threadpool-scheduled</type>
+ <name>global-netconf-ssh-scheduled-executor</name>
+ <max-thread-count xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled">8</max-thread-count>
+
+ <threadFactory xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
+ <name>global-netconf-processing-executor-threadfactory</name>
+ </threadFactory>
+ </module>
</modules>
<services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
<name>global-netconf-processing-executor</name>
<provider>/modules/module[type='threadpool-flexible'][name='global-netconf-processing-executor']</provider>
</instance>
+ <instance>
+ <name>global-netconf-ssh-scheduled-executor</name>
+ <provider>/modules/module[type='threadpool-scheduled'][name='global-netconf-ssh-scheduled-executor']</provider>
+ </instance>
</service>
</services>
<capability>urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher?module=odl-netconfig-client-cfg&revision=2014-04-08</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible?module=threadpool-impl-flexible&revision=2013-12-01</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled?module=threadpool-impl-scheduled&revision=2013-12-01</capability>
</required-capabilities>
</snapshot>
<name>global-netconf-processing-executor</name>
</processing-executor>
</module>
- </modules>
+ </modules>
</data>
</configuration>
<required-capabilities>
<configuration>
<instructions>
<Bundle-Activator>org.opendaylight.controller.netconf.impl.osgi.NetconfImplActivator</Bundle-Activator>
+ <Export-Package>org.opendaylight.controller.netconf.impl.*</Export-Package>
</instructions>
</configuration>
</plugin>
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
</plugins>
</build>
--- /dev/null
+package org.opendaylight.controller.config.yang.config.netconf.northbound.impl;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.netconf.impl.CommitNotifier;
+import org.opendaylight.controller.netconf.impl.NetconfServerDispatcherImpl;
+import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
+import org.opendaylight.controller.netconf.impl.SessionIdProvider;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+
+public class NetconfServerDispatcherModule extends org.opendaylight.controller.config.yang.config.netconf.northbound.impl.AbstractNetconfServerDispatcherModule {
+ public NetconfServerDispatcherModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NetconfServerDispatcherModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.config.netconf.northbound.impl.NetconfServerDispatcherModule oldModule, java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ JmxAttributeValidationException.checkCondition(getConnectionTimeoutMillis() > 0, "Invalid connection timeout", connectionTimeoutMillisJmxAttribute);
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+
+ final NetconfOperationServiceFactoryListenerImpl aggregatedOpProvider = getAggregatedOpProvider();
+ final SessionMonitoringService monitoringService = startMonitoringService(aggregatedOpProvider);
+ final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
+ getTimerDependency(), aggregatedOpProvider, new SessionIdProvider(), getConnectionTimeoutMillis(), CommitNotifier.NoopCommitNotifier.getInstance(), monitoringService);
+ final NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer(
+ serverNegotiatorFactory);
+
+ return new NetconfServerDispatcherImpl(serverChannelInitializer, getBossThreadGroupDependency(), getWorkerThreadGroupDependency()) {
+
+ @Override
+ public void close() {
+ // NOOP, close should not be present here, the deprecated method closes injected evet loop groups
+ }
+ };
+
+ }
+
+ private NetconfMonitoringServiceImpl startMonitoringService(final NetconfOperationServiceFactoryListenerImpl netconfOperationProvider) {
+ return new NetconfMonitoringServiceImpl(netconfOperationProvider);
+ }
+
+ private NetconfOperationServiceFactoryListenerImpl getAggregatedOpProvider() {
+ final NetconfOperationServiceFactoryListenerImpl netconfOperationProvider = new NetconfOperationServiceFactoryListenerImpl();
+ for (final NetconfOperationServiceFactory netconfOperationServiceFactory : getMappersDependency()) {
+ netconfOperationProvider.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory);
+ }
+ return netconfOperationProvider;
+ }
+
+
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: netconf-northbound-impl yang module local name: netconf-server-dispatcher-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Thu Feb 12 11:32:29 CET 2015
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.netconf.northbound.impl;
+public class NetconfServerDispatcherModuleFactory extends org.opendaylight.controller.config.yang.config.netconf.northbound.impl.AbstractNetconfServerDispatcherModuleFactory {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.impl;
+
+import java.util.Set;
+import org.w3c.dom.Element;
+
+public interface CommitNotifier {
+ void sendCommitNotification(String message, Element cfgSnapshot, Set<String> capabilities);
+
+ public static final class NoopCommitNotifier implements CommitNotifier {
+
+ private static final CommitNotifier INSTANCE = new NoopCommitNotifier();
+
+ private NoopCommitNotifier() {}
+
+ public static CommitNotifier getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public void sendCommitNotification(final String message, final Element cfgSnapshot, final Set<String> capabilities) {
+ // NOOP
+ }
+ }
+}
import org.w3c.dom.Element;
public class DefaultCommitNotificationProducer extends NotificationBroadcasterSupport implements
- DefaultCommitOperationMXBean, AutoCloseable {
+ DefaultCommitOperationMXBean, AutoCloseable, CommitNotifier {
private static final Logger LOG = LoggerFactory.getLogger(DefaultCommitNotificationProducer.class);
}
}
+ @Override
public void sendCommitNotification(String message, Element cfgSnapshot, Set<String> capabilities) {
CommitJMXNotification notif = NetconfJMXNotification.afterCommit(this, message, cfgSnapshot, capabilities);
LOG.debug("Notification about commit {} sent", notif);
package org.opendaylight.controller.netconf.impl;
-import com.google.common.annotations.VisibleForTesting;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.concurrent.Promise;
import java.net.InetSocketAddress;
+import org.opendaylight.controller.netconf.api.NetconfServerDispatcher;
import org.opendaylight.controller.netconf.impl.util.DeserializerExceptionHandler;
import org.opendaylight.controller.netconf.nettyutil.AbstractChannelInitializer;
import org.opendaylight.protocol.framework.AbstractDispatcher;
-public class NetconfServerDispatcher extends AbstractDispatcher<NetconfServerSession, NetconfServerSessionListener> {
+public class NetconfServerDispatcherImpl extends AbstractDispatcher<NetconfServerSession, NetconfServerSessionListener> implements NetconfServerDispatcher {
private final ServerChannelInitializer initializer;
- public NetconfServerDispatcher(ServerChannelInitializer serverChannelInitializer, EventLoopGroup bossGroup,
- EventLoopGroup workerGroup) {
+ public NetconfServerDispatcherImpl(ServerChannelInitializer serverChannelInitializer, EventLoopGroup bossGroup,
+ EventLoopGroup workerGroup) {
super(bossGroup, workerGroup);
this.initializer = serverChannelInitializer;
}
- @VisibleForTesting
+ @Override
public ChannelFuture createServer(InetSocketAddress address) {
-
return super.createServer(address, new PipelineInitializer<NetconfServerSession>() {
@Override
public void initializeChannel(final SocketChannel ch, final Promise<NetconfServerSession> promise) {
});
}
+ @Override
public ChannelFuture createLocalServer(LocalAddress address) {
return super.createServer(address, LocalServerChannel.class, new ChannelPipelineInitializer<LocalChannel, NetconfServerSession>() {
@Override
public class NetconfServerSessionListenerFactory implements SessionListenerFactory<NetconfServerSessionListener> {
- private final DefaultCommitNotificationProducer commitNotifier;
+ private final CommitNotifier commitNotifier;
private final SessionMonitoringService monitor;
private final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot;
private final CapabilityProvider capabilityProvider;
- public NetconfServerSessionListenerFactory(final DefaultCommitNotificationProducer commitNotifier,
+ public NetconfServerSessionListenerFactory(final CommitNotifier commitNotifier,
final SessionMonitoringService monitor,
final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot,
final CapabilityProvider capabilityProvider) {
private final SessionIdProvider idProvider;
private final NetconfOperationProvider netconfOperationProvider;
private final long connectionTimeoutMillis;
- private final DefaultCommitNotificationProducer commitNotificationProducer;
+ private final CommitNotifier commitNotificationProducer;
private final SessionMonitoringService monitoringService;
private static final Logger LOG = LoggerFactory.getLogger(NetconfServerSessionNegotiatorFactory.class);
private final Set<String> baseCapabilities;
// TODO too many params, refactor
public NetconfServerSessionNegotiatorFactory(Timer timer, NetconfOperationProvider netconfOperationProvider,
SessionIdProvider idProvider, long connectionTimeoutMillis,
- DefaultCommitNotificationProducer commitNot,
+ CommitNotifier commitNot,
SessionMonitoringService monitoringService) {
this(timer, netconfOperationProvider, idProvider, connectionTimeoutMillis, commitNot, monitoringService, DEFAULT_BASE_CAPABILITIES);
}
// TODO too many params, refactor
public NetconfServerSessionNegotiatorFactory(Timer timer, NetconfOperationProvider netconfOperationProvider,
SessionIdProvider idProvider, long connectionTimeoutMillis,
- DefaultCommitNotificationProducer commitNot,
+ CommitNotifier commitNot,
SessionMonitoringService monitoringService, Set<String> baseCapabilities) {
this.timer = timer;
this.netconfOperationProvider = netconfOperationProvider;
import java.io.InputStream;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
-import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
+import org.opendaylight.controller.netconf.impl.CommitNotifier;
import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
private static final String NOTIFY_ATTR = "notify";
- private final DefaultCommitNotificationProducer notificationProducer;
+ private final CommitNotifier notificationProducer;
private final CapabilityProvider cap;
private final NetconfOperationRouter operationRouter;
- public DefaultCommit(DefaultCommitNotificationProducer notifier, CapabilityProvider cap,
+ public DefaultCommit(CommitNotifier notifier, CapabilityProvider cap,
String netconfSessionIdForReporting, NetconfOperationRouter netconfOperationRouter) {
super(netconfSessionIdForReporting);
this.notificationProducer = notifier;
import java.util.concurrent.TimeUnit;
import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
-import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
+import org.opendaylight.controller.netconf.impl.NetconfServerDispatcherImpl;
import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
import org.opendaylight.controller.netconf.impl.SessionIdProvider;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
eventLoopGroup = new NioEventLoopGroup();
- NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(
+ NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer(
serverNegotiatorFactory);
- NetconfServerDispatcher dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup);
+ NetconfServerDispatcherImpl dispatch = new NetconfServerDispatcherImpl(serverChannelInitializer, eventLoopGroup, eventLoopGroup);
LocalAddress address = NetconfConfigUtil.getNetconfLocalAddress();
LOG.trace("Starting local netconf server at {}", address);
import java.util.Set;
import java.util.TreeMap;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
-import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
+import org.opendaylight.controller.netconf.impl.CommitNotifier;
import org.opendaylight.controller.netconf.impl.NetconfServerSession;
import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
private final Collection<NetconfOperation> allNetconfOperations;
public NetconfOperationRouterImpl(final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot, final CapabilityProvider capabilityProvider,
- final DefaultCommitNotificationProducer commitNotifier) {
+ final CommitNotifier commitNotifier) {
this.netconfOperationServiceSnapshot = Preconditions.checkNotNull(netconfOperationServiceSnapshot);
final String sessionId = netconfOperationServiceSnapshot.getNetconfSessionIdForReporting();
*/
package org.opendaylight.controller.netconf.impl.osgi;
+import org.opendaylight.controller.netconf.api.util.NetconfConstants;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
@Override
public NetconfOperationServiceFactory addingService(ServiceReference<NetconfOperationServiceFactory> reference) {
- NetconfOperationServiceFactory netconfOperationServiceFactory = super.addingService(reference);
- factoriesListener.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory);
- return netconfOperationServiceFactory;
+ Object property = reference.getProperty(NetconfConstants.SERVICE_NAME);
+ if (property != null
+ && (property.equals(NetconfConstants.CONFIG_NETCONF_CONNECTOR)
+ || property.equals(NetconfConstants.NETCONF_MONITORING))) {
+ NetconfOperationServiceFactory netconfOperationServiceFactory = super.addingService(reference);
+ factoriesListener.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory);
+ return netconfOperationServiceFactory;
+ }
+
+ return null;
}
@Override
public void removedService(ServiceReference<NetconfOperationServiceFactory> reference,
NetconfOperationServiceFactory netconfOperationServiceFactory) {
- factoriesListener.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory);
+ if (netconfOperationServiceFactory != null)
+ factoriesListener.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory);
}
}
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module netconf-northbound-impl {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl";
+ prefix "cfg-net-s-i";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import netconf-northbound-mapper { prefix nnm; revision-date 2015-01-14; }
+ import netconf-northbound { prefix nn; revision-date 2015-01-14; }
+ import netty {prefix netty; }
+
+ description
+ "This module contains the base YANG definitions for
+ netconf-server-dispatcher implementation.
+
+ Copyright (c)2013 Cisco Systems, Inc. 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";
+
+ revision "2015-01-12" {
+ description
+ "Initial revision.";
+ }
+
+ identity netconf-server-dispatcher-impl {
+ base config:module-type;
+ config:provided-service nn:netconf-server-dispatcher;
+ config:java-name-prefix NetconfServerDispatcher;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case netconf-server-dispatcher-impl {
+ when "/config:modules/config:module/config:type = 'netconf-server-dispatcher-impl'";
+
+ leaf connection-timeout-millis {
+ description "Specifies timeout in milliseconds after which connection must be established.";
+ type uint32;
+ default 20000;
+ }
+
+ container boss-thread-group {
+ uses config:service-ref {
+ refine type {
+ config:required-identity netty:netty-threadgroup;
+ }
+ }
+ }
+
+ container worker-thread-group {
+ uses config:service-ref {
+ refine type {
+ config:required-identity netty:netty-threadgroup;
+ }
+ }
+ }
+
+ list mappers {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity nnm:netconf-northbound-mapper;
+ }
+ }
+ }
+
+ container timer {
+ uses config:service-ref {
+ refine type {
+ config:required-identity netty:netty-timer;
+ }
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
- NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(serverNegotiatorFactory);
- final NetconfServerDispatcher dispatch = new NetconfServerDispatcher(serverChannelInitializer, nettyGroup, nettyGroup);
+ NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer(serverNegotiatorFactory);
+ final NetconfServerDispatcherImpl dispatch = new NetconfServerDispatcherImpl(serverChannelInitializer, nettyGroup, nettyGroup);
ChannelFuture s = dispatch.createServer(netconfAddress);
s.await();
public class NetconfDispatcherImplTest {
private EventLoopGroup nettyGroup;
- private NetconfServerDispatcher dispatch;
+ private NetconfServerDispatcherImpl dispatch;
private DefaultCommitNotificationProducer commitNot;
private HashedWheelTimer hashedWheelTimer;
NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
hashedWheelTimer, factoriesListener, idProvider, 5000, commitNot, ConcurrentClientsTest.createMockedMonitoringService());
- NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(serverNegotiatorFactory);
+ NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer(serverNegotiatorFactory);
- dispatch = new NetconfServerDispatcher(
+ dispatch = new NetconfServerDispatcherImpl(
serverChannelInitializer, nettyGroup, nettyGroup);
}
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.netconf.api.util.NetconfConstants;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
doNothing().when(listener).onRemoveNetconfOperationServiceFactory(any(NetconfOperationServiceFactory.class));
doReturn(filter).when(context).createFilter(anyString());
doReturn("").when(reference).toString();
+ doReturn(NetconfConstants.CONFIG_NETCONF_CONNECTOR).when(reference).getProperty(NetconfConstants.SERVICE_NAME);
doReturn(factory).when(context).getService(any(ServiceReference.class));
doReturn("").when(factory).toString();
doNothing().when(listener).onAddNetconfOperationServiceFactory(any(NetconfOperationServiceFactory.class));
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreService;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
-import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
+import org.opendaylight.controller.netconf.impl.NetconfServerDispatcherImpl;
import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
import org.opendaylight.controller.netconf.impl.SessionIdProvider;
import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
}
private Channel startNetconfTcpServer(final NetconfOperationServiceFactoryListenerImpl factoriesListener) throws Exception {
- final NetconfServerDispatcher dispatch = createDispatcher(factoriesListener, getNetconfMonitoringService(), getNotificationProducer());
+ final NetconfServerDispatcherImpl dispatch = createDispatcher(factoriesListener, getNetconfMonitoringService(), getNotificationProducer());
final ChannelFuture s;
if(getTcpServerAddress() instanceof LocalAddress) {
return yangDependencies;
}
- protected NetconfServerDispatcher createDispatcher(
+ protected NetconfServerDispatcherImpl createDispatcher(
final NetconfOperationServiceFactoryListenerImpl factoriesListener, final SessionMonitoringService sessionMonitoringService,
final DefaultCommitNotificationProducer commitNotifier) {
final SessionIdProvider idProvider = new SessionIdProvider();
final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
hashedWheelTimer, factoriesListener, idProvider, SERVER_CONNECTION_TIMEOUT_MILLIS, commitNotifier, sessionMonitoringService);
- final NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(
+ final NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer(
serverNegotiatorFactory);
- return new NetconfServerDispatcher(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup);
+ return new NetconfServerDispatcherImpl(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup);
}
protected HashedWheelTimer getHashedWheelTimer() {
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <Export-Package>org.opendaylight.controller.netconf.mapping.api,</Export-Package>
- </instructions>
- </configuration>
</plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
</plugins>
</build>
</project>
--- /dev/null
+module netconf-northbound-mapper {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper";
+ prefix "nnm";
+
+ import config { prefix config; revision-date 2013-04-05; }
+
+ description
+ "This module contains the base YANG definitions for
+ mapping services plugged into a netconf northbound server";
+
+ revision "2015-01-14" {
+ description
+ "Initial revision.";
+ }
+
+ identity netconf-northbound-mapper {
+ base "config:service-type";
+ config:java-class "org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory";
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-subsystem</artifactId>
+ <version>0.3.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>netconf-mdsal-config</artifactId>
+ <description>Configuration files for netconf for mdsal</description>
+ <packaging>jar</packaging>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/initial/08-netconf-mdsal.xml</file>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+
+<snapshot>
+ <configuration>
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:mapper">prefix:netconf-mdsal-mapper</type>
+ <name>netconf-mdsal-mapper</name>
+ <root-schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </root-schema-service>
+ <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:mapper">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+ <name>inmemory-data-broker</name>
+ </dom-broker>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">prefix:netconf-server-dispatcher-impl</type>
+ <name>netconf-mdsal-server-dispatcher</name>
+ <mappers xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">dom:netconf-northbound-mapper</type>
+ <name>netconf-mdsal-mapper</name>
+ </mappers>
+ <boss-thread-group xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
+ <name>global-boss-group</name>
+ </boss-thread-group>
+ <worker-thread-group xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
+ <name>global-worker-group</name>
+ </worker-thread-group>
+ <timer xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-timer</type>
+ <name>global-timer</name>
+ </timer>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">prefix:netconf-northbound-ssh</type>
+ <name>netconf-mdsal-ssh-server</name>
+
+ <event-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>
+ <name>global-event-executor</name>
+ </event-executor>
+ <worker-thread-group xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
+ <name>global-worker-group</name>
+ </worker-thread-group>
+ <processing-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
+ <name>global-netconf-ssh-scheduled-executor</name>
+ </processing-executor>
+ <dispatcher xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound">prefix:netconf-server-dispatcher</type>
+ <name>netconf-mdsal-server-dispatcher</name>
+ </dispatcher>
+
+ <username>admin</username>
+ <password>admin</password>
+ </module>
+
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">prefix:netconf-northbound-mapper</type>
+ <instance>
+ <name>netconf-mdsal-mapper</name>
+ <provider>/modules/module[type='netconf-mdsal-mapper'][name='netconf-mdsal-mapper']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound">prefix:netconf-server-dispatcher</type>
+ <instance>
+ <name>netconf-mdsal-server-dispatcher</name>
+ <provider>/modules/module[type='netconf-server-dispatcher-impl'][name='netconf-mdsal-server-dispatcher']</provider>
+ </instance>
+ </service>
+ </services>
+
+ </data>
+ </configuration>
+ <required-capabilities>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:mapper?module=netconf-mdsal-mapper&revision=2015-01-14</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh?module=netconf-northbound-ssh&revision=2015-01-14</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl?module=netconf-northbound-impl&revision=2015-01-12</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled?module=threadpool-impl-scheduled&revision=2013-12-01</capability>
+ </required-capabilities>
+</snapshot>
package org.opendaylight.controller.netconf.monitoring.osgi;
import com.google.common.base.Preconditions;
+import java.util.Dictionary;
import java.util.Hashtable;
import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.api.util.NetconfConstants;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
final NetconfOperationServiceFactory factory = new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
operationService);
- reg = context.registerService(NetconfOperationServiceFactory.class, factory, new Hashtable<String, Object>());
+ Dictionary<String, String> properties = new Hashtable<>();
+ properties.put(NetconfConstants.SERVICE_NAME, NetconfConstants.NETCONF_MONITORING);
+ reg = context.registerService(NetconfOperationServiceFactory.class, factory, properties);
return netconfMonitoringService;
}
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.Collections;
+import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Set;
+import org.opendaylight.controller.netconf.api.util.NetconfConstants;
import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
}
};
- operationaServiceRegistration = context.registerService(NetconfOperationServiceFactory.class, netconfOperationServiceFactory, new Hashtable<String, Object>());
+ Dictionary<String, String> properties = new Hashtable<>();
+ properties.put(NetconfConstants.SERVICE_NAME, NetconfConstants.NETCONF_MONITORING);
+ operationaServiceRegistration = context.registerService(NetconfOperationServiceFactory.class, netconfOperationServiceFactory, properties);
}
<groupId>${project.groupId}</groupId>
<artifactId>netconf-util</artifactId>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-impl</artifactId>
+ </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>threadpool-config-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netty-config-api</artifactId>
+ <version>0.3.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
</plugins>
</build>
--- /dev/null
+package org.opendaylight.controller.config.yang.netconf.northbound.ssh;
+
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.local.LocalAddress;
+import io.netty.util.concurrent.GenericFutureListener;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.concurrent.Executors;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.apache.sshd.server.session.ServerSession;
+import org.opendaylight.controller.netconf.api.NetconfServerDispatcher;
+import org.opendaylight.controller.netconf.ssh.SshProxyServer;
+import org.opendaylight.controller.netconf.ssh.SshProxyServerConfigurationBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NetconfNorthboundSshModule extends org.opendaylight.controller.config.yang.netconf.northbound.ssh.AbstractNetconfNorthboundSshModule {
+
+ private static final Logger LOG = LoggerFactory.getLogger(NetconfNorthboundSshModule.class);
+
+ public NetconfNorthboundSshModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NetconfNorthboundSshModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.netconf.northbound.ssh.NetconfNorthboundSshModule oldModule, final java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ final NetconfServerDispatcher dispatch = getDispatcherDependency();
+
+ final LocalAddress localAddress = new LocalAddress(getPort().toString());
+ final ChannelFuture localServer = dispatch.createLocalServer(localAddress);
+
+ final SshProxyServer sshProxyServer = new SshProxyServer(Executors.newScheduledThreadPool(1), getWorkerThreadGroupDependency(), getEventExecutorDependency());
+
+ final InetSocketAddress bindingAddress = getInetAddress();
+ final SshProxyServerConfigurationBuilder sshProxyServerConfigurationBuilder = new SshProxyServerConfigurationBuilder();
+ sshProxyServerConfigurationBuilder.setBindingAddress(bindingAddress);
+ sshProxyServerConfigurationBuilder.setLocalAddress(localAddress);
+ sshProxyServerConfigurationBuilder.setAuthenticator(new UserAuthenticator(getUsername(), getPassword()));
+ sshProxyServerConfigurationBuilder.setIdleTimeout(Integer.MAX_VALUE);
+ sshProxyServerConfigurationBuilder.setKeyPairProvider(new PEMGeneratorHostKeyProvider());
+
+ localServer.addListener(new GenericFutureListener<ChannelFuture>() {
+
+ @Override
+ public void operationComplete(final ChannelFuture future) {
+ if(future.isDone() && !future.isCancelled()) {
+ try {
+ sshProxyServer.bind(sshProxyServerConfigurationBuilder.createSshProxyServerConfiguration());
+ LOG.info("Netconf SSH endpoint started successfully at {}", bindingAddress);
+ } catch (final IOException e) {
+ throw new RuntimeException("Unable to start SSH netconf server", e);
+ }
+ } else {
+ LOG.warn("Unable to start SSH netconf server at {}", bindingAddress, future.cause());
+ throw new RuntimeException("Unable to start SSH netconf server", future.cause());
+ }
+ }
+ });
+
+ return new NetconfServerCloseable(localServer, sshProxyServer);
+ }
+
+ private InetSocketAddress getInetAddress() {
+ try {
+ final InetAddress inetAd = InetAddress.getByName(getBindingAddress().getIpv4Address() == null ? getBindingAddress().getIpv6Address().getValue() : getBindingAddress().getIpv4Address().getValue());
+ return new InetSocketAddress(inetAd, getPort().getValue());
+ } catch (final UnknownHostException e) {
+ throw new IllegalArgumentException("Unable to bind netconf endpoint to address " + getBindingAddress(), e);
+ }
+ }
+
+ private static final class NetconfServerCloseable implements AutoCloseable {
+ private final ChannelFuture localServer;
+ private final SshProxyServer sshProxyServer;
+
+ public NetconfServerCloseable(final ChannelFuture localServer, final SshProxyServer sshProxyServer) {
+ this.localServer = localServer;
+ this.sshProxyServer = sshProxyServer;
+ }
+
+ @Override
+ public void close() throws Exception {
+ sshProxyServer.close();
+
+ if(localServer.isDone()) {
+ localServer.channel().close();
+ } else {
+ localServer.cancel(true);
+ }
+ }
+ }
+
+
+ private static final class UserAuthenticator implements PasswordAuthenticator {
+
+ private final String username;
+ private final String password;
+
+ public UserAuthenticator(final String username, final String password) {
+ this.username = username;
+ this.password = password;
+ }
+
+ @Override
+ public boolean authenticate(final String username, final String password, final ServerSession session) {
+ // FIXME use aaa stuff here instead
+ return this.username.equals(username) && this.password.equals(password);
+ }
+ }
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: netconf-northbound-ssh yang module local name: netconf-northbound-ssh
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Mon Feb 09 14:09:07 CET 2015
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.netconf.northbound.ssh;
+public class NetconfNorthboundSshModuleFactory extends org.opendaylight.controller.config.yang.netconf.northbound.ssh.AbstractNetconfNorthboundSshModuleFactory {
+
+}
--- /dev/null
+module netconf-northbound-ssh {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh";
+ prefix "nni";
+
+ import netconf-northbound-mapper { prefix nnm; revision-date 2015-01-14; }
+ import netconf-northbound { prefix nn; revision-date 2015-01-14; }
+ import config { prefix config; revision-date 2013-04-05; }
+ import threadpool {prefix th;}
+ import netty {prefix netty;}
+ import ietf-inet-types { prefix inet; revision-date 2010-09-24; }
+
+ organization "Cisco Systems, Inc.";
+
+ description
+ "This module contains the base YANG definitions for
+ a default implementation of netconf northbound server";
+
+ revision "2015-01-14" {
+ description
+ "Initial revision.";
+ }
+
+ identity netconf-northbound-ssh {
+ base config:module-type;
+ config:java-name-prefix NetconfNorthboundSsh;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case netconf-northbound-ssh {
+ when "/config:modules/config:module/config:type = 'netconf-northbound-ssh'";
+
+ leaf port {
+ type inet:port-number;
+ default 2830;
+ }
+
+ leaf binding-address {
+ type inet:ip-address;
+ default "0.0.0.0";
+ }
+
+ container processing-executor {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity th:scheduled-threadpool;
+ }
+ }
+
+ description "Required by the mina-ssh library used in SSH endpoint";
+ }
+
+ container event-executor {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity netty:netty-event-executor;
+ }
+ }
+ }
+
+ container worker-thread-group {
+ uses config:service-ref {
+ refine type {
+ config:required-identity netty:netty-threadgroup;
+ }
+ }
+ }
+
+ container dispatcher {
+ uses config:service-ref {
+ refine type {
+ config:required-identity nn:netconf-server-dispatcher;
+ }
+ }
+ }
+
+ // FIXME use auth provider from aaa instead
+ leaf username {
+ description "Specifies username credential";
+ type string;
+ }
+
+ leaf password {
+ description "Specifies password credential";
+ type string;
+ }
+
+
+ }
+ }
+
+}
\ No newline at end of file
import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
-import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
+import org.opendaylight.controller.netconf.impl.NetconfServerDispatcherImpl;
import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
import org.opendaylight.controller.netconf.impl.SessionIdProvider;
import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
this.nioExecutor = nioExecutor;
}
- private NetconfServerDispatcher createDispatcher(final Map<ModuleBuilder, String> moduleBuilders, final boolean exi, final int generateConfigsTimeout, final Optional<File> notificationsFile) {
+ private NetconfServerDispatcherImpl createDispatcher(final Map<ModuleBuilder, String> moduleBuilders, final boolean exi, final int generateConfigsTimeout, final Optional<File> notificationsFile) {
final Set<Capability> capabilities = Sets.newHashSet(Collections2.transform(moduleBuilders.keySet(), new Function<ModuleBuilder, Capability>() {
@Override
final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
hashedWheelTimer, simulatedOperationProvider, idProvider, generateConfigsTimeout, commitNotifier, new LoggingMonitoringService(), serverCapabilities);
- final NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(
+ final NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer(
serverNegotiatorFactory);
- return new NetconfServerDispatcher(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup);
+ return new NetconfServerDispatcherImpl(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup);
}
private Map<ModuleBuilder, String> toModuleBuilders(final Map<SourceIdentifier, Map.Entry<ASTSchemaSource, YangTextSchemaSource>> sources) {
final Map<ModuleBuilder, String> moduleBuilders = parseSchemasToModuleBuilders(params);
- final NetconfServerDispatcher dispatcher = createDispatcher(moduleBuilders, params.exi, params.generateConfigsTimeout, Optional.fromNullable(params.notificationFile));
+ final NetconfServerDispatcherImpl dispatcher = createDispatcher(moduleBuilders, params.exi, params.generateConfigsTimeout, Optional.fromNullable(params.notificationFile));
int currentPort = params.startingPort;
<module>netconf-config</module>
<module>netconf-impl</module>
<module>config-netconf-connector</module>
+ <module>mdsal-netconf-connector</module>
<module>netconf-util</module>
<module>netconf-netty-util</module>
<module>config-persister-impl</module>
<module>ietf-netconf-notifications</module>
<module>ietf-netconf-monitoring-extension</module>
<module>netconf-connector-config</module>
+ <module>netconf-mdsal-config</module>
<module>netconf-auth</module>
<module>netconf-usermanager</module>
<module>netconf-testtool</module>