import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
import org.opendaylight.controller.sal.packet.IDataPacketService;
import org.opendaylight.controller.sal.packet.IListenDataPacket;
-import org.opendaylight.controller.sal.routing.IRouting;
import org.opendaylight.controller.switchmanager.ISwitchManager;
import org.opendaylight.controller.topologymanager.ITopologyManager;
import org.slf4j.Logger;
* instantiated in order to get an fully working implementation
* Object
*/
+ @Override
public Object[] getImplementations() {
Object[] res = { ArpHandler.class };
return res;
* also optional per-container different behavior if needed, usually
* should not be the case though.
*/
+ @Override
public void configureInstance(Component c, Object imp, String containerName) {
if (imp.equals(ArpHandler.class)) {
// export the service
"setClusterContainerService", "unsetClusterContainerService")
.setRequired(true));
- c.add(createContainerServiceDependency(containerName).setService(
- IRouting.class).setCallbacks("setRouting","unsetRouting")
- .setRequired(false));
-
// the Host Listener is optional
c.add(createContainerServiceDependency(containerName).setService(
IfHostListener.class).setCallbacks("setHostListener",
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/**
+ * The ArpHandler offers services to react on ARP requests and replies
+ * sent by network hosts. Moreover it allows for creating ARP messages
+ * by the controller itself.
+ *
+ * The ARP Handler on ODL doesn't use the requester MAC address in
+ * order to avoid to have to build a spanning tree where to forward
+ * ARP Requests. The ARP requests are broadcast packets so in order to
+ * reach everywhere need to be flooded, when you flood in a network
+ * that is not a tree (all the networks has some level of redundancy)
+ * that would create forwarding loops without a spanning tree. Given
+ * the need is only to send out the ARP requests toward all the hosts
+ * we actually don't need to implement a flooding mechanism in software
+ * (which would be expensive) we just send out the ARP request toward
+ * all the ports that are suspected to be host ports on all the
+ * switches (from the controller). Now the condition for which a port
+ * is marked as host port could potentially be incorrect so when the
+ * controller sends out the ARP Request that could come back to the
+ * controller and could cause another request not needed. So changing
+ * the source MAC address of the request to be the one of the controller,
+ * controller can protect itself from honoring twice the same request.
+ * This enables an ARP handler resolution, without the need of spanning
+ * tree and limiting software flooding to the minimum required.
+ */
+
public class ArpHandler implements IHostFinder, IListenDataPacket, ICacheUpdateAware<ARPEvent, Boolean> {
private static final Logger log = LoggerFactory.getLogger(ArpHandler.class);
static final String ARP_EVENT_CACHE_NAME = "arphandler.arpRequestReplyEvent";
}
}
- void setRouting(IRouting r) {
- this.routing = r;
- }
-
- void unsetRouting(IRouting r) {
- if (this.routing == r) {
- this.routing = null;
- }
- }
-
void setHostListener(IfHostListener s) {
if (this.hostListeners != null) {
this.hostListeners.add(s);
// see if we know about the host
// Hosttracker hosts db key implementation
- IHostId id = HostIdFactory.create(dIP, null);
- HostNodeConnector host = hostTracker.hostFind(id);
+ HostNodeConnector host = hostTracker.hostFind(dIP);
if (host == null) {
- // if we don't, know about the host, try to find it
+ // if we don't know about the host, try to find it
log.trace("Punted IP pkt to {}, sending bcast ARP event...", dIP);
/*
* unknown destination host, initiate bcast ARP request
*/
arpRequestReplyEvent.put(new ARPRequest(dIP, subnet), false);
- } else if (routing == null || routing.getRoute(p.getNode(), host.getnodeconnectorNode()) != null) {
- /*
- * if IRouting is available, make sure that this packet can get it's
- * destination normally before teleporting it there. If it's not
- * available, then assume it's reachable.
- *
- * TODO: come up with a way to do this in the absence of IRouting
- */
-
- log.trace("forwarding punted IP pkt to {} received at {}", dIP, p);
-
- /*
- * if we know where the host is and there's a path from where this
- * packet was punted to where the host is, then deliver it to the
- * host for now
- */
- NodeConnector nc = host.getnodeConnector();
-
- // re-encode the Ethernet packet (the parent of the IPv4 packet)
- RawPacket rp = this.dataPacketService.encodeDataPacket(pkt.getParent());
- rp.setOutgoingNodeConnector(nc);
- this.dataPacketService.transmitDataPacket(rp);
} else {
- log.trace("ignoring punted IP pkt to {} because there is no route from {}", dIP, p);
+ log.trace("Ignoring punted IP pkt to known host: {} (received on: {})", dIP, p);
}
}
<commons.httpclient.version>0.1.2-SNAPSHOT</commons.httpclient.version>
<concepts.version>0.5.2-SNAPSHOT</concepts.version>
<protocol-framework.version>0.5.0-SNAPSHOT</protocol-framework.version>
- <netty.version>4.0.10.Final</netty.version>
+ <netty.version>4.0.17.Final</netty.version>
<commons.io.version>2.4</commons.io.version>
<bundlescanner.version>0.4.2-SNAPSHOT</bundlescanner.version>
<usermanager.version>0.4.2-SNAPSHOT</usermanager.version>
<artifactId>concepts</artifactId>
<version>${concepts.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-remoterpc-connector</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
<!-- config-->
<artifactId>config-persister-api</artifactId>
<version>${config.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
- <version>${config.version}</version>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-file-xml-adapter</artifactId>
<version>${config.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-adapter</artifactId>
- <version>${config.version}</version>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-directory-xml-adapter</artifactId>
<version>${config.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-autodetect-adapter</artifactId>
- <version>${config.version}</version>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>configuration</artifactId>
<version>${configuration.version}</version>
</dependency>
-
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ <version>1.50</version>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ <version>1.50</version>
+ </dependency>
</dependencies>
</dependencyManagement>
<maven>3.0.4</maven>
</prerequisites>
+ <properties>
+ <jmxGeneratorPath>${project.build.directory}/generated-sources/config</jmxGeneratorPath>
+ <salGeneratorPath>${project.build.directory}/generated-sources/sal</salGeneratorPath>
+ </properties>
+
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netty-config-api</artifactId>
+ </dependency>
<!-- Testing dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-manager</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-manager</artifactId>
+ <version>${config.version}</version>
+ <scope>test</scope>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-util</artifactId>
+ <version>${config.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netty-event-executor-config</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
</execution>
</executions>
</plugin>
+ <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.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>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>maven-sal-api-gen-plugin</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>add-source</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${jmxGeneratorPath}</source>
+ <source>${salGeneratorPath}</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
</project>
--- /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.protocol.framework;
+
+import io.netty.util.concurrent.EventExecutor;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.protocol.framework.NeverReconnectStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
+
+/**
+*
+*/
+public final class NeverReconnectStrategyFactoryModule extends org.opendaylight.controller.config.yang.protocol.framework.AbstractNeverReconnectStrategyFactoryModule
+ {
+
+ public NeverReconnectStrategyFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NeverReconnectStrategyFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ NeverReconnectStrategyFactoryModule oldModule, java.lang.AutoCloseable oldInstance) {
+
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ protected void customValidation(){
+ JmxAttributeValidationException.checkNotNull(getTimeout(), "value is not set.", timeoutJmxAttribute);
+ JmxAttributeValidationException.checkCondition(getTimeout() >= 0, "value " + getTimeout() + " is less than 0",
+ timeoutJmxAttribute);
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ return new NeverReconnectStrategyFactoryCloseable(getExecutorDependency(), getTimeout());
+ }
+
+ private static final class NeverReconnectStrategyFactoryCloseable implements ReconnectStrategyFactory, AutoCloseable {
+
+ private final EventExecutor executor;
+ private final int timeout;
+
+ public NeverReconnectStrategyFactoryCloseable(final EventExecutor executor, final int timeout) {
+ this.executor = executor;
+ this.timeout = timeout;
+ }
+
+ @Override
+ public void close() throws Exception {
+ // no-op
+ }
+
+ @Override
+ public ReconnectStrategy createReconnectStrategy() {
+ return new NeverReconnectStrategy(this.executor, this.timeout);
+ }
+
+ }
+}
--- /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.protocol.framework;
+
+/**
+*
+*/
+public class NeverReconnectStrategyFactoryModuleFactory extends org.opendaylight.controller.config.yang.protocol.framework.AbstractNeverReconnectStrategyFactoryModuleFactory
+{
+
+
+}
--- /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.protocol.framework;
+
+import io.netty.util.concurrent.EventExecutor;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.protocol.framework.ReconnectImmediatelyStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
+
+/**
+*
+*/
+public final class ReconnectImmediatelyStrategyFactoryModule extends org.opendaylight.controller.config.yang.protocol.framework.AbstractReconnectImmediatelyStrategyFactoryModule
+ {
+
+ public ReconnectImmediatelyStrategyFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public ReconnectImmediatelyStrategyFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ ReconnectImmediatelyStrategyFactoryModule oldModule, java.lang.AutoCloseable oldInstance) {
+
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ protected void customValidation(){
+ JmxAttributeValidationException.checkNotNull(getTimeout(), "value is not set.", timeoutJmxAttribute);
+ JmxAttributeValidationException.checkCondition(getTimeout() >= 0, "value " + getTimeout() + " is less than 0",
+ timeoutJmxAttribute);
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ return new ReconnectImmediatelyStrategyFactoryCloseable(getExecutorDependency(), getTimeout());
+ }
+
+ private static final class ReconnectImmediatelyStrategyFactoryCloseable implements ReconnectStrategyFactory, AutoCloseable {
+
+ private final EventExecutor executor;
+ private final int timeout;
+
+ public ReconnectImmediatelyStrategyFactoryCloseable(final EventExecutor executor, final int timeout) {
+ this.executor = executor;
+ this.timeout = timeout;
+ }
+
+ @Override
+ public void close() throws Exception {
+ // no-op
+ }
+
+ @Override
+ public ReconnectStrategy createReconnectStrategy() {
+ return new ReconnectImmediatelyStrategy(this.executor, this.timeout);
+ }
+
+ }
+}
--- /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.protocol.framework;
+
+/**
+*
+*/
+public class ReconnectImmediatelyStrategyFactoryModuleFactory extends org.opendaylight.controller.config.yang.protocol.framework.AbstractReconnectImmediatelyStrategyFactoryModuleFactory
+{
+
+
+}
--- /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.protocol.framework;
+
+import io.netty.util.concurrent.EventExecutor;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
+import org.opendaylight.protocol.framework.TimedReconnectStrategy;
+
+import com.google.common.base.Preconditions;
+
+/**
+*
+*/
+public final class TimedReconnectStrategyFactoryModule extends org.opendaylight.controller.config.yang.protocol.framework.AbstractTimedReconnectStrategyFactoryModule
+ {
+
+ public TimedReconnectStrategyFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public TimedReconnectStrategyFactoryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ TimedReconnectStrategyFactoryModule oldModule, java.lang.AutoCloseable oldInstance) {
+
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ protected void customValidation(){
+ JmxAttributeValidationException.checkNotNull(getSleepFactor(), "value is not set.", sleepFactorJmxAttribute);
+ JmxAttributeValidationException.checkCondition(getSleepFactor().doubleValue() >= 1, "value " + getSleepFactor()
+ + " is less than 1", sleepFactorJmxAttribute);
+
+ JmxAttributeValidationException.checkNotNull(getConnectTime(), "value is not set.", connectTimeJmxAttribute);
+ JmxAttributeValidationException.checkCondition(getConnectTime() >= 0, "value " + getConnectTime()
+ + " is less than 0", connectTimeJmxAttribute);
+
+ JmxAttributeValidationException.checkNotNull(getMinSleep(), "value is not set.", minSleepJmxAttribute);
+ JmxAttributeValidationException.checkCondition(getMaxSleep() == null || getMinSleep() <= getMaxSleep(),
+ "value " + getMinSleep() + " is greter than MaxSleep " + getMaxSleep(), minSleepJmxAttribute);
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ return new TimedReconnectStrategyFactoryCloseable(getExecutorDependency(),
+ getConnectTime(), getMinSleep(), getSleepFactor().doubleValue(), getMaxSleep(), getMaxAttempts(),
+ getDeadline());
+ }
+
+ private static final class TimedReconnectStrategyFactoryCloseable implements ReconnectStrategyFactory, AutoCloseable {
+
+ private final EventExecutor executor;
+ private final Long deadline, maxAttempts, maxSleep;
+ private final double sleepFactor;
+ private final int connectTime;
+ private final long minSleep;
+
+ public TimedReconnectStrategyFactoryCloseable(final EventExecutor executor, final int connectTime, final long minSleep, final double sleepFactor,
+ final Long maxSleep, final Long maxAttempts, final Long deadline) {
+ Preconditions.checkArgument(maxSleep == null || minSleep <= maxSleep);
+ Preconditions.checkArgument(sleepFactor >= 1);
+ Preconditions.checkArgument(connectTime >= 0);
+ this.executor = Preconditions.checkNotNull(executor);
+ this.deadline = deadline;
+ this.maxAttempts = maxAttempts;
+ this.minSleep = minSleep;
+ this.maxSleep = maxSleep;
+ this.sleepFactor = sleepFactor;
+ this.connectTime = connectTime;
+ }
+
+ @Override
+ public void close() throws Exception {
+ // no-op
+ }
+
+ @Override
+ public ReconnectStrategy createReconnectStrategy() {
+ return new TimedReconnectStrategy(this.executor,
+ this.connectTime, this.minSleep, this.sleepFactor, this.maxSleep, this.maxAttempts,
+ this.deadline);
+ }
+
+ }
+}
--- /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.protocol.framework;
+
+/**
+*
+*/
+public class TimedReconnectStrategyFactoryModuleFactory extends org.opendaylight.controller.config.yang.protocol.framework.AbstractTimedReconnectStrategyFactoryModuleFactory
+{
+
+
+}
});
b.childOption(ChannelOption.SO_KEEPALIVE, true);
+ customizeBootstrap(b);
+
// Bind and start to accept incoming connections.
final ChannelFuture f = b.bind(address);
LOG.debug("Initiated server {} at {}.", f, address);
return f;
+ }
+ /**
+ * Customize a server bootstrap before the server is created. This allows
+ * subclasses to assign non-default server options before the server is
+ * created.
+ *
+ * @param b Server bootstrap
+ */
+ protected void customizeBootstrap(final ServerBootstrap b) {
+ // The default is a no-op
}
/**
initializer.initializeChannel(ch, p);
}
});
+
+ customizeBootstrap(b);
+
p.connect();
LOG.debug("Client created.");
return p;
}
+ /**
+ * Customize a client bootstrap before the connection is attempted. This
+ * allows subclasses to assign non-default options before the client is
+ * created.
+ *
+ * @param b Client bootstrap
+ */
+ protected void customizeBootstrap(final Bootstrap b) {
+ // The default is a no-op
+ }
+
/**
* Creates a client.
*
p.connect();
return p;
-
}
/**
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module protocol-framework {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:protocol:framework";
+ prefix "pf";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import netty { prefix netty; revision-date 2013-11-19; }
+
+ organization "Cisco Systems, Inc.";
+
+ contact "Milos Fabian <milfabia@cisco.com>";
+
+ description
+ "This module contains the base YANG definitions for
+ protocol framework.
+
+ 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 "2014-03-13" {
+ description
+ "Changed reconnect-strategy to reconnect-strategy-factory - represents ReconnectStrategyFactory.
+ reconnect-strategy-factory modules provides reconnect-strategy-factory service.";
+ }
+
+ revision "2013-11-09" {
+ description
+ "Initial revision";
+ }
+
+ identity reconnect-strategy-factory {
+ description
+ "Service representing a reconnect strategy factory.";
+
+ base "config:service-type";
+ config:java-class "org.opendaylight.protocol.framework.ReconnectStrategyFactory";
+ }
+
+ identity never-reconnect-strategy-factory {
+ base config:module-type;
+ config:provided-service reconnect-strategy-factory;
+ config:java-name-prefix NeverReconnectStrategyFactory;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case never-reconnect-strategy-factory {
+ when "/config:modules/config:module/config:type = 'never-reconnect-strategy-factory'";
+
+ leaf timeout {
+ mandatory true;
+ type int32;
+ }
+
+ container executor {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity netty:netty-event-executor;
+ }
+ }
+ }
+ }
+ }
+
+ identity reconnect-immediately-strategy-factory {
+ base config:module-type;
+ config:provided-service reconnect-strategy-factory;
+ config:java-name-prefix ReconnectImmediatelyStrategyFactory;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case reconnect-immediately-strategy-factory {
+ when "/config:modules/config:module/config:type = 'reconnect-immediately-strategy-factory'";
+
+ leaf timeout {
+ mandatory true;
+ type int32;
+ }
+
+ container executor {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity netty:netty-event-executor;
+ }
+ }
+ }
+ }
+ }
+
+ identity timed-reconnect-strategy-factory {
+ base config:module-type;
+ config:provided-service reconnect-strategy-factory;
+ config:java-name-prefix TimedReconnectStrategyFactory;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case timed-reconnect-strategy-factory {
+ when "/config:modules/config:module/config:type = 'timed-reconnect-strategy-factory'";
+
+ leaf deadline {
+ type int64;
+ units "epoch nanoseconds";
+ }
+
+ leaf max-attempts {
+ mandatory true;
+ type int64;
+ }
+
+ leaf max-sleep {
+ mandatory true;
+ type int64;
+ units "milliseconds";
+ }
+
+ leaf min-sleep {
+ mandatory true;
+ type int64;
+ units "milliseconds";
+ }
+
+ leaf sleep-factor {
+ mandatory true;
+ type decimal64 {
+ fraction-digits 2;
+ }
+ }
+
+ leaf connect-time {
+ mandatory true;
+ type int32;
+ units "milliseconds";
+ }
+
+ container executor {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity netty:netty-event-executor;
+ }
+ }
+ }
+ }
+ }
+}
\ 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.config.yang.protocol.framework;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.netty.eventexecutor.GlobalEventExecutorModuleFactory;
+
+final class GlobalEventExecutorUtil {
+
+ private GlobalEventExecutorUtil() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static ObjectName create(final ConfigTransactionJMXClient transaction) throws InstanceAlreadyExistsException {
+ try {
+ return transaction.lookupConfigBean(GlobalEventExecutorModuleFactory.NAME,
+ GlobalEventExecutorModuleFactory.SINGLETON_NAME);
+ } catch (InstanceNotFoundException e) {
+ try {
+ return transaction.createModule(GlobalEventExecutorModuleFactory.NAME,
+ GlobalEventExecutorModuleFactory.SINGLETON_NAME);
+ } catch (InstanceAlreadyExistsException e1) {
+ throw new IllegalStateException(e1);
+ }
+ }
+ }
+
+}
--- /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.config.yang.protocol.framework;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.netty.eventexecutor.GlobalEventExecutorModuleFactory;
+
+public class NeverReconnectStrategyModuleTest extends AbstractConfigTest {
+
+ private static final String INSTANCE_NAME = "never-reconect-strategy-factory-impl";
+ private static final String FACTORY_NAME = NeverReconnectStrategyFactoryModuleFactory.NAME;
+
+ @Before
+ public void setUp() throws Exception {
+ super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(
+ new NeverReconnectStrategyFactoryModuleFactory(), new GlobalEventExecutorModuleFactory()));
+ }
+
+ @Test
+ public void testValidationExceptionTimeoutNotSet() throws Exception {
+ try {
+ createInstance(null);
+ fail();
+ } catch (ValidationException e) {
+ assertTrue(e.getMessage().contains("Timeout value is not set."));
+ }
+ }
+
+ @Test
+ public void testValidationExceptionTimeoutMinValue() throws Exception {
+ try {
+ createInstance(-1);
+ fail();
+ } catch (ValidationException e) {
+ assertTrue(e.getMessage().contains("is less than 0"));
+ }
+ }
+
+ @Test
+ public void testCreateBean() throws Exception {
+ final CommitStatus status = createInstance();
+ assertBeanCount(1, FACTORY_NAME);
+ assertStatus(status, 2, 0, 0);
+ }
+
+ @Test
+ public void testReusingOldInstance() throws Exception {
+ createInstance();
+ final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ assertBeanCount(1, FACTORY_NAME);
+ final CommitStatus status = transaction.commit();
+ assertBeanCount(1, FACTORY_NAME);
+ assertStatus(status, 0, 0, 2);
+ }
+
+ @Test
+ public void testReconfigure() throws Exception {
+ createInstance();
+ final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ assertBeanCount(1, FACTORY_NAME);
+ final NeverReconnectStrategyFactoryModuleMXBean mxBean = transaction.newMBeanProxy(
+ transaction.lookupConfigBean(FACTORY_NAME, INSTANCE_NAME), NeverReconnectStrategyFactoryModuleMXBean.class);
+ mxBean.setTimeout(200);
+ final CommitStatus status = transaction.commit();
+ assertBeanCount(1, FACTORY_NAME);
+ assertStatus(status, 0, 1, 1);
+ }
+
+ private CommitStatus createInstance() throws Exception {
+ return createInstance(500);
+ }
+
+ private CommitStatus createInstance(final Integer timeout) throws InstanceAlreadyExistsException,
+ ConflictingVersionException, ValidationException {
+ final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction();
+ createInstance(transaction, timeout);
+ return transaction.commit();
+ }
+
+ private static ObjectName createInstance(final ConfigTransactionJMXClient transaction, final Integer timeout)
+ throws InstanceAlreadyExistsException {
+ final ObjectName nameCreated = transaction.createModule(FACTORY_NAME, INSTANCE_NAME);
+ final NeverReconnectStrategyFactoryModuleMXBean mxBean = transaction.newMBeanProxy(nameCreated,
+ NeverReconnectStrategyFactoryModuleMXBean.class);
+ mxBean.setTimeout(timeout);
+ mxBean.setExecutor(GlobalEventExecutorUtil.create(transaction));
+ return nameCreated;
+ }
+
+}
--- /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.config.yang.protocol.framework;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.netty.eventexecutor.GlobalEventExecutorModuleFactory;
+
+public class ReconnectImmediatelyStrategyModuleTest extends AbstractConfigTest {
+
+ private static final String INSTANCE_NAME = "reconnect-immediately-strategy-factory-impl";
+ private static final String FACTORY_NAME = ReconnectImmediatelyStrategyFactoryModuleFactory.NAME;
+
+ @Before
+ public void setUp() throws Exception {
+ super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(
+ new ReconnectImmediatelyStrategyFactoryModuleFactory(), new GlobalEventExecutorModuleFactory()));
+ }
+
+ @Test
+ public void testValidationExceptionTimeoutNotSet() throws Exception {
+ try {
+ createInstance(null);
+ fail();
+ } catch (ValidationException e) {
+ assertTrue(e.getMessage().contains("Timeout value is not set."));
+ }
+ }
+
+ @Test
+ public void testValidationExceptionTimeoutMinValue() throws Exception {
+ try {
+ createInstance(-1);
+ fail();
+ } catch (ValidationException e) {
+ assertTrue(e.getMessage().contains("is less than 0"));
+ }
+ }
+
+ @Test
+ public void testCreateBean() throws Exception {
+ final CommitStatus status = createInstance();
+ assertBeanCount(1, FACTORY_NAME);
+ assertStatus(status, 2, 0, 0);
+ }
+
+ @Test
+ public void testReusingOldInstance() throws Exception {
+ createInstance();
+ final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ assertBeanCount(1, FACTORY_NAME);
+ final CommitStatus status = transaction.commit();
+ assertBeanCount(1, FACTORY_NAME);
+ assertStatus(status, 0, 0, 2);
+ }
+
+ @Test
+ public void testReconfigure() throws Exception {
+ createInstance();
+ final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ assertBeanCount(1, FACTORY_NAME);
+ final ReconnectImmediatelyStrategyFactoryModuleMXBean mxBean = transaction.newMBeanProxy(
+ transaction.lookupConfigBean(FACTORY_NAME, INSTANCE_NAME),
+ ReconnectImmediatelyStrategyFactoryModuleMXBean.class);
+ mxBean.setTimeout(200);
+ final CommitStatus status = transaction.commit();
+ assertBeanCount(1, FACTORY_NAME);
+ assertStatus(status, 0, 1, 1);
+ }
+
+ private CommitStatus createInstance() throws Exception {
+ return createInstance(500);
+ }
+
+ private CommitStatus createInstance(final Integer timeout) throws Exception {
+ final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ createInstance(transaction, timeout);
+ return transaction.commit();
+ }
+
+ private static ObjectName createInstance(final ConfigTransactionJMXClient transaction, final Integer timeout)
+ throws InstanceAlreadyExistsException {
+ final ObjectName nameCreated = transaction.createModule(FACTORY_NAME, INSTANCE_NAME);
+ final ReconnectImmediatelyStrategyFactoryModuleMXBean mxBean = transaction.newMBeanProxy(nameCreated,
+ ReconnectImmediatelyStrategyFactoryModuleMXBean.class);
+ mxBean.setTimeout(timeout);
+ mxBean.setExecutor(GlobalEventExecutorUtil.create(transaction));
+ return nameCreated;
+ }
+
+}
--- /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.config.yang.protocol.framework;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.math.BigDecimal;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.netty.eventexecutor.GlobalEventExecutorModuleFactory;
+
+public class TimedReconnectStrategyModuleTest extends AbstractConfigTest {
+
+ private static final String INSTANCE_NAME = "timed-reconect-stategy-facotry-impl";
+ private static final String FACTORY_NAME = TimedReconnectStrategyFactoryModuleFactory.NAME;
+
+ @Before
+ public void setUp() throws Exception {
+ super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(
+ new TimedReconnectStrategyFactoryModuleFactory(), new GlobalEventExecutorModuleFactory()));
+ }
+
+ @Test
+ public void testValidationExceptionSleepFactorNotSet() throws Exception {
+ try {
+ createInstance(500, 100L, null, 500L, 10L, 10000L);
+ fail();
+ } catch (ValidationException e) {
+ assertTrue(e.getMessage().contains("SleepFactor value is not set."));
+ }
+ }
+
+ @Test
+ public void testValidationExceptionSleepFactorMinValue() throws Exception {
+ try {
+ createInstance(500, 100L, new BigDecimal(0.5), 500L, 10L, 10000L);
+ fail();
+ } catch (ValidationException e) {
+ assertTrue(e.getMessage().contains("is less than 1"));
+ }
+ }
+
+ @Test
+ public void testValidationExceptionConnectTimeNotSet() throws Exception {
+ try {
+ createInstance(null, 100L, new BigDecimal(1.0), 500L, 10L, 10000L);
+ fail();
+ } catch (ValidationException e) {
+ assertTrue(e.getMessage().contains("ConnectTime value is not set."));
+ }
+ }
+
+ @Test
+ public void testValidationExceptionConnectTimeMinValue() throws Exception {
+ try {
+ createInstance(-1, 100L, new BigDecimal(1.0), 500L, 10L, 10000L);
+ fail();
+ } catch (ValidationException e) {
+ assertTrue(e.getMessage().contains("is less than 0"));
+ }
+ }
+
+ @Test
+ public void testValidationExceptionMinSleepNotSet() throws Exception {
+ try {
+ createInstance(100, null, new BigDecimal(1.0), 100L, 10L, 10000L);
+ fail();
+ } catch (ValidationException e) {
+ assertTrue(e.getMessage().contains("MinSleep value is not set."));
+ }
+ }
+
+ @Test
+ public void testValidationExceptionMaxSleep() throws Exception {
+ try {
+ createInstance(100, 300L, new BigDecimal(1.0), 100L, 10L, 10000L);
+ fail();
+ } catch (ValidationException e) {
+ assertTrue(e.getMessage().contains("is greter than MaxSleep"));
+ }
+ }
+
+ @Test
+ public void testCreateBean() throws Exception {
+ final CommitStatus status = createInstance();
+ assertBeanCount(1, FACTORY_NAME);
+ assertStatus(status, 2, 0, 0);
+ }
+
+ @Test
+ public void testReusingOldInstance() throws Exception {
+ createInstance();
+ final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ assertBeanCount(1, FACTORY_NAME);
+ final CommitStatus status = transaction.commit();
+ assertBeanCount(1, FACTORY_NAME);
+ assertStatus(status, 0, 0, 2);
+ }
+
+ @Test
+ public void testReconfigure() throws Exception {
+ createInstance();
+ final ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ assertBeanCount(1, FACTORY_NAME);
+ final TimedReconnectStrategyFactoryModuleMXBean mxBean = transaction.newMBeanProxy(
+ transaction.lookupConfigBean(FACTORY_NAME, INSTANCE_NAME), TimedReconnectStrategyFactoryModuleMXBean.class);
+ assertEquals(mxBean.getMinSleep(), new Long(100));
+ mxBean.setMinSleep(200L);
+ assertEquals(mxBean.getMinSleep(), new Long(200));
+ final CommitStatus status = transaction.commit();
+ assertBeanCount(1, FACTORY_NAME);
+ assertStatus(status, 0, 1, 1);
+
+ }
+
+ private CommitStatus createInstance() throws Exception {
+ return createInstance(500, 100L, new BigDecimal(1.0), 500L, 10L, 10000L);
+ }
+
+ private CommitStatus createInstance(final Integer connectTime, final Long minSleep, final BigDecimal sleepFactor,
+ final Long maxSleep, final Long maxAttempts, final Long deadline) throws ConflictingVersionException,
+ ValidationException, InstanceAlreadyExistsException {
+ ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ createInstance(transaction, INSTANCE_NAME, connectTime, minSleep, sleepFactor, maxSleep, maxAttempts, deadline);
+ return transaction.commit();
+ }
+
+ public static ObjectName createInstance(final ConfigTransactionJMXClient transaction, final String InstanceName)
+ throws Exception {
+ return createInstance(transaction, InstanceName, 500, 100L, new BigDecimal(1.0), 500L, 10L, 10000L);
+ }
+
+ private static ObjectName createInstance(final ConfigTransactionJMXClient transaction, final String instanceName,
+ final Integer connectTime, final Long minSleep, final BigDecimal sleepFactor, final Long maxSleep,
+ final Long maxAttempts, final Long deadline) throws InstanceAlreadyExistsException {
+ final ObjectName nameCreated = transaction.createModule(FACTORY_NAME, instanceName);
+ final TimedReconnectStrategyFactoryModuleMXBean mxBean = transaction.newMBeanProxy(nameCreated,
+ TimedReconnectStrategyFactoryModuleMXBean.class);
+ mxBean.setConnectTime(connectTime);
+ mxBean.setDeadline(deadline);
+ mxBean.setMaxAttempts(maxAttempts);
+ mxBean.setMaxSleep(maxSleep);
+ mxBean.setMinSleep(minSleep);
+ mxBean.setSleepFactor(sleepFactor);
+ mxBean.setExecutor(GlobalEventExecutorUtil.create(transaction));
+ return nameCreated;
+ }
+
+}
Map<String, Map.Entry<ModuleFactory, BundleContext>> allCurrentFactories = Collections.unmodifiableMap(
resolver.getAllFactories());
+
+ // closed by transaction controller
ConfigTransactionLookupRegistry txLookupRegistry = new ConfigTransactionLookupRegistry(new TransactionIdentifier(
transactionName), factory, allCurrentFactories);
ServiceReferenceWritableRegistry writableRegistry = ServiceReferenceRegistryImpl.createSRWritableRegistry(
}
private void internalAbort() {
+ logger.trace("Aborting {}", this);
transactionStatus.setAborted();
close();
}
public void close() {
dependencyResolverManager.close();
+ txLookupRegistry.close();
}
@Override
import org.opendaylight.controller.config.manager.impl.osgi.mapping.RefreshingSCPModuleInfoRegistry;
import org.opendaylight.controller.config.manager.impl.util.OsgiRegistrationUtil;
import org.opendaylight.controller.config.spi.ModuleFactory;
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
-import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import javax.management.MBeanServer;
import java.lang.management.ManagementFactory;
import java.util.Arrays;
-import java.util.Collection;
import java.util.List;
import static org.opendaylight.controller.config.manager.impl.util.OsgiRegistrationUtil.registerService;
// track bundles containing factories
BlankTransactionServiceTracker blankTransactionServiceTracker = new BlankTransactionServiceTracker(
configRegistry);
- ModuleFactoryBundleTracker moduleFactoryBundleTracker = new ModuleFactoryBundleTracker(
+ ModuleFactoryBundleTracker primaryModuleFactoryBundleTracker = new ModuleFactoryBundleTracker(
blankTransactionServiceTracker);
// start extensible tracker
- ExtensibleBundleTracker<Collection<ObjectRegistration<YangModuleInfo>>> bundleTracker = new ExtensibleBundleTracker<>(context, moduleInfoBundleTracker, moduleFactoryBundleTracker);
+ ExtensibleBundleTracker<?> bundleTracker = new ExtensibleBundleTracker<>(context,
+ primaryModuleFactoryBundleTracker, moduleInfoBundleTracker);
bundleTracker.open();
// register config registry to OSGi
*/
package org.opendaylight.controller.config.manager.impl.osgi.mapping;
+import static java.lang.String.format;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
import org.apache.commons.io.IOUtils;
import org.opendaylight.yangtools.concepts.ObjectRegistration;
-import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.sal.binding.generator.api.ModuleInfoRegistry;
import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-
-import static java.lang.String.format;
-
/**
* Tracks bundles and attempts to retrieve YangModuleInfo, which is then fed into ModuleInfoRegistry
*/
return;
}
- for (Registration<YangModuleInfo> reg : regs) {
+ for (ObjectRegistration<YangModuleInfo> reg : regs) {
try {
reg.close();
} catch (Exception e) {
}
@Test
- public void testAbort() throws InstanceAlreadyExistsException, ValidationException {
+ public void testAbort() throws Exception {
ConfigTransactionJMXClient transaction = configRegistryClient
.createTransaction();
assertEquals(1, configRegistryClient.getOpenConfigs().size());
transaction.abortConfig();
+ assertEquals(0, configRegistryClient.getOpenConfigs().size());
try {
- transaction.createModule(TestingFixedThreadPoolModuleFactory.NAME,
- fixed1);
- fail();
- } catch (IllegalStateException e) {
- assertEquals("Configuration was aborted", e.getMessage());
- }
- try {
- transaction.validateConfig();
+ platformMBeanServer.getMBeanInfo(transaction.getObjectName());
fail();
- } catch (IllegalStateException e) {
- assertEquals("Configuration was aborted", e.getMessage());
+ }catch(InstanceNotFoundException e){
}
- assertEquals(0, configRegistryClient.getOpenConfigs().size());
}
@Test
+++ /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>
- <artifactId>config-subsystem</artifactId>
- <groupId>org.opendaylight.controller</groupId>
- <version>0.2.5-SNAPSHOT</version>
- <relativePath>..</relativePath>
- </parent>
- <artifactId>config-persister-directory-adapter</artifactId>
- <name>${project.artifactId}</name>
- <packaging>bundle</packaging>
-
- <dependencies>
- <!-- compile dependencies -->
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>config-persister-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- </dependency>
-
- <!-- test dependencies -->
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>mockito-configuration</artifactId>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>config-persister-api</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
-
- </dependencies>
-
- <build>
- <plugins>
- <!-- workaround for creating version according to OSGi specification (major.minor.micro[.qualifier] -->
- <plugin>
- <groupId>org.codehaus.groovy.maven</groupId>
- <artifactId>gmaven-plugin</artifactId>
- <executions>
- <execution>
- <phase>generate-sources</phase>
- <goals>
- <goal>execute</goal>
- </goals>
- <configuration>
- <source>
- System.setProperty("osgiversion", "${project.version}".replace('-', '.'))
- </source>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <Fragment-Host>${project.groupId}.config-persister-impl;bundle-version=${osgiversion}
- </Fragment-Host>
- <Provide-Capability>org.opendaylight.controller.config.persister.storage.adapter
- </Provide-Capability>
- <Import-Package>
- com.google.common.base,
- com.google.common.io,
- org.apache.commons.io,
- org.opendaylight.controller.config.persist.api,
- org.slf4j
- </Import-Package>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /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.config.persist.storage.directory;
-
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-import org.apache.commons.io.IOUtils;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolderImpl;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-
-public class DirectoryPersister implements Persister {
- private static final Logger logger = LoggerFactory.getLogger(DirectoryPersister.class);
- private static final Charset ENCODING = Charsets.UTF_8;
-
- public static final String MODULES_START = "//MODULES START";
- static final String SERVICES_START = "//SERVICES START";
- static final String CAPABILITIES_START = "//CAPABILITIES START";
-
-
- private final File storage;
- private static final String header, middle, footer;
-
- static {
- header = readResource("header.txt");
- middle = readResource("middle.txt");
- footer = readResource("footer.txt");
- }
-
- public DirectoryPersister(File storage) {
- checkArgument(storage.exists() && storage.isDirectory(), "Storage directory does not exist: " + storage);
- this.storage = storage;
- }
-
- private static String readResource(String resource) {
- try {
- return IOUtils.toString(DirectoryPersister.class.getResourceAsStream("/" + resource));
- } catch (IOException e) {
- throw new IllegalStateException("Cannot load " + resource, e);
- }
- }
-
- @Override
- public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
- throw new UnsupportedOperationException("This adapter is read only. Please set readonly=true on " + getClass());
- }
-
- @Override
- public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
- File[] filesArray = storage.listFiles();
- if (filesArray == null || filesArray.length == 0) {
- return Collections.emptyList();
- }
- List<File> sortedFiles = new ArrayList<>(Arrays.asList(filesArray));
- Collections.sort(sortedFiles);
- // combine all found files
- logger.debug("Reading files in following order: {}", sortedFiles);
-
- List<ConfigSnapshotHolder> result = new ArrayList<>();
- for (File file : sortedFiles) {
- logger.trace("Adding file '{}' to combined result", file);
-
- ConfigSnapshotHolder configSnapshotHolder = loadLastConfig(file);
- result.add(configSnapshotHolder);
- }
- return result;
- }
-
- public static ConfigSnapshotHolder loadLastConfig(File file) throws IOException {
- final MyLineProcessor lineProcessor = new MyLineProcessor(file.getAbsolutePath());
- Files.readLines(file, ENCODING, lineProcessor);
- return lineProcessor.getConfigSnapshotHolder(header, middle, footer);
- }
-
-
- @Override
- public void close() {
-
- }
-
- @Override
- public String toString() {
- return "FileStorageAdapter [storage=" + storage + "]";
- }
-}
-
-class MyLineProcessor implements com.google.common.io.LineProcessor<String> {
- private final String fileNameForReporting;
-
- private boolean inModules, inServices, inCapabilities;
- private final StringBuffer modulesBuffer = new StringBuffer(), servicesBuilder = new StringBuffer();
- private final SortedSet<String> caps = new TreeSet<>();
-
- MyLineProcessor(String fileNameForReporting) {
- this.fileNameForReporting = fileNameForReporting;
- }
-
- @Override
- public String getResult() {
- return null;
- }
-
- @Override
- public boolean processLine(String line) throws IOException {
-
- String lineWithNewLine = line + System.lineSeparator();
- if (line.equals(DirectoryPersister.MODULES_START)) {
- checkState(inModules == false && inServices == false && inCapabilities == false);
- inModules = true;
- } else if (line.equals(DirectoryPersister.SERVICES_START)) {
- checkState(inModules == true && inServices == false && inCapabilities == false);
- inModules = false;
- inServices = true;
- } else if (line.equals(DirectoryPersister.CAPABILITIES_START)) {
- checkState(inModules == false && inServices == true && inCapabilities == false);
- inServices = false;
- inCapabilities = true;
- } else if (inModules) {
- modulesBuffer.append(lineWithNewLine);
- } else if (inServices) {
- servicesBuilder.append(lineWithNewLine);
- } else {
- caps.add(line);
- }
- return true;
- }
-
- private void checkFileConsistency(){
- checkState(inCapabilities, "File %s is missing delimiters in this order: %s", fileNameForReporting,
- Arrays.asList(DirectoryPersister.MODULES_START,
- DirectoryPersister.SERVICES_START,
- DirectoryPersister.CAPABILITIES_START));
- }
-
- String getModules() {
- checkFileConsistency();
- return modulesBuffer.toString();
- }
-
- String getServices() {
- checkFileConsistency();
- return servicesBuilder.toString();
- }
-
- SortedSet<String> getCapabilities() {
- checkFileConsistency();
- return caps;
- }
-
- ConfigSnapshotHolder getConfigSnapshotHolder(String header, String middle, String footer) {
- String combinedSnapshot = header + getModules() + middle + getServices() + footer;
- ConfigSnapshotHolder result = new ConfigSnapshotHolderImpl(combinedSnapshot, getCapabilities(), fileNameForReporting);
- return result;
- }
-
-}
-
+++ /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.config.persist.storage.directory;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.api.PropertiesProvider;
-import org.opendaylight.controller.config.persist.api.StorageAdapter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-
-/**
- * StorageAdapter that retrieves initial configuration from a directory. If multiple files are present, snapshot and
- * required capabilities will be merged together. Writing to this persister is not supported.
- */
-public class DirectoryStorageAdapter implements StorageAdapter {
- private static final Logger logger = LoggerFactory.getLogger(DirectoryStorageAdapter.class);
-
- public static final String DIRECTORY_STORAGE_PROP = "directoryStorage";
-
-
- @Override
- public Persister instantiate(PropertiesProvider propertiesProvider) {
- String fileStorageProperty = propertiesProvider.getProperty(DIRECTORY_STORAGE_PROP);
- Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(DIRECTORY_STORAGE_PROP));
- File storage = new File(fileStorageProperty);
- logger.debug("Using {}", storage);
- return new DirectoryPersister(storage);
- }
-
-}
+++ /dev/null
- </services>
-</data>
+++ /dev/null
-<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+++ /dev/null
- </modules>
- <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+++ /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.config.persist.storage.directory;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import org.apache.commons.io.IOUtils;
-import org.junit.Test;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.test.PropertiesProviderTest;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-public class DirectoryStorageAdapterTest {
- Persister tested;
-
- private Persister instantiatePersisterFromAdapter(File file){
- PropertiesProviderTest pp = new PropertiesProviderTest();
- pp.addProperty("directoryStorage",file.getPath());
- DirectoryStorageAdapter dsa = new DirectoryStorageAdapter();
- return dsa.instantiate(pp);
- }
- @Test
- public void testEmptyDirectory() throws Exception {
- File folder = new File("target/emptyFolder");
- folder.mkdir();
-
- tested = instantiatePersisterFromAdapter(folder);
- assertEquals(Collections.<ConfigSnapshotHolder>emptyList(), tested.loadLastConfigs());
-
- try {
- tested.persistConfig(new ConfigSnapshotHolder() {
- @Override
- public String getConfigSnapshot() {
- throw new RuntimeException();
- }
-
- @Override
- public SortedSet<String> getCapabilities() {
- throw new RuntimeException();
- }
- });
- fail();
- } catch (UnsupportedOperationException e) {
-
- }
- }
-
- private File getFolder(String folderName) {
- File result = new File(("src/test/resources/" +
- folderName).replace("/", File.separator));
- assertTrue(result + " is not a directory", result.isDirectory());
- return result;
- }
-
- @Test
- public void testOneFile() throws Exception {
- File folder = getFolder("oneFile");
-
- tested = instantiatePersisterFromAdapter(folder);
-
- List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
- assertEquals(1, results.size());
- ConfigSnapshotHolder result = results.get(0);
- assertSnapshot(result, "oneFileExpected");
- }
-
-
- @Test
- public void testTwoFiles() throws Exception {
- File folder = getFolder("twoFiles");
- tested = instantiatePersisterFromAdapter(folder);
-
- List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
- assertEquals(2, results.size());
- assertSnapshot(results.get(0), "twoFilesExpected1");
- assertSnapshot(results.get(1), "twoFilesExpected2");
- }
-
- private void assertSnapshot(ConfigSnapshotHolder result, String directory) throws Exception {
- SortedSet<String> expectedCapabilities = new TreeSet<>(IOUtils.readLines(getClass().getResourceAsStream("/" + directory + "/expectedCapabilities.txt")));
- String expectedSnapshot = IOUtils.toString(getClass().getResourceAsStream("/" + directory + "/expectedSnapshot.xml"));
- expectedSnapshot = expectedSnapshot.replaceAll("\r","");
- String _snapshot = result.getConfigSnapshot();
- _snapshot = _snapshot.replaceAll("\r","");
- assertEquals(expectedCapabilities, result.getCapabilities());
- assertEquals(expectedSnapshot, _snapshot);
- }
-
-}
+++ /dev/null
-//MODULES START
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
- <name>yang-schema-service</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
- <name>hash-map-data-store</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
- <name>dom-broker</name>
- <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <name>ref_hash-map-data-store</name>
- </data-store>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
- <name>binding-broker-impl</name>
- <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <name>ref_binding-notification-broker</name>
- </notification-service>
- <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <name>ref_binding-data-broker</name>
- </data-broker>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
- <name>runtime-mapping-singleton</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
- <name>binding-notification-broker</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
- <name>binding-data-broker</name>
- <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <name>ref_dom-broker</name>
- </dom-broker>
- <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
- <name>ref_runtime-mapping-singleton</name>
- </mapping-service>
- </module>
-//SERVICES START
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
- <instance>
- <name>ref_yang-schema-service</name>
- <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <instance>
- <name>ref_binding-notification-broker</name>
- <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <instance>
- <name>ref_hash-map-data-store</name>
- <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
- <instance>
- <name>ref_binding-broker-impl</name>
- <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
- <instance>
- <name>ref_runtime-mapping-singleton</name>
- <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <instance>
- <name>ref_dom-broker</name>
- <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <instance>
- <name>ref_binding-data-broker</name>
- <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
- </instance>
- </service>
-//CAPABILITIES START
-urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
-urn:ietf:params:netconf:capability:candidate:1.0
-urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
-urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12
-urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
-urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
-urn:ietf:params:netconf:capability:rollback-on-error:1.0
-urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
-urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
-urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
-urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19
+++ /dev/null
-urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
-urn:ietf:params:netconf:capability:candidate:1.0
-urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
-urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12
-urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
-urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
-urn:ietf:params:netconf:capability:rollback-on-error:1.0
-urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
-urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
-urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
-urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19
+++ /dev/null
-<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:md:sal:dom:impl">prefix:schema-service-singleton</type>
- <name>yang-schema-service</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
- <name>hash-map-data-store</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
- <name>dom-broker</name>
- <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <name>ref_hash-map-data-store</name>
- </data-store>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
- <name>binding-broker-impl</name>
- <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <name>ref_binding-notification-broker</name>
- </notification-service>
- <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <name>ref_binding-data-broker</name>
- </data-broker>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
- <name>runtime-mapping-singleton</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
- <name>binding-notification-broker</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
- <name>binding-data-broker</name>
- <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <name>ref_dom-broker</name>
- </dom-broker>
- <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
- <name>ref_runtime-mapping-singleton</name>
- </mapping-service>
- </module>
- </modules>
- <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
- <instance>
- <name>ref_yang-schema-service</name>
- <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <instance>
- <name>ref_binding-notification-broker</name>
- <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <instance>
- <name>ref_hash-map-data-store</name>
- <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
- <instance>
- <name>ref_binding-broker-impl</name>
- <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
- <instance>
- <name>ref_runtime-mapping-singleton</name>
- <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <instance>
- <name>ref_dom-broker</name>
- <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <instance>
- <name>ref_binding-data-broker</name>
- <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
- </instance>
- </service>
- </services>
-</data>
+++ /dev/null
-//MODULES START
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
- <name>yang-schema-service</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
- <name>hash-map-data-store</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
- <name>dom-broker</name>
- <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <name>ref_hash-map-data-store</name>
- </data-store>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
- <name>binding-broker-impl</name>
- <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <name>ref_binding-notification-broker</name>
- </notification-service>
- <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <name>ref_binding-data-broker</name>
- </data-broker>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
- <name>runtime-mapping-singleton</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
- <name>binding-notification-broker</name>
- </module>
-//SERVICES START
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
- <instance>
- <name>ref_yang-schema-service</name>
- <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <instance>
- <name>ref_binding-notification-broker</name>
- <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <instance>
- <name>ref_hash-map-data-store</name>
- <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
- <instance>
- <name>ref_binding-broker-impl</name>
- <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
- <instance>
- <name>ref_runtime-mapping-singleton</name>
- <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <instance>
- <name>ref_dom-broker</name>
- <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
- </instance>
- </service>
-//CAPABILITIES START
-urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
+++ /dev/null
-//MODULES START
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
- <name>binding-data-broker</name>
- <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <name>ref_dom-broker</name>
- </dom-broker>
- <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
- <name>ref_runtime-mapping-singleton</name>
- </mapping-service>
- </module>
-//SERVICES START
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <instance>
- <name>ref_binding-data-broker</name>
- <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
- </instance>
- </service>
-//CAPABILITIES START
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
-urn:ietf:params:netconf:capability:candidate:1.0
-urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
-urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12
-urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
-urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
-urn:ietf:params:netconf:capability:rollback-on-error:1.0
-urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
-urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
-urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
-urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19
+++ /dev/null
-urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
+++ /dev/null
-<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:md:sal:dom:impl">prefix:schema-service-singleton</type>
- <name>yang-schema-service</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
- <name>hash-map-data-store</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
- <name>dom-broker</name>
- <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <name>ref_hash-map-data-store</name>
- </data-store>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
- <name>binding-broker-impl</name>
- <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <name>ref_binding-notification-broker</name>
- </notification-service>
- <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <name>ref_binding-data-broker</name>
- </data-broker>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
- <name>runtime-mapping-singleton</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
- <name>binding-notification-broker</name>
- </module>
- </modules>
- <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
- <instance>
- <name>ref_yang-schema-service</name>
- <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <instance>
- <name>ref_binding-notification-broker</name>
- <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <instance>
- <name>ref_hash-map-data-store</name>
- <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
- <instance>
- <name>ref_binding-broker-impl</name>
- <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
- <instance>
- <name>ref_runtime-mapping-singleton</name>
- <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <instance>
- <name>ref_dom-broker</name>
- <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
- </instance>
- </service>
- </services>
-</data>
+++ /dev/null
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
-urn:ietf:params:netconf:capability:candidate:1.0
-urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
-urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12
-urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
-urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
-urn:ietf:params:netconf:capability:rollback-on-error:1.0
-urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
-urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
-urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
-urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16
-urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
-urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19
+++ /dev/null
-<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:md:sal:binding:impl">prefix:binding-data-broker</type>
- <name>binding-data-broker</name>
- <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <name>ref_dom-broker</name>
- </dom-broker>
- <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
- <name>ref_runtime-mapping-singleton</name>
- </mapping-service>
- </module>
- </modules>
- <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <instance>
- <name>ref_binding-data-broker</name>
- <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
- </instance>
- </service>
- </services>
-</data>
+++ /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>
- <artifactId>config-subsystem</artifactId>
- <groupId>org.opendaylight.controller</groupId>
- <version>0.2.5-SNAPSHOT</version>
- <relativePath>..</relativePath>
- </parent>
- <artifactId>config-persister-directory-autodetect-adapter</artifactId>
- <name>${project.artifactId}</name>
- <packaging>bundle</packaging>
-
- <dependencies>
- <!-- compile dependencies -->
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>config-persister-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-adapter</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-xml-adapter</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- </dependency>
-
- <!-- test dependencies -->
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>mockito-configuration</artifactId>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>config-persister-api</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
-
- </dependencies>
-
- <build>
- <plugins>
- <!-- workaround for creating version according to OSGi specification (major.minor.micro[.qualifier] -->
- <plugin>
- <groupId>org.codehaus.groovy.maven</groupId>
- <artifactId>gmaven-plugin</artifactId>
- <executions>
- <execution>
- <phase>generate-sources</phase>
- <goals>
- <goal>execute</goal>
- </goals>
- <configuration>
- <source>
- System.setProperty("osgiversion", "${project.version}".replace('-', '.'))
- </source>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <Fragment-Host>${project.groupId}.config-persister-impl;bundle-version=${osgiversion}
- </Fragment-Host>
- <Provide-Capability>org.opendaylight.controller.config.persister.storage.adapter
- </Provide-Capability>
- <Import-Package>
- com.google.common.base,
- com.google.common.io,
- org.apache.commons.io,
- org.opendaylight.controller.config.persist.api,
- org.slf4j,
- com.google.common.collect,
- javax.xml.bind,
- javax.xml.bind.annotation,
- javax.xml.transform,
- javax.xml.transform.stream,
- org.eclipse.persistence.jaxb,
- org.apache.commons.lang3
- </Import-Package>
- <Private-Package>
- org.opendaylight.controller.config.persist.storage.file.xml.model,
- org.opendaylight.controller.config.persist.storage.directory,
- org.opendaylight.controller.config.persist.storage.directory.xml,
- </Private-Package>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /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.config.persist.storage.directory.autodetect;
-
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.storage.directory.DirectoryPersister;
-import org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryPersister;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.xml.bind.JAXBException;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-public class AutodetectDirectoryPersister implements Persister {
- private static final Logger logger = LoggerFactory.getLogger(AutodetectDirectoryPersister.class);
-
- private final File storage;
-
- public AutodetectDirectoryPersister(File storage) {
- checkArgument(storage.exists() && storage.isDirectory(), "Storage directory does not exist: " + storage);
- this.storage = storage;
- }
-
- @Override
- public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
- throw new UnsupportedOperationException("This adapter is read only. Please set readonly=true on " + getClass());
- }
-
- @Override
- public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
- File[] filesArray = storage.listFiles();
- if (filesArray == null || filesArray.length == 0) {
- return Collections.emptyList();
- }
- List<File> sortedFiles = new ArrayList<>(Arrays.asList(filesArray));
- Collections.sort(sortedFiles);
-
- // combine all found files
- logger.debug("Reading files in following order: {}", sortedFiles);
-
- List<ConfigSnapshotHolder> result = new ArrayList<>();
- for (File file : sortedFiles) {
- logger.trace("Adding file '{}' to combined result", file);
-
- FileType fileType = FileType.getFileType(file);
- logger.trace("File '{}' detected as {} storage", file, fileType);
-
- ConfigSnapshotHolder snapshot = loadLastConfig(file, fileType);
- result.add(snapshot);
- }
- return result;
- }
-
- private ConfigSnapshotHolder loadLastConfig(File file, FileType fileType) throws IOException {
- switch (fileType) {
- case plaintext:
- logger.warn("Plaintext configuration files are deprecated, and will not be supported in future versions. " +
- "Use xml files instead");
- return DirectoryPersister.loadLastConfig(file);
- case xml:
- try {
- return XmlDirectoryPersister.loadLastConfig(file);
- } catch (JAXBException e) {
- logger.warn("Unable to restore configuration snapshot from {}", file, e);
- throw new IllegalStateException("Unable to restore configuration snapshot from " + file, e);
- }
- default:
- throw new IllegalStateException("Unknown storage type " + fileType);
- }
- }
-
- @Override
- public void close() {
-
- }
-
- @Override
- public String toString() {
- final StringBuffer sb = new StringBuffer("AutodetectDirectoryPersister{");
- sb.append("storage=").append(storage);
- sb.append('}');
- return sb.toString();
- }
-}
+++ /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.config.persist.storage.directory.autodetect;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.api.PropertiesProvider;
-import org.opendaylight.controller.config.persist.api.StorageAdapter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-
-/**
- * StorageAdapter that retrieves initial configuration from a directory. If multiple files are present, snapshot and
- * required capabilities will be merged together. Writing to this persister is not supported.
- */
-public class AutodetectDirectoryStorageAdapter implements StorageAdapter {
- private static final Logger logger = LoggerFactory.getLogger(AutodetectDirectoryStorageAdapter.class);
-
- public static final String DIRECTORY_STORAGE_PROP = "directoryStorage";
-
-
- @Override
- public Persister instantiate(PropertiesProvider propertiesProvider) {
- String fileStorageProperty = propertiesProvider.getProperty(DIRECTORY_STORAGE_PROP);
- Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(DIRECTORY_STORAGE_PROP));
- File storage = new File(fileStorageProperty);
- logger.debug("Using {}", storage);
- return new AutodetectDirectoryPersister(storage);
- }
-
-}
+++ /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.config.persist.storage.directory.autodetect;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-import org.apache.commons.lang3.StringUtils;
-import org.opendaylight.controller.config.persist.storage.directory.DirectoryPersister;
-import org.opendaylight.controller.config.persist.storage.file.xml.model.ConfigSnapshot;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-
-enum FileType {
-
- plaintext, xml;
-
- public static final String XML_STORAGE_FIRST_LINE = "<" + ConfigSnapshot.SNAPSHOT_ROOT_ELEMENT_NAME + ">";
- private static final String XML_FILE_DEFINITION_LINE = "<?xml";
-
- static FileType getFileType(File file) {
- String firstLine = readFirstLine(file);
- if(isPlaintextStorage(firstLine)) {
- return plaintext;
- } else if(isXmlStorage(firstLine))
- return xml;
-
- throw new IllegalArgumentException("File " + file + " is not of permitted storage type: " + Arrays.toString(FileType.values()));
- }
-
- private static boolean isXmlStorage(String firstLine) {
- boolean isXml = false;
- isXml |= firstLine.startsWith(XML_STORAGE_FIRST_LINE);
- isXml |= firstLine.startsWith(XML_FILE_DEFINITION_LINE);
- return isXml;
- }
-
- private static boolean isPlaintextStorage(String firstLine) {
- return firstLine.startsWith(DirectoryPersister.MODULES_START);
-
- }
-
- @VisibleForTesting
- static String readFirstLine(File file) {
- FirstLineReadingProcessor callback = new FirstLineReadingProcessor();
- try {
- return Files.readLines(file, Charsets.UTF_8, callback);
- } catch (IOException e) {
- throw new IllegalArgumentException("Unable to detect file type of file " + file, e);
- }
- }
-
-
- private static class FirstLineReadingProcessor implements com.google.common.io.LineProcessor<String> {
- private String firstNonBlankLine;
-
- @Override
- public boolean processLine(String line) throws IOException {
- if(isEmptyLine(line)) {
- return true;
- } else {
- firstNonBlankLine = line.trim();
- return false;
- }
- }
-
- private boolean isEmptyLine(String line) {
- return StringUtils.isBlank(line);
- }
-
- @Override
- public String getResult() {
- return firstNonBlankLine;
- }
- }
-}
+++ /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.config.persist.storage.directory.autodetect;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import junit.framework.Assert;
-import org.junit.Test;
-import org.junit.matchers.JUnitMatchers;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.test.PropertiesProviderTest;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
-public class AutodetectDirectoryPersisterTest {
-
- @Test
- public void testCombined() throws Exception {
- File resourcePath = FileTypeTest.getResourceAsFile("/combined/1controller.txt.config");
- File parentFile = resourcePath.getParentFile();
-
- AutodetectDirectoryStorageAdapter adapter = new AutodetectDirectoryStorageAdapter();
-
- PropertiesProviderTest pp = new PropertiesProviderTest();
- pp.addProperty("directoryStorage",parentFile.getPath());
- AutodetectDirectoryPersister persister = (AutodetectDirectoryPersister) adapter.instantiate(pp);
- List<ConfigSnapshotHolder> configs = persister.loadLastConfigs();
-
- Assert.assertEquals(2, configs.size());
- String snapFromTxt = configs.get(0).getConfigSnapshot();
- org.junit.Assert.assertThat(snapFromTxt, JUnitMatchers.containsString("<config>txt</config>"));
- org.junit.Assert.assertThat(snapFromTxt, JUnitMatchers.containsString("<service>txt</service>"));
-
- String snapFromXml = configs.get(1).getConfigSnapshot();
- org.junit.Assert.assertThat(snapFromXml, JUnitMatchers.containsString("<config>xml</config>"));
-
- Assert.assertEquals(configs.get(0).getCapabilities(), configs.get(1).getCapabilities());
- }
-
- @Test
- public void testInvalidXml() throws Exception {
- File resourcePath = FileTypeTest.getResourceAsFile("/bad_controller.xml.config");
- File parentFile = resourcePath.getParentFile();
-
- AutodetectDirectoryStorageAdapter adapter = new AutodetectDirectoryStorageAdapter();
-
- PropertiesProviderTest pp = new PropertiesProviderTest();
- pp.addProperty("directoryStorage",parentFile.getPath());
- AutodetectDirectoryPersister persister = (AutodetectDirectoryPersister) adapter.instantiate(pp);
- try {
- List<ConfigSnapshotHolder> configs = persister.loadLastConfigs();
- fail("An exception of type " + IllegalStateException.class + " was expected");
- } catch (IllegalStateException ise){
- String message = ise.getMessage();
- assertThat(message, JUnitMatchers.containsString("Unable to restore configuration snapshot from "));
- }
-
- }
-
- @Test
- public void testPersistConfig() throws Exception {
- File resourcePath = FileTypeTest.getResourceAsFile("/combined/1controller.txt.config");
- File parentFile = resourcePath.getParentFile();
-
- AutodetectDirectoryStorageAdapter adapter = new AutodetectDirectoryStorageAdapter();
-
- PropertiesProviderTest pp = new PropertiesProviderTest();
- pp.addProperty("directoryStorage",parentFile.getPath());
- AutodetectDirectoryPersister persister = (AutodetectDirectoryPersister) adapter.instantiate(pp);
- List<ConfigSnapshotHolder> configs = null;
- try {
- configs = persister.loadLastConfigs();
- } catch (IOException e) {
- fail("An exception of type " + UnsupportedOperationException.class + " was expected");
- }
- Assert.assertEquals(2, configs.size());
- try {
- persister.persistConfig(configs.get(0));
- } catch (UnsupportedOperationException uoe){
- String message = uoe.getMessage();
- assertThat(message,JUnitMatchers.containsString("This adapter is read only. Please set readonly=true on class"));
- }
- }
-
-}
+++ /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.config.persist.storage.directory.autodetect;
-
-import junit.framework.Assert;
-import org.junit.Test;
-import org.junit.matchers.JUnitMatchers;
-
-import java.io.File;
-
-public class FileTypeTest {
-
- @Test
- public void testPlaintext() throws Exception {
- File file = getResourceAsFile("/test.txt.config");
-
- FileType type = FileType.getFileType(file);
- Assert.assertEquals(FileType.plaintext, type);
-
- }
-
- @Test
- public void testXml() throws Exception {
- File file = getResourceAsFile("/test.xml.config");
-
- FileType type = FileType.getFileType(file);
- Assert.assertEquals(FileType.xml, type);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testUnknown() throws Exception {
- File file = getResourceAsFile("/unknown.config");
-
- try {
- FileType.getFileType(file);
- } catch (IllegalArgumentException e) {
- org.junit.Assert.assertThat(e.getMessage(), JUnitMatchers.containsString("File " + file + " is not of permitted storage type"));
- throw e;
- }
- }
-
- static File getResourceAsFile(String resource) {
- String f = FileTypeTest.class.getResource(resource).getFile();
- return new File(f);
- }
-
-}
+++ /dev/null
-<snapshot>
- <required-capabilities>
- <capability
- <capability>cap2</capability>
- <capability>capa a</capability>
- </required-capabilities>
- <configuration>
- <config>xml</config>
- </configuration>
-</snapshot>
\ No newline at end of file
+++ /dev/null
-//MODULES START
- <config>txt</config>
-//SERVICES START
- <service>txt</service>
-//CAPABILITIES START
-cap1&rev
-cap2
-capa a
\ No newline at end of file
+++ /dev/null
-<snapshot>
- <required-capabilities>
- <capability>cap1&rev</capability>
- <capability>cap2</capability>
- <capability>capa a</capability>
- </required-capabilities>
- <configuration>
- <config>xml</config>
- </configuration>
-</snapshot>
\ No newline at end of file
+++ /dev/null
-//MODULES START
-configuration
\ No newline at end of file
+++ /dev/null
-
-<snapshot>
\ No newline at end of file
+++ /dev/null
-unknown
-file type
\ No newline at end of file
*/
package org.opendaylight.controller.config.persist.storage.directory.xml;
+import com.google.common.base.Optional;
+import com.google.common.io.Files;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.persist.storage.file.xml.model.ConfigSnapshot;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
+import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.SortedSet;
import static com.google.common.base.Preconditions.checkArgument;
private static final Logger logger = LoggerFactory.getLogger(XmlDirectoryPersister.class);
private final File storage;
+ private final Optional<FilenameFilter> extensionsFilter;
+ /**
+ * Creates XmlDirectoryPersister that picks up all files in specified folder
+ */
public XmlDirectoryPersister(File storage) {
+ this(storage, Optional.<FilenameFilter>absent());
+ }
+
+ /**
+ * Creates XmlDirectoryPersister that picks up files only with specified file extension
+ */
+ public XmlDirectoryPersister(File storage, Set<String> fileExtensions) {
+ this(storage, Optional.of(getFilter(fileExtensions)));
+ }
+
+ private XmlDirectoryPersister(File storage, Optional<FilenameFilter> extensionsFilter) {
checkArgument(storage.exists() && storage.isDirectory(), "Storage directory does not exist: " + storage);
this.storage = storage;
+ this.extensionsFilter = extensionsFilter;
}
@Override
@Override
public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
- File[] filesArray = storage.listFiles();
+ File[] filesArray = extensionsFilter.isPresent() ? storage.listFiles(extensionsFilter.get()) : storage.listFiles();
if (filesArray == null || filesArray.length == 0) {
return Collections.emptyList();
}
List<ConfigSnapshotHolder> result = new ArrayList<>();
for (File file : sortedFiles) {
logger.trace("Adding file '{}' to combined result", file);
- ConfigSnapshotHolder h = fromXmlSnapshot(file);
- result.add(h);
+ Optional<ConfigSnapshotHolder> h = fromXmlSnapshot(file);
+ // Ignore non valid snapshot
+ if(h.isPresent() == false) {
+ continue;
+ }
+
+ result.add(h.get());
}
return result;
}
- private ConfigSnapshotHolder fromXmlSnapshot(File file) {
+ private Optional<ConfigSnapshotHolder> fromXmlSnapshot(File file) {
try {
- return loadLastConfig(file);
+ return Optional.of(loadLastConfig(file));
} catch (JAXBException e) {
- logger.warn("Unable to restore configuration snapshot from {}", file, e);
- throw new IllegalStateException("Unable to restore configuration snapshot from " + file, e);
+ // In case of parse error, issue a warning, ignore and continue
+ logger.warn(
+ "Unable to parse configuration snapshot from {}. Initial config from {} will be IGNORED in this run. " +
+ "Note that subsequent config files may fail due to this problem. " +
+ "Xml markup in this file needs to be fixed, for detailed information see enclosed exception.",
+ file, file, e);
}
+
+ return Optional.absent();
}
public static ConfigSnapshotHolder loadLastConfig(File file) throws JAXBException {
};
}
+ private static FilenameFilter getFilter(final Set<String>fileExtensions) {
+ checkArgument(fileExtensions.isEmpty() == false, "No file extension provided", fileExtensions);
+
+ return new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ String ext = Files.getFileExtension(name);
+ return fileExtensions.contains(ext);
+ }
+ };
+ }
@Override
public void close() {
package org.opendaylight.controller.config.persist.storage.directory.xml;
import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Sets;
import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.persist.api.PropertiesProvider;
import org.opendaylight.controller.config.persist.api.StorageAdapter;
import org.slf4j.LoggerFactory;
import java.io.File;
+import java.util.Set;
/**
* StorageAdapter that retrieves initial configuration from a directory. If multiple files are present, snapshot and
private static final Logger logger = LoggerFactory.getLogger(XmlDirectoryStorageAdapter.class);
public static final String DIRECTORY_STORAGE_PROP = "directoryStorage";
+ public static final String INCLUDE_EXT_PROP = "includeExtensions";
+ private static final char EXTENSIONS_SEPARATOR = ',';
@Override
String fileStorageProperty = propertiesProvider.getProperty(DIRECTORY_STORAGE_PROP);
Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(DIRECTORY_STORAGE_PROP));
File storage = new File(fileStorageProperty);
- logger.debug("Using {}", storage);
- return new XmlDirectoryPersister(storage);
+ String fileExtensions = propertiesProvider.getProperty(INCLUDE_EXT_PROP);
+
+ logger.debug("Using storage: {}", storage);
+
+ if(fileExtensions != null) {
+ logger.debug("Using extensions: {}", fileExtensions);
+ return new XmlDirectoryPersister(storage, splitExtensions(fileExtensions));
+ } else {
+ return new XmlDirectoryPersister(storage);
+ }
+ }
+
+ private Set<String> splitExtensions(String fileExtensions) {
+ return Sets.newHashSet(Splitter.on(EXTENSIONS_SEPARATOR).trimResults().omitEmptyStrings()
+ .split(fileExtensions));
}
}
import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
+
+import com.google.common.base.Optional;
import org.junit.Test;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.config.persist.api.Persister;
Persister tested;
Logger logger = LoggerFactory.getLogger(DirectoryStorageAdapterTest.class.toString());
- private Persister instantiatePersisterFromAdapter(File file){
+ private Persister instantiatePersisterFromAdapter(File file, Optional<String> extensions){
PropertiesProviderTest pp = new PropertiesProviderTest();
- pp.addProperty("directoryStorage",file.getPath());
+ pp.addProperty(XmlDirectoryStorageAdapter.DIRECTORY_STORAGE_PROP,file.getPath());
+ if(extensions.isPresent()) {
+ pp.addProperty(XmlDirectoryStorageAdapter.INCLUDE_EXT_PROP, extensions.get());
+ }
+
XmlDirectoryStorageAdapter dsa = new XmlDirectoryStorageAdapter();
return dsa.instantiate(pp);
}
+ private Persister instantiatePersisterFromAdapter(File file){
+ return instantiatePersisterFromAdapter(file, Optional.<String>absent());
+ }
+
@Test
public void testEmptyDirectory() throws Exception {
File folder = new File("target/emptyFolder");
@Test
public void testOneFile() throws Exception {
File folder = getFolder("oneFile");
- tested = instantiatePersisterFromAdapter(folder);
+ tested = instantiatePersisterFromAdapter(folder, Optional.of("xml"));
- logger.info("Testing : "+tested.toString());
+ logger.info("Testing : " + tested.toString());
List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
assertEquals(1, results.size());
ConfigSnapshotHolder result = results.get(0);
assertResult(result, "<config>1</config>", "cap1&rev", "cap2", "capa a");
}
+ @Test
+ public void testOneFileWrongExtension() throws Exception {
+ File folder = getFolder("oneFile");
+ tested = instantiatePersisterFromAdapter(folder, Optional.of("aa, bb"));
+ logger.info("Testing : " + tested.toString());
+ }
+
private void assertResult(ConfigSnapshotHolder result, String s, String... caps) {
assertEquals(s, result.getConfigSnapshot().replaceAll("\\s", ""));
int i = 0;
}
@Test
- public void testTwoFiles() throws Exception {
+ public void testTwoFilesAllExtensions() throws Exception {
File folder = getFolder("twoFiles");
tested = instantiatePersisterFromAdapter(folder);
- logger.info("Testing : "+tested.toString());
+ logger.info("Testing : " + tested.toString());
List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
assertEquals(2, results.size());
assertResult(results.get(0), "<config>1</config>", "cap1-a", "cap2-a", "capa a-a");
assertResult(results.get(1), "<config>2</config>", "cap1-b", "cap2-b", "capa a-b");
+ }
+
+ @Test
+ public void testTwoFilesTwoExtensions() throws Exception {
+ File folder = getFolder("twoFiles");
+ tested = instantiatePersisterFromAdapter(folder, Optional.of("xml, xml2"));
+ logger.info("Testing : " + tested.toString());
+ assertEquals(2, tested.loadLastConfigs().size());
+ }
+
+ @Test
+ public void testTwoFilesOnlyOneExtension() throws Exception {
+ File folder = getFolder("twoFiles");
+ tested = instantiatePersisterFromAdapter(folder, Optional.of("xml"));
+ logger.info("Testing : " + tested.toString());
+ List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
+ assertEquals(1, results.size());
+ assertResult(results.get(0), "<config>1</config>", "cap1-a", "cap2-a", "capa a-a");
+ }
+
+ @Test
+ public void testTwoFilesOneInvalid() throws Exception {
+ File folder = getFolder("twoFiles_corrupt");
+ tested = instantiatePersisterFromAdapter(folder, Optional.of("xml"));
+ logger.info("Testing : " + tested.toString());
+ List<ConfigSnapshotHolder> results = tested.loadLastConfigs();
+ assertEquals(1, results.size());
+
+ assertResult(results.get(0), "<config>1</config>", "cap1-a", "cap2-a", "capa a-a");
}
}
--- /dev/null
+<snapshot>
+ <required-capabilities>
+ <capability>cap1-a</capability>
+ <capability>cap2-a</capability>
+ <capability>capa a-a</capability>
+ </required-capabilities>
+ <configuration>
+ <config>1</config>
+ </configuration>
+</snapshot>
\ No newline at end of file
--- /dev/null
+<snapshotInvalid>
+ <required-capabilities>
+ <capability>cap1-b</capability>
+ <capability>cap2-b</capability>
+ <capability>capa a-b</capability>
+ </required-capabilities>
+ <configuration>
+ <config>2</config>
+ </configuration>
+</snapshotInvalid>
\ No newline at end of file
+++ /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>
- <artifactId>config-subsystem</artifactId>
- <groupId>org.opendaylight.controller</groupId>
- <version>0.2.5-SNAPSHOT</version>
- <relativePath>..</relativePath>
- </parent>
- <artifactId>config-persister-file-adapter</artifactId>
- <name>${project.artifactId}</name>
- <packaging>bundle</packaging>
-
- <dependencies>
- <!-- compile dependencies -->
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>config-persister-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
-
- <!-- test dependencies -->
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>mockito-configuration</artifactId>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>config-persister-api</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <!-- workaround for creating version according to OSGi specification (major.minor.micro[.qualifier] -->
- <plugin>
- <groupId>org.codehaus.groovy.maven</groupId>
- <artifactId>gmaven-plugin</artifactId>
- <executions>
- <execution>
- <phase>generate-sources</phase>
- <goals>
- <goal>execute</goal>
- </goals>
- <configuration>
- <source>
- System.setProperty("osgiversion", "${project.version}".replace('-', '.'))
- </source>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <Fragment-Host>${project.groupId}.config-persister-impl;bundle-version=${osgiversion}
- </Fragment-Host>
- <Provide-Capability>org.opendaylight.controller.config.persister.storage.adapter
- </Provide-Capability>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
+++ /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.config.persist.storage.file;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Charsets;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.io.Files;
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import org.apache.commons.lang3.StringUtils;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolderImpl;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.api.PropertiesProvider;
-import org.opendaylight.controller.config.persist.api.StorageAdapter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * StorageAdapter that stores configuration in a plain file.
- */
-public class FileStorageAdapter implements StorageAdapter, Persister {
- private static final Logger logger = LoggerFactory.getLogger(FileStorageAdapter.class);
-
-
- private static final Charset ENCODING = Charsets.UTF_8;
-
- public static final String FILE_STORAGE_PROP = "fileStorage";
- public static final String NUMBER_OF_BACKUPS = "numberOfBackups";
-
-
- private static final String SEPARATOR_E_PURE = "//END OF CONFIG";
- private static final String SEPARATOR_E = newLine(SEPARATOR_E_PURE);
-
- private static final String SEPARATOR_M_PURE = "//END OF SNAPSHOT";
- private static final String SEPARATOR_M = newLine(SEPARATOR_M_PURE);
-
- private static final String SEPARATOR_S = newLine("//START OF CONFIG");
-
- private static final String SEPARATOR_SL_PURE = "//START OF CONFIG-LAST";
- private static final String SEPARATOR_SL = newLine(SEPARATOR_SL_PURE);
-
- private static Integer numberOfStoredBackups;
- private File storage;
-
- @Override
- public Persister instantiate(PropertiesProvider propertiesProvider) {
- File storage = extractStorageFileFromProperties(propertiesProvider);
- logger.debug("Using file {}", storage.getAbsolutePath());
- // Create file if it does not exist
- File parentFile = storage.getAbsoluteFile().getParentFile();
- if (parentFile.exists() == false) {
- logger.debug("Creating parent folders {}", parentFile);
- parentFile.mkdirs();
- }
- if (storage.exists() == false) {
- logger.debug("Storage file does not exist, creating empty file");
- try {
- boolean result = storage.createNewFile();
- if (result == false)
- throw new RuntimeException("Unable to create storage file " + storage);
- } catch (IOException e) {
- throw new RuntimeException("Unable to create storage file " + storage, e);
- }
- }
- if (numberOfStoredBackups == 0) {
- throw new RuntimeException(NUMBER_OF_BACKUPS
- + " property should be either set to positive value, or ommited. Can not be set to 0.");
- }
- setFileStorage(storage);
- return this;
- }
-
- @VisibleForTesting
- void setFileStorage(File storage) {
- this.storage = storage;
- }
-
- @VisibleForTesting
- void setNumberOfBackups(Integer numberOfBackups) {
- numberOfStoredBackups = numberOfBackups;
- }
-
- private static File extractStorageFileFromProperties(PropertiesProvider propertiesProvider) {
- String fileStorageProperty = propertiesProvider.getProperty(FILE_STORAGE_PROP);
- Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + propertiesProvider.getFullKeyForReporting(FILE_STORAGE_PROP));
- File result = new File(fileStorageProperty);
- String numberOfBAckupsAsString = propertiesProvider.getProperty(NUMBER_OF_BACKUPS);
- if (numberOfBAckupsAsString != null) {
- numberOfStoredBackups = Integer.valueOf(numberOfBAckupsAsString);
- } else {
- numberOfStoredBackups = Integer.MAX_VALUE;
- }
- logger.trace("Property {} set to {}", NUMBER_OF_BACKUPS, numberOfStoredBackups);
- return result;
- }
-
- private static String newLine(String string) {
- return string + "\n";
- }
-
- @Override
- public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
- Preconditions.checkNotNull(storage, "Storage file is null");
-
- String content = Files.toString(storage, ENCODING);
- if (numberOfStoredBackups == Integer.MAX_VALUE) {
- resetLastConfig(content);
- persistLastConfig(holder);
- } else {
- if (numberOfStoredBackups == 1) {
- Files.write("", storage, ENCODING);
- persistLastConfig(holder);
- } else {
- int count = StringUtils.countMatches(content, SEPARATOR_S);
- if ((count + 1) < numberOfStoredBackups) {
- resetLastConfig(content);
- persistLastConfig(holder);
- } else {
- String contentSubString = StringUtils.substringBefore(content, SEPARATOR_E);
- contentSubString = contentSubString.concat(SEPARATOR_E_PURE);
- content = StringUtils.substringAfter(content, contentSubString);
- resetLastConfig(content);
- persistLastConfig(holder);
- }
- }
- }
- }
-
- private void resetLastConfig(String content) throws IOException {
- content = content.replaceFirst(SEPARATOR_SL, SEPARATOR_S);
- Files.write(content, storage, ENCODING);
- }
-
- private void persistLastConfig(ConfigSnapshotHolder holder) throws IOException {
- Files.append(SEPARATOR_SL, storage, ENCODING);
- String snapshotAsString = holder.getConfigSnapshot();
- Files.append(newLine(snapshotAsString), storage, ENCODING);
- Files.append(SEPARATOR_M, storage, ENCODING);
- Files.append(toStringCaps(holder.getCapabilities()), storage, ENCODING);
- Files.append(SEPARATOR_E, storage, ENCODING);
- }
-
- private CharSequence toStringCaps(Set<String> capabilities) {
- StringBuilder b = new StringBuilder();
- for (String capability : capabilities) {
- b.append(newLine(capability));
- }
- return b.toString();
- }
-
- @Override
- public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
- Preconditions.checkNotNull(storage, "Storage file is null");
-
- if (!storage.exists()) {
- return Collections.emptyList();
- }
-
- final LineProcessor lineProcessor = new LineProcessor();
- Files.readLines(storage, ENCODING, lineProcessor);
-
- if (lineProcessor.getConfigSnapshot().isPresent() == false) {
- return Collections.emptyList();
- } else {
- return Arrays.<ConfigSnapshotHolder>asList(new ConfigSnapshotHolderImpl(lineProcessor.getConfigSnapshot().get(),
- lineProcessor.getCapabilities(), storage.getAbsolutePath()));
- }
-
- }
-
- private static final class LineProcessor implements com.google.common.io.LineProcessor<String> {
-
- private boolean inLastConfig, inLastSnapshot;
- private final StringBuffer snapshotBuffer = new StringBuffer();
- private final SortedSet<String> caps = new TreeSet<>();
-
- @Override
- public String getResult() {
- return null;
- }
-
- @Override
- public boolean processLine(String line) throws IOException {
- if (inLastConfig && line.equals(SEPARATOR_E_PURE)) {
- inLastConfig = false;
- return false;
- }
-
- if (inLastConfig && line.equals(SEPARATOR_M_PURE)) {
- inLastSnapshot = false;
- return true;
- }
-
- if (inLastConfig) {
- if (inLastSnapshot) {
- snapshotBuffer.append(line);
- snapshotBuffer.append(System.lineSeparator());
- } else {
- caps.add(line);
- }
- }
-
- if (line.equals(SEPARATOR_SL_PURE)) {
- inLastConfig = true;
- inLastSnapshot = true;
- }
-
- return true;
- }
-
- Optional<String> getConfigSnapshot() {
- final String xmlContent = snapshotBuffer.toString();
- if (xmlContent.equals("")) {
- return Optional.absent();
- } else {
- return Optional.of(xmlContent);
- }
- }
-
- SortedSet<String> getCapabilities() {
- return caps;
- }
-
- }
-
- @Override
- public void close() {
-
- }
-
- @Override
- public String toString() {
- return "FileStorageAdapter [storage=" + storage + "]";
- }
-
-}
+++ /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.config.persist.storage.file;
-
-import com.google.common.base.Charsets;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
-import java.io.File;
-import java.nio.file.Files;
-import java.util.Collection;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
-import org.opendaylight.controller.config.persist.api.Persister;
-import org.opendaylight.controller.config.persist.test.PropertiesProviderTest;
-import static junit.framework.Assert.assertFalse;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-public class FileStorageAdapterTest {
-
- private static int i;
- private File file;
-
- @Before
- public void setUp() throws Exception {
- file = Files.createTempFile("testFilePersist", ".txt").toFile();
- if (!file.exists())
- return;
- com.google.common.io.Files.write("", file, Charsets.UTF_8);
- i = 1;
- }
-
-
- @Test
- public void testFileAdapterAsPersister() throws Exception {
- FileStorageAdapter storage = new FileStorageAdapter();
- PropertiesProviderTest pp = new PropertiesProviderTest();
- pp.addProperty("fileStorage",file.getPath());
- pp.addProperty("numberOfBackups",Integer.toString(Integer.MAX_VALUE));
-
- Persister configPersister = storage.instantiate(pp);
- final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() {
- @Override
- public String getConfigSnapshot() {
- return createConfig();
- }
-
- @Override
- public SortedSet<String> getCapabilities() {
- return createCaps();
- }
- };
- configPersister.persistConfig(holder);
-
- configPersister.persistConfig(holder);
-
- Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
- new Predicate<String>() {
-
- @Override
- public boolean apply(String input) {
- if (input.equals(""))
- return false;
- return true;
- }
- });
- assertEquals(14, readLines.size());
-
- List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
- assertEquals(1, lastConf.size());
- ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
- assertEquals("<config>2</config>",
- configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
- assertEquals(createCaps(), configSnapshotHolder.getCapabilities());
- }
- @Test
- public void testFileAdapter() throws Exception {
- FileStorageAdapter storage = new FileStorageAdapter();
- storage.setFileStorage(file);
- storage.setNumberOfBackups(Integer.MAX_VALUE);
- final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() {
- @Override
- public String getConfigSnapshot() {
- return createConfig();
- }
-
- @Override
- public SortedSet<String> getCapabilities() {
- return createCaps();
- }
- };
- storage.persistConfig(holder);
-
- storage.persistConfig(holder);
-
- Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
- new Predicate<String>() {
-
- @Override
- public boolean apply(String input) {
- if (input.equals(""))
- return false;
- return true;
- }
- });
- assertEquals(14, readLines.size());
-
- List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
- assertEquals(1, lastConf.size());
- ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
- assertEquals("<config>2</config>",
- configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
- assertEquals(createCaps(), configSnapshotHolder.getCapabilities());
- }
-
- private SortedSet<String> createCaps() {
- SortedSet<String> caps = new TreeSet<>();
-
- caps.add("cap1");
- caps.add("cap2");
- caps.add("capaaaa as dasfasdf s2");
- return caps;
- }
-
- @Test
- public void testFileAdapterOneBackup() throws Exception {
- FileStorageAdapter storage = new FileStorageAdapter();
- storage.setFileStorage(file);
- storage.setNumberOfBackups(1);
- final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() {
- @Override
- public String getConfigSnapshot() {
- return createConfig();
- }
-
- @Override
- public SortedSet<String> getCapabilities() {
- return createCaps();
- }
- };
- storage.persistConfig(holder);
-
- storage.persistConfig(holder);
-
- Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
- new Predicate<String>() {
-
- @Override
- public boolean apply(String input) {
- if (input.equals(""))
- return false;
- return true;
- }
- });
- assertEquals(7, readLines.size());
-
- List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
- assertEquals(1, lastConf.size());
- ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
- assertEquals("<config>2</config>",
- configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
- }
-
- @Test
- public void testFileAdapterOnlyTwoBackups() throws Exception {
- FileStorageAdapter storage = new FileStorageAdapter();
- storage.setFileStorage(file);
- storage.setNumberOfBackups(2);
- final ConfigSnapshotHolder holder = new ConfigSnapshotHolder() {
- @Override
- public String getConfigSnapshot() {
- return createConfig();
- }
-
- @Override
- public SortedSet<String> getCapabilities() {
- return createCaps();
- }
- };
- storage.persistConfig(holder);
-
- storage.persistConfig(holder);
- storage.persistConfig(holder);
-
- Collection<String> readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8),
- new Predicate<String>() {
-
- @Override
- public boolean apply(String input) {
- if (input.equals(""))
- return false;
- return true;
- }
- });
-
- assertEquals(14, readLines.size());
-
- List<ConfigSnapshotHolder> lastConf = storage.loadLastConfigs();
- assertEquals(1, lastConf.size());
- ConfigSnapshotHolder configSnapshotHolder = lastConf.get(0);
- assertEquals("<config>3</config>",
- configSnapshotHolder.getConfigSnapshot().replaceAll("\\s", ""));
- assertFalse(readLines.contains(holder.getConfigSnapshot()));
- }
-
- @Test
- public void testNoLastConfig() throws Exception {
- File file = Files.createTempFile("testFilePersist", ".txt").toFile();
- if (!file.exists())
- return;
- FileStorageAdapter storage = new FileStorageAdapter();
- storage.setFileStorage(file);
-
- List<ConfigSnapshotHolder> elementOptional = storage.loadLastConfigs();
- assertThat(elementOptional.size(), is(0));
- }
-
- @Test(expected = NullPointerException.class)
- public void testNoProperties() throws Exception {
- FileStorageAdapter storage = new FileStorageAdapter();
- storage.loadLastConfigs();
- }
-
- @Test(expected = NullPointerException.class)
- public void testNoProperties2() throws Exception {
- FileStorageAdapter storage = new FileStorageAdapter();
- storage.persistConfig(new ConfigSnapshotHolder() {
- @Override
- public String getConfigSnapshot() {
- return Mockito.mock(String.class);
- }
-
- @Override
- public SortedSet<String> getCapabilities() {
- return new TreeSet<>();
- }
- } );
- }
-
- static String createConfig() {
- return "<config>" + i++ + "</config>";
- }
-
-}
<module>config-plugin-parent</module>
<module>config-util</module>
<module>config-persister-api</module>
- <module>config-persister-file-adapter</module>
<module>config-persister-file-xml-adapter</module>
<module>yang-jmx-generator</module>
<module>yang-jmx-generator-plugin</module>
<module>netty-threadgroup-config</module>
<module>netty-event-executor-config</module>
<module>netty-timer-config</module>
- <module>config-persister-directory-adapter</module>
<module>config-persister-directory-xml-adapter</module>
- <module>config-persister-directory-autodetect-adapter</module>
<module>yang-test-plugin</module>
<module>shutdown-api</module>
<module>shutdown-impl</module>
<?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>
- <prerequisites>
- <maven>3.0</maven>
- </prerequisites>
- <scm>
- <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
- <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
- <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
- <tag>HEAD</tag>
- </scm>
- <parent>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>commons.opendaylight</artifactId>
- <version>1.4.2-SNAPSHOT</version>
- <relativePath>../../commons/opendaylight</relativePath>
- </parent>
+<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>
+ <prerequisites>
+ <maven>3.0</maven>
+ </prerequisites>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
+ <tag>HEAD</tag>
+ </scm>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.2-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+ <artifactId>distribution.opendaylight</artifactId>
+ <version>0.1.2-SNAPSHOT</version>
+ <packaging>pom</packaging>
- <profiles>
- <profile>
- <id>notduringrelease</id>
- <activation>
- <property>
- <name>!DOINGRELEASE</name>
- </property>
- </activation>
- <dependencies>
- <!-- md-sal -->
+
+ <profiles>
+ <profile>
+ <id>notduringrelease</id>
+ <activation>
+ <property>
+ <name>!DOINGRELEASE</name>
+ </property>
+ </activation>
+ <dependencies>
+ <!-- md-sal -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-netconf-connector</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-broker-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-remote</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-restconf-broker</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-compatibility</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-connector-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-rest-connector</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-inventory</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-flow-base</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-flow-service</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-flow-statistics</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-flow-management</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.md</groupId>
+ <artifactId>inventory-manager</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.md</groupId>
+ <artifactId>forwardingrules-manager</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.md</groupId>
+ <artifactId>topology-lldp-discovery</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.md</groupId>
+ <artifactId>topology-manager</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-topology</artifactId>
+ <version>1.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-topology</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.md</groupId>
+ <artifactId>statistics-manager</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>protocol-framework</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>restconf-client-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>restconf-client-impl</artifactId>
+ </dependency>
+
+ <!-- clustering -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>remoterpc-routingtable.implementation</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-remoterpc-connector</artifactId>
+ </dependency>
+
+ <!-- config-->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-manager</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>logback-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-persister-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-persister-file-xml-adapter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-persister-directory-xml-adapter</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>shutdown-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>shutdown-impl</artifactId>
+ </dependency>
+
+ <!-- Netconf -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-mapping-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-ssh</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-netconf-connector</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-monitoring</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ietf-netconf-monitoring-extension</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-persister-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.xerces</artifactId>
+ <version>2.11.0_1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.birt.runtime.3_7_1</groupId>
+ <artifactId>org.apache.xml.resolver</artifactId>
+ <version>1.2.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ </dependency>
+
+ <!-- threadpool -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>threadpool-config-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netty-config-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>threadpool-config-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netty-threadgroup-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netty-event-executor-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netty-timer-config</artifactId>
+ </dependency>
+
+ <!-- toaster example I'm pretty sure we should trim -->
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster-consumer</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster-provider</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
+ <!-- yangtools dependencies I'm pretty sure we can trim -->
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-type-provider</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-generator-spi</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-generator-api</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-generator-impl</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-generator-util</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-model-api</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>2.4</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools.thirdparty</groupId>
+ <artifactId>antlr4-runtime-osgi-nohead</artifactId>
+ <version>4.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.thirdparty</groupId>
+ <artifactId>xtend-lib-osgi</artifactId>
+ <version>2.4.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-api</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>yang-ext</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>ganymed</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.zeromq</groupId>
+ <artifactId>jeromq</artifactId>
+ <version>0.3.1</version>
+ </dependency>
+ <!-- yang model dependencies -->
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-yang-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>opendaylight-l2-types</artifactId>
+ </dependency>
+ </dependencies>
+ </profile>
+ <profile>
+ <id>integrationtests</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.8</version>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sanitytest</artifactId>
+ <version>${controller.version}</version>
+ </dependency>
+ </dependencies>
+ <executions>
+ <execution>
+ <id>copy</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sanitytest</artifactId>
+ <type>jar</type>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.2.1</version>
+ <executions>
+ <execution>
+ <id>sanity-test</id>
+ <phase>package</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <executable>${java.home}/bin/java</executable>
+ <arguments>
+ <argument>-cp</argument>
+ <argument>./target/dependency/*</argument>
+ <argument>org.opendaylight.controller.distribution.Sanity</argument>
+ </arguments>
+ <environmentVariables>
+ <JAVA_HOME>
+ ${java.home}
+ </JAVA_HOME>
+ </environmentVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sanitytest</artifactId>
+ </dependency>
+ </dependencies>
+ </profile>
+ </profiles>
+
+
+ <dependencies>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>forwarding.staticrouting</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-util</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-netconf-connector</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services-implementation</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-core-api</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>configuration</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-broker-impl</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>configuration.implementation</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-remote</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>routing.dijkstra_implementation</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-restconf-broker</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>arphandler</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-core-spi</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>hosttracker</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-api</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>hosttracker.implementation</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-impl</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-api</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager.implementation</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-config</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>appauth</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-broker-impl</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>switchmanager</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-compatibility</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>switchmanager.implementation</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-connector-api</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>statisticsmanager</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-rest-connector</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>statisticsmanager.implementation</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-inventory</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>topologymanager</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-flow-base</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>usermanager</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-flow-service</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>usermanager.implementation</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-flow-statistics</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>connectionmanager</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-flow-management</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>connectionmanager.implementation</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller.md</groupId>
- <artifactId>inventory-manager</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>security</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller.md</groupId>
- <artifactId>forwardingrules-manager</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>forwardingrulesmanager</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller.md</groupId>
- <artifactId>topology-lldp-discovery</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>forwardingrulesmanager.implementation</artifactId>
</dependency>
+
+ <!-- SAL bundles -->
<dependency>
- <groupId>org.opendaylight.controller.md</groupId>
- <artifactId>topology-manager</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-topology</artifactId>
- <version>1.1-SNAPSHOT</version>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal.implementation</artifactId>
</dependency>
+
+ <!-- SAL Extension bundles -->
+
<dependency>
- <groupId>org.opendaylight.yangtools.model</groupId>
- <artifactId>ietf-topology</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal.connection</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-util</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal.connection.implementation</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller.md</groupId>
- <artifactId>statistics-manager</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal.networkconfiguration</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>concepts</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal.networkconfiguration.implementation</artifactId>
</dependency>
+
+ <!-- Web bundles -->
+
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>protocol-framework</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>web</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>concepts</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>flows.web</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>restconf-client-api</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>devices.web</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>restconf-client-impl</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>troubleshoot.web</artifactId>
</dependency>
-
- <!-- clustering -->
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>remoterpc-routingtable.implementation</artifactId>
- <version>${mdsal.version}</version>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>topology.web</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-remoterpc-connector</artifactId>
- <version>${mdsal.version}</version>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>osgi-brandfragment.web</artifactId>
</dependency>
- <!-- config-->
+ <!-- Neutron -->
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-api</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>networkconfig.neutron</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-manager</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>networkconfig.neutron.implementation</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>yang-jmx-generator</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>networkconfig.neutron.northbound</artifactId>
</dependency>
+
+ <!-- Northbound bundles -->
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>logback-config</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.northbound</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-api</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>bundlescanner</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>bundlescanner.implementation</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-xml-adapter</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>topology.northbound</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-adapter</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>forwarding.staticrouting.northbound</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-xml-adapter</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>statistics.northbound</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-directory-autodetect-adapter</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>flowprogrammer.northbound</artifactId>
</dependency>
-
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>shutdown-api</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>hosttracker.northbound</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>shutdown-impl</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>subnets.northbound</artifactId>
</dependency>
-
- <!-- Netconf -->
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netconf-api</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>switchmanager.northbound</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netconf-impl</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager.northbound</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netconf-util</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>networkconfig.bridgedomain.northbound</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netconf-client</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>httpservice-bridge</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netconf-mapping-api</artifactId>
+ <groupId>org.jolokia</groupId>
+ <artifactId>jolokia-osgi</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netconf-ssh</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>jolokia-bridge</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-netconf-connector</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>connectionmanager.northbound</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netconf-monitoring</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>usermanager.northbound</artifactId>
</dependency>
<dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>ietf-netconf-monitoring</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>controllermanager.northbound</artifactId>
</dependency>
+ <!-- Debug and logging -->
+
<dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>ietf-netconf-monitoring-extension</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>logging.bridge</artifactId>
</dependency>
+
+ <!-- Southbound bundles -->
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-impl</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>protocol_plugins.openflow</artifactId>
</dependency>
+
+ <!-- samples -->
<dependency>
- <groupId>org.apache.servicemix.bundles</groupId>
- <artifactId>org.apache.servicemix.bundles.xerces</artifactId>
- <version>2.11.0_1</version>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>samples.loadbalancer</artifactId>
</dependency>
<dependency>
- <groupId>org.eclipse.birt.runtime.3_7_1</groupId>
- <artifactId>org.apache.xml.resolver</artifactId>
- <version>1.2.0</version>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>samples.loadbalancer.northbound</artifactId>
</dependency>
-
- <!-- threadpool -->
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>threadpool-config-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netty-config-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>threadpool-config-impl</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netty-threadgroup-config</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netty-event-executor-config</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netty-timer-config</artifactId>
- </dependency>
-
- <!-- toaster example I'm pretty sure we should trim -->
- <dependency>
- <groupId>org.opendaylight.controller.samples</groupId>
- <artifactId>sample-toaster</artifactId>
- <version>${mdsal.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.samples</groupId>
- <artifactId>sample-toaster-consumer</artifactId>
- <version>${mdsal.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.samples</groupId>
- <artifactId>sample-toaster-provider</artifactId>
- <version>${mdsal.version}</version>
- </dependency>
- <!-- yangtools dependencies I'm pretty sure we can trim -->
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-binding</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>binding-type-provider</artifactId>
- <version>${yangtools.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>binding-generator-spi</artifactId>
- <version>${yangtools.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>binding-generator-api</artifactId>
- <version>${yangtools.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>binding-generator-impl</artifactId>
- <version>${yangtools.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>binding-generator-util</artifactId>
- <version>${yangtools.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>binding-model-api</artifactId>
- <version>${yangtools.version}</version>
- </dependency>
- <dependency>
- <groupId>commons-lang</groupId>
- <artifactId>commons-lang</artifactId>
- <version>2.4</version>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.yangtools.thirdparty</groupId>
- <artifactId>antlr4-runtime-osgi-nohead</artifactId>
- <version>4.0</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools.thirdparty</groupId>
- <artifactId>xtend-lib-osgi</artifactId>
- <version>2.4.3</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-parser-api</artifactId>
- <version>${yangtools.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-model-util</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-parser-impl</artifactId>
- <version>${yangtools.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-common</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-impl</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-util</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-model-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools.model</groupId>
- <artifactId>yang-ext</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.thirdparty</groupId>
- <artifactId>ganymed</artifactId>
- </dependency>
- <dependency>
- <groupId>org.zeromq</groupId>
- <artifactId>jeromq</artifactId>
- <version>0.3.1</version>
- </dependency>
- <!-- yang model dependencies -->
- <dependency>
- <groupId>org.opendaylight.yangtools.model</groupId>
- <artifactId>ietf-inet-types</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools.model</groupId>
- <artifactId>ietf-yang-types</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools.model</groupId>
- <artifactId>opendaylight-l2-types</artifactId>
- </dependency>
- </dependencies>
- </profile>
- <profile>
- <id>integrationtests</id>
- <activation>
- <activeByDefault>false</activeByDefault>
- </activation>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-dependency-plugin</artifactId>
- <version>2.8</version>
- <dependencies>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sanitytest</artifactId>
- <version>${controller.version}</version>
- </dependency>
- </dependencies>
- <executions>
- <execution>
- <id>copy</id>
- <phase>package</phase>
- <goals>
- <goal>copy</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <artifactItems>
- <artifactItem>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sanitytest</artifactId>
- <type>jar</type>
- </artifactItem>
- </artifactItems>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>exec-maven-plugin</artifactId>
- <version>1.2.1</version>
- <executions>
- <execution>
- <id>sanity-test</id>
- <phase>package</phase>
- <goals>
- <goal>exec</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <executable>${java.home}/bin/java</executable>
- <arguments>
- <argument>-cp</argument>
- <argument>./target/dependency/*</argument>
- <argument>org.opendaylight.controller.distribution.Sanity</argument>
- </arguments>
- <environmentVariables>
- <JAVA_HOME>
- ${java.home}
- </JAVA_HOME>
- </environmentVariables>
- </configuration>
- </plugin>
- </plugins>
- </build>
- <dependencies>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sanitytest</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>samples.simpleforwarding</artifactId>
</dependency>
- </dependencies>
- </profile>
- </profiles>
-
- <artifactId>distribution.opendaylight</artifactId>
- <version>0.1.2-SNAPSHOT</version>
- <packaging>pom</packaging>
-
- <dependencies>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>forwarding.staticrouting</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>clustering.services</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>clustering.services-implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>configuration</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>configuration.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>routing.dijkstra_implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>arphandler</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>hosttracker</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>hosttracker.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>appauth</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>switchmanager</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>switchmanager.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>statisticsmanager</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>statisticsmanager.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>topologymanager</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>usermanager</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>usermanager.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>connectionmanager</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>connectionmanager.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>security</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>forwardingrulesmanager</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>forwardingrulesmanager.implementation</artifactId>
- </dependency>
-
- <!-- SAL bundles -->
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal.implementation</artifactId>
- </dependency>
-
- <!-- SAL Extension bundles -->
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal.connection</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal.connection.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal.networkconfiguration</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal.networkconfiguration.implementation</artifactId>
- </dependency>
-
- <!-- Web bundles -->
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>flows.web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>devices.web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>troubleshoot.web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>topology.web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>osgi-brandfragment.web</artifactId>
- </dependency>
- <!-- Neutron -->
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>networkconfig.neutron</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>networkconfig.neutron.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>networkconfig.neutron.northbound</artifactId>
- </dependency>
-
- <!-- Northbound bundles -->
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>commons.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>bundlescanner</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>bundlescanner.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>topology.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>forwarding.staticrouting.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>statistics.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>flowprogrammer.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>hosttracker.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>subnets.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>switchmanager.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>networkconfig.bridgedomain.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>httpservice-bridge</artifactId>
- </dependency>
- <dependency>
- <groupId>org.jolokia</groupId>
- <artifactId>jolokia-osgi</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>jolokia-bridge</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>connectionmanager.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>usermanager.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>controllermanager.northbound</artifactId>
- </dependency>
- <!-- Debug and logging -->
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>logging.bridge</artifactId>
- </dependency>
-
- <!-- Southbound bundles -->
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>protocol_plugins.openflow</artifactId>
- </dependency>
-
- <!-- samples -->
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>samples.loadbalancer</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>samples.loadbalancer.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>samples.simpleforwarding</artifactId>
- </dependency>
-
- <!-- Third party depedencies -->
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>jcl-over-slf4j</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>log4j-over-slf4j</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- </dependency>
+ <!-- Third party depedencies -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>log4j-over-slf4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-annotations</artifactId>
- </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.jaxrs</groupId>
- <artifactId>jackson-jaxrs-json-provider</artifactId>
- </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.jaxrs</groupId>
+ <artifactId>jackson-jaxrs-json-provider</artifactId>
+ </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.jaxrs</groupId>
- <artifactId>jackson-jaxrs-base</artifactId>
- </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.jaxrs</groupId>
+ <artifactId>jackson-jaxrs-base</artifactId>
+ </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.module</groupId>
- <artifactId>jackson-module-jaxb-annotations</artifactId>
- </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.module</groupId>
+ <artifactId>jackson-module-jaxb-annotations</artifactId>
+ </dependency>
- <dependency>
- <groupId>org.codehaus.jettison</groupId>
- <artifactId>jettison</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>org.javassist</groupId>
- <artifactId>javassist</artifactId>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- </dependency>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- </dependency>
- <dependency>
- <groupId>commons-fileupload</groupId>
- <artifactId>commons-fileupload</artifactId>
- </dependency>
- <dependency>
- <groupId>commons-net</groupId>
- <artifactId>commons-net</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>javax.servlet</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>javax.servlet.jsp</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.eclipse.equinox.ds</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.eclipse.equinox.util</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.eclipse.osgi.services</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.eclipse.osgi</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.apache.felix.gogo.command</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.apache.felix.gogo.runtime</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.apache.felix.gogo.shell</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.eclipse.equinox.cm</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.eclipse.equinox.console</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.eclipse.equinox.launcher</artifactId>
- </dependency>
- <!-- Gemini Web -->
- <dependency>
- <groupId>geminiweb</groupId>
- <artifactId>org.eclipse.gemini.web.core</artifactId>
- </dependency>
- <dependency>
- <groupId>geminiweb</groupId>
- <artifactId>org.eclipse.gemini.web.extender</artifactId>
- </dependency>
- <dependency>
- <groupId>geminiweb</groupId>
- <artifactId>org.eclipse.gemini.web.tomcat</artifactId>
- </dependency>
- <dependency>
- <groupId>geminiweb</groupId>
- <artifactId>org.eclipse.virgo.kernel.equinox.extensions</artifactId>
- </dependency>
- <dependency>
- <groupId>geminiweb</groupId>
- <artifactId>org.eclipse.virgo.util.common</artifactId>
- </dependency>
- <dependency>
- <groupId>geminiweb</groupId>
- <artifactId>org.eclipse.virgo.util.io</artifactId>
- </dependency>
- <dependency>
- <groupId>geminiweb</groupId>
- <artifactId>org.eclipse.virgo.util.math</artifactId>
- </dependency>
- <dependency>
- <groupId>geminiweb</groupId>
- <artifactId>org.eclipse.virgo.util.osgi</artifactId>
- </dependency>
- <dependency>
- <groupId>geminiweb</groupId>
- <artifactId>org.eclipse.virgo.util.osgi.manifest</artifactId>
- </dependency>
- <dependency>
- <groupId>geminiweb</groupId>
- <artifactId>org.eclipse.virgo.util.parser.manifest</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.dependencymanager</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.dependencymanager.shell</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- </dependency>
- <dependency>
- <groupId>org.jboss.spec.javax.transaction</groupId>
- <artifactId>jboss-transaction-api_1.1_spec</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.fileinstall</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- </dependency>
- <dependency>
- <groupId>virgomirror</groupId>
- <artifactId>org.eclipse.jdt.core.compiler.batch</artifactId>
- </dependency>
- <dependency>
- <groupId>eclipselink</groupId>
- <artifactId>javax.persistence</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>javax.activation</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>javax.annotation</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>javax.ejb</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>javax.el</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>javax.mail.glassfish</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>javax.xml.rpc</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>org.apache.catalina</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>org.apache.catalina.ha</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>org.apache.catalina.tribes</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>org.apache.coyote</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>org.apache.el</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>org.apache.jasper</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>org.apache.juli.extras</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>org.apache.tomcat.api</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>org.apache.tomcat.util</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>javax.servlet.jsp.jstl</artifactId>
- </dependency>
- <dependency>
- <groupId>orbit</groupId>
- <artifactId>javax.servlet.jsp.jstl.impl</artifactId>
- </dependency>
- <dependency>
- <groupId>eclipselink</groupId>
- <artifactId>javax.resource</artifactId>
- </dependency>
- <!-- Add Pax Exam -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>org.springframework.asm</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>org.springframework.aop</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>org.springframework.context</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>org.springframework.context.support</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>org.springframework.core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>org.springframework.beans</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>org.springframework.expression</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>org.springframework.web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.aopalliance</groupId>
- <artifactId>com.springsource.org.aopalliance</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>org.springframework.web.servlet</artifactId>
- </dependency>
- <!-- Spring security -->
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-config</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.security</groupId>
- <artifactId>spring-security-taglibs</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>org.springframework.transaction</artifactId>
- </dependency>
- <!-- Visual VM hook -->
- <dependency>
- <groupId>org.ow2.chameleon.management</groupId>
- <artifactId>chameleon-mbeans</artifactId>
- </dependency>
- <!-- Jersey for JAXRS -->
- <dependency>
- <groupId>com.sun.jersey</groupId>
- <artifactId>jersey-core</artifactId>
- </dependency>
- <dependency>
- <groupId>com.sun.jersey</groupId>
- <artifactId>jersey-server</artifactId>
- </dependency>
- <dependency>
- <groupId>com.sun.jersey</groupId>
- <artifactId>jersey-client</artifactId>
- </dependency>
+ <dependency>
+ <groupId>org.codehaus.jettison</groupId>
+ <artifactId>jettison</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.javassist</groupId>
+ <artifactId>javassist</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-fileupload</groupId>
+ <artifactId>commons-fileupload</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-net</groupId>
+ <artifactId>commons-net</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>javax.servlet</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>javax.servlet.jsp</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>org.eclipse.equinox.ds</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>org.eclipse.equinox.util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>org.eclipse.osgi.services</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>org.eclipse.osgi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>org.apache.felix.gogo.command</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>org.apache.felix.gogo.runtime</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>org.apache.felix.gogo.shell</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>org.eclipse.equinox.cm</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>org.eclipse.equinox.console</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>org.eclipse.equinox.launcher</artifactId>
+ </dependency>
+ <!-- Gemini Web -->
+ <dependency>
+ <groupId>geminiweb</groupId>
+ <artifactId>org.eclipse.gemini.web.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>geminiweb</groupId>
+ <artifactId>org.eclipse.gemini.web.extender</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>geminiweb</groupId>
+ <artifactId>org.eclipse.gemini.web.tomcat</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>geminiweb</groupId>
+ <artifactId>org.eclipse.virgo.kernel.equinox.extensions</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>geminiweb</groupId>
+ <artifactId>org.eclipse.virgo.util.common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>geminiweb</groupId>
+ <artifactId>org.eclipse.virgo.util.io</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>geminiweb</groupId>
+ <artifactId>org.eclipse.virgo.util.math</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>geminiweb</groupId>
+ <artifactId>org.eclipse.virgo.util.osgi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>geminiweb</groupId>
+ <artifactId>org.eclipse.virgo.util.osgi.manifest</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>geminiweb</groupId>
+ <artifactId>org.eclipse.virgo.util.parser.manifest</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.dependencymanager</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.dependencymanager.shell</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.transaction</groupId>
+ <artifactId>jboss-transaction-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.fileinstall</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>virgomirror</groupId>
+ <artifactId>org.eclipse.jdt.core.compiler.batch</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>eclipselink</groupId>
+ <artifactId>javax.persistence</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>javax.activation</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>javax.annotation</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>javax.ejb</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>javax.el</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>javax.mail.glassfish</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>javax.xml.rpc</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.catalina</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.catalina.ha</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.catalina.tribes</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.coyote</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.el</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.jasper</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.juli.extras</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.tomcat.api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>org.apache.tomcat.util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>javax.servlet.jsp.jstl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>orbit</groupId>
+ <artifactId>javax.servlet.jsp.jstl.impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>eclipselink</groupId>
+ <artifactId>javax.resource</artifactId>
+ </dependency>
+ <!-- Add Pax Exam -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>org.springframework.asm</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>org.springframework.aop</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>org.springframework.context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>org.springframework.context.support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>org.springframework.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>org.springframework.beans</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>org.springframework.expression</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>org.springframework.web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.aopalliance</groupId>
+ <artifactId>com.springsource.org.aopalliance</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>org.springframework.web.servlet</artifactId>
+ </dependency>
+ <!-- Spring security -->
+ <dependency>
+ <groupId>org.springframework.security</groupId>
+ <artifactId>spring-security-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.security</groupId>
+ <artifactId>spring-security-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.security</groupId>
+ <artifactId>spring-security-web</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.security</groupId>
+ <artifactId>spring-security-taglibs</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>org.springframework.transaction</artifactId>
+ </dependency>
+ <!-- Visual VM hook -->
+ <dependency>
+ <groupId>org.ow2.chameleon.management</groupId>
+ <artifactId>chameleon-mbeans</artifactId>
+ </dependency>
+ <!-- Jersey for JAXRS -->
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-server</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-client</artifactId>
+ </dependency>
- <dependency>
- <groupId>org.ow2.asm</groupId>
- <artifactId>asm-all</artifactId>
- </dependency>
- <dependency>
- <groupId>org.eclipse.persistence</groupId>
- <artifactId>org.eclipse.persistence.moxy</artifactId>
- </dependency>
- <dependency>
- <groupId>org.eclipse.persistence</groupId>
- <artifactId>org.eclipse.persistence.core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.eclipse.persistence</groupId>
- <artifactId>org.eclipse.persistence.antlr</artifactId>
- </dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-all</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.persistence</groupId>
+ <artifactId>org.eclipse.persistence.moxy</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.persistence</groupId>
+ <artifactId>org.eclipse.persistence.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.persistence</groupId>
+ <artifactId>org.eclipse.persistence.antlr</artifactId>
+ </dependency>
- <dependency>
- <groupId>org.eclipse.equinox.http</groupId>
- <artifactId>servlet</artifactId>
- </dependency>
- <!-- felix webconsole -->
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.webconsole</artifactId>
- <classifier>all</classifier>
- </dependency>
+ <dependency>
+ <groupId>org.eclipse.equinox.http</groupId>
+ <artifactId>servlet</artifactId>
+ </dependency>
+ <!-- felix webconsole -->
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.webconsole</artifactId>
+ <classifier>all</classifier>
+ </dependency>
- <!-- Third parties from opendaylight released -->
- <dependency>
- <groupId>org.opendaylight.controller.thirdparty</groupId>
- <artifactId>net.sf.jung2</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.thirdparty</groupId>
- <artifactId>org.openflow.openflowj</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.thirdparty</groupId>
- <artifactId>com.sun.jersey.jersey-servlet</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.thirdparty</groupId>
- <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
- </dependency>
+ <!-- Third parties from opendaylight released -->
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>net.sf.jung2</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>org.openflow.openflowj</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>com.sun.jersey.jersey-servlet</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
+ </dependency>
- <!--Netty-->
- <dependency>
- <groupId>io.netty</groupId>
- <artifactId>netty-handler</artifactId>
- </dependency>
- <dependency>
- <groupId>io.netty</groupId>
- <artifactId>netty-codec</artifactId>
- </dependency>
- <dependency>
- <groupId>io.netty</groupId>
- <artifactId>netty-buffer</artifactId>
- </dependency>
- <dependency>
- <groupId>io.netty</groupId>
- <artifactId>netty-transport</artifactId>
- </dependency>
- <dependency>
- <groupId>io.netty</groupId>
- <artifactId>netty-common</artifactId>
- </dependency>
- <dependency>
- <groupId>io.netty</groupId>
- <artifactId>netty-codec-http</artifactId>
- </dependency>
+ <!--Netty-->
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-handler</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-codec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-buffer</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-transport</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-codec-http</artifactId>
+ </dependency>
- <!-- testing dependencies I'm pretty sure we should trim -->
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>clustering.test</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>commons.httpclient</artifactId>
- </dependency>
- </dependencies>
+ <!-- testing dependencies I'm pretty sure we should trim -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.test</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.httpclient</artifactId>
+ </dependency>
+ </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>buildnumber-maven-plugin</artifactId>
- <version>1.2</version>
- <executions>
- <execution>
- <phase>validate</phase>
- <goals>
- <goal>create</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <doCheck>false</doCheck>
- <doUpdate>false</doUpdate>
- <revisionOnScmFailure>VersionUnknown</revisionOnScmFailure>
- </configuration>
- </plugin>
- <plugin>
- <artifactId>maven-assembly-plugin</artifactId>
- <version>2.3</version>
- <executions>
- <execution>
- <id>distro-assembly</id>
- <phase>package</phase>
- <goals>
- <goal>single</goal>
- </goals>
- <configuration>
- <descriptors>
- <descriptor>src/assemble/bin.xml</descriptor>
- </descriptors>
- <finalName>${project.artifactId}</finalName>
- </configuration>
- </execution>
- </executions>
- </plugin>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>buildnumber-maven-plugin</artifactId>
+ <version>1.2</version>
+ <executions>
+ <execution>
+ <phase>validate</phase>
+ <goals>
+ <goal>create</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <doCheck>false</doCheck>
+ <doUpdate>false</doUpdate>
+ <revisionOnScmFailure>VersionUnknown</revisionOnScmFailure>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.3</version>
+ <executions>
+ <execution>
+ <id>distro-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/assemble/bin.xml</descriptor>
+ </descriptors>
+ <finalName>${project.artifactId}</finalName>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
- <!--Make checkstyle ignore initial xml configuration files by overriding its configuration from parent-->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-checkstyle-plugin</artifactId>
- <version>${checkstyle.version}</version>
- <configuration>
- <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/configuration\/initial\/</excludes>
- </configuration>
- </plugin>
- </plugins>
- </build>
+ <!--Make checkstyle ignore initial xml configuration files by overriding its configuration from parent-->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>${checkstyle.version}</version>
+ <configuration>
+ <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/configuration\/initial\/</excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
</project>
</directory>
<excludes>
<exclude>version.properties</exclude>
+ <exclude>configuration/config.ini</exclude>
</excludes>
<outputDirectory>
opendaylight/
<outputDirectory>opendaylight</outputDirectory>
<filtered>true</filtered>
</file>
+ <file>
+ <source>src/main/resources/configuration/config.ini</source>
+ <outputDirectory>opendaylight/configuration</outputDirectory>
+ <filtered>true</filtered>
+ </file>
</files>
</assembly>
+++ /dev/null
------BEGIN RSA PRIVATE KEY-----
-MIIEogIBAAKCAQEAuC9hbEacpewvylI0mwFwjy3Wou2hpr/ncN9BBiFDSaG5yW2k
-3Oy+SCAcFCL+ZKWb6cc6Ch4gUeCwyEHRojZguuhliKtak9YQf6qbvpPLe00842Lx
-iqNAGurMpzizCDsGFq8ChaAkBZQI3TvcHuPoSUWSMJ+K8xHpRyUdVr6g2yEjezKJ
-sTXBtWaeCCh6YUafFujuDJk7fvYcPW7Je5KRBBStIKvxcMW0zB+7eq04deTHwGbJ
-gGjKWilQ72hsDDP3Hbp5CJMAYg1r4GlCmFx3KyHRGztgWgNgaD7nNpKCkTLjtmA6
-b4x7TA+jrzZ6Af2z5TMrI4dv5w1SrxHaZ+ziLQIDAQABAoIBAHTndeGgq/rQf8De
-Do+4CTaHtK0zQSAyu/azbXUzlZ7drKuCEVs8VMY4wzmwwGEnkF+A2YDkgEUX5X0l
-8aYQ97KKoS9u+43MGCrAIhyDeGrpqlT1TzRcy+qJz53v6gq2U/X/3QztiQ+VV078
-mIluxNgE9XYxPaNsYfGLSCTv1+9c8y/hjGVX2kwFK+u4ut0ZZETggNa8UxfaHVDS
-fIJQX9Gm3J3GSUV30fDGMBIUW6ESLc2L8b7u8Mp9TRP39ZeQSuEUjBe8MYKv0Rel
-oEpjZvcnniMTpFbLpndBYn7/AoIiEBvtCN8faVTuRRcvvLcsRm09IctzKQYnMh6M
-6PLKV+ECgYEA8HFRYaKHUzxpzE/fyon82GQbzqFFY0/bbWrfWICMfNbIgshJUie6
-FmH5iUFMfeqaT7v557HFM0GB9FeIeSbvd88YmiBAcRopZ3DfMkDH+DT73yJ+/TKG
-2nrQtdhyuTIs4bwHqeS2BBJYs7PK9R2rratF3l34Tf7mjlvyOgygHdUCgYEAxBo2
-8hEBlAVNcNb1hTYUxe1w1B6675/mFlmw98Xmj9dRYfICXNhahs8tX3/lsBEd+vBu
-fI0oyHaff8m5bPgGzD1ZMybfeROujNrgxaKVk7Ef0FDRRCop4bm18OroFlFAt9l8
-wMp++ToACbdvQvL/mjWMPYlIxhB/YxHswICZZvkCgYAexxKYwdo6sGAGlC7cWT9x
-X5cjowcjyEQZRHXkeUgCbufpvcOM7aLnXJE5nY8yCwbHsBM0MlBA2GDPKylAANjk
-aDEJAZneIHAuWodngl1Wi0m2bU7+ECqs6s2uiU9eH2sZVh1RBQK7kLGkBx6ys6KX
-L3ZZGYRAT6GplWFzRsx0JQKBgCeVlxPD5QqpC1nEumi6YvUVGdpnnZpzL3HBhxxs
-wT612wKnZFyze4qM1X7ahVXGDsQxtkvD/sCAWW/lG13orw6ZL6FIroF1PJ3ILOkY
-CZN3hJF7TtKwpCWhZB2OfWzL2AGEkE8mUP0j/Q/5DCd6f6f0OSvOw3bfq6cm3iB5
-lP2ZAoGAXsRN5TZTX4AQ2xTlrDQ8A5XgcvyWQpJOmEXMTyHV7VaJVzmNWFVAvndK
-5UIq8ALDwB2t7vjmMUW6euvIwqtXiop7G79UOb3e3NhzeyWFGQyBLqCRznGaXQTT
-dlFy73xhukZMhFnj006bjKCYvOPnwuGl3+0fuWil5Rq3jOuY5c8=
------END RSA PRIVATE KEY-----
reference\:file\:../lib/slf4j-api-1.7.2.jar@1:start,\
reference\:file\:../lib/logback-classic-1.0.9.jar@1:start,\
reference\:file\:../lib/logback-core-1.0.9.jar@1:start,\
- reference\:file\:../lib/logging.bridge-0.4.2-SNAPSHOT@1:start,\
+ reference\:file\:../lib/logging.bridge-${controller.version}@1:start,\
reference\:file\:../lib/jersey-core-1.17.jar@2:start,\
reference\:file\:../lib/jersey-server-1.17.jar@2:start
netconf.config.persister.active=1,2
# read startup configuration
-#netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.DirectoryStorageAdapter
-#netconf.config.persister.1.properties.directoryStorage=configuration/initial/
-#netconf.config.persister.1.readonly=true
-
-netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.autodetect.AutodetectDirectoryStorageAdapter
+netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryStorageAdapter
netconf.config.persister.1.properties.directoryStorage=configuration/initial/
+# include only xml files, files with other extensions will be skipped, multiple extensions are permitted e.g. netconf.config.persister.1.properties.includeExtensions=xml,cfg,config
+netconf.config.persister.1.properties.includeExtensions=xml
netconf.config.persister.1.readonly=true
-#netconf.config.persister.3.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryStorageAdapter
-#netconf.config.persister.3.properties.directoryStorage=configuration/initialXml/
-#netconf.config.persister.3.readonly=true
-
-#netconf.config.persister.4.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
-#netconf.config.persister.4.properties.fileStorage=configuration/current/controller.currentconfig.txt
-#netconf.config.persister.4.properties.numberOfBackups=1
-
netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter
netconf.config.persister.2.properties.fileStorage=configuration/current/controller.currentconfig.xml
netconf.config.persister.2.properties.numberOfBackups=1
# Set Default start level for framework
osgi.bundles.defaultStartLevel=4
# Extra packages to import from the boot class loader
-org.osgi.framework.system.packages.extra=sun.reflect,sun.reflect.misc,sun.misc
+org.osgi.framework.system.packages.extra=sun.reflect,sun.reflect.misc,sun.misc,sun.nio.ch
# This is not Eclipse App
eclipse.ignoreApp=true
# Don't shutdown equinox if the eclipse App has ended,
-<snapshot>
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<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:md:sal:dom:impl">prefix:schema-service-singleton</type>
<name>yang-schema-service</name>
</module>
+ <!-- To enable use of new in-memory datastore and new implementations
+ of data brokers, comment out all parts of this
+ xml which are marked with DATA-BROKER and uncomment all parts
+ of this xml which are marked with NEW-DATA-BROKER
+ -->
+ <!-- DATA-BROKER start-->
<module>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
<name>hash-map-data-store</name>
</module>
+ <!-- DATA BROKER end -->
+ <!-- NEW-DATA-BROKER start -->
+ <!--
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-inmemory-data-broker</type>
+ <name>async-data-broker</name>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ </module>
+ -->
+ <!-- NEW-DATA-BROKER end -->
<module>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
<name>dom-broker</name>
+ <!-- DATA-BROKER start -->
<data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
<type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
<!-- to switch to the clustered data store, comment out the hash-map-data-store <name> and uncomment the cluster-data-store one -->
<name>hash-map-data-store</name>
<!-- <name>cluster-data-store</name> -->
</data-store>
+ <!-- DATA-BROKER end -->
+ <!-- NEW-DATA-BROKER start -->
+ <!--
+ <async-data-broker>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+ <name>async-data-broker</name>
+ </async-data-broker>
+ -->
+ <!-- NEW-DATA-BROKER end -->
</module>
<module>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
<name>binding-notification-broker</name>
</module>
+ <!-- DATA-BROKER start -->
<module>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
<name>binding-data-broker</name>
<name>runtime-mapping-singleton</name>
</mapping-service>
</module>
-
+ <!-- DATA-BROKER end -->
+ <!-- NEW-DATA-BROKER start -->
+ <!--
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-compatible-broker</type>
+ <name>binding-data-broker</name>
+ <dom-async-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <name>dom-broker</name>
+ </dom-async-broker>
+ <binding-mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </binding-mapping-service>
+ </module>
+ -->
+ <!-- NEW-DATA-BROKER end -->
</modules>
-
<services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
- <instance>
- <name>yang-schema-service</name>
- <provider>/modules/module[type='schema-service-singleton'][name='yang-schema-service']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
- <instance>
- <name>binding-notification-broker</name>
- <provider>/modules/module[type='binding-notification-broker'][name='binding-notification-broker']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
- <instance>
- <name>hash-map-data-store</name>
- <provider>/modules/module[type='hash-map-data-store'][name='hash-map-data-store']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
- <instance>
- <name>binding-osgi-broker</name>
- <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
- <instance>
- <name>binding-rpc-broker</name>
- <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
- <instance>
- <name>runtime-mapping-singleton</name>
- <provider>/modules/module[type='runtime-generated-mapping'][name='runtime-mapping-singleton']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
- <instance>
- <name>dom-broker</name>
- <provider>/modules/module[type='dom-broker-impl'][name='dom-broker']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <instance>
- <name>binding-data-broker</name>
- <provider>/modules/module[type='binding-data-broker'][name='binding-data-broker']</provider>
- </instance>
- </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <instance>
+ <name>yang-schema-service</name>
+ <provider>/modules/module[type='schema-service-singleton'][name='yang-schema-service']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+ <instance>
+ <name>binding-notification-broker</name>
+ <provider>/modules/module[type='binding-notification-broker'][name='binding-notification-broker']</provider>
+ </instance>
+ </service>
+ <!-- DATA-BROKER start -->
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+ <instance>
+ <name>hash-map-data-store</name>
+ <provider>/modules/module[type='hash-map-data-store'][name='hash-map-data-store']</provider>
+ </instance>
+ </service>
+ <!-- DATA-BROKER end -->
+ <!-- NEW-DATA-BROKER start -->
+ <!--
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+ <instance>
+ <name>async-data-broker</name>
+ <provider>/modules/module[type='dom-inmemory-data-broker'][name='async-data-broker']</provider>
+ </instance>
+ </service>
+ -->
+ <!-- NEW-DATA-BROKER end -->
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
+ <instance>
+ <name>binding-osgi-broker</name>
+ <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
+ <instance>
+ <name>binding-rpc-broker</name>
+ <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
+ <instance>
+ <name>runtime-mapping-singleton</name>
+ <provider>/modules/module[type='runtime-generated-mapping'][name='runtime-mapping-singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <instance>
+ <name>dom-broker</name>
+ <provider>/modules/module[type='dom-broker-impl'][name='dom-broker']</provider>
+ </instance>
+ </service>
+
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <instance>
+ <name>binding-data-broker</name>
+ <!-- DATA-BROKER start -->
+ <provider>/modules/module[type='binding-data-broker'][name='binding-data-broker']</provider>
+ <!-- DATA-BROKER end -->
+ <!-- NEW-DATA-BROKER start -->
+ <!--
+ <provider>/modules/module[type='binding-data-compatible-broker'][name='binding-data-broker']</provider>
+ -->
+ <!-- NEW-DATA-BROKER end -->
+ </instance>
+ </service>
</services>
</data>
-
</configuration>
-
<required-capabilities>
<capability>urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28</capability>
</required-capabilities>
-
</snapshot>
-
import org.opendaylight.controller.sal.action.Drop;
import org.opendaylight.controller.sal.action.Enqueue;
import org.opendaylight.controller.sal.action.Flood;
+import org.opendaylight.controller.sal.action.FloodAll;
import org.opendaylight.controller.sal.action.HwPath;
import org.opendaylight.controller.sal.action.Loopback;
import org.opendaylight.controller.sal.action.Output;
continue;
}
+ sstr = Pattern.compile(ActionType.ENQUEUE + "=(.*)").matcher(actiongrp);
+ if (sstr.matches()) {
+ for (String t : sstr.group(1).split(",")) {
+ if (t != null) {
+ String parts[] = t.split(":");
+ String nc = String.format("%s|%s@%s", node.getType(), parts[0], node.toString());
+ if (NodeConnector.fromString(nc) == null) {
+ return new Status(StatusCode.BADREQUEST, String.format("Enqueue port is not valid"));
+ }
+ if (parts.length > 1) {
+ try {
+ Integer.parseInt(parts[1]);
+ } catch (NumberFormatException e) {
+ return new Status(StatusCode.BADREQUEST, String.format("Enqueue %s is not in the range 0 - 2147483647", parts[1]));
+ }
+ }
+ }
+ }
+ continue;
+ }
+
sstr = Pattern.compile(ActionType.SET_VLAN_PCP.toString() + "=(.*)").matcher(actiongrp);
if (sstr.matches()) {
if ((sstr.group(1) != null) && !isVlanPriorityValid(sstr.group(1))) {
continue;
}
+ sstr = Pattern.compile(ActionType.FLOOD_ALL.toString()).matcher(actiongrp);
+ if (sstr.matches()) {
+ actionList.add(new FloodAll());
+ continue;
+ }
+
sstr = Pattern.compile(ActionType.SW_PATH.toString()).matcher(actiongrp);
if (sstr.matches()) {
actionList.add(new SwPath());
static final String ACTIVE_HOST_CACHE = "hosttracker.ActiveHosts";
static final String INACTIVE_HOST_CACHE = "hosttracker.InactiveHosts";
private static final Logger logger = LoggerFactory.getLogger(HostTracker.class);
- protected final Set<IHostFinder> hostFinder = new CopyOnWriteArraySet<IHostFinder>();;
+ protected final Set<IHostFinder> hostFinders = new CopyOnWriteArraySet<IHostFinder>();
protected ConcurrentMap<IHostId, HostNodeConnector> hostsDB;
/*
* Following is a list of hosts which have been requested by NB APIs to be
}
public void setArpHandler(IHostFinder hostFinder) {
- if (this.hostFinder != null) {
- this.hostFinder.add(hostFinder);
- }
+ this.hostFinders.add(hostFinder);
}
public void unsetArpHandler(IHostFinder hostFinder) {
- if (this.hostFinder != null) {
- logger.debug("Arp Handler Service removed!");
- this.hostFinder.remove(hostFinder);
- }
+ logger.debug("Arp Handler Service removed!");
+ this.hostFinders.remove(hostFinder);
}
public void setTopologyManager(ITopologyManager s) {
* already handles the null return
*/
- if (hostFinder == null) {
- logger.debug("Exiting hostFind, null hostFinder");
+ if (hostFinders.isEmpty()) {
+ logger.debug("No available host finders, exiting hostFind()");
return null;
}
logger.debug("hostFind(): Host Not Found for IP: {}, Inititated Host Discovery ...", id);
/* host is not found, initiate a discovery */
- for (IHostFinder hf : hostFinder) {
+ for (IHostFinder hf : hostFinders) {
InetAddress addr = decodeIPFromId(id);
hf.find(addr);
}
for (Entry<IHostId, ARPPending> entry : failedARPReqList.entrySet()) {
ARPPending arphost;
arphost = entry.getValue();
- if (hostFinder == null) {
- logger.warn("ARPHandler Services are not available on subnet addition");
+ if (hostFinders.isEmpty()) {
+ logger.debug("ARPHandler Services are not available on subnet addition");
continue;
}
logger.debug("Sending the ARP from FailedARPReqList fors IP: {}", decodeIPFromId(arphost.getHostId()));
- for (IHostFinder hf : hostFinder) {
+ for (IHostFinder hf : hostFinders) {
hf.find(decodeIPFromId(arphost.getHostId()));
}
}
* next one. Before sending the ARP, check if ARPHandler
* is available or not
*/
- if (hostFinder == null) {
+ if (hostFinders.isEmpty()) {
logger.warn("ARPHandler Services are not available for Outstanding ARPs");
continue;
}
- for (IHostFinder hf : hostFinder) {
+ for (IHostFinder hf : hostFinders) {
hf.find(decodeIPFromId(arphost.getHostId()));
}
arphost.sent_count++;
HexEncode.bytesToHexString(host.getDataLayerAddressBytes()) });
}
host.setArpSendCountDown(arp_cntdown);
- if (hostFinder == null) {
+ if (hostFinders.isEmpty()) {
/*
* If hostfinder is not available, then can't send
* the probe. However, continue the age out the
logger.trace("ARPHandler is not avaialable, can't send the probe");
continue;
}
- for (IHostFinder hf : hostFinder) {
+ for (IHostFinder hf : hostFinders) {
hf.probe(host);
}
}
for (Entry<IHostId, ARPPending> entry : failedARPReqList.entrySet()) {
arphost = entry.getValue();
logger.trace("Sending the ARP from FailedARPReqList fors IP: {}", arphost.getHostId());
- if (hostFinder == null) {
+ if (hostFinders.isEmpty()) {
logger.warn("ARPHandler is not available at interface up");
logger.warn("Since this event is missed, host(s) connected to interface {} may not be discovered",
nodeConnector);
byte[] dataLayerAddress = NetUtils.getBroadcastMACAddr();
host = new HostNodeConnector(dataLayerAddress, decodeIPFromId(arphost.getHostId()), nodeConnector,
(short) 0);
- for (IHostFinder hf : hostFinder) {
+ for (IHostFinder hf : hostFinders) {
hf.probe(host);
}
} catch (ConstructionException e) {
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
+ <artifactId>config-persister-file-xml-adapter</artifactId>
<version>${config.version}</version>
</dependency>
<dependency>
<artifactId>config-persister-impl</artifactId>
<version>${config.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
- <version>${config.version}</version>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-impl</artifactId>
class FRMRuntimeDataProvider implements RuntimeDataProvider, DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> {
- static val FLOWS_PATH = InstanceIdentifier.builder().node(Flows).toInstance;
+ static val FLOWS_PATH = InstanceIdentifier.builder(Flows).toInstance;
@Property
var DataProviderService dataService;
DataModificationTransaction transaction = dataService.beginTransaction();
Flow flow = createSampleFlow("foo", null);
- InstanceIdentifier<Flow> path = InstanceIdentifier.builder().node(Flows.class).node(Flow.class, flow.getKey())
+ InstanceIdentifier<Flow> path = InstanceIdentifier.builder(Flows.class).child(Flow.class, flow.getKey())
.toInstance();
transaction.putConfigurationData(path, flow);
*/
package org.opendaylight.controller.md.compatibility.switchmanager
-import org.opendaylight.controller.switchmanager.ISwitchManager
-import org.opendaylight.controller.sal.core.NodeConnector
-import org.opendaylight.controller.sal.core.Property
-import java.util.List
-import org.opendaylight.controller.sal.core.Node
import java.net.InetAddress
+import java.net.NetworkInterface
+import java.net.SocketException
+import java.util.ArrayList
+import java.util.Collections
+import java.util.HashSet
+import java.util.List
+import java.util.Map
import org.opendaylight.controller.sal.binding.api.data.DataBrokerService
-import static extension org.opendaylight.controller.sal.compatibility.NodeMapping.*
-import org.opendaylight.controller.sal.core.Description
-import org.opendaylight.controller.sal.core.Tier
import org.opendaylight.controller.sal.core.Bandwidth
+import org.opendaylight.controller.sal.core.Description
import org.opendaylight.controller.sal.core.ForwardingMode
import org.opendaylight.controller.sal.core.MacAddress
-
-import org.slf4j.LoggerFactory
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.yangtools.yang.binding.DataObject
-import java.net.NetworkInterface
-import java.net.SocketException
-import java.util.Collections
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes
-import java.util.ArrayList
+import org.opendaylight.controller.sal.core.Node
+import org.opendaylight.controller.sal.core.NodeConnector
+import org.opendaylight.controller.sal.core.Property
+import org.opendaylight.controller.sal.core.Tier
+import org.opendaylight.controller.switchmanager.ISwitchManager
import org.opendaylight.controller.switchmanager.Switch
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId
-import java.util.Map
-import java.util.HashSet
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortState
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
+import org.opendaylight.yangtools.yang.binding.DataObject
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
+import org.slf4j.LoggerFactory
+
+import static extension org.opendaylight.controller.sal.compatibility.NodeMapping.*
class CompatibleSwitchManager extends ConfigurableSwitchManager implements ISwitchManager {
}
override getNetworkDevices() {
- val path = InstanceIdentifier.builder().node(Nodes).toInstance;
+ val path = InstanceIdentifier.builder(Nodes).toInstance;
val data = dataService.readOperationalData(path) as Nodes;
val ret = new ArrayList<Switch>();
for (node : data.node) {
}
override getNodes() {
- val path = InstanceIdentifier.builder().node(Nodes).toInstance;
+ val path = InstanceIdentifier.builder(Nodes).toInstance;
val data = dataService.readOperationalData(path) as Nodes;
val ret = new HashSet<Node>();
for (node : data.node) {
*/
package org.opendaylight.controller.md.compatibility.topology
+import java.util.ArrayList
+import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider
+import org.opendaylight.controller.sal.core.Edge
+import org.opendaylight.controller.sal.core.NodeConnector
import org.opendaylight.controller.switchmanager.ISwitchManager
import org.opendaylight.controller.topologymanager.ITopologyManager
-import org.opendaylight.controller.md.sal.common.api.data.DataReader
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.yangtools.yang.binding.DataObject
-import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider
+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.TopologyId
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder
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.topology.Node
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder
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.NetworkTopology
-import org.opendaylight.controller.md.compatibility.topology.TopologyMapping
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkBuilder
-
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder
-import java.util.ArrayList
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey
-import org.opendaylight.controller.sal.core.NodeConnector
-import org.opendaylight.controller.sal.topology.TopoEdgeUpdate
-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.node.TerminationPoint
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder
-import org.opendaylight.controller.sal.core.Edge
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder
+import org.opendaylight.yangtools.yang.binding.DataObject
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
class TopologyReader implements RuntimeDataProvider {
new() {
_topologyKey = new TopologyKey(new TopologyId("compatibility:ad-sal"));
- _topologyPath = InstanceIdentifier.builder().node(NetworkTopology).child(Topology, topologyKey).toInstance;
+ _topologyPath = InstanceIdentifier.builder(NetworkTopology).child(Topology, topologyKey).toInstance;
_mapping = new TopologyMapping(topologyKey, topologyPath);
}
import java.util.Map
import org.opendaylight.controller.sal.core.Edge
import java.util.Set
-import java.util.List
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node
-import java.util.Collections
-import com.google.common.collect.FluentIterable
import java.util.HashSet
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId
import org.opendaylight.controller.sal.compatibility.NodeMapping
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Destination
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey
import java.util.HashMap
new(TopologyKey topology) {
topologyMapping = topology;
- _topologyPath = InstanceIdentifier.builder.node(NetworkTopology).child(Topology, topology).toInstance;
+ _topologyPath = InstanceIdentifier.builder(NetworkTopology).child(Topology, topology).toInstance;
}
def InstanceIdentifier<TerminationPoint> toTerminationPoint(NodeConnector connector) {
- InstanceIdentifier.builder(topologyPath).node(Node).child(TerminationPoint, connector.toTerminationPointKey()).toInstance;
+ InstanceIdentifier.builder(topologyPath).child(Node).child(TerminationPoint, connector.toTerminationPointKey()).toInstance;
}
def Map<Edge, Set<org.opendaylight.controller.sal.core.Property>> toEdgePropertiesMap(Iterable<Link> links) {
topology.dataService = session.getSALService(DataProviderService)
tpProvider.dataService = session.getSALService(DataProviderService)
- inventory.start();
+ inventory.startAdapter();
- tpProvider.start();
+ tpProvider.startAdapter();
subscribe.registerNotificationListener(dataPacket)
}
private final Lock nodeToNodeConnectorsLock = new ReentrantLock();
- def start(){
+ def startAdapter(){
inventoryNotificationProvider.dataProviderService = dataProviderService;
inventoryNotificationProvider.inventoryPublisher = inventoryPublisher;
// inventoryNotificationProvider.start();
}
+ def start(){
+ }
+
def setInventoryPublisher(IPluginOutInventoryService listener){
inventoryPublisher.add(listener);
}
public static NodeRef toNodeRef(final Node node) {
final NodeId nodeId = new NodeId(InventoryMapping.toNodeId(node));
final NodeKey nodeKey = new NodeKey(nodeId);
- final InstanceIdentifierBuilder<? extends Object> builder = InstanceIdentifier.builder();
- final InstanceIdentifierBuilder<Nodes> nodes = builder.<Nodes>node(Nodes.class);
+ final InstanceIdentifierBuilder<Nodes> nodes = InstanceIdentifier.builder(Nodes.class);
final InstanceIdentifierBuilder<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> child =
nodes.<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, NodeKey>child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, nodeKey);
final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> path = child.toInstance();
val sourceActions = sourceFlow.actions;
val targetActions = new ArrayList<Action>();
+ var int action = 0;
for (sourceAction : sourceActions) {
- targetActions.add(sourceAction.toAction());
+ targetActions.add(sourceAction.toAction(action));
+ action = action + 1;
}
instructions = targetActions.toApplyInstruction();
match = sourceFlow.match.toMatch();
val sourceActions = sourceFlow.actions;
val targetActions = new ArrayList<Action>();
+ var int action = 0;
for (sourceAction : sourceActions) {
- targetActions.add(sourceAction.toAction());
+ targetActions.add(sourceAction.toAction(action));
+ action = action+ 1;
}
instructions = targetActions.toApplyInstruction();
match = sourceFlow.match.toMatch();
return it.build();
}
- public static dispatch def toAction(Controller sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(Controller sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
actionBuilder.action = new ControllerActionCaseBuilder().setControllerAction(new ControllerActionBuilder().build()).build();
return actionBuilder.build();
}
- public static dispatch def toAction(Drop sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(Drop sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
actionBuilder.action = new DropActionCaseBuilder().setDropAction(new DropActionBuilder().build()).build();
return actionBuilder.build();
}
- public static dispatch def toAction(Flood sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(Flood sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
actionBuilder.action = new FloodActionCaseBuilder().setFloodAction(new FloodActionBuilder().build).build();
return actionBuilder.build();
}
- public static dispatch def toAction(FloodAll sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(FloodAll sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
actionBuilder.action = new FloodAllActionCaseBuilder().setFloodAllAction(new FloodAllActionBuilder().build()).build();
return actionBuilder.build();
}
- public static dispatch def toAction(HwPath sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(HwPath sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
actionBuilder.action = new HwPathActionCaseBuilder().setHwPathAction(new HwPathActionBuilder().build()).build();
return actionBuilder.build();
}
- public static dispatch def toAction(Loopback sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(Loopback sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
actionBuilder.action = new LoopbackActionCaseBuilder().setLoopbackAction(new LoopbackActionBuilder().build()).build();
return actionBuilder.build();
}
- public static dispatch def toAction(Output sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(Output sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new OutputActionBuilder();
outputNodeConnector = sourceAction.port.toUri;
actionBuilder.action = new OutputActionCaseBuilder().setOutputAction(it.build()).build();
}
- public static dispatch def toAction(PopVlan sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(PopVlan sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
actionBuilder.action = new PopVlanActionCaseBuilder().build();
return actionBuilder.build();
}
- public static dispatch def toAction(PushVlan sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(PushVlan sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new PushVlanActionBuilder();
cfi = new VlanCfi(sourceAction.cfi);
vlanId = new VlanId(sourceAction.vlanId);
return actionBuilder.build();
}
- public static dispatch def toAction(SetDlDst sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(SetDlDst sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new SetDlDstActionBuilder();
address = sourceAction.dlAddress.toMacAddress();
actionBuilder.action = new SetDlDstActionCaseBuilder().setSetDlDstAction(it.build()).build;
return actionBuilder.build();
}
- public static dispatch def toAction(SetDlSrc sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(SetDlSrc sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new SetDlSrcActionBuilder();
address = sourceAction.dlAddress.toMacAddress;
actionBuilder.action = new SetDlSrcActionCaseBuilder().setSetDlSrcAction(it.build()).build;
return actionBuilder.build();
}
- public static dispatch def toAction(SetDlType sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(SetDlType sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new SetDlTypeActionBuilder();
dlType = new EtherType(sourceAction.dlType as long);
actionBuilder.action = new SetDlTypeActionCaseBuilder().setSetDlTypeAction(it.build()).build();
return actionBuilder.build();
}
- public static dispatch def toAction(SetNextHop sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(SetNextHop sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new SetNextHopActionBuilder();
val inetAddress = sourceAction.address;
address = inetAddress.toInetAddress;
return actionBuilder.build();
}
- public static dispatch def toAction(SetNwDst sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(SetNwDst sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new SetNwDstActionBuilder();
val inetAddress = sourceAction.address;
address = inetAddress.toInetAddress;
return actionBuilder.build();
}
- public static dispatch def toAction(SetNwSrc sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(SetNwSrc sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new SetNwSrcActionBuilder();
val inetAddress = sourceAction.address;
address = inetAddress.toInetAddress;
return actionBuilder.build();
}
- public static dispatch def toAction(SetNwTos sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(SetNwTos sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new SetNwTosActionBuilder();
tos = sourceAction.nwTos;
actionBuilder.action = new SetNwTosActionCaseBuilder().setSetNwTosAction(it.build).build;
return actionBuilder.build();
}
- public static dispatch def toAction(SetTpDst sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(SetTpDst sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new SetTpDstActionBuilder();
port = new PortNumber(sourceAction.port);
actionBuilder.action = new SetTpDstActionCaseBuilder().setSetTpDstAction(it.build()).build();
return actionBuilder.build();
}
- public static dispatch def toAction(SetTpSrc sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(SetTpSrc sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new SetTpSrcActionBuilder();
port = new PortNumber(sourceAction.port);
actionBuilder.action = new SetTpSrcActionCaseBuilder().setSetTpSrcAction(it.build()).build();
return actionBuilder.build();
}
- public static dispatch def toAction(SetVlanCfi sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(SetVlanCfi sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new SetVlanCfiActionBuilder();
vlanCfi = new VlanCfi(sourceAction.cfi);
actionBuilder.action = new SetVlanCfiActionCaseBuilder().setSetVlanCfiAction(it.build()).build();
return actionBuilder.build();
}
- public static dispatch def toAction(SetVlanId sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(SetVlanId sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new SetVlanIdActionBuilder();
vlanId = new VlanId(sourceAction.vlanId);
return actionBuilder.build();
}
- public static dispatch def toAction(SetVlanPcp sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(SetVlanPcp sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
val it = new SetVlanPcpActionBuilder();
vlanPcp = new VlanPcp(sourceAction.pcp as short);
actionBuilder.action = new SetVlanPcpActionCaseBuilder().setSetVlanPcpAction(it.build).build;
return actionBuilder.build();
}
- public static dispatch def toAction(SwPath sourceAction) {
- val actionBuilder = new ActionBuilder();
+ public static dispatch def toAction(SwPath sourceAction, int order) {
+ val actionBuilder = new ActionBuilder().setOrder(order);
actionBuilder.action = new SwPathActionCaseBuilder().setSwPathAction(new SwPathActionBuilder().build()).build();
return actionBuilder.build();
}
val sourceActions = sourceFlow.actions;
val targetActions = new ArrayList<Action>();
+ var int action = 0;
for (sourceAction : sourceActions) {
- targetActions.add(sourceAction.toAction());
+ targetActions.add(sourceAction.toAction(action));
+ action = action + 1;
}
instructions = targetActions.toApplyInstruction();
match = sourceFlow.match.toMatch();
Preconditions.checkArgument(MD_SAL_TYPE.equals(node.getType()));
final String nodeId = Arguments.<String>checkInstanceOf(node.getID(), String.class);
final NodeKey nodeKey = new NodeKey(new NodeId(nodeId));
- final InstanceIdentifier<Node> nodePath = InstanceIdentifier.builder().node(Nodes.class).child(NODE_CLASS, nodeKey).toInstance();
+ final InstanceIdentifier<Node> nodePath = InstanceIdentifier.builder(Nodes.class).child(NODE_CLASS, nodeKey).toInstance();
return new NodeRef(nodePath);
}
DataProviderService dataService;
Registration<DataCommitHandler<InstanceIdentifier<? extends DataObject>,DataObject>> commitHandlerRegistration;
-
+
def void start() {
+
+ }
+ def void startAdapter() {
+ if(dataService == null){
+ LOG.error("dataService not set");
+ return;
+ }
commitHandler = new TopologyCommitHandler(dataService)
commitHandler.setTopologyPublisher(topologyPublisher)
val InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(NetworkTopology)
def setTopologyPublisher(IPluginOutTopologyService topologyPublisher) {
_topologyPublisher = topologyPublisher;
- commitHandler.setTopologyPublisher(topologyPublisher);
+ if(commitHandler != null){
+ commitHandler.setTopologyPublisher(topologyPublisher);
+ }
}
}
<groupId>org.eclipse.xtend</groupId>
<artifactId>org.eclipse.xtend.lib</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<build>
listenerRegistration?.close();
}
-}
-
-class NodeChangeCommiter implements OpendaylightInventoryListener {
-
- static val LOG = LoggerFactory.getLogger(NodeChangeCommiter);
-
- @Property
- val FlowCapableInventoryProvider manager;
-
- new(FlowCapableInventoryProvider manager) {
- _manager = manager;
- }
-
- override onNodeConnectorRemoved(NodeConnectorRemoved connector) {
- val ref = connector.nodeConnectorRef;
-
- // Check path
- val it = manager.startChange()
-
- LOG.debug("removing node connector : " + ref.value.toString());
-
- removeOperationalData(ref.value as InstanceIdentifier<? extends DataObject>);
- commit()
- }
-
- override onNodeConnectorUpdated(NodeConnectorUpdated connector) {
- val ref = connector.nodeConnectorRef;
-
- val flowConnector = connector.getAugmentation(FlowCapableNodeConnectorUpdated);
-
- val it = manager.startChange()
- val data = new NodeConnectorBuilder(connector);
- data.key = (new NodeConnectorKey(connector.id))
- if (flowConnector != null) {
- val augment = flowConnector.toInventoryAugment()
- data.addAugmentation(FlowCapableNodeConnector, augment)
- }
-
- LOG.debug("updating node connector : " + ref.value.toString());
-
- putOperationalData(ref.value as InstanceIdentifier<NodeConnector>, data.build());
- commit()
- }
-
- override onNodeRemoved(NodeRemoved node) {
- val ref = node.nodeRef;
- val it = manager.startChange()
-
- LOG.debug("removing node : " + ref.value.toString());
-
- removeOperationalData(ref.value as InstanceIdentifier<? extends DataObject>);
- commit()
- }
-
- override onNodeUpdated(NodeUpdated node) {
- val ref = node.nodeRef;
- val flowNode = node.getAugmentation(FlowCapableNodeUpdated);
-
- val it = manager.startChange()
- val data = new NodeBuilder(node);
- data.key = (new NodeKey(node.id))
- if (flowNode != null) {
- val augment = flowNode.toInventoryAugment();
- data.addAugmentation(FlowCapableNode, augment)
- }
-
- LOG.debug("updating node : " + ref.value.toString());
-
- putOperationalData(ref.value as InstanceIdentifier<Node>, data.build())
- commit()
- }
-}
+}
\ No newline at end of file
package org.opendaylight.controller.md.inventory.manager
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowNodeConnector
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode
--- /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.md.inventory.manager;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated;
+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.NodeRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.OpendaylightInventoryListener;
+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.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Objects;
+
+@SuppressWarnings("all")
+public class NodeChangeCommiter implements OpendaylightInventoryListener {
+ private final static Logger LOG = LoggerFactory.getLogger(NodeChangeCommiter.class);
+
+ private final FlowCapableInventoryProvider manager;
+
+ public NodeChangeCommiter(final FlowCapableInventoryProvider manager) {
+ this.manager = manager;
+ }
+
+ public FlowCapableInventoryProvider getManager() {
+ return this.manager;
+ }
+
+ @Override
+ public synchronized void onNodeConnectorRemoved(final NodeConnectorRemoved connector) {
+
+ final NodeConnectorRef ref = connector.getNodeConnectorRef();
+ final DataModificationTransaction it = this.getManager().startChange();
+ NodeChangeCommiter.LOG.debug("removing node connector {} ", ref.getValue());
+ it.removeOperationalData(ref.getValue());
+ Future<RpcResult<TransactionStatus>> commitResult = it.commit();
+ try {
+ commitResult.get();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Node Connector {} not removed.", ref.getValue(), e);
+ }
+
+ }
+
+ @Override
+ public synchronized void onNodeConnectorUpdated(final NodeConnectorUpdated connector) {
+
+ final NodeConnectorRef ref = connector.getNodeConnectorRef();
+ final FlowCapableNodeConnectorUpdated flowConnector = connector
+ .getAugmentation(FlowCapableNodeConnectorUpdated.class);
+ final DataModificationTransaction it = this.getManager().startChange();
+ final NodeConnectorBuilder data = new NodeConnectorBuilder(connector);
+ NodeConnectorId id = connector.getId();
+ NodeConnectorKey nodeConnectorKey = new NodeConnectorKey(id);
+ data.setKey(nodeConnectorKey);
+ boolean notEquals = (!Objects.equal(flowConnector, null));
+ if (notEquals) {
+ final FlowCapableNodeConnector augment = InventoryMapping.toInventoryAugment(flowConnector);
+ data.addAugmentation(FlowCapableNodeConnector.class, augment);
+ }
+ InstanceIdentifier<? extends Object> value = ref.getValue();
+ String string = value.toString();
+ String plus = ("updating node connector : " + string);
+ NodeChangeCommiter.LOG.debug(plus);
+ InstanceIdentifier<? extends Object> value1 = ref.getValue();
+ NodeConnector build = data.build();
+ it.putOperationalData((value1), build);
+ Future<RpcResult<TransactionStatus>> commitResult = it.commit();
+ try {
+ commitResult.get();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Node Connector {} not updated.", ref.getValue(), e);
+ }
+
+ }
+
+ @Override
+ public synchronized void onNodeRemoved(final NodeRemoved node) {
+
+ final NodeRef ref = node.getNodeRef();
+ FlowCapableInventoryProvider manager = this.getManager();
+ final DataModificationTransaction it = manager.startChange();
+ InstanceIdentifier<? extends Object> value = ref.getValue();
+ String string = value.toString();
+ String plus = ("removing node : " + string);
+ NodeChangeCommiter.LOG.debug(plus);
+ InstanceIdentifier<? extends Object> value1 = ref.getValue();
+ it.removeOperationalData((value1));
+ Future<RpcResult<TransactionStatus>> commitResult = it.commit();
+ try {
+ commitResult.get();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Node {} not removed.", ref.getValue(), e);
+ }
+ }
+
+ @Override
+ public synchronized void onNodeUpdated(final NodeUpdated node) {
+
+ final NodeRef ref = node.getNodeRef();
+ final FlowCapableNodeUpdated flowNode = node
+ .<FlowCapableNodeUpdated> getAugmentation(FlowCapableNodeUpdated.class);
+ FlowCapableInventoryProvider manager = this.getManager();
+ final DataModificationTransaction it = manager.startChange();
+ NodeBuilder nodeBuilder = new NodeBuilder(node);
+ final NodeBuilder data = nodeBuilder;
+ NodeId id = node.getId();
+ NodeKey nodeKey = new NodeKey(id);
+ data.setKey(nodeKey);
+ boolean equals = Objects.equal(flowNode, null);
+ if (equals) {
+ return;
+ }
+ final FlowCapableNode augment = InventoryMapping.toInventoryAugment(flowNode);
+ data.addAugmentation(FlowCapableNode.class, augment);
+ InstanceIdentifier<? extends Object> value = ref.getValue();
+ InstanceIdentifierBuilder<Node> builder = InstanceIdentifier.<Node> builder(((InstanceIdentifier<Node>) value));
+ InstanceIdentifierBuilder<FlowCapableNode> augmentation = builder
+ .<FlowCapableNode> augmentation(FlowCapableNode.class);
+ final InstanceIdentifier<FlowCapableNode> path = augmentation.build();
+ NodeChangeCommiter.LOG.debug("updating node :{} ", path);
+ it.putOperationalData(path, augment);
+
+ Future<RpcResult<TransactionStatus>> commitResult = it.commit();
+ try {
+ commitResult.get();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Node {} not updated.", ref.getValue(), e);
+ }
+
+ }
+}
uses tr:transaction-aware;
}
}
-
- rpc get-port {
- output {
- uses port-type:flow-capable-port;
- }
- }
notification port-updated {
uses port-update;
namespace "urn:opendaylight:model:statistics:types";
prefix stat-types;
- import ietf-yang-types {prefix yang;}
+ import ietf-yang-types {prefix yang; revision-date "2010-09-24";}
revision "2013-09-25" {
description "Initial revision of flow service";
</scm>
<dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-common</artifactId>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common</artifactId>
+ <artifactId>sal-common-api</artifactId>
</dependency>
-
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-api</artifactId>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+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 interface BindingDataBroker extends AsyncDataBroker<InstanceIdentifier<?>, DataObject, BindingDataChangeListener>{
+ @Override
+ BindingDataReadTransaction newReadOnlyTransaction();
+
+ @Override
+ BindingDataReadWriteTransaction newReadWriteTransaction();
+
+ @Override
+ BindingDataWriteTransaction newWriteOnlyTransaction();
+
+ @Override
+ ListenerRegistration<BindingDataChangeListener> registerDataChangeListener(LogicalDatastoreType store,
+ InstanceIdentifier<?> path, BindingDataChangeListener listener, DataChangeScope triggeringScope);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface BindingDataChangeListener extends AsyncDataChangeListener<InstanceIdentifier<?>, DataObject> {
+ @Override
+ void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface BindingDataReadTransaction extends AsyncReadTransaction<InstanceIdentifier<?>, DataObject> {
+ @Override
+ ListenableFuture<Optional<DataObject>> read(LogicalDatastoreType store, InstanceIdentifier<?> 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.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Logical capture of a combination of both {@link BindingDataReadTransaction} and
+ * {@link BindingDataWriteTransaction}.
+ */
+public interface BindingDataReadWriteTransaction extends BindingDataReadTransaction, BindingDataWriteTransaction, AsyncReadWriteTransaction<InstanceIdentifier<?>, DataObject> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface BindingDataWriteTransaction extends AsyncWriteTransaction<InstanceIdentifier<?>, DataObject> {
+ @Override
+ void put(LogicalDatastoreType store, InstanceIdentifier<?> path, DataObject data);
+
+ @Override
+ void delete(LogicalDatastoreType store, InstanceIdentifier<?> path);
+}
import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;
import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
-import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
import org.opendaylight.yangtools.yang.binding.BaseIdentity;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.RpcService;
/**
* Binding-aware core of the SAL layer responsible for wiring the SAL consumers.
- *
+ *
* The responsibility of the broker is to maintain registration of SAL
* functionality {@link Consumer}s and {@link Provider}s, store provider and
* consumer specific context and functionality registration via
* {@link ConsumerContext} and provide access to infrastructure services, which
* removes direct dependencies between providers and consumers.
- *
+ *
* The Binding-aware broker is also responsible for translation from Java
* classes modeling the functionality and data to binding-independent form which
* is used in SAL Core.
- *
- *
+ *
+ *
* <h3>Infrastructure services</h3> Some examples of infrastructure services:
- *
+ *
* <ul>
* <li>YANG Module service - see {@link ConsumerContext#getRpcService(Class)},
* {@link ProviderContext}
* <li>Data Store access and modification - see {@link DataBrokerService} and
* {@link DataProviderService}
* </ul>
- *
+ *
* The services are exposed via session.
- *
+ *
* <h3>Session-based access</h3>
- *
+ *
* The providers and consumers needs to register in order to use the
* binding-independent SAL layer and to expose functionality via SAL layer.
- *
+ *
* For more information about session-based access see {@link ConsumerContext}
* and {@link ProviderContext}
- *
- *
- *
+ *
+ *
+ *
*/
public interface BindingAwareBroker {
/**
* Registers the {@link BindingAwareConsumer}, which will use the SAL layer.
- *
+ *
* <p>
* Note that consumer could register additional functionality at later point
* by using service and functionality specific APIs.
- *
+ *
* <p>
* The consumer is required to use returned session for all communication
* with broker or one of the broker services. The session is announced to
* the consumer by invoking
* {@link Consumer#onSessionInitiated(ConsumerContext)}.
- *
+ *
* @param cons
* Consumer to be registered.
* @return a session specific to consumer registration
/**
* Registers the {@link BindingAwareProvider}, which will use the SAL layer.
- *
+ *
* <p>
* During the registration, the broker obtains the initial functionality
* from consumer, using the
* {@link BindingAwareProvider#getImplementations()}, and register that
* functionality into system and concrete infrastructure services.
- *
+ *
* <p>
* Note that provider could register additional functionality at later point
* by using service and functionality specific APIs.
- *
+ *
* <p>
* The consumer is <b>required to use</b> returned session for all
* communication with broker or one of the broker services. The session is
* announced to the consumer by invoking
* {@link BindingAwareProvider#onSessionInitiated(ProviderContext)}.
- *
- *
+ *
+ *
* @param prov
* Provider to be registered.
* @return a session unique to the provider registration.
/**
* {@link BindingAwareConsumer} specific access to the SAL functionality.
- *
+ *
* <p>
* ConsumerSession is {@link BindingAwareConsumer}-specific access to the
* SAL functionality and infrastructure services.
- *
+ *
* <p>
* The session serves to store SAL context (e.g. registration of
* functionality) for the consumer and provides access to the SAL
* infrastructure services and other functionality provided by
* {@link Provider}s.
- *
- *
- *
+ *
+ *
+ *
*/
public interface ConsumerContext extends RpcConsumerRegistry {
/**
* Returns a session specific instance (implementation) of requested
* binding-aware infrastructural service
- *
+ *
* @param service
* Broker service
* @return Session specific implementation of service
/**
* {@link BindingAwareProvider} specific access to the SAL functionality.
- *
+ *
* <p>
* ProviderSession is {@link BindingAwareProvider}-specific access to the
* SAL functionality and infrastructure services, which also allows for
* exposing the provider's functionality to the other
* {@link BindingAwareConsumer}s.
- *
+ *
* <p>
* The session serves to store SAL context (e.g. registration of
* functionality) for the providers and exposes access to the SAL
* infrastructure services, dynamic functionality registration and any other
* functionality provided by other {@link BindingAwareConsumer}s.
- *
+ *
*/
public interface ProviderContext extends ConsumerContext, RpcProviderRegistry {
void unregisterFunctionality(ProviderFunctionality functionality);
}
- public interface RpcRegistration<T extends RpcService> extends Registration<T> {
+ public interface RpcRegistration<T extends RpcService> extends ObjectRegistration<T> {
Class<T> getServiceType();
}
/**
* Register particular instance identifier to be processed by this
* RpcService
- *
- * Deprecated in favor of {@link RoutedRegistration#registerPath(Object, Object)}.
- *
+ *
+ * Deprecated in favor of {@link RoutedRegistration#registerPath(Object, Object)}.
+ *
* @param context
* @param instance
*/
/**
* Unregister particular instance identifier to be processed by this
* RpcService
- *
- * Deprecated in favor of {@link RoutedRegistration#unregisterPath(Object, Object)}.
- *
+ *
+ * Deprecated in favor of {@link RoutedRegistration#unregisterPath(Object, Object)}.
+ *
* @param context
* @param instance
*/
org.opendaylight.controller.sal.binding.impl.*,
org.opendaylight.controller.sal.binding.codegen,
org.opendaylight.controller.sal.binding.codegen.*,
+ org.opendaylight.controller.md.sal.binding.impl,
<!--org.opendaylight.controller.sal.binding.dom.*,-->
org.opendaylight.controller.sal.binding.osgi.*,
org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.binding.impl.rev131028
--- /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.md.sal.binding.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opendaylight.controller.md.sal.binding.impl.ForwardedBackwardsCompatibleDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
+import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingDomConnectorDeployer;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+import org.osgi.framework.BundleContext;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+/**
+*
+*/
+public final class ForwardedCompatibleDataBrokerImplModule extends
+ org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractForwardedCompatibleDataBrokerImplModule
+ implements Provider {
+
+ private BundleContext bundleContext;
+
+ public ForwardedCompatibleDataBrokerImplModule(
+ final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public ForwardedCompatibleDataBrokerImplModule(
+ final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ final ForwardedCompatibleDataBrokerImplModule oldModule, final java.lang.AutoCloseable oldInstance) {
+
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ protected void customValidation() {
+ // Add custom validation for module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ ListeningExecutorService listeningExecutor = SingletonHolder.getDefaultCommitExecutor();
+ BindingIndependentMappingService mappingService = getBindingMappingServiceDependency();
+
+ Broker domBroker = getDomAsyncBrokerDependency();
+ ProviderSession session = domBroker.registerProvider(this, getBundleContext());
+ DOMDataBroker domDataBroker = session.getService(DOMDataBroker.class);
+ ForwardedBackwardsCompatibleDataBroker dataBroker = new ForwardedBackwardsCompatibleDataBroker(domDataBroker,
+ mappingService, listeningExecutor);
+
+ session.getService(SchemaService.class).registerSchemaServiceListener(dataBroker);
+
+ dataBroker.setConnector(BindingDomConnectorDeployer.createConnector(getBindingMappingServiceDependency()));
+ dataBroker.setDomProviderContext(session);
+ return dataBroker;
+ }
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setBundleContext(final BundleContext bundleContext2) {
+ this.bundleContext = bundleContext2;
+ }
+
+ @Override
+ public void onSessionInitiated(final ProviderSession session) {
+
+ }
+
+ @Override
+ public Collection<ProviderFunctionality> getProviderFunctionality() {
+ return Collections.emptySet();
+ }
+}
--- /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.md.sal.binding.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 ForwardedCompatibleDataBrokerImplModuleFactory extends org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractForwardedCompatibleDataBrokerImplModuleFactory
+{
+
+
+ @Override
+ public Module createModule(final String instanceName, final DependencyResolver dependencyResolver, final BundleContext bundleContext) {
+ ForwardedCompatibleDataBrokerImplModule module = (ForwardedCompatibleDataBrokerImplModule) super.createModule(instanceName, dependencyResolver, bundleContext);
+ module.setBundleContext(bundleContext);
+ return module;
+ }
+
+ @Override
+ public Module createModule(final String instanceName, final DependencyResolver dependencyResolver,
+ final DynamicMBeanWithInstance old, final BundleContext bundleContext) throws Exception {
+ ForwardedCompatibleDataBrokerImplModule module = (ForwardedCompatibleDataBrokerImplModule) super.createModule(instanceName, dependencyResolver, old, bundleContext);
+ module.setBundleContext(bundleContext);
+ return module;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.eclipse.xtext.xbase.lib.Exceptions;
+import org.opendaylight.controller.md.sal.binding.api.BindingDataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector;
+import org.opendaylight.controller.sal.binding.impl.forward.DomForwardedBroker;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.Delegator;
+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.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractForwardedDataBroker implements Delegator<DOMDataBroker>, DomForwardedBroker, SchemaContextListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractForwardedDataBroker.class);
+ // The Broker to whom we do all forwarding
+ private final DOMDataBroker domDataBroker;
+
+ // Mapper to convert from Binding Independent objects to Binding Aware
+ // objects
+ private final BindingIndependentMappingService mappingService;
+
+ private final BindingToNormalizedNodeCodec codec;
+ private BindingIndependentConnector connector;
+ private ProviderSession context;
+
+ protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker,
+ final BindingIndependentMappingService mappingService) {
+ this.domDataBroker = domDataBroker;
+ this.mappingService = mappingService;
+ this.codec = new BindingToNormalizedNodeCodec(mappingService);
+ }
+
+ protected BindingToNormalizedNodeCodec getCodec() {
+ return codec;
+ }
+
+ protected BindingIndependentMappingService getMappingService() {
+ return mappingService;
+ }
+
+ @Override
+ public DOMDataBroker getDelegate() {
+ return domDataBroker;
+ }
+
+ @Override
+ public void onGlobalContextUpdated(final SchemaContext ctx) {
+ codec.onGlobalContextUpdated(ctx);
+ }
+
+ public ListenerRegistration<BindingDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path, final BindingDataChangeListener listener,
+ final DataChangeScope triggeringScope) {
+ DOMDataChangeListener domDataChangeListener = new TranslatingDataChangeInvoker(store, path, listener,
+ triggeringScope);
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier domPath = codec.toNormalized(path);
+ ListenerRegistration<DOMDataChangeListener> domRegistration = domDataBroker.registerDataChangeListener(store, domPath, domDataChangeListener, triggeringScope);
+ return new ListenerRegistrationImpl(listener, domRegistration);
+ }
+
+ protected Map<InstanceIdentifier<?>, DataObject> fromDOMToData(
+ final Map<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized) {
+ Map<InstanceIdentifier<?>, DataObject> newMap = new HashMap<>();
+ for (Map.Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> entry : normalized
+ .entrySet()) {
+ try {
+ Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding = getCodec().toBinding(entry);
+ newMap.put(binding.getKey(), binding.getValue());
+ } catch (DeserializationException e) {
+ LOG.debug("Ommiting {}",entry,e);
+ }
+ }
+ return newMap;
+ }
+
+ private class TranslatingDataChangeInvoker implements DOMDataChangeListener {
+ private final BindingDataChangeListener bindingDataChangeListener;
+ private final LogicalDatastoreType store;
+ private final InstanceIdentifier<?> path;
+ private final DataChangeScope triggeringScope;
+
+ public TranslatingDataChangeInvoker(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
+ final BindingDataChangeListener bindingDataChangeListener, final DataChangeScope triggeringScope) {
+ this.store = store;
+ this.path = path;
+ this.bindingDataChangeListener = bindingDataChangeListener;
+ this.triggeringScope = triggeringScope;
+ }
+
+ @Override
+ public void onDataChanged(
+ final AsyncDataChangeEvent<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> change) {
+ bindingDataChangeListener.onDataChanged(new TranslatedDataChangeEvent(change,path));
+ }
+ }
+
+ private class TranslatedDataChangeEvent implements AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> {
+ private final AsyncDataChangeEvent<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> domEvent;
+ private InstanceIdentifier<?> path;
+
+ public TranslatedDataChangeEvent(
+ final AsyncDataChangeEvent<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> change) {
+ this.domEvent = change;
+ }
+
+ public TranslatedDataChangeEvent(
+ final AsyncDataChangeEvent<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> change,
+ final InstanceIdentifier<?> path) {
+ this.domEvent = change;
+ this.path = path;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getCreatedData() {
+ return fromDOMToData(domEvent.getCreatedData());
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getUpdatedData() {
+ return fromDOMToData(domEvent.getUpdatedData());
+
+ }
+
+ @Override
+ public Set<InstanceIdentifier<?>> getRemovedPaths() {
+ final Set<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier> removedPaths = domEvent
+ .getRemovedPaths();
+ final Set<InstanceIdentifier<?>> output = new HashSet<>();
+ for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier instanceIdentifier : removedPaths) {
+ try {
+ output.add(mappingService.fromDataDom(instanceIdentifier));
+ } catch (DeserializationException e) {
+ Exceptions.sneakyThrow(e);
+ }
+ }
+
+ return output;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, ? extends DataObject> getOriginalData() {
+ return fromDOMToData(domEvent.getOriginalData());
+
+ }
+
+ @Override
+ public DataObject getOriginalSubtree() {
+
+ return toBindingData(path,domEvent.getOriginalSubtree());
+ }
+
+ @Override
+ public DataObject getUpdatedSubtree() {
+
+ return toBindingData(path,domEvent.getUpdatedSubtree());
+ }
+
+ @Override
+ public String toString() {
+ return "TranslatedDataChangeEvent [domEvent=" + domEvent + "]";
+ }
+ }
+
+ private static class ListenerRegistrationImpl extends AbstractListenerRegistration<BindingDataChangeListener> {
+ private final ListenerRegistration<DOMDataChangeListener> registration;
+
+ public ListenerRegistrationImpl(final BindingDataChangeListener listener,
+ final ListenerRegistration<DOMDataChangeListener> registration) {
+ super(listener);
+ this.registration = registration;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ registration.close();
+ }
+ }
+
+ protected DataObject toBindingData(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> data) {
+ try {
+ return getCodec().toBinding(path, data);
+ } catch (DeserializationException e) {
+ return null;
+ }
+ }
+
+
+ @Override
+ public BindingIndependentConnector getConnector() {
+ return this.connector;
+ }
+
+ @Override
+ public ProviderSession getDomProviderContext() {
+ return this.context;
+ }
+
+ @Override
+ public void setConnector(final BindingIndependentConnector connector) {
+ this.connector = connector;
+ }
+
+ @Override
+ public void setDomProviderContext(final ProviderSession domProviderContext) {
+ this.context = domProviderContext;
+ }
+
+ @Override
+ public void startForwarding() {
+ // NOOP
+ }
+
+
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nullable;
+
+import org.eclipse.xtext.xbase.lib.Exceptions;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public class AbstractForwardedTransaction<T extends AsyncTransaction<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>>
+ implements Delegator<T> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractForwardedTransaction.class);
+ private final T delegate;
+ private final static CacheBuilder<Object, Object> CACHE_BUILDER = CacheBuilder.newBuilder()
+ .expireAfterWrite(10, TimeUnit.MILLISECONDS).maximumSize(100);
+ private final BindingToNormalizedNodeCodec codec;
+ private final EnumMap<LogicalDatastoreType, Cache<InstanceIdentifier<?>, DataObject>> cacheMap;
+
+ protected AbstractForwardedTransaction(final T delegate, final BindingToNormalizedNodeCodec codec) {
+ super();
+ this.delegate = delegate;
+ this.codec = codec;
+
+ this.cacheMap = new EnumMap<>(LogicalDatastoreType.class);
+ cacheMap.put(LogicalDatastoreType.OPERATIONAL, CACHE_BUILDER.<InstanceIdentifier<?>, DataObject> build());
+ cacheMap.put(LogicalDatastoreType.CONFIGURATION, CACHE_BUILDER.<InstanceIdentifier<?>, DataObject> build());
+
+ }
+
+ @Override
+ public T getDelegate() {
+ return delegate;
+ }
+
+ protected final BindingToNormalizedNodeCodec getCodec() {
+ return codec;
+ }
+
+ protected ListenableFuture<Optional<DataObject>> transformFuture(final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path, final ListenableFuture<Optional<NormalizedNode<?, ?>>> future) {
+ return Futures.transform(future, new Function<Optional<NormalizedNode<?, ?>>, Optional<DataObject>>() {
+ @Nullable
+ @Override
+ public Optional<DataObject> apply(@Nullable final Optional<NormalizedNode<?, ?>> normalizedNode) {
+ try {
+ final DataObject dataObject = normalizedNode.isPresent() ? codec.toBinding(path,
+ normalizedNode.get()) : null;
+ if(dataObject != null) {
+ updateCache(store, path, dataObject);
+ }
+ return Optional.fromNullable(dataObject);
+ } catch (DeserializationException e) {
+ Exceptions.sneakyThrow(e);
+ }
+ return null;
+ }
+ });
+ }
+
+ protected void doPut(final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path, final DataObject data) {
+ invalidateCache(store, path);
+ final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = codec
+ .toNormalizedNode(path, data);
+ writeTransaction.put(store, normalized.getKey(), normalized.getValue());
+ }
+
+ protected void doPutWithEnsureParents(final DOMDataReadWriteTransaction writeTransaction,
+ final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
+ invalidateCache(store, path);
+ final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = codec
+ .toNormalizedNode(path, data);
+
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath = normalized.getKey();
+ try {
+ List<PathArgument> currentArguments = new ArrayList<>();
+ DataNormalizationOperation<?> currentOp = codec.getDataNormalizer().getRootOperation();
+ Iterator<PathArgument> iterator = normalizedPath.getPath().iterator();
+ while (iterator.hasNext()) {
+ PathArgument currentArg = iterator.next();
+ currentOp = currentOp.getChild(currentArg);
+ currentArguments.add(currentArg);
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier currentPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
+ currentArguments);
+ boolean isPresent = writeTransaction.read(store, currentPath).get().isPresent();
+ if (isPresent == false && iterator.hasNext()) {
+ writeTransaction.put(store, currentPath, currentOp.createDefault(currentArg));
+ }
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ e.printStackTrace();
+ }
+ //LOG .info("Tx: {} : Putting data {}",getDelegate().getIdentifier(),normalized.getKey());
+ writeTransaction.put(store, normalized.getKey(), normalized.getValue());
+
+ }
+
+ protected void doMerge(final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path, final DataObject data) {
+ invalidateCache(store, path);
+ final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = codec
+ .toNormalizedNode(path, data);
+ writeTransaction.merge(store, normalized.getKey(), normalized.getValue());
+ }
+
+ protected void doDelete(final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path) {
+ invalidateCache(store, path);
+ final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = codec.toNormalized(path);
+ writeTransaction.delete(store, normalized);
+ }
+
+ protected ListenableFuture<RpcResult<TransactionStatus>> doCommit(final DOMDataWriteTransaction writeTransaction) {
+ return writeTransaction.commit();
+ }
+
+ protected void doCancel(final DOMDataWriteTransaction writeTransaction) {
+ writeTransaction.cancel();
+ }
+
+ protected ListenableFuture<Optional<DataObject>> doRead(final DOMDataReadTransaction readTransaction,
+ final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+ final DataObject dataObject = getFromCache(store, path);
+ if (dataObject == null) {
+ final ListenableFuture<Optional<NormalizedNode<?, ?>>> future = readTransaction.read(store,
+ codec.toNormalized(path));
+ return transformFuture(store, path, future);
+ } else {
+ return Futures.immediateFuture(Optional.of(dataObject));
+ }
+ }
+
+ private DataObject getFromCache(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+ Cache<InstanceIdentifier<?>, DataObject> cache = cacheMap.get(store);
+ if (cache != null) {
+ return cache.getIfPresent(path);
+ }
+ return null;
+ }
+
+ private void updateCache(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
+ final DataObject dataObject) {
+ // Check if cache exists. If not create one.
+ Cache<InstanceIdentifier<?>, DataObject> cache = cacheMap.get(store);
+ if (cache == null) {
+ cache = CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(1, TimeUnit.MINUTES).build();
+
+ }
+
+ cache.put(path, dataObject);
+ }
+
+ private void invalidateCache(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+ // FIXME: Optimization: invalidate only parents and children of path
+ Cache<InstanceIdentifier<?>, DataObject> cache = cacheMap.get(store);
+ cache.invalidateAll();
+ LOG.trace("Cache invalidated");
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableList;
+
+public class BindingToNormalizedNodeCodec implements SchemaContextListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(BindingToNormalizedNodeCodec.class);
+
+ private final BindingIndependentMappingService bindingToLegacy;
+ private DataNormalizer legacyToNormalized;
+
+ public BindingToNormalizedNodeCodec(final BindingIndependentMappingService mappingService) {
+ super();
+ this.bindingToLegacy = mappingService;
+ }
+
+ public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toNormalized(
+ final InstanceIdentifier<? extends DataObject> binding) {
+ return legacyToNormalized.toNormalized(bindingToLegacy.toDataDom(binding));
+ }
+
+ public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
+ final InstanceIdentifier<? extends DataObject> bindingPath, final DataObject bindingObject) {
+ return toNormalizedNode(toEntry(bindingPath, bindingObject));
+
+ }
+
+ public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
+ final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> binding) {
+ Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalizedEntry = legacyToNormalized.toNormalized(bindingToLegacy.toDataDom(binding));
+ if(Augmentation.class.isAssignableFrom(binding.getKey().getTargetType())) {
+
+ for(DataContainerChild<? extends PathArgument, ?> child : ((DataContainerNode<?>) normalizedEntry.getValue()).getValue()) {
+ if(child instanceof AugmentationNode) {
+ ImmutableList<PathArgument> childArgs = ImmutableList.<PathArgument>builder()
+ .addAll(normalizedEntry.getKey().getPath())
+ .add(child.getIdentifier())
+ .build();
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(childArgs);
+ return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>(childPath,child);
+ }
+ }
+
+ }
+ return normalizedEntry;
+
+
+ }
+
+ public InstanceIdentifier<? extends DataObject> toBinding(
+ final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
+ throws DeserializationException {
+
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath = legacyToNormalized
+ .toLegacy(normalized);
+ return bindingToLegacy.fromDataDom(legacyPath);
+ }
+
+ private static final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toEntry(
+ final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> key,
+ final DataObject value) {
+ return new SimpleEntry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>(
+ key, value);
+ }
+
+ public DataObject toBinding(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> normalizedNode)
+ throws DeserializationException {
+ return bindingToLegacy.dataObjectFromDataDom(path, (CompositeNode) DataNormalizer.toLegacy(normalizedNode));
+ }
+
+ public DataNormalizer getDataNormalizer() {
+ return legacyToNormalized;
+ }
+
+ public Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toBinding(
+ final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
+ throws DeserializationException {
+ InstanceIdentifier<? extends DataObject> bindingPath = toBinding(normalized.getKey());
+ DataObject bindingData = toBinding(bindingPath, normalized.getValue());
+ return toEntry(bindingPath, bindingData);
+ }
+
+ @Override
+ public void onGlobalContextUpdated(final SchemaContext arg0) {
+ legacyToNormalized = new DataNormalizer(arg0);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.binding.api.BindingDataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+public class ForwardedBackwardsCompatibleDataBroker extends AbstractForwardedDataBroker implements DataProviderService, AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ForwardedBackwardsCompatibleDataBroker.class);
+
+ private final ConcurrentHashMap<InstanceIdentifier<?>, CommitHandlerRegistrationImpl> commitHandlers = new ConcurrentHashMap<>();
+ private final ListenerRegistry<DataChangeListener> fakeRegistry = ListenerRegistry.create();
+ private final ListeningExecutorService executorService;
+
+ public ForwardedBackwardsCompatibleDataBroker(final DOMDataBroker domDataBroker,
+ final BindingIndependentMappingService mappingService, final ListeningExecutorService executor) {
+ super(domDataBroker, mappingService);
+ executorService = executor;
+ LOG.info("ForwardedBackwardsCompatibleBroker started.");
+ }
+
+ @Override
+ public DataModificationTransaction beginTransaction() {
+ return new ForwardedBackwardsCompatibleTransacion(getDelegate().newReadWriteTransaction(), getCodec());
+ }
+
+ @Override
+ public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+ DataModificationTransaction tx = beginTransaction();
+ return tx.readConfigurationData(path);
+ }
+
+ @Override
+ public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+ DataModificationTransaction tx = beginTransaction();
+ return tx.readOperationalData(path);
+ }
+
+ @Override
+ public Registration<DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject>> registerCommitHandler(
+ final InstanceIdentifier<? extends DataObject> path,
+ final DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> commitHandler) {
+
+
+ //transformingCommitHandler = new TransformingDataChangeListener
+ //fakeCommitHandler = registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, path, listener, DataChangeScope.SUBTREE);
+
+ CommitHandlerRegistrationImpl reg = new CommitHandlerRegistrationImpl(path, commitHandler);
+ commitHandlers.put(path, reg);
+ return reg;
+ }
+
+ @Override
+ @Deprecated
+ public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>>> registerCommitHandlerListener(
+ final RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>> commitHandlerListener) {
+ throw new UnsupportedOperationException("Not supported contract.");
+ }
+
+ @Override
+ public ListenerRegistration<DataChangeListener> registerDataChangeListener(
+ final InstanceIdentifier<? extends DataObject> path, final DataChangeListener listener) {
+
+
+ BindingDataChangeListener asyncOperListener = new BackwardsCompatibleOperationalDataChangeInvoker(listener);
+ BindingDataChangeListener asyncCfgListener = new BackwardsCompatibleConfigurationDataChangeInvoker(listener);
+
+ ListenerRegistration<BindingDataChangeListener> cfgReg = registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, path, asyncCfgListener, DataChangeScope.SUBTREE);
+ ListenerRegistration<BindingDataChangeListener> operReg = registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, path, asyncOperListener, DataChangeScope.SUBTREE);
+
+ return new LegacyListenerRegistration(listener,cfgReg,operReg);
+ }
+
+ @Override
+ public Registration<DataReader<InstanceIdentifier<? extends DataObject>, DataObject>> registerDataReader(
+ final InstanceIdentifier<? extends DataObject> path,
+ final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> reader) {
+ throw new UnsupportedOperationException("Data reader contract is not supported.");
+ }
+
+ @Override
+ public void close() throws Exception {
+ // TODO Auto-generated method stub
+
+ }
+
+ public ListenableFuture<RpcResult<TransactionStatus>> commit(final ForwardedBackwardsCompatibleTransacion tx) {
+
+ final List<DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject>> subTrans = new ArrayList<>();
+ LOG.debug("Tx: {} Submitted.",tx.getIdentifier());
+ ListenableFuture<Boolean> requestCommit = executorService.submit(new Callable<Boolean>() {
+
+ @Override
+ public Boolean call() throws Exception {
+ try {
+ for (CommitHandlerRegistrationImpl handler : commitHandlers.values()) {
+
+ DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx = handler
+ .getInstance().requestCommit(tx);
+ subTrans.add(subTx);
+ }
+ } catch (Exception e) {
+ LOG.error("Tx: {} Rollback.",tx.getIdentifier(),e);
+ for (DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx : subTrans) {
+ subTx.rollback();
+ }
+ return false;
+ }
+ LOG.debug("Tx: {} Can Commit True.",tx.getIdentifier());
+ return true;
+ }
+
+ });
+
+ ListenableFuture<RpcResult<TransactionStatus>> dataStoreCommit = Futures.transform(requestCommit, new AsyncFunction<Boolean, RpcResult<TransactionStatus>>() {
+
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> apply(final Boolean requestCommitSuccess) throws Exception {
+ if(requestCommitSuccess) {
+ return tx.getDelegate().commit();
+ }
+ return Futures.immediateFuture(Rpcs.getRpcResult(false, TransactionStatus.FAILED, Collections.<RpcError>emptySet()));
+ }
+ });
+
+ return Futures.transform(dataStoreCommit, new Function<RpcResult<TransactionStatus>,RpcResult<TransactionStatus>>() {
+ @Override
+ public RpcResult<TransactionStatus> apply(final RpcResult<TransactionStatus> input) {
+ if(input.isSuccessful()) {
+ for(DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx : subTrans ) {
+ subTx.finish();
+ }
+ } else {
+ LOG.error("Tx: {} Rollback - Datastore commit failed.",tx.getIdentifier());
+ for(DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx : subTrans ) {
+ subTx.rollback();
+ }
+ }
+ return input;
+ }
+ });
+ }
+
+ private class ForwardedBackwardsCompatibleTransacion extends
+ AbstractForwardedTransaction<DOMDataReadWriteTransaction> implements DataModificationTransaction {
+
+ private final Map<InstanceIdentifier<? extends DataObject>, DataObject> updated = new HashMap<>();
+ private final Map<InstanceIdentifier<? extends DataObject>, DataObject> created = new HashMap<>();
+ private final Set<InstanceIdentifier<? extends DataObject>> removed = new HashSet<>();
+ private final Map<InstanceIdentifier<? extends DataObject>, DataObject> original = new HashMap<>();
+ private TransactionStatus status = TransactionStatus.NEW;
+
+ @Override
+ public final TransactionStatus getStatus() {
+ return status;
+ }
+
+ protected ForwardedBackwardsCompatibleTransacion(final DOMDataReadWriteTransaction delegate,
+ final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+ LOG.debug("Tx {} allocated.",getIdentifier());
+ }
+
+ @Override
+ public void putOperationalData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
+
+ doPutWithEnsureParents(getDelegate(), LogicalDatastoreType.OPERATIONAL, path, data);
+ }
+
+ @Override
+ public void putConfigurationData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
+ DataObject originalObj = readConfigurationData(path);
+ if (originalObj != null) {
+ original.put(path, originalObj);
+
+ } else {
+ created.put(path, data);
+ }
+ updated.put(path, data);
+ doPutWithEnsureParents(getDelegate(), LogicalDatastoreType.CONFIGURATION, path, data);
+ }
+
+ @Override
+ public void removeOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+ doDelete(getDelegate(), LogicalDatastoreType.OPERATIONAL, path);
+
+ }
+
+ @Override
+ public void removeConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+ doDelete(getDelegate(), LogicalDatastoreType.CONFIGURATION, path);
+ }
+
+ @Override
+ public Map<InstanceIdentifier<? extends DataObject>, DataObject> getCreatedOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<? extends DataObject>, DataObject> getCreatedConfigurationData() {
+ return created;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<? extends DataObject>, DataObject> getUpdatedOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<? extends DataObject>, DataObject> getUpdatedConfigurationData() {
+ return updated;
+ }
+
+ @Override
+ public Set<InstanceIdentifier<? extends DataObject>> getRemovedConfigurationData() {
+ return removed;
+ }
+
+ @Override
+ public Set<InstanceIdentifier<? extends DataObject>> getRemovedOperationalData() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<? extends DataObject>, DataObject> getOriginalConfigurationData() {
+ return original;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<? extends DataObject>, DataObject> getOriginalOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+ try {
+ return doRead(getDelegate(), LogicalDatastoreType.OPERATIONAL, path).get().orNull();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Read of {} failed.", path,e);
+ return null;
+ }
+ }
+
+ @Override
+ public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+ try {
+ return doRead(getDelegate(), LogicalDatastoreType.CONFIGURATION, path).get().orNull();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Read of {} failed.", path,e);
+ return null;
+ }
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return getDelegate().getIdentifier();
+ }
+
+ private void changeStatus(TransactionStatus status) {
+ LOG.trace("Transaction {} changed status to {}", getIdentifier(), status);
+ this.status = status;
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ final ListenableFuture<RpcResult<TransactionStatus>> f = ForwardedBackwardsCompatibleDataBroker.this.commit(this);
+
+ changeStatus(TransactionStatus.SUBMITED);
+
+ Futures.addCallback(f, new FutureCallback<RpcResult<TransactionStatus>>() {
+ @Override
+ public void onSuccess(RpcResult<TransactionStatus> result) {
+ changeStatus(result.getResult());
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ LOG.error("Transaction {} failed to complete", getIdentifier(), t);
+ changeStatus(TransactionStatus.FAILED);
+ }
+ });
+
+ return f;
+ }
+
+ @Override
+ public ListenerRegistration<DataTransactionListener> registerListener(final DataTransactionListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+
+ private class CommitHandlerRegistrationImpl extends
+ AbstractObjectRegistration<DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject>> {
+
+ private final InstanceIdentifier<? extends DataObject> path;
+
+ public CommitHandlerRegistrationImpl(final InstanceIdentifier<? extends DataObject> path,
+ final DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> commitHandler) {
+ super(commitHandler);
+ this.path = path;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ commitHandlers.remove(path, this);
+ }
+
+ }
+
+
+ private static final class LegacyListenerRegistration implements ListenerRegistration<DataChangeListener> {
+
+ private final DataChangeListener instance;
+ private final ListenerRegistration<BindingDataChangeListener> cfgReg;
+ private final ListenerRegistration<BindingDataChangeListener> operReg;
+
+ public LegacyListenerRegistration(final DataChangeListener listener,
+ final ListenerRegistration<BindingDataChangeListener> cfgReg,
+ final ListenerRegistration<BindingDataChangeListener> operReg) {
+ this.instance = listener;
+ this.cfgReg = cfgReg;
+ this.operReg = operReg;
+ }
+
+ @Override
+ public DataChangeListener getInstance() {
+ return instance;
+ }
+
+ @Override
+ public void close() {
+ cfgReg.close();
+ operReg.close();
+ }
+
+ }
+
+ private static class BackwardsCompatibleOperationalDataChangeInvoker implements BindingDataChangeListener, Delegator<DataChangeListener> {
+
+ private final org.opendaylight.controller.md.sal.common.api.data.DataChangeListener<?,?> delegate;
+
+
+ public BackwardsCompatibleOperationalDataChangeInvoker(final DataChangeListener listener) {
+ this.delegate = listener;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+
+ DataChangeEvent legacyChange = LegacyDataChangeEvent.createOperational(change);
+ delegate.onDataChanged(legacyChange);
+
+ }
+
+ @Override
+ public DataChangeListener getDelegate() {
+ return (DataChangeListener) delegate;
+ }
+
+ }
+
+ private static class BackwardsCompatibleConfigurationDataChangeInvoker implements BindingDataChangeListener, Delegator<DataChangeListener> {
+
+
+ @SuppressWarnings("rawtypes")
+ private final org.opendaylight.controller.md.sal.common.api.data.DataChangeListener<?,?> delegate;
+
+ public BackwardsCompatibleConfigurationDataChangeInvoker(final DataChangeListener listener) {
+ this.delegate = listener;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+
+ DataChangeEvent legacyChange = LegacyDataChangeEvent.createConfiguration(change);
+
+ delegate.onDataChanged(legacyChange);
+
+ }
+
+ @Override
+ public DataChangeListener getDelegate() {
+ return (DataChangeListener) delegate;
+ }
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.binding.api.BindingDataBroker;
+import org.opendaylight.controller.md.sal.binding.api.BindingDataReadTransaction;
+import org.opendaylight.controller.md.sal.binding.api.BindingDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.api.BindingDataWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * The DataBrokerImpl simply defers to the DOMDataBroker for all its operations.
+ * All transactions and listener registrations are wrapped by the DataBrokerImpl
+ * to allow binding aware components to use the DataBroker transparently.
+ *
+ * Besides this the DataBrokerImpl and it's collaborators also cache data that
+ * is already transformed from the binding independent to binding aware format
+ *
+ * TODO : All references in this class to CompositeNode should be switched to
+ * NormalizedNode once the MappingService is updated
+ *
+ */
+public class ForwardedBindingDataBroker extends AbstractForwardedDataBroker implements BindingDataBroker {
+
+ public ForwardedBindingDataBroker(final DOMDataBroker domDataBroker, final BindingIndependentMappingService mappingService) {
+ super(domDataBroker, mappingService);
+ }
+
+ @Override
+ public BindingDataReadTransaction newReadOnlyTransaction() {
+ return new BindingDataReadTransactionImpl(getDelegate().newReadOnlyTransaction(),getCodec());
+ }
+
+ @Override
+ public BindingDataReadWriteTransaction newReadWriteTransaction() {
+ return new BindingDataReadWriteTransactionImpl(getDelegate().newReadWriteTransaction(),getCodec());
+ }
+
+ @Override
+ public BindingDataWriteTransaction newWriteOnlyTransaction() {
+ return new BindingDataWriteTransactionImpl<DOMDataWriteTransaction>(getDelegate().newWriteOnlyTransaction(),getCodec());
+ }
+
+ private abstract class AbstractBindingTransaction<T extends AsyncTransaction<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>>
+ extends AbstractForwardedTransaction<T> implements AsyncTransaction<InstanceIdentifier<?>, DataObject> {
+
+ protected AbstractBindingTransaction(final T delegate, final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return getDelegate().getIdentifier();
+ }
+
+ @Override
+ public void close() {
+ getDelegate().close();
+ }
+
+ }
+
+ private class BindingDataReadTransactionImpl extends AbstractBindingTransaction<DOMDataReadTransaction> implements
+ BindingDataReadTransaction {
+
+ protected BindingDataReadTransactionImpl(final DOMDataReadTransaction delegate,
+ final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+ }
+
+ @Override
+ public ListenableFuture<Optional<DataObject>> read(final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path) {
+ return doRead(getDelegate(), store, path);
+ }
+ }
+
+ private class BindingDataWriteTransactionImpl<T extends DOMDataWriteTransaction> extends
+ AbstractBindingTransaction<T> implements BindingDataWriteTransaction {
+
+ protected BindingDataWriteTransactionImpl(final T delegate, final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+
+ }
+
+ @Override
+ public void cancel() {
+ doCancel(getDelegate());
+ }
+
+ @Override
+ public void put(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
+ doPut(getDelegate(), store, path, data);
+ }
+
+ @Override
+ public void merge(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
+ doMerge(getDelegate(), store, path, data);
+ }
+
+ @Override
+ public void delete(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+ doDelete(getDelegate(), store, path);
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ return doCommit(getDelegate());
+ }
+ }
+
+ private class BindingDataReadWriteTransactionImpl extends
+ BindingDataWriteTransactionImpl<DOMDataReadWriteTransaction> implements BindingDataReadWriteTransaction {
+
+ protected BindingDataReadWriteTransactionImpl(final DOMDataReadWriteTransaction delegate,
+ final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+ }
+
+ @Override
+ public ListenableFuture<Optional<DataObject>> read(final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path) {
+ return doRead(getDelegate(), store, 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.md.sal.binding.impl;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public abstract class LegacyDataChangeEvent implements
+ DataChangeEvent<InstanceIdentifier<? extends DataObject>, DataObject> {
+
+ private LegacyDataChangeEvent() {
+ }
+
+ public static final DataChangeEvent<InstanceIdentifier<?>, DataObject> createOperational(
+ final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ return new OperationalChangeEvent(change);
+ }
+
+ public static final DataChangeEvent<InstanceIdentifier<?>, DataObject> createConfiguration(
+ final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ return new ConfigurationChangeEvent(change);
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getCreatedOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getCreatedConfigurationData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getUpdatedOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getUpdatedConfigurationData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Set<InstanceIdentifier<?>> getRemovedConfigurationData() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set<InstanceIdentifier<?>> getRemovedOperationalData() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getOriginalConfigurationData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getOriginalOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public DataObject getOriginalConfigurationSubtree() {
+ return null;
+ }
+
+ @Override
+ public DataObject getOriginalOperationalSubtree() {
+ return null;
+ }
+
+ @Override
+ public DataObject getUpdatedConfigurationSubtree() {
+ return null;
+ }
+
+ @Override
+ public DataObject getUpdatedOperationalSubtree() {
+ return null;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private final static class OperationalChangeEvent extends LegacyDataChangeEvent {
+
+ private final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> delegate;
+
+ public OperationalChangeEvent(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ this.delegate = change;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getCreatedOperationalData() {
+ return delegate.getCreatedData();
+ }
+
+ @Override
+ public Set<InstanceIdentifier<?>> getRemovedOperationalData() {
+ return delegate.getRemovedPaths();
+ }
+
+ @Override
+ public DataObject getOriginalOperationalSubtree() {
+ return delegate.getOriginalSubtree();
+ }
+
+ @Override
+ public DataObject getUpdatedOperationalSubtree() {
+ return delegate.getUpdatedSubtree();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getOriginalOperationalData() {
+ return (Map) delegate.getOriginalData();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getUpdatedOperationalData() {
+ return delegate.getUpdatedData();
+ }
+
+ @Override
+ public String toString() {
+ return "OperationalChangeEvent [delegate=" + delegate + "]";
+ }
+
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private final static class ConfigurationChangeEvent extends LegacyDataChangeEvent {
+
+ private final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> delegate;
+
+ public ConfigurationChangeEvent(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ this.delegate = change;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getCreatedConfigurationData() {
+ return delegate.getCreatedData();
+ }
+
+ @Override
+ public Set<InstanceIdentifier<?>> getRemovedConfigurationData() {
+ return delegate.getRemovedPaths();
+ }
+
+ @Override
+ public DataObject getOriginalConfigurationSubtree() {
+ return delegate.getOriginalSubtree();
+ }
+
+ @Override
+ public DataObject getUpdatedConfigurationSubtree() {
+ return delegate.getUpdatedSubtree();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getOriginalConfigurationData() {
+ return (Map) delegate.getOriginalData();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getUpdatedConfigurationData() {
+ return delegate.getUpdatedData();
+ }
+
+ @Override
+ public String toString() {
+ return "ConfigurationChangeEvent [delegate=" + delegate + "]";
+ }
+
+ }
+
+}
public RpcProxyRegistration(Class<T> type, T service, RpcProviderRegistryImpl registry) {
super(service);
- serviceType = type;
+ this.serviceType = type;
+ this.registry = registry;
}
@Override
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
+import org.opendaylight.controller.md.sal.binding.impl.AbstractForwardedDataBroker;
import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
private final Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier> toDOMInstanceIdentifier = new Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier>() {
@Override
- public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier apply(InstanceIdentifier<?> input) {
+ public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier apply(final InstanceIdentifier<?> input) {
return mappingService.toDataDom(input);
}
}
@Override
- public DataObject readOperationalData(InstanceIdentifier<? extends DataObject> path) {
+ public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
try {
org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath = mappingService.toDataDom(path);
CompositeNode result = biDataService.readOperationalData(biPath);
}
private DataObject potentialAugmentationRead(InstanceIdentifier<? extends DataObject> path,
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath, CompositeNode result)
+ final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath, final CompositeNode result)
throws DeserializationException {
Class<? extends DataObject> targetType = path.getTargetType();
if (Augmentation.class.isAssignableFrom(targetType)) {
}
@Override
- public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
+ public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
try {
org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath = mappingService.toDataDom(path);
CompositeNode result = biDataService.readConfigurationData(biPath);
}
private DataModificationTransaction createBindingToDomTransaction(
- DataModification<InstanceIdentifier<? extends DataObject>, DataObject> source) {
+ final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> source) {
DataModificationTransaction target = biDataService.beginTransaction();
LOG.debug("Created DOM Transaction {} for {},", target.getIdentifier(),source.getIdentifier());
for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedConfigurationData()) {
}
private org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction createDomToBindingTransaction(
- DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> source) {
+ final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> source) {
org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction target = baDataService
.beginTransaction();
for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedConfigurationData()) {
return biDataService;
}
- protected void setDomDataService(org.opendaylight.controller.sal.core.api.data.DataProviderService biDataService) {
+ protected void setDomDataService(final org.opendaylight.controller.sal.core.api.data.DataProviderService biDataService) {
this.biDataService = biDataService;
}
return baDataService;
}
- protected void setBindingDataService(DataProviderService baDataService) {
+ protected void setBindingDataService(final DataProviderService baDataService) {
this.baDataService = baDataService;
}
return baRpcRegistry;
}
- protected void setBindingRpcRegistry(RpcProviderRegistry rpcRegistry) {
+ protected void setBindingRpcRegistry(final RpcProviderRegistry rpcRegistry) {
this.baRpcRegistry = rpcRegistry;
}
public void startDataForwarding() {
+ if(baDataService instanceof AbstractForwardedDataBroker) {
+ dataForwarding = true;
+ return;
+ }
checkState(!dataForwarding, "Connector is already forwarding data.");
baDataReaderRegistration = baDataService.registerDataReader(ROOT, this);
baCommitHandlerRegistration = baDataService.registerCommitHandler(ROOT, bindingToDomCommitHandler);
}
}
- protected void setMappingService(BindingIndependentMappingService mappingService) {
+ protected void setMappingService(final BindingIndependentMappingService mappingService) {
this.mappingService = mappingService;
}
}
@Override
- public void onSessionInitiated(ProviderSession session) {
+ public void onSessionInitiated(final ProviderSession session) {
setDomDataService(session.getService(org.opendaylight.controller.sal.core.api.data.DataProviderService.class));
setDomRpcRegistry(session.getService(RpcProvisionRegistry.class));
}
- public <T extends RpcService> void onRpcRouterCreated(Class<T> serviceType, RpcRouter<T> router) {
+ public <T extends RpcService> void onRpcRouterCreated(final Class<T> serviceType, final RpcRouter<T> router) {
}
- public void setDomRpcRegistry(RpcProvisionRegistry registry) {
+ public void setDomRpcRegistry(final RpcProvisionRegistry registry) {
biRpcRegistry = registry;
}
private final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> modification;
public DomToBindingTransaction(
- org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction backing,
- DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> modification) {
+ final org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction backing,
+ final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> modification) {
super();
this.backing = backing;
this.modification = modification;
private final DataModificationTransaction backing;
private final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification;
- public BindingToDomTransaction(DataModificationTransaction backing,
- DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification) {
+ public BindingToDomTransaction(final DataModificationTransaction backing,
+ final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification) {
this.backing = backing;
this.modification = modification;
domOpenedTransactions.put(backing.getIdentifier(), this);
@Override
public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> requestCommit(
- DataModification<InstanceIdentifier<? extends DataObject>, DataObject> bindingTransaction) {
+ final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> bindingTransaction) {
/**
* Transaction was created as DOM transaction, in that case we do
DataCommitHandler<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> {
@Override
- public void onRegister(DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
+ public void onRegister(final DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
org.opendaylight.yangtools.yang.data.api.InstanceIdentifier domPath = mappingService.toDataDom(registration
.getPath());
}
@Override
- public void onUnregister(DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
+ public void onUnregister(final DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
// NOOP for now
// FIXME: do registration based on only active commit handlers.
}
@Override
public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> requestCommit(
- DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> domTransaction) {
+ final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> domTransaction) {
Object identifier = domTransaction.getIdentifier();
/**
return registryImpl;
}
- public void setRegistryImpl(RpcProviderRegistryImpl registryImpl) {
+ public void setRegistryImpl(final RpcProviderRegistryImpl registryImpl) {
this.registryImpl = registryImpl;
}
@Override
- public void onGlobalRpcRegistered(Class<? extends RpcService> cls) {
+ public void onGlobalRpcRegistered(final Class<? extends RpcService> cls) {
getRpcForwarder(cls, null);
}
@Override
- public void onGlobalRpcUnregistered(Class<? extends RpcService> cls) {
+ public void onGlobalRpcUnregistered(final Class<? extends RpcService> cls) {
// NOOP
}
@Override
- public void onRpcRouterCreated(RpcRouter<?> router) {
+ public void onRpcRouterCreated(final RpcRouter<?> router) {
Class<? extends BaseIdentity> ctx = router.getContexts().iterator().next();
getRpcForwarder(router.getServiceType(), ctx);
}
@Override
- public void onRouteChange(RouteChange<RpcContextIdentifier, InstanceIdentifier<?>> change) {
+ public void onRouteChange(final RouteChange<RpcContextIdentifier, InstanceIdentifier<?>> change) {
for (Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry : change.getAnnouncements().entrySet()) {
bindingRoutesAdded(entry);
}
}
- private void bindingRoutesAdded(Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry) {
+ private void bindingRoutesAdded(final Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry) {
Class<? extends BaseIdentity> context = entry.getKey().getRoutingContext();
Class<? extends RpcService> service = entry.getKey().getRpcService();
if (context != null) {
}
}
- private DomToBindingRpcForwarder getRpcForwarder(Class<? extends RpcService> service,
- Class<? extends BaseIdentity> context) {
+ private DomToBindingRpcForwarder getRpcForwarder(final Class<? extends RpcService> service,
+ final Class<? extends BaseIdentity> context) {
DomToBindingRpcForwarder potential = forwarders.get(service);
if (potential != null) {
return potential;
private final Map<QName, RpcInvocationStrategy> strategiesByQName = new HashMap<>();
private final WeakHashMap<Method, RpcInvocationStrategy> strategiesByMethod = new WeakHashMap<>();
- public DomToBindingRpcForwarder(Class<? extends RpcService> service) {
+ public DomToBindingRpcForwarder(final Class<? extends RpcService> service) {
this.rpcServiceType = new WeakReference<Class<? extends RpcService>>(service);
this.supportedRpcs = mappingService.getRpcQNamesFor(service);
try {
* @param service
* @param context
*/
- public DomToBindingRpcForwarder(Class<? extends RpcService> service, Class<? extends BaseIdentity> context) {
+ public DomToBindingRpcForwarder(final Class<? extends RpcService> service, final Class<? extends BaseIdentity> context) {
this.rpcServiceType = new WeakReference<Class<? extends RpcService>>(service);
this.supportedRpcs = mappingService.getRpcQNamesFor(service);
Builder<RoutedRpcRegistration> registrationsBuilder = ImmutableSet
registrations = registrationsBuilder.build();
}
- public void registerPaths(Class<? extends BaseIdentity> context, Class<? extends RpcService> service,
- Set<InstanceIdentifier<?>> set) {
+ public void registerPaths(final Class<? extends BaseIdentity> context, final Class<? extends RpcService> service,
+ final Set<InstanceIdentifier<?>> set) {
QName ctx = BindingReflections.findQName(context);
for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
toDOMInstanceIdentifier)) {
@Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
if (EQUALS_METHOD.equals(method)) {
return false;
}
return strategy.forwardToDomBroker(null);
}
- public void removePaths(Class<? extends BaseIdentity> context, Class<? extends RpcService> service,
- Set<InstanceIdentifier<?>> set) {
+ public void removePaths(final Class<? extends BaseIdentity> context, final Class<? extends RpcService> service,
+ final Set<InstanceIdentifier<?>> set) {
QName ctx = BindingReflections.findQName(context);
for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
toDOMInstanceIdentifier)) {
}
@Override
- public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode domInput) {
+ public RpcResult<CompositeNode> invokeRpc(final QName rpc, final CompositeNode domInput) {
checkArgument(rpc != null);
checkArgument(domInput != null);
}
}
- private RpcInvocationStrategy resolveInvocationStrategy(QName rpc) {
+ private RpcInvocationStrategy resolveInvocationStrategy(final QName rpc) {
return strategiesByQName.get(rpc);
}
protected final Method targetMethod;
protected final QName rpc;
- public RpcInvocationStrategy(QName rpc, Method targetMethod) {
+ public RpcInvocationStrategy(final QName rpc, final Method targetMethod) {
this.targetMethod = targetMethod;
this.rpc = rpc;
}
public abstract RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput)
throws Exception;
- public RpcResult<CompositeNode> invokeOn(RpcService rpcService, CompositeNode domInput) throws Exception {
+ public RpcResult<CompositeNode> invokeOn(final RpcService rpcService, final CompositeNode domInput) throws Exception {
return uncheckedInvoke(rpcService, domInput);
}
}
private final WeakReference<Class> outputClass;
@SuppressWarnings({ "rawtypes", "unchecked" })
- public DefaultInvocationStrategy(QName rpc, Method targetMethod, Class<?> outputClass,
- Class<? extends DataContainer> inputClass) {
+ public DefaultInvocationStrategy(final QName rpc, final Method targetMethod, final Class<?> outputClass,
+ final Class<? extends DataContainer> inputClass) {
super(rpc, targetMethod);
this.outputClass = new WeakReference(outputClass);
this.inputClass = new WeakReference(inputClass);
@SuppressWarnings("unchecked")
@Override
- public RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput) throws Exception {
+ public RpcResult<CompositeNode> uncheckedInvoke(final RpcService rpcService, final CompositeNode domInput) throws Exception {
DataContainer bindingInput = mappingService.dataObjectFromDataDom(inputClass.get(), domInput);
Future<RpcResult<?>> futureResult = (Future<RpcResult<?>>) targetMethod.invoke(rpcService, bindingInput);
if (futureResult == null) {
}
@Override
- public Future<RpcResult<?>> forwardToDomBroker(DataObject input) {
+ public Future<RpcResult<?>> forwardToDomBroker(final DataObject input) {
if(biRpcRegistry != null) {
CompositeNode xml = mappingService.toDataDom(input);
CompositeNode wrappedXml = ImmutableCompositeNode.create(rpc, ImmutableList.<Node<?>> of(xml));
private class NoInputNoOutputInvocationStrategy extends RpcInvocationStrategy {
- public NoInputNoOutputInvocationStrategy(QName rpc, Method targetMethod) {
+ public NoInputNoOutputInvocationStrategy(final QName rpc, final Method targetMethod) {
super(rpc, targetMethod);
}
@Override
- public RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput) throws Exception {
+ public RpcResult<CompositeNode> uncheckedInvoke(final RpcService rpcService, final CompositeNode domInput) throws Exception {
@SuppressWarnings("unchecked")
Future<RpcResult<Void>> result = (Future<RpcResult<Void>>) targetMethod.invoke(rpcService);
RpcResult<Void> bindingResult = result.get();
}
@Override
- public Future<RpcResult<?>> forwardToDomBroker(DataObject input) {
+ public Future<RpcResult<?>> forwardToDomBroker(final DataObject input) {
return Futures.immediateFuture(null);
}
}
private final WeakReference<Class> inputClass;
@SuppressWarnings({ "rawtypes", "unchecked" })
- public NoOutputInvocationStrategy(QName rpc, Method targetMethod,
- Class<? extends DataContainer> inputClass) {
+ public NoOutputInvocationStrategy(final QName rpc, final Method targetMethod,
+ final Class<? extends DataContainer> inputClass) {
super(rpc,targetMethod);
this.inputClass = new WeakReference(inputClass);
}
@Override
- public RpcResult<CompositeNode> uncheckedInvoke(RpcService rpcService, CompositeNode domInput) throws Exception {
+ public RpcResult<CompositeNode> uncheckedInvoke(final RpcService rpcService, final CompositeNode domInput) throws Exception {
DataContainer bindingInput = mappingService.dataObjectFromDataDom(inputClass.get(), domInput);
Future<RpcResult<?>> result = (Future<RpcResult<?>>) targetMethod.invoke(rpcService, bindingInput);
if (result == null) {
}
@Override
- public Future<RpcResult<?>> forwardToDomBroker(DataObject input) {
+ public Future<RpcResult<?>> forwardToDomBroker(final DataObject input) {
if(biRpcRegistry != null) {
CompositeNode xml = mappingService.toDataDom(input);
CompositeNode wrappedXml = ImmutableCompositeNode.create(rpc,ImmutableList.<Node<?>>of(xml));
return mappingService;
}
- public void setBindingNotificationService(NotificationProviderService baService) {
+ public void setBindingNotificationService(final NotificationProviderService baService) {
this.baNotifyService = baService;
}
- public void setDomNotificationService(NotificationPublishService domService) {
+ public void setDomNotificationService(final NotificationPublishService domService) {
this.domNotificationService = domService;
}
}
@Override
- public void onNotification(CompositeNode notification) {
+ public void onNotification(final CompositeNode notification) {
QName qname = notification.getNodeType();
WeakReference<Class<? extends Notification>> potential = notifications.get(qname);
if (potential != null) {
}
@Override
- public void onNotificationSubscribtion(Class<? extends Notification> notificationType) {
+ public void onNotificationSubscribtion(final Class<? extends Notification> notificationType) {
QName qname = BindingReflections.findQName(notificationType);
if (qname != null) {
WeakReference<Class<? extends Notification>> already = notifications.putIfAbsent(qname,
import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector;
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
-interface DomForwardedBroker {
+public interface DomForwardedBroker {
public BindingIndependentConnector getConnector();
-
+
public void setConnector(BindingIndependentConnector connector);
-
+
public void setDomProviderContext(ProviderSession domProviderContext);
public ProviderSession getDomProviderContext();
module opendaylight-sal-binding-broker-impl {
- yang-version 1;
+ yang-version 1;
namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl";
prefix "binding-impl";
description
"Service definition for Binding Aware MD-SAL.";
-
+
revision "2013-10-28" {
description
"Initial revision";
config:provided-service sal:binding-rpc-registry;
config:java-name-prefix BindingBrokerImpl;
}
-
+
identity binding-data-broker {
base config:module-type;
config:provided-service sal:binding-data-broker;
config:provided-service sal:binding-data-consumer-broker;
config:java-name-prefix DataBrokerImpl;
}
-
+
+ identity binding-data-compatible-broker {
+ base config:module-type;
+ config:provided-service sal:binding-data-broker;
+ config:provided-service sal:binding-data-consumer-broker;
+ config:java-name-prefix ForwardedCompatibleDataBrokerImpl;
+ }
+
identity binding-rpc-broker {
base config:module-type;
config:provided-service sal:binding-rpc-registry;
config:java-name-prefix RpcBrokerImpl;
}
-
+
identity binding-notification-broker {
base config:module-type;
config:provided-service sal:binding-notification-service;
augment "/config:modules/config:module/config:configuration" {
case binding-broker-impl {
when "/config:modules/config:module/config:type = 'binding-broker-impl'";
-
+
/*
container rpc-registry {
uses config:service-ref {
}
}
}*/
-
+
container data-broker {
uses config:service-ref {
refine type {
}
}
}
-
+
container notification-service {
uses config:service-ref {
refine type {
config:required-identity binding-dom-mapping-service;
}
}
- }
+ }
+ }
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case binding-data-compatible-broker {
+ when "/config:modules/config:module/config:type = 'binding-data-compatible-broker'";
+
+ container dom-async-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dom:dom-broker-osgi-registry;
+ }
+ }
+ }
+
+ container binding-mapping-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity binding-dom-mapping-service;
+ }
+ }
+ }
}
}
-
+
augment "/config:modules/config:module/config:state" {
case runtime-generated-mapping {
uses common:notification-state;
}
}
-}
\ No newline at end of file
+}
*/
package org.opendaylight.controller.sal.binding.test.bugfix;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
import org.junit.Test;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.node.attributes.igp.node.attributes.Prefix;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.node.attributes.igp.node.attributes.PrefixBuilder;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-
-
-
-
-
-
-
-import static org.junit.Assert.*;
-
public class UnionSerializationTest extends AbstractDataServiceTest {
public static final String PREFIX_STRING = "192.168.0.1/32";
assertNotNull(serialized.getFirstSimpleByName(Prefix.QNAME));
assertEquals(PREFIX_STRING, serialized.getFirstSimpleByName(Prefix.QNAME).getValue());
- Prefix deserialized = (Prefix) testContext.getBindingToDomMappingService().dataObjectFromDataDom(InstanceIdentifier.builder().node(Prefix.class).build(), serialized);
+ Prefix deserialized = (Prefix) testContext.getBindingToDomMappingService().dataObjectFromDataDom(Prefix.class, serialized);
assertNotNull(deserialized);
assertNotNull(deserialized.getPrefix());
assertNotNull(deserialized.getPrefix().getIpv4Prefix());
*/
package org.opendaylight.controller.sal.binding.test.util;
+import static com.google.common.base.Preconditions.checkState;
+
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javassist.ClassPool;
+import org.opendaylight.controller.md.sal.binding.impl.ForwardedBackwardsCompatibleDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMDataBrokerImpl;
+import org.opendaylight.controller.md.sal.dom.broker.impl.compat.BackwardsCompatibleDataBroker;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sal.binding.api.mount.MountProviderService;
import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
import org.opendaylight.controller.sal.core.api.data.DataStore;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
import org.opendaylight.controller.sal.dom.broker.BrokerImpl;
import org.opendaylight.controller.sal.dom.broker.MountPointManagerImpl;
import org.opendaylight.controller.sal.dom.broker.impl.DataStoreStatsWrapper;
import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import com.google.common.base.Predicate;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ListeningExecutorService;
-import static com.google.common.base.Preconditions.*;
-
public class BindingTestContext implements AutoCloseable, SchemaContextProvider {
public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier TREE_ROOT = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
private BindingIndependentConnector baConnectImpl;
private org.opendaylight.controller.sal.dom.broker.DataBrokerImpl biDataImpl;
+ private org.opendaylight.controller.sal.core.api.data.DataProviderService biDataLegacyBroker;
private BrokerImpl biBrokerImpl;
private HashMapDataStore rawDataStore;
private SchemaAwareDataStoreAdapter schemaAwareDataStore;
private DataStoreStatsWrapper dataStoreStats;
private DataStore dataStore;
- private boolean dataStoreStatisticsEnabled = false;
+ private final boolean dataStoreStatisticsEnabled = false;
private final ListeningExecutorService executor;
private final ClassPool classPool;
private SchemaContext schemaContext;
+ private ImmutableMap<LogicalDatastoreType, DOMStore> newDatastores;
+
+ private BackwardsCompatibleDataBroker biCompatibleBroker;
+
+ private final List<SchemaContextListener> schemaListeners = new ArrayList<>();
+
+ private DataProviderService baData;
+
+ private DOMDataBroker newDOMDataBroker;
+
+ @Override
public SchemaContext getSchemaContext() {
return schemaContext;
}
- protected BindingTestContext(ListeningExecutorService executor, ClassPool classPool, boolean startWithSchema) {
+ protected BindingTestContext(final ListeningExecutorService executor, final ClassPool classPool, final boolean startWithSchema) {
this.executor = executor;
this.classPool = classPool;
this.startWithSchema = startWithSchema;
checkState(executor != null, "Executor needs to be set");
biDataImpl = new org.opendaylight.controller.sal.dom.broker.DataBrokerImpl();
biDataImpl.setExecutor(executor);
+ biDataLegacyBroker = biDataImpl;
+ }
+
+ public void startNewDomDataBroker() {
+ checkState(executor != null, "Executor needs to be set");
+ InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", executor);
+ InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", executor);
+ newDatastores = ImmutableMap.<LogicalDatastoreType, DOMStore>builder()
+ .put(LogicalDatastoreType.OPERATIONAL, operStore)
+ .put(LogicalDatastoreType.CONFIGURATION, configStore)
+ .build();
+
+ newDOMDataBroker = new DOMDataBrokerImpl(newDatastores, executor);
+
+ biCompatibleBroker = new BackwardsCompatibleDataBroker(newDOMDataBroker);
+
+ schemaListeners.add(configStore);
+ schemaListeners.add(operStore);
+ schemaListeners.add(biCompatibleBroker);
+ biDataLegacyBroker = biCompatibleBroker;
}
public void startBindingDataBroker() {
checkState(executor != null, "Executor needs to be set");
baDataImpl = new DataBrokerImpl();
baDataImpl.setExecutor(executor);
+ baData = baDataImpl;
}
public void startBindingBroker() {
checkState(executor != null, "Executor needs to be set");
- checkState(baDataImpl != null, "Binding Data Broker must be started");
+ checkState(baData != null, "Binding Data Broker must be started");
checkState(baNotifyImpl != null, "Notification Service must be started");
baBrokerImpl = new DomForwardedBindingBrokerImpl("test");
baBrokerImpl.getMountManager().setDataCommitExecutor(executor);
baBrokerImpl.getMountManager().setNotificationExecutor(executor);
baBrokerImpl.setRpcBroker(new RpcProviderRegistryImpl("test"));
- baBrokerImpl.setDataBroker(baDataImpl);
+ baBrokerImpl.setDataBroker(baData);
baBrokerImpl.setNotificationBroker(baNotifyImpl);
baBrokerImpl.start();
}
public void startForwarding() {
- checkState(baDataImpl != null, "Binding Data Broker needs to be started");
- checkState(biDataImpl != null, "DOM Data Broker needs to be started.");
+ checkState(baData != null, "Binding Data Broker needs to be started");
+ checkState(biDataLegacyBroker != null, "DOM Data Broker needs to be started.");
checkState(mappingServiceImpl != null, "DOM Mapping Service needs to be started.");
baConnectImpl = BindingDomConnectorDeployer.createConnector(getBindingToDomMappingService());
}
private ProviderSession createMockContext() {
- // TODO Auto-generated method stub
+
final ClassToInstanceMap<BrokerService> domBrokerServices = ImmutableClassToInstanceMap
.<BrokerService> builder()
//
- .put(org.opendaylight.controller.sal.core.api.data.DataProviderService.class, biDataImpl) //
+ .put(org.opendaylight.controller.sal.core.api.data.DataProviderService.class, biDataLegacyBroker) //
.put(RpcProvisionRegistry.class, biBrokerImpl.getRouter()) //
.put(MountProvisionService.class, biMountImpl) //
.build();
return new ProviderSession() {
@Override
- public Future<RpcResult<CompositeNode>> rpc(QName rpc, CompositeNode input) {
+ public Future<RpcResult<CompositeNode>> rpc(final QName rpc, final CompositeNode input) {
throw new UnsupportedOperationException();
}
@Override
- public <T extends BrokerService> T getService(Class<T> service) {
+ public <T extends BrokerService> T getService(final Class<T> service) {
return domBrokerServices.getInstance(service);
}
@Override
public ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(
- RpcRegistrationListener listener) {
+ final RpcRegistrationListener listener) {
return null;
}
@Override
- public RpcRegistration addRpcImplementation(QName rpcType, RpcImplementation implementation)
+ public RpcRegistration addRpcImplementation(final QName rpcType, final RpcImplementation implementation)
throws IllegalArgumentException {
return null;
}
@Override
- public RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
+ public RoutedRpcRegistration addRoutedRpcImplementation(final QName rpcType, final RpcImplementation implementation) {
return null;
}
@Override
- public RoutedRpcRegistration addMountedRpcImplementation(QName rpcType, RpcImplementation implementation) {
+ public RoutedRpcRegistration addMountedRpcImplementation(final QName rpcType, final RpcImplementation implementation) {
return null;
}
};
mappingServiceImpl.init();
}
- public void updateYangSchema(String[] files) {
+ public void updateYangSchema(final String[] files) {
schemaContext = getContext(files);
+
if (schemaAwareDataStore != null) {
schemaAwareDataStore.onGlobalContextUpdated(schemaContext);
}
if (mappingServiceImpl != null) {
mappingServiceImpl.onGlobalContextUpdated(schemaContext);
}
+ for(SchemaContextListener listener : schemaListeners) {
+ listener.onGlobalContextUpdated(schemaContext);
+ }
}
public static String[] getAllYangFilesOnClasspath() {
Predicate<String> predicate = new Predicate<String>() {
@Override
- public boolean apply(String input) {
+ public boolean apply(final String input) {
return input.endsWith(".yang");
}
};
Reflections reflection = new Reflections("META-INF.yang", new ResourcesScanner());
Set<String> result = reflection.getResources(predicate);
- return (String[]) result.toArray(new String[result.size()]);
+ return result.toArray(new String[result.size()]);
}
- private static SchemaContext getContext(String[] yangFiles) {
+ private static SchemaContext getContext(final String[] yangFiles) {
ClassLoader loader = BindingTestContext.class.getClassLoader();
List<InputStream> streams = new ArrayList<>();
for (String string : yangFiles) {
return parser.resolveSchemaContext(modules);
}
- public void start() {
+ public void startLegacy() {
startBindingDataBroker();
startBindingNotificationBroker();
startBindingBroker();
}
}
+ public void start() {
+ startNewDomDataBroker();
+ startDomBroker();
+ startDomMountPoint();
+ startBindingToDomMappingService();
+ startNewBindingDataBroker();
+
+ startBindingNotificationBroker();
+ startBindingBroker();
+
+ startForwarding();
+ if (startWithSchema) {
+ loadYangSchemaFromClasspath();
+ }
+ }
+
+ public void startNewBindingDataBroker() {
+ ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, mappingServiceImpl, executor);
+ schemaListeners.add(forwarded);
+ baData = forwarded;
+ }
+
private void startDomMountPoint() {
biMountImpl = new MountPointManagerImpl();
biMountImpl.setDataBroker(getDomDataBroker());
biBrokerImpl = new BrokerImpl();
biBrokerImpl.setExecutor(executor);
biBrokerImpl.setRouter(new SchemaAwareRpcBroker("/", this));
+
}
public void startBindingNotificationBroker() {
}
public DataProviderService getBindingDataBroker() {
- return baDataImpl;
+ return baData;
}
public org.opendaylight.controller.sal.core.api.data.DataProviderService getDomDataBroker() {
- return biDataImpl;
+ return biDataLegacyBroker;
}
public DataStore getDomDataStore() {
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import org.junit.Ignore;
import org.junit.Test;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
*
* Reported by Depthi V V
*
+ * @deprecated This test tests indirect generation, which should be tested
+ * different way. the test creates conflicting transactions
+ * and assumes correct commit - to test codec generation
+ *
*/
@Test
+ @Ignore
+ @Deprecated
public void testIndirectGeneration() throws Exception {
ExecutorService basePool = Executors.newFixedThreadPool(2);
private class CreateFlowTask implements Callable<Void> {
- public CreateFlowTask(Object startSync) {
+ public CreateFlowTask(final Object startSync) {
}
@Override
private static final NodeRef NODE_REF = new NodeRef(NODE_INSTANCE_ID_BA);
/**
- * This test is ignored, till found out better way to test generation
- * of classes without leaking of instances from previous run
+ * This test is ignored, till found out better way to test generation of
+ * classes without leaking of instances from previous run
*
* @throws Exception
*/
ExecutorService testExecutor = Executors.newFixedThreadPool(1);
testContext.loadYangSchemaFromClasspath();
- Future<Future<RpcResult<TransactionStatus>>> future = testExecutor.submit(new Callable<Future<RpcResult<TransactionStatus>>>() {
- @Override
- public Future<RpcResult<TransactionStatus>> call() throws Exception {
- NodesBuilder nodesBuilder = new NodesBuilder();
- nodesBuilder.setNode(Collections.<Node> emptyList());
- DataModificationTransaction transaction = baDataService.beginTransaction();
- transaction.putOperationalData(NODES_INSTANCE_ID_BA, nodesBuilder.build());
- return transaction.commit();
- }
- });
-
+ Future<Future<RpcResult<TransactionStatus>>> future = testExecutor
+ .submit(new Callable<Future<RpcResult<TransactionStatus>>>() {
+ @Override
+ public Future<RpcResult<TransactionStatus>> call() throws Exception {
+ NodesBuilder nodesBuilder = new NodesBuilder();
+ nodesBuilder.setNode(Collections.<Node> emptyList());
+ DataModificationTransaction transaction = baDataService.beginTransaction();
+ transaction.putOperationalData(NODES_INSTANCE_ID_BA, nodesBuilder.build());
+ return transaction.commit();
+ }
+ });
RpcResult<TransactionStatus> result = future.get().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import com.google.common.util.concurrent.SettableFuture;
+
public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataChangeListener {
private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
.node(SUPPORTED_ACTIONS_QNAME) //
.toInstance();
- private DataChangeEvent<InstanceIdentifier<?>, DataObject> receivedChangeEvent;
+ private final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> receivedChangeEvent = SettableFuture.create();
RpcResult<TransactionStatus> result = transaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- assertNotNull(receivedChangeEvent);
+ DataChangeEvent<InstanceIdentifier<?>, DataObject> potential = receivedChangeEvent.get(1000,TimeUnit.MILLISECONDS);
+ assertNotNull(potential);
- verifyNodes((Nodes) receivedChangeEvent.getUpdatedOperationalSubtree(),original);
+ verifyNodes((Nodes) potential.getUpdatedOperationalSubtree(),original);
assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
Nodes nodes = checkForNodes();
verifyNodes(nodes,original);
assertNull(node);
}
- private void verifyNodes(Nodes nodes,Node original) {
+ private void verifyNodes(final Nodes nodes,final Node original) {
assertNotNull(nodes);
assertNotNull(nodes.getNode());
assertEquals(1, nodes.getNode().size());
}
private void assertBindingIndependentVersion(
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier nodeId) {
+ final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier nodeId) {
CompositeNode node = biDataService.readOperationalData(nodeId);
assertNotNull(node);
}
}
@Override
- public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
- receivedChangeEvent = change;
+ public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ receivedChangeEvent.set(change);
}
}
import java.util.Collections;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import org.junit.Ignore;
import org.junit.Test;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import com.google.common.util.concurrent.SettableFuture;
+
public class PutAugmentationTest extends AbstractDataServiceTest implements DataChangeListener {
private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
.augmentation(FlowCapableNode.class) //
.build();
- private DataChangeEvent<InstanceIdentifier<?>, DataObject> lastReceivedChangeEvent;
+ private SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> lastReceivedChangeEvent;
/**
* Test for Bug 148
* @throws Exception
*/
@Test
+ @Ignore
public void putNodeAndAugmentation() throws Exception {
-
+ lastReceivedChangeEvent = SettableFuture.create();
baDataService.registerDataChangeListener(ALL_FLOW_CAPABLE_NODES, this);
DataModificationTransaction augmentedTransaction = baDataService.beginTransaction();
augmentedTransaction.putOperationalData(augmentIdentifier, fnu);
+
+ lastReceivedChangeEvent = SettableFuture.create();
result = augmentedTransaction.commit().get();
assertEquals(TransactionStatus.COMMITED, result.getResult());
- assertNotNull(lastReceivedChangeEvent);
- assertTrue(lastReceivedChangeEvent.getCreatedOperationalData().containsKey(FLOW_AUGMENTATION_PATH));
+ DataChangeEvent<InstanceIdentifier<?>, DataObject> potential = lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS);
+ assertNotNull(potential);
+ assertTrue(potential.getCreatedOperationalData().containsKey(FLOW_AUGMENTATION_PATH));
+
+ lastReceivedChangeEvent = SettableFuture.create();
Node augmentedNode = (Node) baDataService.readOperationalData(NODE_INSTANCE_ID_BA);
assertNotNull(node);
assertEquals(fnu.getDescription(), readedAugmentation.getDescription());
assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
testNodeRemove();
- assertTrue(lastReceivedChangeEvent.getRemovedOperationalData().contains(FLOW_AUGMENTATION_PATH));
+ assertTrue(lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS).getRemovedOperationalData().contains(FLOW_AUGMENTATION_PATH));
}
@Test
+ @Ignore
public void putNodeWithAugmentation() throws Exception {
-
+ lastReceivedChangeEvent = SettableFuture.create();
baDataService.registerDataChangeListener(ALL_FLOW_CAPABLE_NODES, this);
NodeBuilder nodeBuilder = new NodeBuilder();
baseTransaction.putOperationalData(NODE_INSTANCE_ID_BA, nodeBuilder.build());
RpcResult<TransactionStatus> result = baseTransaction.commit().get();
- assertNotNull(lastReceivedChangeEvent);
- assertTrue(lastReceivedChangeEvent.getCreatedOperationalData().containsKey(FLOW_AUGMENTATION_PATH));
- lastReceivedChangeEvent = null;
+
+ DataChangeEvent<InstanceIdentifier<?>, DataObject> potential = lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS);
+ assertNotNull(potential);
+ assertTrue(potential.getCreatedOperationalData().containsKey(FLOW_AUGMENTATION_PATH));
+ lastReceivedChangeEvent = SettableFuture.create();
assertEquals(TransactionStatus.COMMITED, result.getResult());
FlowCapableNode readedAugmentation = (FlowCapableNode) baDataService.readOperationalData(InstanceIdentifier
assertEquals(fnu.getHardware(), readedAugmentation.getHardware());
testPutNodeConnectorWithAugmentation();
- lastReceivedChangeEvent = null;
+ lastReceivedChangeEvent = SettableFuture.create();
testNodeRemove();
- assertTrue(lastReceivedChangeEvent.getRemovedOperationalData().contains(FLOW_AUGMENTATION_PATH));
+ assertTrue(lastReceivedChangeEvent.get(1000,TimeUnit.MILLISECONDS).getRemovedOperationalData().contains(FLOW_AUGMENTATION_PATH));
}
private void testPutNodeConnectorWithAugmentation() throws Exception {
assertNull(node);
}
- private void verifyNodes(Nodes nodes, Node original) {
+ private void verifyNodes(final Nodes nodes, final Node original) {
assertNotNull(nodes);
assertNotNull(nodes.getNode());
assertEquals(1, nodes.getNode().size());
}
- private void assertBindingIndependentVersion(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier nodeId) {
+ private void assertBindingIndependentVersion(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier nodeId) {
CompositeNode node = biDataService.readOperationalData(nodeId);
assertNotNull(node);
}
}
@Override
- public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
- lastReceivedChangeEvent = change;
+ public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ lastReceivedChangeEvent.set(change);
}
}
*/
package org.opendaylight.controller.sal.binding.test.connect.dom;
-import com.google.common.collect.ImmutableMap;
+import static org.junit.Assert.assertEquals;
+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.junit.Test;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
-import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
-import org.opendaylight.controller.md.sal.common.api.data.DataModification;
-import org.opendaylight.controller.sal.common.util.CommitHandlerTransactions;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+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.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.yangtools.concepts.Registration;
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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.SettableFuture;
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 FLOW_NODE_QNAME = QName.create(Flow.QNAME, "node");
private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
private static final FlowKey FLOW_KEY = new FlowKey(FLOW_ID);
- private DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modificationCapture;
+ private final SettableFuture<DataChangeEvent<InstanceIdentifier<?>, DataObject>> modificationCapture = SettableFuture.create();
private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
NODE_ID);
assertNull(biDataService.readConfigurationData(FLOW_INSTANCE_ID_BI));
- registerCommitHandler();
+ registerChangeListener();
CompositeNode domflow = createTestFlow();
DataModificationTransaction biTransaction = biDataService.beginTransaction();
biTransaction.putConfigurationData(FLOW_INSTANCE_ID_BI, domflow);
RpcResult<TransactionStatus> biResult = biTransaction.commit().get();
assertEquals(TransactionStatus.COMMITED, biResult.getResult());
- assertNotNull(modificationCapture);
- Flow flow = (Flow) modificationCapture.getCreatedConfigurationData().get(FLOW_INSTANCE_ID_BA);
+ 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());
assertEquals(TransactionStatus.COMMITED, biResult.getResult());
}
- private void registerCommitHandler() {
- DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> flowTestCommitHandler = new DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject>() {
+ private void registerChangeListener() {
+ baDataService.registerDataChangeListener(FLOWS_PATH_BA, new DataChangeListener() {
@Override
- public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> requestCommit(
- DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification) {
- modificationCapture = modification;
- return CommitHandlerTransactions.allwaysSuccessfulTransaction(modification);
+ public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ LOG.info("Data Change listener invoked.");
+ modificationCapture.set(change);
}
-
- };
- Registration<DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject>> registration = baDataService
- .registerCommitHandler(FLOWS_PATH_BA, flowTestCommitHandler);
- assertNotNull(registration);
+ });
}
private CompositeNode createTestFlow() {
// 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
bindingAwareSalBundles(),
mavenBundle("commons-codec", "commons-codec").versionAsInProject(),
- mavenBundle(CONTROLLER, "protocol-framework").versionAsInProject(), //
+ systemProperty("org.osgi.framework.system.packages.extra").value("sun.nio.ch"),
+ mavenBundle("io.netty", "netty-common").versionAsInProject(), //
+ mavenBundle("io.netty", "netty-buffer").versionAsInProject(), //
+ mavenBundle("io.netty", "netty-handler").versionAsInProject(), //
+ mavenBundle("io.netty", "netty-codec").versionAsInProject(), //
+ mavenBundle("io.netty", "netty-transport").versionAsInProject(), //
+
mavenBundle(CONTROLLER, "config-manager").versionAsInProject(), // //
mavenBundle("commons-io", "commons-io").versionAsInProject(), //
mavenBundle(CONTROLLER, "config-manager").versionAsInProject(), //
mavenBundle(CONTROLLER, "ietf-netconf-monitoring-extension").versionAsInProject(), //
mavenBundle(CONTROLLER, "netconf-monitoring").versionAsInProject(), //
+ mavenBundle(CONTROLLER, "netty-config-api").versionAsInProject(), //
+ mavenBundle(CONTROLLER, "protocol-framework").versionAsInProject(), //
+
mavenBundle(CONTROLLER, "netconf-client").versionAsInProject(), //
mavenBundle(CONTROLLER, "netconf-util").versionAsInProject(), //
mavenBundle(CONTROLLER + ".thirdparty", "ganymed").versionAsInProject(), //
mavenBundle(CONTROLLER, "config-persister-impl").versionAsInProject(), //
- mavenBundle("io.netty", "netty-handler").versionAsInProject(), //
- mavenBundle("io.netty", "netty-codec").versionAsInProject(), //
- mavenBundle("io.netty", "netty-buffer").versionAsInProject(), //
- mavenBundle("io.netty", "netty-transport").versionAsInProject(), //
- mavenBundle("io.netty", "netty-common").versionAsInProject(), //
-
mavenBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.xerces", "2.11.0_1"),
mavenBundle("org.eclipse.birt.runtime.3_7_1", "org.apache.xml.resolver", "1.2.0"),
import static org.opendaylight.controller.test.sal.binding.it.TestHelper.mdSalCoreBundles;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackages;
import static org.ops4j.pax.exam.CoreOptions.systemProperty;
@RunWith(PaxExam.class)
mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
systemProperty("osgi.bundles.defaultStartLevel").value("4"),
+ systemPackages("sun.nio.ch"),
mdSalCoreBundles(),
-<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">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-parent</artifactId>
<version>1.1-SNAPSHOT</version>
</parent>
+
<artifactId>sal-common-api</artifactId>
+ <packaging>bundle</packaging>
+
<scm>
- <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
- <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
- <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
- <tag>HEAD</tag>
- </scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ <tag>HEAD</tag>
+ </scm>
<dependencies>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common</artifactId>
- </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-common</artifactId>
<artifactId>concepts</artifactId>
</dependency>
<dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.core</artifactId>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
</dependency>
</dependencies>
- <packaging>bundle</packaging>
</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataBroker<P extends Path<P>, D, L extends AsyncDataChangeListener<P, D>> extends //
+ AsyncDataTransactionFactory<P, D> {
+
+ /**
+ *
+ * Scope of Data Change
+ *
+ * Represents scope of data change (addition, replacement, deletion).
+ *
+ * The terminology for types is reused from LDAP
+ *
+ * @see http://www.idevelopment.info/data/LDAP/LDAP_Resources/SEARCH_Setting_the_SCOPE_Parameter.shtml
+ */
+ public enum DataChangeScope {
+
+ /**
+ * Represents only a direct change of the node, such as replacement of node,
+ * addition or deletion.
+ *
+ */
+ BASE,
+ /**
+ * Represent a change (addition,replacement,deletion)
+ * of the node or one of it's direct childs.
+ *
+ */
+ ONE,
+ /**
+ * Represents a change of the node or any of it's child nodes.
+ *
+ */
+ SUBTREE
+ }
+
+ @Override
+ public AsyncReadTransaction<P, D> newReadOnlyTransaction();
+
+ @Override
+ public AsyncReadWriteTransaction<P,D> newReadWriteTransaction();
+
+ @Override
+ public AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
+
+ /**
+ * Registers {@link DataChangeListener} for Data Change callbacks
+ * which will be triggered on which will be triggered on the store
+ *
+ *Â @param store Logical store in which listener is registered.
+ * @param path Path (subtree identifier) on which client listener will be invoked.
+ * @param listener Instance of listener which should be invoked on
+ * @param triggeringScope Scope of change which triggers callback.
+ * @return Listener registration of the listener, call {@link ListenerRegistration#close()}
+ * to stop delivery of change events.
+ */
+ ListenerRegistration<L> registerDataChangeListener(LogicalDatastoreType store, P path, L listener, DataChangeScope triggeringScope);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataChangeEvent<P extends Path<P>,D> extends Immutable {
+ /**
+ * Returns a immutable map of paths and newly created objects
+ *
+ * @return map of paths and newly created objects
+ */
+ Map<P, D> getCreatedData();
+
+ /**
+ * Returns a immutable map of paths and respective updated objects after update.
+ *
+ * Original state of the object is in
+ * {@link #getOriginalData()}
+ *
+ * @return map of paths and newly created objects
+ */
+ Map<P, D> getUpdatedData();
+
+ /**
+ * Returns a immutable set of removed paths.
+ *
+ * Original state of the object is in
+ * {@link #getOriginalData()}
+ *
+ * @return set of removed paths
+ */
+ Set<P> getRemovedPaths();
+
+ /**
+ * Return a immutable map of paths and original state of updated and removed objects.
+ *
+ * This map is populated if at changed path was previous object, and captures
+ * state of previous object.
+ *
+ * @return map of paths and original state of updated and removed objects.
+ */
+ Map<P, ? extends D> getOriginalData();
+
+ /**
+ * Returns a immutable stable view of data state, which
+ * captures state of data store before the reported change.
+ *
+ *
+ * The view is rooted at the point where the listener, to which the event is being delivered, was registered.
+ *
+ * @return Stable view of data before the change happened, rooted at the listener registration path.
+ *
+ */
+ D getOriginalSubtree();
+
+ /**
+ * Returns a immutable stable view of data, which captures state of data store
+ * after the reported change.
+ *
+ * The view is rooted at the point where the listener, to which the event is being delivered, was registered.
+ *
+ * @return Stable view of data after the change happened, rooted at the listener registration path.
+ */
+ D getUpdatedSubtree();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataChangeListener<P extends Path<P>, D> extends EventListener {
+ /**
+ * Note that this method may be invoked from a shared thread pool, so
+ * implementations SHOULD NOT perform CPU-intensive operations and they
+ * definitely MUST NOT invoke any potentially blocking operations.
+ *
+ * @param change Data Change Event being delivered.
+ */
+ void onDataChanged(AsyncDataChangeEvent<P, D> change);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface AsyncDataTransactionFactory<P extends Path<P>, D> {
+
+ AsyncReadTransaction<P, D> newReadOnlyTransaction();
+
+ AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
+
+ AsyncWriteTransaction<P,D> newWriteOnlyTransaction();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface AsyncReadTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
+
+ /**
+ *
+ * Reads data from provided logical data store located at provided path
+ *
+ *
+ * @param store
+ * Logical data store from which read should occur.
+ * @param path
+ * Path which uniquely identifies subtree which client want to
+ * read
+ * @return Listenable Future which contains read result
+ * <ul>
+ * <li>If data at supplied path exists the {@link Future#get()}
+ * returns Optional object containing data
+ * <li>If data at supplied path does not exists the
+ * {@link Future#get()} returns {@link Optional#absent()}.
+ * </ul>
+ */
+ ListenableFuture<Optional<D>> read(LogicalDatastoreType store, P 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.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Transaction enabling client to have combined transaction,
+ * which provides read and write capabilities.
+ *
+ *
+ * @param <P> Path Type
+ * @param <D> Data Type
+ */
+public interface AsyncReadWriteTransaction<P extends Path<P>, D> extends AsyncReadTransaction<P, D>,
+ AsyncWriteTransaction<P, D> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Path;
+
+
+/**
+ *
+ * @author
+ *
+ * @param <P> Type of path (subtree identifier), which represents location in tree
+ * @param <D> Type of data (payload), which represents data payload
+ */
+public interface AsyncTransaction<P extends Path<P>,D> extends //
+ Identifiable<Object>,
+ AutoCloseable {
+
+ @Override
+ public Object getIdentifier();
+
+ /**
+ * Closes transaction and releases all resources associated with it.
+ */
+ @Override
+ public void close();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface AsyncWriteTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
+ /**
+ * Cancels transaction.
+ *
+ * Transaction could be only cancelled if it's status
+ * is {@link TransactionStatus#NEW} or {@link TransactionStatus#SUBMITED}
+ *
+ * Invoking cancel() on {@link TransactionStatus#FAILED} or {@link TransactionStatus#CANCELED}
+ * will have no effect.
+ *
+ * @throws IllegalStateException If transaction status is {@link TransactionStatus#COMMITED}
+ *
+ */
+ public void cancel();
+
+ /**
+ * Store a piece of data at specified path. This acts as a add / replace operation,
+ * which is to say that whole subtree will be replaced by specified path.
+ *
+ * If you need add or merge of current object with specified use {@link #merge(LogicalDatastoreType, Path, Object)}
+ *
+ * @param store Logical data store which should be modified
+ * @param path Data object path
+ * @param data Data object to be written to specified path
+ * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+ */
+ public void put(LogicalDatastoreType store, P path, D data);
+
+ /**
+ * Store a piece of data at specified path. This acts as a merge operation,
+ * which is to say that any pre-existing data which is not explicitly
+ * overwritten will be preserved. This means that if you store a container,
+ * its child lists will be merged. Performing the following put operations:
+ *
+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ *
+ * will result in the following data being present:
+ *
+ * container { list [ a, b ] }
+ *
+ * This also means that storing the container will preserve any augmentations
+ * which have been attached to it.
+ *
+ * If you require an explicit replace operation, use {@link #put(LogicalDatastoreType, Path, Object)} instead.
+ *
+ * @param store Logical data store which should be modified
+ * @param path Data object path
+ * @param data Data object to be written to specified path
+ * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+ */
+ public void merge(LogicalDatastoreType store, P path, D data);
+
+ /**
+ * Remove a piece of data from specified path. This operation does not fail
+ * if the specified path does not exist.
+ *
+ * @param store Logical data store which should be modified
+ * @param path Data object path
+ * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+ */
+ public void delete(LogicalDatastoreType store, P path);
+
+ /**
+ *
+ * Closes transaction and resources allocated to the transaction.
+ *
+ * This call does not change Transaction status. Client SHOULD
+ * explicitly {@link #commit()} or {@link #cancel()} transaction.
+ *
+ * @throws IllegalStateException if the transaction has not been
+ * updated by invoking {@link #commit()} or {@link #cancel()}.
+ */
+ @Override
+ public void close();
+
+ /**
+ * Initiates a commit of modification. This call logically seals the
+ * transaction, preventing any the client from interacting with the
+ * data stores. The transaction is marked as {@link TransactionStatus#SUBMITED}
+ * and enqueued into the data store backed for processing.
+ *
+ * <p>
+ * The successful commit changes the state of the system and may affect
+ * several components.
+ *
+ * <p>
+ * The effects of successful commit of data are described in the
+ * specifications and YANG models describing the Provider components of
+ * controller. It is assumed that Consumer has an understanding of this
+ * changes.
+ *
+ * @see DataCommitHandler for further information how two-phase commit is
+ * processed.
+ * @param store Identifier of the store, where commit should occur.
+ * @return Result of the Commit, containing success information or list of
+ * encountered errors, if commit was not successful. The Future
+ * blocks until {@link TransactionStatus#COMMITED} or
+ * {@link TransactionStatus#FAILED} is reached.
+ * @throws IllegalStateException if the transaction is not {@link TransactionStatus#NEW}
+ */
+ public ListenableFuture<RpcResult<TransactionStatus>> commit();
+
+}
import org.opendaylight.yangtools.concepts.Path;
public interface DataChangeListener<P extends Path<P>, D> extends EventListener {
-
+ /**
+ * Note that this method may be invoked from a shared thread pool, so
+ * implementations SHOULD NOT perform CPU-intensive operations and they
+ * definitely MUST NOT invoke any potentially blocking operations.
+ *
+ * @param change Data Change Event being delivered.
+ **/
void onDataChanged(DataChangeEvent<P, D> change);
}
*/
package org.opendaylight.controller.md.sal.common.api.data;
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;
import org.opendaylight.yangtools.concepts.Path;
import org.opendaylight.yangtools.yang.common.RpcResult;
/**
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+public enum LogicalDatastoreType {
+
+ /**
+ * Logical atastore representing operational state of the system
+ * and it's components
+ *
+ * This datastore is used to describe operational state of
+ * the system and it's operation related data.
+ *
+ */
+ OPERATIONAL,
+ /**
+ * Logical Datastore representing configuration state of the system
+ * and it's components.
+ *
+ * This datastore is used to describe intended state of
+ * the system and intended operation mode.
+ *
+ */
+ CONFIGURATION
+
+}
* A chain of transactions. Transactions in a chain need to be committed in sequence and each
* transaction should see the effects of previous transactions as if they happened. A chain
* makes no guarantees of atomicity, in fact transactions are committed as soon as possible.
+ *
*/
-public interface TransactionChain<P extends Path<P>, D> extends AutoCloseable {
+public interface TransactionChain<P extends Path<P>, D> extends AutoCloseable, AsyncDataTransactionFactory<P, D> {
+
/**
- * Create a new transaction which will continue the chain. The previous transaction
- * has to be either COMMITTED or CANCELLED.
+ * Create a new read only transaction which will continue the chain.
+ * The previous read-write transaction has to be either COMMITED or CANCELLED.
*
* @return New transaction in the chain.
- * @throws IllegalStateException if the previous transaction was not COMMITTED or CANCELLED.
+ * @throws IllegalStateException if the previous transaction was not COMMITED
+ * or CANCELLED.
* @throws TransactionChainClosedException if the chain has been closed.
*/
- DataModification<P, D> newTransaction();
+ @Override
+ public AsyncReadTransaction<P, D> newReadOnlyTransaction();
+
+
+ /**
+ * Create a new read write transaction which will continue the chain.
+ * The previous read-write transaction has to be either COMMITED or CANCELLED.
+ *
+ * @return New transaction in the chain.
+ * @throws IllegalStateException if the previous transaction was not COMMITTED
+ * or CANCELLED.
+ * @throws TransactionChainClosedException if the chain has been closed.
+ */
+ @Override
+ public AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
@Override
void close();
* @param transaction Transaction which caused the chain to fail
* @param cause The cause of transaction failure
*/
- void onTransactionChainFailed(TransactionChain<?, ?> chain, DataModification<?, ?> transaction, Throwable cause);
+ void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction, Throwable cause);
/**
* Invoked when a transaction chain is completed. A transaction chain is considered completed when it has been
<groupId>org.eclipse.xtend</groupId>
<artifactId>org.eclipse.xtend.lib</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-api</artifactId>
+ </dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.NEW;
import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import org.opendaylight.controller.md.sal.common.api.data.DataModification;
import org.opendaylight.controller.md.sal.common.api.data.DataReader;
public abstract class AbstractDataModification<P extends Path<P>, D> implements DataModification<P, D> {
- private final ConcurrentMap<P, D> operationalOriginal;
- private final ConcurrentMap<P, D> configurationOriginal;
+ private final Map<P, D> operationalOriginal;
+ private final Map<P, D> configurationOriginal;
- private final ConcurrentMap<P, D> operationalCreated;
- private final ConcurrentMap<P, D> configurationCreated;
+ private final Map<P, D> operationalCreated;
+ private final Map<P, D> configurationCreated;
- private final ConcurrentMap<P, D> configurationUpdate;
- private final ConcurrentMap<P, D> operationalUpdate;
+ private final Map<P, D> configurationUpdate;
+ private final Map<P, D> operationalUpdate;
- private final ConcurrentMap<P, P> configurationRemove;
- private final ConcurrentMap<P, P> operationalRemove;
+ private final Map<P, P> configurationRemove;
+ private final Map<P, P> operationalRemove;
private final Map<P, D> unmodifiable_configurationOriginal;
private final Map<P, D> unmodifiable_operationalOriginal;
private final Set<P> unmodifiable_OperationalRemove;
private final DataReader<P, D> reader;
- public AbstractDataModification(DataReader<P, D> reader) {
+ public AbstractDataModification(final DataReader<P, D> reader) {
this.reader = reader;
- this.configurationUpdate = new ConcurrentHashMap<>();
- this.operationalUpdate = new ConcurrentHashMap<>();
- this.configurationRemove = new ConcurrentHashMap<>();
- this.operationalRemove = new ConcurrentHashMap<>();
+ this.configurationUpdate = new LinkedHashMap<>();
+ this.operationalUpdate = new LinkedHashMap<>();
+ this.configurationRemove = new LinkedHashMap<>();
+ this.operationalRemove = new LinkedHashMap<>();
- this.configurationOriginal = new ConcurrentHashMap<>();
- this.operationalOriginal = new ConcurrentHashMap<>();
+ this.configurationOriginal = new LinkedHashMap<>();
+ this.operationalOriginal = new LinkedHashMap<>();
- this.configurationCreated = new ConcurrentHashMap<>();
- this.operationalCreated = new ConcurrentHashMap<>();
+ this.configurationCreated = new LinkedHashMap<>();
+ this.operationalCreated = new LinkedHashMap<>();
unmodifiable_configurationOriginal = Collections.unmodifiableMap(configurationOriginal);
unmodifiable_operationalOriginal = Collections.unmodifiableMap(operationalOriginal);
}
@Override
- public final void putConfigurationData(P path, D data) {
+ public final void putConfigurationData(final P path, final D data) {
checkMutable();
D original = null;
if ((original = getConfigurationOriginal(path)) == null) {
}
@Override
- public final void putOperationalData(P path, D data) {
+ public final void putOperationalData(final P path, final D data) {
checkMutable();
D original = null;
if ((original = getOperationalOriginal(path)) == null) {
}
@Override
- public final void removeOperationalData(P path) {
+ public final void removeOperationalData(final P path) {
checkMutable();
getOperationalOriginal(path);
operationalUpdate.remove(path);
}
@Override
- public final void removeConfigurationData(P path) {
+ public final void removeConfigurationData(final P path) {
checkMutable();
getConfigurationOriginal(path);
configurationUpdate.remove(path);
}
@Override
- public D readOperationalData(P path) {
+ public D readOperationalData(final P path) {
return reader.readOperationalData(path);
}
@Override
- public D readConfigurationData(P path) {
+ public D readConfigurationData(final P path) {
return reader.readConfigurationData(path);
}
- private D getConfigurationOriginal(P path) {
+ private D getConfigurationOriginal(final P path) {
D data = configurationOriginal.get(path);
if (data != null) {
return data;
}
data = reader.readConfigurationData(path);
if (data != null) {
- configurationOriginal.putIfAbsent(path, data);
+ configurationOriginal.put(path, data);
return data;
}
return null;
}
- private D getOperationalOriginal(P path) {
+ private D getOperationalOriginal(final P path) {
D data = operationalOriginal.get(path);
if (data != null) {
return data;
}
data = reader.readOperationalData(path);
if (data != null) {
- operationalOriginal.putIfAbsent(path, data);
+ operationalOriginal.put(path, data);
return data;
}
return null;
}
- protected D mergeOperationalData(P path,D stored, D modified) {
+ protected D mergeOperationalData(final P path,final D stored, final D modified) {
return modified;
}
- protected D mergeConfigurationData(P path,D stored, D modified) {
+ protected D mergeConfigurationData(final P path,final D stored, final D modified) {
return modified;
}
}
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.md.sal.common.impl;
-
-import org.opendaylight.yangtools.concepts.Registration;
-
-public abstract class AbstractRegistration<T> implements Registration<T> {
-
-
- private final T instance;
-
- public AbstractRegistration(T instance) {
- super();
- this.instance = instance;
- }
-
- @Override
- public final T getInstance() {
- return instance;
- }
-
-}
package org.opendaylight.controller.md.sal.common.impl;
import org.opendaylight.controller.md.sal.common.api.routing.RoutedRegistration;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
import org.opendaylight.yangtools.concepts.Path;
-public abstract class AbstractRoutedRegistration<C, P extends Path<P>, S> extends AbstractRegistration<S> implements
+public abstract class AbstractRoutedRegistration<C, P extends Path<P>, S> extends AbstractObjectRegistration<S> implements
RoutedRegistration<C, P, S> {
public AbstractRoutedRegistration(S instance) {
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
-final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChangeEvent<P,D> {
+public final class ImmutableDataChangeEvent<P extends Path<P>, D> implements DataChangeEvent<P,D> {
private final D updatedOperationalSubtree;
private final Map<P, D> updatedOperational;
private final Map<P, D> createdConfiguration;
- public ImmutableDataChangeEvent(Builder<P, D> builder) {
+ private ImmutableDataChangeEvent(final Builder<P, D> builder) {
createdConfiguration = builder.getCreatedConfiguration().build();
createdOperational = builder.getCreatedOperational().build();
return updatedOperationalSubtree;
}
- static final <P extends Path<P>,D> Builder<P, D> builder() {
+ public static final <P extends Path<P>,D> Builder<P, D> builder() {
return new Builder<>();
}
- static final class Builder<P extends Path<P>,D> {
+ public static final class Builder<P extends Path<P>,D> {
private D updatedOperationalSubtree;
private D originalOperationalSubtree;
private final ImmutableMap.Builder<P, D> createdConfiguration = ImmutableMap.builder();
- protected Builder<P,D> addTransaction(DataModification<P, D> data, Predicate<P> keyFilter) {
+
+
+
+ protected Builder<P,D> addTransaction(final DataModification<P, D> data, final Predicate<P> keyFilter) {
updatedOperational.putAll(Maps.filterKeys(data.getUpdatedOperationalData(), keyFilter));
updatedConfiguration.putAll(Maps.filterKeys(data.getUpdatedConfigurationData(), keyFilter));
originalConfiguration.putAll(Maps.filterKeys(data.getOriginalConfigurationData(), keyFilter));
return this;
}
- protected Builder<P, D> addConfigurationChangeSet(RootedChangeSet<P, D> changeSet) {
+ protected Builder<P, D> addConfigurationChangeSet(final RootedChangeSet<P, D> changeSet) {
if(changeSet == null) {
return this;
}
return this;
}
- protected Builder<P, D> addOperationalChangeSet(RootedChangeSet<P, D> changeSet) {
+ protected Builder<P, D> addOperationalChangeSet(final RootedChangeSet<P, D> changeSet) {
if(changeSet == null) {
return this;
}
return this;
}
- protected ImmutableDataChangeEvent<P, D> build() {
+ public ImmutableDataChangeEvent<P, D> build() {
return new ImmutableDataChangeEvent<P,D>(this);
}
return updatedOperationalSubtree;
}
- protected Builder<P, D> setUpdatedOperationalSubtree(D updatedOperationalSubtree) {
+ public Builder<P, D> setUpdatedOperationalSubtree(final D updatedOperationalSubtree) {
this.updatedOperationalSubtree = updatedOperationalSubtree;
return this;
}
return originalOperationalSubtree;
}
- protected Builder<P,D> setOriginalOperationalSubtree(D originalOperationalSubtree) {
+ public Builder<P,D> setOriginalOperationalSubtree(final D originalOperationalSubtree) {
this.originalOperationalSubtree = originalOperationalSubtree;
return this;
}
return originalConfigurationSubtree;
}
- protected Builder<P, D> setOriginalConfigurationSubtree(D originalConfigurationSubtree) {
+ public Builder<P, D> setOriginalConfigurationSubtree(final D originalConfigurationSubtree) {
this.originalConfigurationSubtree = originalConfigurationSubtree;
return this;
}
return updatedConfigurationSubtree;
}
- protected Builder<P,D> setUpdatedConfigurationSubtree(D updatedConfigurationSubtree) {
+ public Builder<P,D> setUpdatedConfigurationSubtree(final D updatedConfigurationSubtree) {
this.updatedConfigurationSubtree = updatedConfigurationSubtree;
return this;
}
protected ImmutableMap.Builder<P, D> getCreatedConfiguration() {
return createdConfiguration;
}
+
+ public Builder<P,D> putOriginalOperational(final Map<? extends P, ? extends D> originalData) {
+ originalOperational.putAll(originalData);
+ return this;
+ }
+
+ public Builder<P,D> putCreatedOperational(final Map<? extends P, ? extends D> originalData) {
+ createdOperational.putAll(originalData);
+ return this;
+ }
+
+ public Builder<P,D> putUpdatedOperational(final Map<? extends P, ? extends D> originalData) {
+ updatedOperational.putAll(originalData);
+ return this;
+ }
+
+ public Builder<P,D> putRemovedOperational(final Set<? extends P> originalData) {
+ removedOperational.addAll(originalData);
+ return this;
+ }
}
}
--- /dev/null
+package org.opendaylight.controller.md.sal.common.impl.util.compat;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public abstract class DataNormalizationOperation<T extends PathArgument> implements Identifiable<T> {
+
+ private final T identifier;
+
+ @Override
+ public T getIdentifier() {
+ return identifier;
+ };
+
+ protected DataNormalizationOperation(final T identifier) {
+ super();
+ this.identifier = identifier;
+ }
+
+ public boolean isMixin() {
+ return false;
+ }
+
+
+ public boolean isKeyedEntry() {
+ return false;
+ }
+
+ protected Set<QName> getQNameIdentifiers() {
+ return Collections.singleton(identifier.getNodeType());
+ }
+
+ public abstract DataNormalizationOperation<?> getChild(final PathArgument child);
+
+ public abstract DataNormalizationOperation<?> getChild(QName child);
+
+ public abstract NormalizedNode<?, ?> normalize(Node<?> legacyData);
+
+ private static abstract class SimpleTypeNormalization<T extends PathArgument> extends DataNormalizationOperation<T> {
+
+ protected SimpleTypeNormalization(final T identifier) {
+ super(identifier);
+ }
+
+ @Override
+ public NormalizedNode<?, ?> normalize(final Node<?> legacyData) {
+ checkArgument(legacyData != null);
+ checkArgument(legacyData instanceof SimpleNode<?>);
+ return normalizeImpl((SimpleNode<?>) legacyData);
+ }
+
+ protected abstract NormalizedNode<?, ?> normalizeImpl(SimpleNode<?> node);
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final PathArgument child) {
+ return null;
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final QName child) {
+ return null;
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ }
+
+ private static final class LeafNormalization extends SimpleTypeNormalization<NodeIdentifier> {
+
+ protected LeafNormalization(final NodeIdentifier identifier) {
+ super(identifier);
+ }
+
+ @Override
+ protected NormalizedNode<?, ?> normalizeImpl(final SimpleNode<?> node) {
+ return ImmutableNodes.leafNode(node.getNodeType(), node.getValue());
+ }
+
+ }
+
+ private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> {
+
+ public LeafListEntryNormalization(final LeafListSchemaNode potential) {
+ super(new NodeWithValue(potential.getQName(), null));
+ }
+
+ @Override
+ protected NormalizedNode<?, ?> normalizeImpl(final SimpleNode<?> node) {
+ NodeWithValue nodeId = new NodeWithValue(node.getNodeType(), node.getValue());
+ return Builders.leafSetEntryBuilder().withNodeIdentifier(nodeId).withValue(node.getValue()).build();
+ }
+
+
+ @Override
+ public boolean isKeyedEntry() {
+ return true;
+ }
+ }
+
+ private static abstract class CompositeNodeNormalizationOpertation<T extends PathArgument> extends
+ DataNormalizationOperation<T> {
+
+ protected CompositeNodeNormalizationOpertation(final T identifier) {
+ super(identifier);
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Override
+ public final NormalizedNodeContainer<?, ?, ?> normalize(final Node<?> legacyData) {
+ checkArgument(legacyData != null);
+ if (!isMixin() && getIdentifier().getNodeType() != null) {
+ checkArgument(getIdentifier().getNodeType().equals(legacyData.getNodeType()),
+ "Node QName must be %s was %s", getIdentifier().getNodeType(), legacyData.getNodeType());
+ }
+ checkArgument(legacyData instanceof CompositeNode, "Node %s should be composite", legacyData);
+ CompositeNode compositeNode = (CompositeNode) legacyData;
+ NormalizedNodeContainerBuilder builder = createBuilder(compositeNode);
+
+ Set<DataNormalizationOperation<?>> usedMixins = new HashSet<>();
+ for (Node<?> childLegacy : compositeNode.getValue()) {
+ DataNormalizationOperation childOp = getChild(childLegacy.getNodeType());
+
+ // We skip unknown nodes if this node is mixin since
+ // it's nodes and parent nodes are interleaved
+ if (childOp == null && isMixin()) {
+ continue;
+ }
+
+ checkArgument(childOp != null, "Node %s is not allowed inside %s", childLegacy.getNodeType(),
+ getIdentifier());
+ if (childOp.isMixin()) {
+ if (usedMixins.contains(childOp)) {
+ // We already run / processed that mixin, so to avoid
+ // dupliciry we are
+ // skiping next nodes.
+ continue;
+ }
+ builder.addChild(childOp.normalize(compositeNode));
+ usedMixins.add(childOp);
+ } else {
+ builder.addChild(childOp.normalize(childLegacy));
+ }
+ }
+ return (NormalizedNodeContainer<?, ?, ?>) builder.build();
+ }
+
+ @SuppressWarnings("rawtypes")
+ protected abstract NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode);
+
+ }
+
+ private static abstract class DataContainerNormalizationOperation<T extends PathArgument> extends
+ CompositeNodeNormalizationOpertation<T> {
+
+ private final DataNodeContainer schema;
+ private final Map<QName, DataNormalizationOperation<?>> byQName;
+ private final Map<PathArgument, DataNormalizationOperation<?>> byArg;
+
+ protected DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) {
+ super(identifier);
+ this.schema = schema;
+ this.byArg = new ConcurrentHashMap<>();
+ this.byQName = new ConcurrentHashMap<>();
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final PathArgument child) {
+ DataNormalizationOperation<?> potential = byArg.get(child);
+ if (potential != null) {
+ return potential;
+ }
+ potential = fromSchema(schema, child);
+ return register(potential);
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final QName child) {
+ DataNormalizationOperation<?> potential = byQName.get(child);
+ if (potential != null) {
+ return potential;
+ }
+ potential = fromSchemaAndPathArgument(schema, child);
+ return register(potential);
+ }
+
+ private DataNormalizationOperation<?> register(final DataNormalizationOperation<?> potential) {
+ if (potential != null) {
+ byArg.put(potential.getIdentifier(), potential);
+ for (QName qName : potential.getQNameIdentifiers()) {
+ byQName.put(qName, potential);
+ }
+ }
+ return potential;
+ }
+
+ }
+
+ private static final class ListItemNormalization extends
+ DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
+
+ private final List<QName> keyDefinition;
+
+ protected ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
+ super(identifier, schema);
+ keyDefinition = schema.getKeyDefinition();
+ }
+
+ @Override
+ protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+ ImmutableMap.Builder<QName, Object> keys = ImmutableMap.builder();
+ for (QName key : keyDefinition) {
+
+ SimpleNode<?> valueNode = checkNotNull(compositeNode.getFirstSimpleByName(key),
+ "List node %s MUST contain leaf %s with value.", getIdentifier().getNodeType(), key);
+ keys.put(key, valueNode.getValue());
+ }
+
+ return Builders.mapEntryBuilder().withNodeIdentifier(
+ new NodeIdentifierWithPredicates(getIdentifier().getNodeType(), keys.build()));
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
+ .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg);
+ for (Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg).getKeyValues().entrySet()) {
+ builder.addChild(Builders.leafBuilder()
+ //
+ .withNodeIdentifier(new NodeIdentifier(keyValue.getKey())).withValue(keyValue.getValue())
+ .build());
+ }
+ return builder.build();
+ }
+
+
+ @Override
+ public boolean isKeyedEntry() {
+ return true;
+ }
+ }
+
+ private static final class ContainerNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
+
+ protected ContainerNormalization(final ContainerSchemaNode schema) {
+ super(new NodeIdentifier(schema.getQName()), schema);
+ }
+
+ @Override
+ protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+ return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.containerBuilder().withNodeIdentifier((NodeIdentifier) currentArg).build();
+ }
+
+ }
+
+ private static abstract class MixinNormalizationOp<T extends PathArgument> extends
+ CompositeNodeNormalizationOpertation<T> {
+
+ protected MixinNormalizationOp(final T identifier) {
+ super(identifier);
+ }
+
+ @Override
+ public final boolean isMixin() {
+ return true;
+ }
+
+ }
+
+ private static final class LeafListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+ private final DataNormalizationOperation<?> innerOp;
+
+ public LeafListMixinNormalization(final LeafListSchemaNode potential) {
+ super(new NodeIdentifier(potential.getQName()));
+ innerOp = new LeafListEntryNormalization(potential);
+ }
+
+ @Override
+ protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+ return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier()).build();
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final PathArgument child) {
+ if (child instanceof NodeWithValue) {
+ return innerOp;
+ }
+ return null;
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final QName child) {
+ if (getIdentifier().getNodeType().equals(child)) {
+ return innerOp;
+ }
+ return null;
+ }
+ }
+
+ private static final class AugmentationNormalization extends MixinNormalizationOp<AugmentationIdentifier> {
+
+ private final Map<QName, DataNormalizationOperation<?>> byQName;
+ private final Map<PathArgument, DataNormalizationOperation<?>> byArg;
+
+ public AugmentationNormalization(final AugmentationSchema augmentation, final DataNodeContainer schema) {
+ super(augmentationIdentifierFrom(augmentation));
+
+ ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder();
+ ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder = ImmutableMap.builder();
+
+ for (DataSchemaNode augNode : augmentation.getChildNodes()) {
+ DataSchemaNode resolvedNode = schema.getDataChildByName(augNode.getQName());
+ DataNormalizationOperation<?> resolvedOp = fromDataSchemaNode(resolvedNode);
+ byArgBuilder.put(resolvedOp.getIdentifier(), resolvedOp);
+ for (QName resQName : resolvedOp.getQNameIdentifiers()) {
+ byQNameBuilder.put(resQName, resolvedOp);
+ }
+ }
+ byQName = byQNameBuilder.build();
+ byArg = byArgBuilder.build();
+
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final PathArgument child) {
+ return byArg.get(child);
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final QName child) {
+ return byQName.get(child);
+ }
+
+ @Override
+ protected Set<QName> getQNameIdentifiers() {
+ return getIdentifier().getPossibleChildNames();
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+ return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier()).build();
+ }
+
+ }
+
+ private static final class ListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+ private final ListItemNormalization innerNode;
+
+ public ListMixinNormalization(final ListSchemaNode list) {
+ super(new NodeIdentifier(list.getQName()));
+ this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(),
+ Collections.<QName, Object> emptyMap()), list);
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+ return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.mapBuilder().withNodeIdentifier(getIdentifier()).build();
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final PathArgument child) {
+ if (child.getNodeType().equals(getIdentifier().getNodeType())) {
+ return innerNode;
+ }
+ return null;
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final QName child) {
+ if (getIdentifier().getNodeType().equals(child)) {
+ return innerNode;
+ }
+ return null;
+ }
+
+ }
+
+ private static class ChoiceNodeNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+ private final ImmutableMap<QName, DataNormalizationOperation<?>> byQName;
+ private final ImmutableMap<PathArgument, DataNormalizationOperation<?>> byArg;
+
+ protected ChoiceNodeNormalization(final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) {
+ super(new NodeIdentifier(schema.getQName()));
+ ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder();
+ ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder = ImmutableMap.builder();
+
+ for (ChoiceCaseNode caze : schema.getCases()) {
+ for (DataSchemaNode cazeChild : caze.getChildNodes()) {
+ DataNormalizationOperation<?> childOp = fromDataSchemaNode(cazeChild);
+ byArgBuilder.put(childOp.getIdentifier(), childOp);
+ for (QName qname : childOp.getQNameIdentifiers()) {
+ byQNameBuilder.put(qname, childOp);
+ }
+ }
+ }
+ byQName = byQNameBuilder.build();
+ byArg = byArgBuilder.build();
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final PathArgument child) {
+ return byArg.get(child);
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final QName child) {
+ return byQName.get(child);
+ }
+
+ @Override
+ protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+ return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.choiceBuilder().withNodeIdentifier(getIdentifier()).build();
+ }
+ }
+
+ public static DataNormalizationOperation<?> fromSchemaAndPathArgument(final DataNodeContainer schema,
+ final QName child) {
+ DataSchemaNode potential = schema.getDataChildByName(child);
+ if (potential == null) {
+ Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices = FluentIterable.from(
+ schema.getChildNodes()).filter(org.opendaylight.yangtools.yang.model.api.ChoiceNode.class);
+ potential = findChoice(choices, child);
+ }
+ checkArgument(potential != null, "Supplied QName %s is not valid according to schema %s", child, schema);
+ if ((schema instanceof DataSchemaNode) && !((DataSchemaNode) schema).isAugmenting() && potential.isAugmenting()) {
+ return fromAugmentation(schema, (AugmentationTarget) schema, potential);
+ }
+ return fromDataSchemaNode(potential);
+ }
+
+ private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice(
+ final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices, final QName child) {
+ org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null;
+ choiceLoop: for (org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) {
+ for (ChoiceCaseNode caze : choice.getCases()) {
+ if (caze.getDataChildByName(child) != null) {
+ foundChoice = choice;
+ break choiceLoop;
+ }
+ }
+ }
+ return foundChoice;
+ }
+
+ public static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) {
+ ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
+ for (DataSchemaNode child : augmentation.getChildNodes()) {
+ potentialChildren.add(child.getQName());
+ }
+ return new AugmentationIdentifier(null, potentialChildren.build());
+ }
+
+ private static AugmentationNormalization fromAugmentation(final DataNodeContainer schema,
+ final AugmentationTarget augments, final DataSchemaNode potential) {
+ AugmentationSchema augmentation = null;
+ for (AugmentationSchema aug : augments.getAvailableAugmentations()) {
+ DataSchemaNode child = aug.getDataChildByName(potential.getQName());
+ if (child != null) {
+ augmentation = aug;
+ break;
+ }
+
+ }
+ if (augmentation != null) {
+ return new AugmentationNormalization(augmentation, schema);
+ } else {
+ return null;
+ }
+ }
+
+ private static DataNormalizationOperation<?> fromSchema(final DataNodeContainer schema, final PathArgument child) {
+ if (child instanceof AugmentationIdentifier) {
+ return fromSchemaAndPathArgument(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
+ .iterator().next());
+ }
+ return fromSchemaAndPathArgument(schema, child.getNodeType());
+ }
+
+ public static DataNormalizationOperation<?> fromDataSchemaNode(final DataSchemaNode potential) {
+ if (potential instanceof ContainerSchemaNode) {
+ return new ContainerNormalization((ContainerSchemaNode) potential);
+ } else if (potential instanceof ListSchemaNode) {
+ return new ListMixinNormalization((ListSchemaNode) potential);
+ } else if (potential instanceof LeafSchemaNode) {
+ return new LeafNormalization(new NodeIdentifier(potential.getQName()));
+ } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
+ return new ChoiceNodeNormalization((org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential);
+ } else if (potential instanceof LeafListSchemaNode) {
+ return new LeafListMixinNormalization((LeafListSchemaNode) potential);
+ }
+ return null;
+ }
+
+ public static DataNormalizationOperation<?> from(final SchemaContext ctx) {
+ return new ContainerNormalization(ctx);
+ }
+
+ public abstract NormalizedNode<?, ?> createDefault(PathArgument currentArg);
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.common.impl.util.compat;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MixinNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class DataNormalizer {
+
+ private final DataNormalizationOperation<?> operation;
+
+ public DataNormalizer(final SchemaContext ctx) {
+ operation = DataNormalizationOperation.from(ctx);
+ }
+
+ public InstanceIdentifier toNormalized(final InstanceIdentifier legacy) {
+ ImmutableList.Builder<PathArgument> normalizedArgs = ImmutableList.builder();
+
+ DataNormalizationOperation<?> currentOp = operation;
+ Iterator<PathArgument> arguments = legacy.getPath().iterator();
+ while ( arguments.hasNext() ) {
+ PathArgument legacyArg = arguments.next();
+ currentOp = currentOp.getChild(legacyArg);
+ checkArgument(currentOp != null, "Legacy Instance Identifier %s is not correct. Normalized Instance Identifier so far %s",legacy,normalizedArgs.build());
+ while (currentOp.isMixin()) {
+ normalizedArgs.add(currentOp.getIdentifier());
+ currentOp = currentOp.getChild(legacyArg.getNodeType());
+ }
+ if(arguments.hasNext() || (!currentOp.isKeyedEntry() || legacyArg instanceof NodeIdentifierWithPredicates || legacyArg instanceof NodeWithValue)) {
+ normalizedArgs.add(legacyArg);
+ }
+ }
+ return new InstanceIdentifier(normalizedArgs.build());
+ }
+
+ public Map.Entry<InstanceIdentifier,NormalizedNode<?, ?>> toNormalized(final Map.Entry<InstanceIdentifier,CompositeNode> legacy) {
+ return toNormalized(legacy.getKey(), legacy.getValue());
+ }
+
+ public Map.Entry<InstanceIdentifier,NormalizedNode<?, ?>> toNormalized(final InstanceIdentifier legacyPath, final CompositeNode legacyData) {
+
+ InstanceIdentifier normalizedPath = toNormalized(legacyPath);
+
+ DataNormalizationOperation<?> currentOp = operation;
+ for (PathArgument arg : normalizedPath.getPath()) {
+ currentOp = currentOp.getChild(arg);
+ }
+ // Write Augmentaiton data resolution
+ if (legacyData.getChildren().size() == 1) {
+ DataNormalizationOperation<?> potentialOp = currentOp.getChild(legacyData.getChildren().get(0)
+ .getNodeType());
+ if(potentialOp.getIdentifier() instanceof AugmentationIdentifier) {
+ currentOp = potentialOp;
+ ArrayList<PathArgument> reworkedArgs = new ArrayList<>(normalizedPath.getPath());
+ reworkedArgs.add(potentialOp.getIdentifier());
+ normalizedPath = new InstanceIdentifier(reworkedArgs);
+ }
+ }
+
+ Preconditions.checkArgument(currentOp != null,
+ "Instance Identifier %s does not reference correct schema Node.", normalizedPath);
+ return new AbstractMap.SimpleEntry<InstanceIdentifier,NormalizedNode<?, ?>>(normalizedPath,currentOp.normalize(legacyData));
+ }
+
+ public InstanceIdentifier toLegacy(final InstanceIdentifier normalized) {
+ ImmutableList.Builder<PathArgument> legacyArgs = ImmutableList.builder();
+ PathArgument previous = null;
+ for (PathArgument normalizedArg : normalized.getPath()) {
+ if (normalizedArg instanceof NodeIdentifier) {
+ if (previous != null) {
+ legacyArgs.add(previous);
+ }
+ previous = normalizedArg;
+ } else if (normalizedArg instanceof NodeIdentifierWithPredicates) {
+ // We skip previous node, which was mixin.
+ previous = normalizedArg;
+ } else if (normalizedArg instanceof AugmentationIdentifier) {
+ // We ignore argument
+ }
+ // FIXME : Add option for reading choice
+ }
+ if (previous != null) {
+ legacyArgs.add(previous);
+ }
+ return new InstanceIdentifier(legacyArgs.build());
+ }
+
+ public CompositeNode toLegacy(final InstanceIdentifier normalizedPath, final NormalizedNode<?, ?> normalizedData) {
+ // Preconditions.checkArgument(normalizedData instanceof
+ // DataContainerNode<?>,"Node object %s, %s should be of type DataContainerNode",normalizedPath,normalizedData);
+ if (normalizedData instanceof DataContainerNode<?>) {
+ return toLegacyFromDataContainer((DataContainerNode<?>) normalizedData);
+ }
+ return null;
+ }
+
+ public static Node<?> toLegacy(final NormalizedNode<?, ?> node) {
+ if (node instanceof MixinNode) {
+ /**
+ * Direct reading of MixinNodes is not supported,
+ * since it is not possible in legacy APIs create pointer
+ * to Mixin Nodes.
+ *
+ */
+ return null;
+ }
+
+ if (node instanceof DataContainerNode<?>) {
+ return toLegacyFromDataContainer((DataContainerNode<?>) node);
+ }
+ return toLegacySimple(node);
+
+ }
+
+ private static SimpleNode<?> toLegacySimple(final NormalizedNode<?, ?> node) {
+ return new SimpleNodeTOImpl<Object>(node.getNodeType(), null, node.getValue());
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private static CompositeNode toLegacyFromDataContainer(final DataContainerNode<?> node) {
+ CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
+ builder.setQName(node.getNodeType());
+ for (NormalizedNode<?, ?> child : node.getValue()) {
+ if (child instanceof MixinNode && child instanceof NormalizedNodeContainer<?, ?, ?>) {
+ builder.addAll(toLegacyNodesFromMixin((NormalizedNodeContainer) child));
+ } else {
+ addToBuilder(builder, toLegacy(child));
+ }
+ }
+ return builder.toInstance();
+ }
+
+ private static void addToBuilder(final CompositeNodeBuilder<ImmutableCompositeNode> builder, final Node<?> legacy) {
+ if (legacy != null) {
+ builder.add(legacy);
+ }
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private static Iterable<Node<?>> toLegacyNodesFromMixin(
+ final NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> mixin) {
+ ArrayList<Node<?>> ret = new ArrayList<>();
+ for (NormalizedNode<?, ?> child : mixin.getValue()) {
+ if(child instanceof MixinNode && child instanceof NormalizedNodeContainer<?, ?, ?>) {
+ Iterables.addAll(ret,toLegacyNodesFromMixin((NormalizedNodeContainer) child));
+ } else {
+ ret.add(toLegacy(child));
+ }
+ }
+ return FluentIterable.from(ret).filter(new Predicate<Node<?>>() {
+
+ @Override
+ public boolean apply(final Node<?> input) {
+ return input != null;
+ }
+ });
+ }
+
+ public DataNormalizationOperation<?> getRootOperation() {
+ return operation;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataBroker extends AsyncDataBroker<InstanceIdentifier, NormalizedNode<?, ?>, DOMDataChangeListener>, BrokerService {
+ @Override
+ DOMDataReadTransaction newReadOnlyTransaction();
+
+ @Override
+ DOMDataReadWriteTransaction newReadWriteTransaction();
+
+ @Override
+ DOMDataWriteTransaction newWriteOnlyTransaction();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataChangeListener extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataReadTransaction extends AsyncReadTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataReadWriteTransaction extends DOMDataReadTransaction, DOMDataWriteTransaction, AsyncReadWriteTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataWriteTransaction extends AsyncWriteTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
import org.opendaylight.controller.sal.core.api.notify.NotificationService;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(RpcRegistrationListener listener);
}
- public interface RpcRegistration extends Registration<RpcImplementation> {
+ public interface RpcRegistration extends ObjectRegistration<RpcImplementation> {
QName getType();
@Override
package org.opendaylight.controller.sal.core.api.mount;
-import java.util.concurrent.Future;
-
import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
import org.opendaylight.controller.sal.core.api.notify.NotificationService;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Interface representing a single mount instance and represents a way for
+ * clients to access underlying data, RPCs and notifications.
+ */
public interface MountInstance extends //
NotificationService, //
DataBrokerService {
- Future<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input);
+ /**
+ * Invoke an RPC on the system underlying the mount instance.
+ *
+ * @param type RPC type
+ * @param input RPC input arguments
+ * @return Future representing execution of the RPC.
+ */
+ ListenableFuture<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input);
+ /**
+ * Get {@link SchemaContext} of the system underlying the mount instance.
+ *
+ * @return A schema context.
+ */
SchemaContext getSchemaContext();
}
@Override
public MountProvisionInstance getMountPoint(InstanceIdentifier path);
-
+
MountProvisionInstance createMountPoint(InstanceIdentifier path);
-
+
MountProvisionInstance createOrGetMountPoint(InstanceIdentifier path);
-
+
ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener);
-
- public interface MountProvisionListener extends EventListener {
-
+
+ public interface MountProvisionListener extends EventListener {
+
void onMountPointCreated(InstanceIdentifier path);
-
+
void onMountPointRemoved(InstanceIdentifier path);
-
+
}
}
import org.opendaylight.controller.sal.core.api.BrokerService;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-
+/**
+ * Client-level interface for interacting with mount points. It provides access
+ * to {@link MountInstance} instances based on their path.
+ */
public interface MountService extends BrokerService {
-
+ /**
+ * Obtain access to a mount instance registered at the specified path.
+ *
+ * @param path Path at which the instance is registered
+ * @return Reference to the instance, or null if no such instance exists.
+ */
MountInstance getMountPoint(InstanceIdentifier path);
}
base "config:service-type";
config:java-class "org.opendaylight.controller.sal.core.api.data.DataProviderService";
}
+
+ identity dom-async-data-broker {
+ base "config:service-type";
+ config:java-class "org.opendaylight.controller.md.sal.dom.api.DOMDataBroker";
+ }
identity dom-data-store {
base "config:service-type";
</scm>
<dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-core-api</artifactId>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>${slf4j.version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<Private-Package>
org.opendaylight.controller.sal.dom.broker,
org.opendaylight.controller.sal.dom.broker.impl,
+ org.opendaylight.controller.sal.dom.broker.impl.*,
org.opendaylight.controller.sal.dom.broker.osgi,
org.opendaylight.controller.sal.dom.broker.util,
org.opendaylight.controller.config.yang.md.sal.dom.impl,
org.opendaylight.controller.config.yang.md.sal.dom.statistics,
+ org.opendaylight.controller.md.sal.dom.broker.impl,
+ org.opendaylight.controller.md.sal.dom.broker.impl.*,
+ org.opendaylight.controller.md.sal.dom.store.impl,
+ org.opendaylight.controller.md.sal.dom.store.impl.*,
org.opendaylight.yangtools.yang.util,
org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.dom.impl.rev131028.*
</Private-Package>
*/
package org.opendaylight.controller.config.yang.md.sal.dom.impl;
-import org.opendaylight.controller.config.yang.md.sal.dom.statistics.DomBrokerRuntimeMXBeanImpl;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.sal.core.api.data.DataStore;
import org.opendaylight.controller.sal.dom.broker.BrokerConfigActivator;
import org.opendaylight.controller.sal.dom.broker.BrokerImpl;
import org.osgi.framework.BundleContext;
-import static com.google.common.base.Preconditions.*;
-
/**
*
*/
private BundleContext bundleContext;
- public DomBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ public DomBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
}
- public DomBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, DomBrokerImplModule oldModule, java.lang.AutoCloseable oldInstance) {
+ public DomBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final DomBrokerImplModule oldModule, final java.lang.AutoCloseable oldInstance) {
super(identifier, dependencyResolver, oldModule, oldInstance);
}
@Override
public void validate(){
super.validate();
- checkArgument(getDataStore() != null, "Data Store needs to be provided for DomBroker");
}
-
+
@Override
public java.lang.AutoCloseable createInstance() {
final BrokerImpl broker = new BrokerImpl();
final BrokerConfigActivator activator = new BrokerConfigActivator();
final DataStore store = getDataStoreDependency();
- activator.start(broker, store, getBundleContext());
-
- final DomBrokerImplRuntimeMXBean domBrokerRuntimeMXBean = new DomBrokerRuntimeMXBeanImpl(activator.getDataService());
- getRootRuntimeBeanRegistratorWrapper().register(domBrokerRuntimeMXBean);
+ final DOMDataBroker asyncBroker= getAsyncDataBrokerDependency();
+
+ activator.start(broker, store, asyncBroker,getBundleContext());
+
+// final DomBrokerImplRuntimeMXBean domBrokerRuntimeMXBean = new DomBrokerRuntimeMXBeanImpl(activator.getDataService());
+// getRootRuntimeBeanRegistratorWrapper().register(domBrokerRuntimeMXBean);
return broker;
}
return this.bundleContext;
}
- public void setBundleContext(BundleContext bundleContext) {
+ public void setBundleContext(final BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
}
--- /dev/null
+/**
+ * Generated file
+
+ * Generated from: yang module name: opendaylight-sal-dom-broker-impl yang module local name: dom-inmemory-data-broker
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Fri Mar 28 17:32:48 CET 2014
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.md.sal.dom.impl;
+
+import java.util.Hashtable;
+import java.util.concurrent.Executors;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMDataBrokerImpl;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.osgi.framework.BundleContext;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+/**
+*
+*/
+public final class DomInmemoryDataBrokerModule extends
+ org.opendaylight.controller.config.yang.md.sal.dom.impl.AbstractDomInmemoryDataBrokerModule {
+
+ private BundleContext bundleContext;
+
+ public DomInmemoryDataBrokerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public DomInmemoryDataBrokerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ final DomInmemoryDataBrokerModule oldModule, final java.lang.AutoCloseable oldInstance) {
+
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ protected void customValidation() {
+ // Add custom validation for module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ ListeningExecutorService storeExecutor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(2));
+ InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("DOM-OPER", storeExecutor);
+ InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("DOM-CFG", storeExecutor);
+ ImmutableMap<LogicalDatastoreType, DOMStore> datastores = ImmutableMap
+ .<LogicalDatastoreType, DOMStore> builder().put(LogicalDatastoreType.OPERATIONAL, operStore)
+ .put(LogicalDatastoreType.CONFIGURATION, configStore).build();
+
+ DOMDataBrokerImpl newDataBroker = new DOMDataBrokerImpl(datastores, MoreExecutors.sameThreadExecutor());
+
+ getBundleContext().registerService(DOMDataBroker.class, newDataBroker, new Hashtable<String, String>());
+
+ getSchemaServiceDependency().registerSchemaServiceListener(operStore);
+ getSchemaServiceDependency().registerSchemaServiceListener(configStore);
+
+ return newDataBroker;
+ }
+
+ private BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ void setBundleContext(final BundleContext ctx) {
+ bundleContext = ctx;
+ }
+}
--- /dev/null
+/**
+* Generated file
+
+* Generated from: yang module name: opendaylight-sal-dom-broker-impl yang module local name: dom-inmemory-data-broker
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Fri Mar 28 17:32:48 CET 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.md.sal.dom.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 DomInmemoryDataBrokerModuleFactory extends org.opendaylight.controller.config.yang.md.sal.dom.impl.AbstractDomInmemoryDataBrokerModuleFactory
+{
+
+
+
+ @Override
+ public Module createModule(final String instanceName, final DependencyResolver dependencyResolver, final BundleContext bundleContext) {
+ DomInmemoryDataBrokerModule module = (DomInmemoryDataBrokerModule) super.createModule(instanceName, dependencyResolver, bundleContext);
+ module.setBundleContext(bundleContext);
+ return module;
+ }
+
+ @Override
+ public Module createModule(final String instanceName, final DependencyResolver dependencyResolver,
+ final DynamicMBeanWithInstance old, final BundleContext bundleContext) throws Exception {
+ DomInmemoryDataBrokerModule module = (DomInmemoryDataBrokerModule) super.createModule(instanceName, dependencyResolver, old, bundleContext);
+ module.setBundleContext(bundleContext);
+ return module;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+public class DOMDataBrokerImpl implements DOMDataBroker, AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DOMDataBrokerImpl.class);
+ private static final Logger COORDINATOR_LOG = LoggerFactory.getLogger(CommitCoordination.class);
+ private final ImmutableMap<LogicalDatastoreType, DOMStore> datastores;
+ private final ListeningExecutorService executor;
+ private final AtomicLong txNum = new AtomicLong();
+
+ public DOMDataBrokerImpl(final ImmutableMap<LogicalDatastoreType, DOMStore> datastores,
+ final ListeningExecutorService executor) {
+ super();
+ this.datastores = datastores;
+ this.executor = executor;
+ }
+
+ private static final Function<Iterable<Boolean>, Boolean> AND_FUNCTION = new Function<Iterable<Boolean>, Boolean>() {
+
+ @Override
+ public Boolean apply(final Iterable<Boolean> input) {
+
+ for (Boolean value : input) {
+ if (value == false) {
+ return Boolean.FALSE;
+ }
+ }
+ return Boolean.TRUE;
+ }
+ };
+
+ @Override
+ public DOMDataReadTransaction newReadOnlyTransaction() {
+ ImmutableMap.Builder<LogicalDatastoreType, DOMStoreReadTransaction> builder = ImmutableMap.builder();
+ for (Entry<LogicalDatastoreType, DOMStore> store : datastores.entrySet()) {
+ builder.put(store.getKey(), store.getValue().newReadOnlyTransaction());
+ }
+ return new ReadOnlyTransactionImpl(newTransactionIdentifier(), builder.build());
+ }
+
+ private Object newTransactionIdentifier() {
+ return "DOM-" + txNum.getAndIncrement();
+ }
+
+ @Override
+ public DOMDataReadWriteTransaction newReadWriteTransaction() {
+ ImmutableMap.Builder<LogicalDatastoreType, DOMStoreReadWriteTransaction> builder = ImmutableMap.builder();
+ for (Entry<LogicalDatastoreType, DOMStore> store : datastores.entrySet()) {
+ builder.put(store.getKey(), store.getValue().newReadWriteTransaction());
+ }
+ return new ReadWriteTransactionImpl(newTransactionIdentifier(), builder.build(), this);
+ }
+
+ @Override
+ public DOMDataWriteTransaction newWriteOnlyTransaction() {
+ ImmutableMap.Builder<LogicalDatastoreType, DOMStoreWriteTransaction> builder = ImmutableMap.builder();
+ for (Entry<LogicalDatastoreType, DOMStore> store : datastores.entrySet()) {
+ builder.put(store.getKey(), store.getValue().newWriteOnlyTransaction());
+ }
+ return new WriteTransactionImpl<DOMStoreWriteTransaction>(newTransactionIdentifier(), builder.build(), this);
+ }
+
+ @Override
+ public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+ final InstanceIdentifier path, final DOMDataChangeListener listener, final DataChangeScope triggeringScope) {
+
+ DOMStore potentialStore = datastores.get(store);
+ checkState(potentialStore != null, "Requested logical data store is not available.");
+ return potentialStore.registerChangeListener(path, listener, triggeringScope);
+ }
+
+ private ListenableFuture<RpcResult<TransactionStatus>> submit(
+ final WriteTransactionImpl<? extends DOMStoreWriteTransaction> transaction) {
+ LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier());
+ return executor.submit(new CommitCoordination(transaction));
+ }
+
+ private abstract static class AbstractCompositeTransaction<K, T extends DOMStoreTransaction> implements
+ AsyncTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+ private final ImmutableMap<K, T> backingTxs;
+ private final Object identifier;
+
+ protected AbstractCompositeTransaction(final Object identifier, final ImmutableMap<K, T> backingTxs) {
+ this.identifier = checkNotNull(identifier, "Identifier should not be null");
+ this.backingTxs = checkNotNull(backingTxs, "Backing transactions should not be null");
+ }
+
+ protected T getSubtransaction(final K key) {
+ return backingTxs.get(key);
+ }
+
+ public Iterable<T> getSubtransactions() {
+ return backingTxs.values();
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public void close() {
+ try {
+ for (T subtransaction : backingTxs.values()) {
+ subtransaction.close();
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException("Uncaught exception occured during closing transaction.", e);
+ }
+ }
+
+ }
+
+ private static class ReadOnlyTransactionImpl extends
+ AbstractCompositeTransaction<LogicalDatastoreType, DOMStoreReadTransaction> implements
+ DOMDataReadTransaction {
+
+ protected ReadOnlyTransactionImpl(final Object identifier,
+ final ImmutableMap<LogicalDatastoreType, DOMStoreReadTransaction> backingTxs) {
+ super(identifier, backingTxs);
+ }
+
+ @Override
+ public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
+ final InstanceIdentifier path) {
+ return getSubtransaction(store).read(path);
+ }
+
+ }
+
+ private static class WriteTransactionImpl<T extends DOMStoreWriteTransaction> extends
+ AbstractCompositeTransaction<LogicalDatastoreType, T> implements DOMDataWriteTransaction {
+
+ private final DOMDataBrokerImpl broker;
+ private ImmutableList<DOMStoreThreePhaseCommitCohort> cohorts;
+
+ protected WriteTransactionImpl(final Object identifier, final ImmutableMap<LogicalDatastoreType, T> backingTxs,
+ final DOMDataBrokerImpl broker) {
+ super(identifier, backingTxs);
+ this.broker = broker;
+ }
+
+ public Iterable<DOMStoreThreePhaseCommitCohort> ready() {
+ checkState(cohorts == null, "Transaction was already marked as ready.");
+ ImmutableList.Builder<DOMStoreThreePhaseCommitCohort> cohortsBuilder = ImmutableList.builder();
+ for (DOMStoreWriteTransaction subTx : getSubtransactions()) {
+ cohortsBuilder.add(subTx.ready());
+ }
+ cohorts = cohortsBuilder.build();
+ return cohorts;
+ }
+
+ protected ImmutableList<DOMStoreThreePhaseCommitCohort> getCohorts() {
+ return cohorts;
+ }
+
+ @Override
+ public void put(final LogicalDatastoreType store, final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ getSubtransaction(store).write(path, data);
+ }
+
+ @Override
+ public void delete(final LogicalDatastoreType store, final InstanceIdentifier path) {
+ getSubtransaction(store).delete(path);
+ }
+
+ @Override
+ public void merge(final LogicalDatastoreType store, final InstanceIdentifier path,
+ final NormalizedNode<?, ?> data) {
+ // TODO Auto-generated method stub
+ throw new UnsupportedOperationException("Not implemented yet.");
+ }
+
+ @Override
+ public void cancel() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+
+ ready();
+ return broker.submit(this);
+ }
+
+ }
+
+ private static class ReadWriteTransactionImpl extends WriteTransactionImpl<DOMStoreReadWriteTransaction> implements
+ DOMDataReadWriteTransaction {
+
+ protected ReadWriteTransactionImpl(final Object identifier,
+ final ImmutableMap<LogicalDatastoreType, DOMStoreReadWriteTransaction> backingTxs,
+ final DOMDataBrokerImpl broker) {
+ // super(identifier, backingTxs);
+ super(identifier, backingTxs, broker);
+ }
+
+ @Override
+ public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
+ final InstanceIdentifier path) {
+ return getSubtransaction(store).read(path);
+ }
+
+ @Override
+ public void merge(final LogicalDatastoreType store, final InstanceIdentifier path,
+ final NormalizedNode<?, ?> data) {
+
+ }
+ }
+
+ private final class CommitCoordination implements Callable<RpcResult<TransactionStatus>> {
+
+ private final WriteTransactionImpl<? extends DOMStoreWriteTransaction> transaction;
+
+ public CommitCoordination(final WriteTransactionImpl<? extends DOMStoreWriteTransaction> transaction) {
+ this.transaction = transaction;
+ }
+
+ @Override
+ public RpcResult<TransactionStatus> call() throws Exception {
+
+ try {
+ Boolean canCommit = canCommit().get();
+
+ if (canCommit) {
+ try {
+ preCommit().get();
+ try {
+ commit().get();
+ COORDINATOR_LOG.debug("Tx: {} Is commited.", transaction.getIdentifier());
+ return Rpcs.getRpcResult(true, TransactionStatus.COMMITED,
+ Collections.<RpcError> emptySet());
+
+ } catch (InterruptedException | ExecutionException e) {
+ COORDINATOR_LOG.error("Tx: {} Error during commit", transaction.getIdentifier(), e);
+ }
+
+ } catch (InterruptedException | ExecutionException e) {
+ COORDINATOR_LOG.warn("Tx: {} Error during preCommit, starting Abort",
+ transaction.getIdentifier(), e);
+ }
+ } else {
+ COORDINATOR_LOG.info("Tx: {} Did not pass canCommit phase.", transaction.getIdentifier());
+ abort().get();
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ COORDINATOR_LOG.warn("Tx: {} Error during canCommit, starting Abort", transaction.getIdentifier(), e);
+
+ }
+ try {
+ abort().get();
+ } catch (InterruptedException | ExecutionException e) {
+ COORDINATOR_LOG.error("Tx: {} Error during abort", transaction.getIdentifier(), e);
+ }
+ return Rpcs.getRpcResult(false, TransactionStatus.FAILED, Collections.<RpcError> emptySet());
+ }
+
+ public ListenableFuture<Void> preCommit() {
+ COORDINATOR_LOG.debug("Transaction {}: PreCommit Started ", transaction.getIdentifier());
+ Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
+ for (DOMStoreThreePhaseCommitCohort cohort : transaction.getCohorts()) {
+ ops.add(cohort.preCommit());
+ }
+ return (ListenableFuture) Futures.allAsList(ops.build());
+ }
+
+ public ListenableFuture<Void> commit() {
+ COORDINATOR_LOG.debug("Transaction {}: Commit Started ", transaction.getIdentifier());
+ Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
+ for (DOMStoreThreePhaseCommitCohort cohort : transaction.getCohorts()) {
+ ops.add(cohort.commit());
+ }
+ return (ListenableFuture) Futures.allAsList(ops.build());
+ }
+
+ public ListenableFuture<Boolean> canCommit() {
+ COORDINATOR_LOG.debug("Transaction {}: CanCommit Started ", transaction.getIdentifier());
+ Builder<ListenableFuture<Boolean>> canCommitOperations = ImmutableList.builder();
+ for (DOMStoreThreePhaseCommitCohort cohort : transaction.getCohorts()) {
+ canCommitOperations.add(cohort.canCommit());
+ }
+ ListenableFuture<List<Boolean>> allCanCommits = Futures.allAsList(canCommitOperations.build());
+ return Futures.transform(allCanCommits, AND_FUNCTION);
+ }
+
+ public ListenableFuture<Void> abort() {
+ COORDINATOR_LOG.debug("Transaction {}: Abort Started ", transaction.getIdentifier());
+ Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
+ for (DOMStoreThreePhaseCommitCohort cohort : transaction.getCohorts()) {
+ ops.add(cohort.abort());
+ }
+ return (ListenableFuture) Futures.allAsList(ops.build());
+ };
+
+ }
+
+ @Override
+ public void close() throws Exception {
+
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.broker.impl.compat;
+
+import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.sal.common.DataStoreIdentifier;
+import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.core.api.data.DataProviderService;
+import org.opendaylight.controller.sal.core.api.data.DataValidator;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+
+public class BackwardsCompatibleDataBroker implements DataProviderService, SchemaContextListener {
+
+ DOMDataBroker backingBroker;
+ DataNormalizer normalizer;
+ private final ListenerRegistry<DataChangeListener> fakeRegistry = ListenerRegistry.create();
+
+
+ public BackwardsCompatibleDataBroker(final DOMDataBroker newBiDataImpl) {
+ backingBroker = newBiDataImpl;
+ }
+
+ @Override
+ public void onGlobalContextUpdated(final SchemaContext ctx) {
+ normalizer = new DataNormalizer(ctx);
+ }
+
+ @Override
+ public CompositeNode readConfigurationData(final InstanceIdentifier legacyPath) {
+ BackwardsCompatibleTransaction<?> tx = BackwardsCompatibleTransaction.readOnlyTransaction(backingBroker.newReadOnlyTransaction(),normalizer);
+ try {
+ return tx.readConfigurationData(legacyPath);
+ } finally {
+ tx.commit();
+ }
+ }
+
+ @Override
+ public CompositeNode readOperationalData(final InstanceIdentifier legacyPath) {
+ BackwardsCompatibleTransaction<?> tx = BackwardsCompatibleTransaction.readOnlyTransaction(backingBroker.newReadOnlyTransaction(),normalizer);
+ try {
+ return tx.readOperationalData(legacyPath);
+ } finally {
+ tx.commit();
+ }
+ }
+
+ @Override
+ public DataModificationTransaction beginTransaction() {
+ return BackwardsCompatibleTransaction.readWriteTransaction(backingBroker.newReadWriteTransaction(), normalizer);
+ }
+
+ @Override
+ public ListenerRegistration<DataChangeListener> registerDataChangeListener(final InstanceIdentifier path,
+ final DataChangeListener listener) {
+ return fakeRegistry .register(listener);
+ }
+
+ @Override
+ public Registration<DataCommitHandler<InstanceIdentifier, CompositeNode>> registerCommitHandler(
+ final InstanceIdentifier path, final DataCommitHandler<InstanceIdentifier, CompositeNode> commitHandler) {
+ // FIXME Do real forwarding
+ return new AbstractObjectRegistration<DataCommitHandler<InstanceIdentifier,CompositeNode>>(commitHandler) {
+ @Override
+ protected void removeRegistration() {
+ // NOOP
+ }
+ };
+ }
+
+ @Override
+ public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier, CompositeNode>>> registerCommitHandlerListener(
+ final RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier, CompositeNode>> commitHandlerListener) {
+ return null;
+ }
+
+ // Obsolote functionality
+
+ @Override
+ public void addValidator(final DataStoreIdentifier store, final DataValidator validator) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeValidator(final DataStoreIdentifier store, final DataValidator validator) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void addRefresher(final DataStoreIdentifier store, final DataRefresher refresher) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeRefresher(final DataStoreIdentifier store, final DataRefresher refresher) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Registration<DataReader<InstanceIdentifier, CompositeNode>> registerConfigurationReader(
+ final InstanceIdentifier path, final DataReader<InstanceIdentifier, CompositeNode> reader) {
+ throw new UnsupportedOperationException("Data Reader contract is not supported.");
+ }
+
+ @Override
+ public Registration<DataReader<InstanceIdentifier, CompositeNode>> registerOperationalReader(
+ final InstanceIdentifier path, final DataReader<InstanceIdentifier, CompositeNode> reader) {
+ throw new UnsupportedOperationException("Data Reader contract is not supported.");
+ }
+
+ private final class TranslatingListenerInvoker implements DOMDataChangeListener, Delegator<DataChangeListener> {
+
+
+ private DataChangeListener delegate;
+
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> normalizedChange) {
+
+ DataChangeEvent<InstanceIdentifier, CompositeNode> legacyChange = null;
+ delegate.onDataChanged(legacyChange);
+ }
+
+ @Override
+ public DataChangeListener getDelegate() {
+
+ return delegate;
+ }
+
+
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.broker.impl.compat;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+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.Builders;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public abstract class BackwardsCompatibleTransaction<T extends DOMDataReadTransaction> implements
+ DataModificationTransaction, Delegator<T> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(BackwardsCompatibleTransaction.class);
+
+ private final T asyncTx;
+ private final DataNormalizer normalizer;
+
+ protected BackwardsCompatibleTransaction(final T asyncTx, final DataNormalizer normalizer) {
+ super();
+ this.asyncTx = asyncTx;
+ this.normalizer = normalizer;
+ }
+
+ public static BackwardsCompatibleTransaction<?> readOnlyTransaction(final DOMDataReadTransaction readTx,
+ final DataNormalizer normalizer) {
+
+ return new BackwardsCompatibleTransaction<DOMDataReadTransaction>(readTx, normalizer) {
+
+ @Override
+ public TransactionStatus getStatus() {
+ return TransactionStatus.NEW;
+ }
+
+ @Override
+ public Future<RpcResult<TransactionStatus>> commit() {
+ getDelegate().close();
+ return null;
+ }
+ };
+ }
+
+ public static BackwardsCompatibleTransaction<?> readWriteTransaction(final DOMDataReadWriteTransaction rwTx,
+ final DataNormalizer normalizer) {
+ return new ReadWriteTransaction(rwTx, normalizer);
+ }
+
+ protected DataNormalizer getNormalizer() {
+ return normalizer;
+ }
+
+ @Override
+ public T getDelegate() {
+ return asyncTx;
+ };
+
+ @Override
+ public CompositeNode readConfigurationData(final InstanceIdentifier legacyPath) {
+
+ InstanceIdentifier normalizedPath = normalizer.toNormalized(legacyPath);
+
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> normalizedData = asyncTx.read(
+ LogicalDatastoreType.CONFIGURATION, normalizedPath);
+
+ try {
+ return normalizer.toLegacy(normalizedPath, normalizedData.get().orNull());
+ } catch (InterruptedException | ExecutionException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public CompositeNode readOperationalData(final InstanceIdentifier legacyPath) {
+ InstanceIdentifier normalizedPath = normalizer.toNormalized(legacyPath);
+
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> normalizedData = asyncTx.read(
+ LogicalDatastoreType.OPERATIONAL, normalizedPath);
+
+ try {
+ return normalizer.toLegacy(normalizedPath, normalizedData.get().orNull());
+ } catch (InterruptedException | ExecutionException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public ListenerRegistration<DataTransactionListener> registerListener(final DataTransactionListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<InstanceIdentifier, CompositeNode> getCreatedConfigurationData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier, CompositeNode> getCreatedOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier, CompositeNode> getOriginalConfigurationData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier, CompositeNode> getOriginalOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Set<InstanceIdentifier> getRemovedConfigurationData() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set<InstanceIdentifier> getRemovedOperationalData() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Map<InstanceIdentifier, CompositeNode> getUpdatedConfigurationData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier, CompositeNode> getUpdatedOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public void putConfigurationData(final InstanceIdentifier path, final CompositeNode data) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void putOperationalData(final InstanceIdentifier path, final CompositeNode data) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeConfigurationData(final InstanceIdentifier path) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeOperationalData(final InstanceIdentifier path) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return asyncTx.getIdentifier();
+ }
+
+ private static final class ReadWriteTransaction extends BackwardsCompatibleTransaction<DOMDataReadWriteTransaction> {
+
+ private TransactionStatus status = TransactionStatus.NEW;
+
+ protected ReadWriteTransaction(final DOMDataReadWriteTransaction asyncTx, final DataNormalizer normalizer) {
+ super(asyncTx, normalizer);
+ }
+
+ @Override
+ public TransactionStatus getStatus() {
+ return status;
+ }
+
+ @Override
+ public Future<RpcResult<TransactionStatus>> commit() {
+ Preconditions.checkState(status == TransactionStatus.NEW);
+ status = TransactionStatus.SUBMITED;
+ return getDelegate().commit();
+ }
+
+ @Override
+ public void putConfigurationData(final InstanceIdentifier legacyPath, final CompositeNode legacyData) {
+ checkNotNull(legacyPath, "Path MUST NOT be null.");
+ checkNotNull(legacyData, "Data for path %s MUST NOT be null",legacyData);
+ Entry<InstanceIdentifier, NormalizedNode<?, ?>> normalizedData = getNormalizer().toNormalized(legacyPath, legacyData);
+ putWithEnsuredParents(LogicalDatastoreType.CONFIGURATION, normalizedData.getKey(), normalizedData.getValue());
+ }
+
+ @Override
+ public void putOperationalData(final InstanceIdentifier legacyPath, final CompositeNode legacyData) {
+ checkNotNull(legacyPath, "Path MUST NOT be null.");
+ checkNotNull(legacyData, "Data for path %s MUST NOT be null",legacyData);
+ Entry<InstanceIdentifier, NormalizedNode<?, ?>> normalizedData = getNormalizer().toNormalized(legacyPath, legacyData);
+ putWithEnsuredParents(LogicalDatastoreType.OPERATIONAL, normalizedData.getKey(), normalizedData.getValue());
+ }
+
+ private void putWithEnsuredParents(final LogicalDatastoreType store, final InstanceIdentifier normalizedPath,
+ final NormalizedNode<?, ?> normalizedData) {
+
+ LOG.trace("write {}:{} ",store,normalizedPath);
+ try {
+ List<PathArgument> currentArguments = new ArrayList<>();
+ DataNormalizationOperation<?> currentOp = getNormalizer().getRootOperation();
+ Iterator<PathArgument> iterator = normalizedPath.getPath().iterator();
+ while(iterator.hasNext()) {
+ PathArgument currentArg = iterator.next();
+ currentOp = currentOp.getChild(currentArg);
+ currentArguments.add(currentArg);
+ InstanceIdentifier currentPath = new InstanceIdentifier(currentArguments);
+ boolean isPresent = getDelegate().read(store, currentPath).get().isPresent();
+ if(isPresent == false && iterator.hasNext()) {
+ getDelegate().put(store, currentPath, currentOp.createDefault(currentArg));
+ }
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Exception durring read.",e);
+ }
+
+ getDelegate().put(store, normalizedPath, normalizedData);
+ }
+
+ private boolean isAugmentationChild(final InstanceIdentifier normalizedPath) {
+ List<PathArgument> parentArgs = parentPath(normalizedPath).getPath();
+ if(parentArgs.isEmpty()) {
+ return false;
+ }
+ return Iterables.getLast(parentArgs) instanceof AugmentationIdentifier;
+ }
+
+ private void ensureParentNode(final LogicalDatastoreType store, final InstanceIdentifier normalizedPath,
+ final NormalizedNode<?, ?> normalizedData) {
+ InstanceIdentifier parentPath = parentPath(normalizedPath);
+ PathArgument parentType = Iterables.getLast(parentPath.getPath());
+ if(parentType instanceof AugmentationIdentifier) {
+ AugmentationNode node = Builders.augmentationBuilder()
+ .withNodeIdentifier((AugmentationIdentifier) parentType)
+ .build();
+ getDelegate().put(store, parentPath, node);
+ }
+ if(normalizedData instanceof MapEntryNode) {
+ MapNode mapNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(normalizedData.getNodeType())).build();
+ getDelegate().put(store, parentPath, mapNode);
+ } else if (normalizedData instanceof LeafSetNode<?>){
+ LeafSetNode<Object> leafNode = Builders.leafSetBuilder().withNodeIdentifier(new NodeIdentifier(normalizedData.getNodeType())).build();
+ getDelegate().put(store, parentPath, leafNode);
+ }
+
+
+ }
+
+ private InstanceIdentifier parentPath(final InstanceIdentifier normalizedPath) {
+ List<PathArgument> childArgs = normalizedPath.getPath();
+ return new InstanceIdentifier(childArgs.subList(0, childArgs.size() -1));
+ }
+
+ private boolean parentNodeDoesNotExists(final LogicalDatastoreType store, final InstanceIdentifier normalizedPath) {
+ try {
+ return !getDelegate().read(store, parentPath(normalizedPath)).get().isPresent();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Override
+ public void removeConfigurationData(final InstanceIdentifier legacyPath) {
+ checkNotNull(legacyPath, "Path MUST NOT be null.");
+ getDelegate().delete(LogicalDatastoreType.CONFIGURATION, getNormalizer().toNormalized(legacyPath));
+ }
+
+ @Override
+ public void removeOperationalData(final InstanceIdentifier legacyPath) {
+ checkNotNull(legacyPath, "Path MUST NOT be null.");
+ getDelegate().delete(LogicalDatastoreType.OPERATIONAL, getNormalizer().toNormalized(legacyPath));
+ }
+ }
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class ChangeListenerNotifyTask implements Runnable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ChangeListenerNotifyTask.class);
+ private final Iterable<? extends DataChangeListenerRegistration<?>> listeners;
+ private final AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> event;
+
+ public ChangeListenerNotifyTask(final Iterable<? extends DataChangeListenerRegistration<?>> listeners,
+ final AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> event) {
+ this.listeners = listeners;
+ this.event = event;
+ }
+
+ @Override
+ public void run() {
+
+ for (DataChangeListenerRegistration<?> listener : listeners) {
+ try {
+ listener.getInstance().onDataChanged(event);
+ } catch (Exception e) {
+ LOG.error("Unhandled exception during invoking listener {} with event {}", listener, event, e);
+ }
+ }
+
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public final class DOMImmutableDataChangeEvent implements
+ AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+ private final NormalizedNode<?, ?> original;
+ private final NormalizedNode<?, ?> updated;
+ private final Map<InstanceIdentifier, ? extends NormalizedNode<?, ?>> originalData;
+ private final Map<InstanceIdentifier, NormalizedNode<?, ?>> createdData;
+ private final Map<InstanceIdentifier, NormalizedNode<?, ?>> updatedData;
+ private final Set<InstanceIdentifier> removedPaths;
+
+ private DOMImmutableDataChangeEvent(final Builder change) {
+ original = change.before;
+ updated = change.after;
+ originalData = change.original.build();
+ createdData = change.created.build();
+ updatedData = change.updated.build();
+ removedPaths = change.removed.build();
+ }
+
+ public static final Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public NormalizedNode<?, ?> getOriginalSubtree() {
+ return original;
+ }
+
+ @Override
+ public NormalizedNode<?, ?> getUpdatedSubtree() {
+ return updated;
+ }
+
+ @Override
+ public Map<InstanceIdentifier, ? extends NormalizedNode<?, ?>> getOriginalData() {
+ return originalData;
+ }
+
+ @Override
+ public Map<InstanceIdentifier, NormalizedNode<?, ?>> getCreatedData() {
+ return createdData;
+ }
+
+ @Override
+ public Map<InstanceIdentifier, NormalizedNode<?, ?>> getUpdatedData() {
+ return updatedData;
+ }
+
+ @Override
+ public Set<InstanceIdentifier> getRemovedPaths() {
+ return removedPaths;
+ }
+
+ @Override
+ public String toString() {
+ return "DOMImmutableDataChangeEvent [created=" + createdData.keySet() + ", updated=" + updatedData.keySet()
+ + ", removed=" + removedPaths + "]";
+ }
+
+ public static class Builder {
+
+ private NormalizedNode<?, ?> after;
+ private NormalizedNode<?, ?> before;
+
+ private final ImmutableMap.Builder<InstanceIdentifier, NormalizedNode<?, ?>> original = ImmutableMap.builder();
+ private final ImmutableMap.Builder<InstanceIdentifier, NormalizedNode<?, ?>> created = ImmutableMap.builder();
+ private final ImmutableMap.Builder<InstanceIdentifier, NormalizedNode<?, ?>> updated = ImmutableMap.builder();
+ private final ImmutableSet.Builder<InstanceIdentifier> removed = ImmutableSet.builder();
+
+ private Builder() {
+
+ }
+
+ public Builder setAfter(final NormalizedNode<?, ?> node) {
+ after = node;
+ return this;
+ }
+
+ public DOMImmutableDataChangeEvent build() {
+
+ return new DOMImmutableDataChangeEvent(this);
+ }
+
+ public void merge(final DOMImmutableDataChangeEvent nestedChanges) {
+
+ original.putAll(nestedChanges.getOriginalData());
+ created.putAll(nestedChanges.getCreatedData());
+ updated.putAll(nestedChanges.getUpdatedData());
+ removed.addAll(nestedChanges.getRemovedPaths());
+
+ }
+
+ public Builder setBefore(final NormalizedNode<?, ?> node) {
+ this.before = node;
+ return this;
+ }
+
+ public Builder addCreated(final InstanceIdentifier path, final NormalizedNode<?, ?> node) {
+ created.put(path, node);
+ return this;
+ }
+
+ public Builder addRemoved(final InstanceIdentifier path, final NormalizedNode<?, ?> node) {
+ original.put(path, node);
+ removed.add(path);
+ return this;
+ }
+
+ public Builder addUpdated(final InstanceIdentifier path, final NormalizedNode<?, ?> before,
+ final NormalizedNode<?, ?> after) {
+ original.put(path, before);
+ updated.put(path, after);
+ return this;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.TreeNodeUtils;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+import com.google.common.primitives.UnsignedLong;
+
+class DataAndMetadataSnapshot {
+
+ private final StoreMetadataNode metadataTree;
+ private final Optional<SchemaContext> schemaContext;
+
+ private DataAndMetadataSnapshot(final StoreMetadataNode metadataTree, final Optional<SchemaContext> schemaCtx) {
+ this.metadataTree = metadataTree;
+ this.schemaContext = schemaCtx;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static DataAndMetadataSnapshot createEmpty() {
+ return createEmpty(new NodeIdentifier(SchemaContext.NAME));
+ }
+
+
+ public static DataAndMetadataSnapshot createEmpty(final NodeIdentifier rootNode) {
+ NormalizedNode<?, ?> data = Builders.containerBuilder().withNodeIdentifier(rootNode).build();
+ StoreMetadataNode metadata = StoreMetadataNode.builder()
+ .setNodeVersion(UnsignedLong.ZERO)
+ .setSubtreeVersion(UnsignedLong.ZERO)
+ .setData(data)
+ .build();
+ return new DataAndMetadataSnapshot(metadata,Optional.<SchemaContext>absent());
+ }
+
+ public static DataAndMetadataSnapshot createEmpty(final SchemaContext ctx) {
+ NodeIdentifier rootNodeIdentifier = new NodeIdentifier(ctx.getQName());
+ NormalizedNode<?, ?> data = Builders.containerBuilder().withNodeIdentifier(rootNodeIdentifier).build();
+ StoreMetadataNode metadata = StoreMetadataNode.builder()
+ .setData(data)
+ .setNodeVersion(UnsignedLong.ZERO)
+ .setSubtreeVersion(UnsignedLong.ZERO)
+ .build();
+ return new DataAndMetadataSnapshot(metadata, Optional.of(ctx));
+ }
+
+ public Optional<SchemaContext> getSchemaContext() {
+ return schemaContext;
+ }
+
+ public NormalizedNode<?, ?> getDataTree() {
+ return metadataTree.getData();
+ }
+
+ public StoreMetadataNode getMetadataTree() {
+ return metadataTree;
+ }
+
+ public Optional<StoreMetadataNode> read(final InstanceIdentifier path) {
+ return TreeNodeUtils.findNode(metadataTree, path);
+ }
+
+ public static class Builder {
+ private NormalizedNode<?, ?> dataTree;
+ private StoreMetadataNode metadataTree;
+ private SchemaContext schemaContext;
+
+ public NormalizedNode<?, ?> getDataTree() {
+ return dataTree;
+ }
+
+ public Builder setMetadataTree(final StoreMetadataNode metadataTree) {
+ this.metadataTree = metadataTree;
+ return this;
+ }
+
+ public Builder setSchemaContext(final SchemaContext schemaContext) {
+ this.schemaContext = schemaContext;
+ return this;
+ }
+
+ public DataAndMetadataSnapshot build() {
+ return new DataAndMetadataSnapshot(metadataTree, Optional.fromNullable(schemaContext));
+ }
+
+ }
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.builder;
+import static org.opendaylight.controller.md.sal.dom.store.impl.StoreUtils.append;
+import static org.opendaylight.controller.md.sal.dom.store.impl.tree.TreeNodeUtils.getChild;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerRegistrationNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class DataChangeEventResolver {
+
+
+ private static final Logger LOG = LoggerFactory.getLogger(DataChangeEventResolver.class);
+
+ private static final DOMImmutableDataChangeEvent NO_CHANGE = builder().build();
+ private InstanceIdentifier rootPath;
+ private ListenerRegistrationNode listenerRoot;
+ private NodeModification modificationRoot;
+ private Optional<StoreMetadataNode> beforeRoot;
+ private Optional<StoreMetadataNode> afterRoot;
+ private final ImmutableList.Builder<ChangeListenerNotifyTask> tasks = ImmutableList.builder();
+
+ protected InstanceIdentifier getRootPath() {
+ return rootPath;
+ }
+
+ protected DataChangeEventResolver setRootPath(final InstanceIdentifier rootPath) {
+ this.rootPath = rootPath;
+ return this;
+ }
+
+ protected ListenerRegistrationNode getListenerRoot() {
+ return listenerRoot;
+ }
+
+ protected DataChangeEventResolver setListenerRoot(final ListenerRegistrationNode listenerRoot) {
+ this.listenerRoot = listenerRoot;
+ return this;
+ }
+
+ protected NodeModification getModificationRoot() {
+ return modificationRoot;
+ }
+
+ protected DataChangeEventResolver setModificationRoot(final NodeModification modificationRoot) {
+ this.modificationRoot = modificationRoot;
+ return this;
+ }
+
+ protected Optional<StoreMetadataNode> getBeforeRoot() {
+ return beforeRoot;
+ }
+
+ protected DataChangeEventResolver setBeforeRoot(final Optional<StoreMetadataNode> beforeRoot) {
+ this.beforeRoot = beforeRoot;
+ return this;
+ }
+
+ protected Optional<StoreMetadataNode> getAfterRoot() {
+ return afterRoot;
+ }
+
+ protected DataChangeEventResolver setAfterRoot(final Optional<StoreMetadataNode> afterRoot) {
+ this.afterRoot = afterRoot;
+ return this;
+ }
+
+ public Iterable<ChangeListenerNotifyTask> resolve() {
+ LOG.trace("Resolving events for {}" ,modificationRoot);
+ resolveAnyChangeEvent(rootPath, Optional.of(listenerRoot), modificationRoot, beforeRoot, afterRoot);
+ return tasks.build();
+ }
+
+ private DOMImmutableDataChangeEvent resolveAnyChangeEvent(final InstanceIdentifier path,
+ final Optional<ListenerRegistrationNode> listeners, final NodeModification modification,
+ final Optional<StoreMetadataNode> before, final Optional<StoreMetadataNode> after) {
+ // No listeners are present in listener registration subtree
+ // no before and after state is present
+ if (!before.isPresent() && !after.isPresent()) {
+ return NO_CHANGE;
+ }
+ switch (modification.getModificationType()) {
+ case SUBTREE_MODIFIED:
+ return resolveSubtreeChangeEvent(path, listeners, modification, before.get(), after.get());
+ case WRITE:
+ if (before.isPresent()) {
+ return resolveReplacedEvent(path, listeners, modification, before.get(), after.get());
+ } else {
+ return resolveCreateEvent(path, listeners, after.get());
+ }
+ case DELETE:
+ return resolveDeleteEvent(path, listeners, before.get());
+ default:
+ return NO_CHANGE;
+ }
+
+ }
+
+ /**
+ * Resolves create events deep down the interest listener tree.
+ *
+ *
+ * @param path
+ * @param listeners
+ * @param afterState
+ * @return
+ */
+ private DOMImmutableDataChangeEvent resolveCreateEvent(final InstanceIdentifier path,
+ final Optional<ListenerRegistrationNode> listeners, final StoreMetadataNode afterState) {
+ final NormalizedNode<?, ?> node = afterState.getData();
+ Builder builder = builder().setAfter(node).addCreated(path, node);
+
+ for (StoreMetadataNode child : afterState.getChildren()) {
+ PathArgument childId = child.getIdentifier();
+ Optional<ListenerRegistrationNode> childListeners = getChild(listeners, childId);
+
+ InstanceIdentifier childPath = StoreUtils.append(path, childId);
+ builder.merge(resolveCreateEvent(childPath, childListeners, child));
+ }
+
+ DOMImmutableDataChangeEvent event = builder.build();
+ if (listeners.isPresent()) {
+ addNotifyTask(listeners.get().getListeners(), event);
+ }
+ return event;
+ }
+
+ private DOMImmutableDataChangeEvent resolveDeleteEvent(final InstanceIdentifier path,
+ final Optional<ListenerRegistrationNode> listeners, final StoreMetadataNode beforeState) {
+ final NormalizedNode<?, ?> node = beforeState.getData();
+ Builder builder = builder().setBefore(node).addRemoved(path, node);
+
+ for (StoreMetadataNode child : beforeState.getChildren()) {
+ PathArgument childId = child.getIdentifier();
+ Optional<ListenerRegistrationNode> childListeners = getChild(listeners, childId);
+ InstanceIdentifier childPath = StoreUtils.append(path, childId);
+ builder.merge(resolveDeleteEvent(childPath, childListeners, child));
+ }
+ DOMImmutableDataChangeEvent event = builder.build();
+ if (listeners.isPresent()) {
+ addNotifyTask(listeners.get().getListeners(), event);
+ }
+ return event;
+
+ }
+
+ private DOMImmutableDataChangeEvent resolveSubtreeChangeEvent(final InstanceIdentifier path,
+ final Optional<ListenerRegistrationNode> listeners, final NodeModification modification,
+ final StoreMetadataNode before, final StoreMetadataNode after) {
+
+ Builder one = builder().setBefore(before.getData()).setAfter(after.getData());
+
+ Builder subtree = builder();
+
+ for (NodeModification childMod : modification.getModifications()) {
+ PathArgument childId = childMod.getIdentifier();
+ InstanceIdentifier childPath = append(path, childId);
+ Optional<ListenerRegistrationNode> childListen = getChild(listeners, childId);
+
+ Optional<StoreMetadataNode> childBefore = before.getChild(childId);
+ Optional<StoreMetadataNode> childAfter = after.getChild(childId);
+
+ switch (childMod.getModificationType()) {
+ case WRITE:
+ case DELETE:
+ one.merge(resolveAnyChangeEvent(childPath, childListen, childMod, childBefore, childAfter));
+ break;
+ case SUBTREE_MODIFIED:
+ subtree.merge(resolveSubtreeChangeEvent(childPath, childListen, childMod, childBefore.get(),
+ childAfter.get()));
+ break;
+ case UNMODIFIED:
+ // no-op
+ break;
+ }
+ }
+ DOMImmutableDataChangeEvent oneChangeEvent = one.build();
+ subtree.merge(oneChangeEvent);
+ DOMImmutableDataChangeEvent subtreeEvent = subtree.build();
+ if (listeners.isPresent()) {
+ addNotifyTask(listeners.get(), DataChangeScope.ONE, oneChangeEvent);
+ addNotifyTask(listeners.get(), DataChangeScope.SUBTREE, subtreeEvent);
+ }
+ return subtreeEvent;
+ }
+
+ private DOMImmutableDataChangeEvent resolveReplacedEvent(final InstanceIdentifier path,
+ final Optional<ListenerRegistrationNode> listeners, final NodeModification modification,
+ final StoreMetadataNode before, final StoreMetadataNode after) {
+ // FIXME Add task
+ return builder().build();
+ }
+
+ private void addNotifyTask(final ListenerRegistrationNode listenerRegistrationNode, final DataChangeScope scope,
+ final DOMImmutableDataChangeEvent event) {
+ Collection<DataChangeListenerRegistration<?>> potential = listenerRegistrationNode.getListeners();
+ if(potential.isEmpty()) {
+ return;
+ }
+ ArrayList<DataChangeListenerRegistration<?>> toNotify = new ArrayList<>(potential.size());
+ for(DataChangeListenerRegistration<?> listener : potential) {
+ if(scope.equals(listener.getScope())) {
+ toNotify.add(listener);
+ }
+ }
+ addNotifyTask(toNotify, event);
+
+ }
+
+ private void addNotifyTask(final Collection<DataChangeListenerRegistration<?>> listeners,
+ final DOMImmutableDataChangeEvent event) {
+ if(!listeners.isEmpty()) {
+ tasks.add(new ChangeListenerNotifyTask(ImmutableSet.copyOf(listeners),event));
+ }
+ }
+
+ public static DataChangeEventResolver create() {
+ return new DataChangeEventResolver();
+ }
+
+
+
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DataChangeListenerRegistration<L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>>
+extends ListenerRegistration<L> {
+
+
+ @Override
+ public L getInstance();
+
+ InstanceIdentifier getPath();
+
+ DataChangeScope getScope();
+
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static org.opendaylight.controller.md.sal.dom.store.impl.StoreUtils.increase;
+
+import java.util.Collections;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerRegistrationNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerRegistrationNode.DataChangeListenerRegistration;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeUtils;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.primitives.UnsignedLong;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+public class InMemoryDOMDataStore implements DOMStore, Identifiable<String>, SchemaContextListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataStore.class);
+ private static final InstanceIdentifier PUBLIC_ROOT_PATH = InstanceIdentifier.builder().build();
+
+
+ private final ListeningExecutorService executor;
+ private final String name;
+ private final AtomicLong txCounter = new AtomicLong(0);
+ private final ListenerRegistrationNode listenerTree;
+ private final AtomicReference<DataAndMetadataSnapshot> snapshot;
+
+ private ModificationApplyOperation operationTree;
+
+ private SchemaContext schemaContext;
+
+ public InMemoryDOMDataStore(final String name, final ListeningExecutorService executor) {
+ this.name = Preconditions.checkNotNull(name);
+ this.executor = Preconditions.checkNotNull(executor);
+ this.listenerTree = ListenerRegistrationNode.createRoot();
+ this.snapshot = new AtomicReference<DataAndMetadataSnapshot>(DataAndMetadataSnapshot.createEmpty());
+ this.operationTree = new AlwaysFailOperation();
+ }
+
+ @Override
+ public final String getIdentifier() {
+ return name;
+ }
+
+ @Override
+ public DOMStoreReadTransaction newReadOnlyTransaction() {
+ return new SnapshotBackedReadTransaction(nextIdentifier(), snapshot.get());
+ }
+
+ @Override
+ public DOMStoreReadWriteTransaction newReadWriteTransaction() {
+ return new SnapshotBackedReadWriteTransaction(nextIdentifier(), snapshot.get(), this, operationTree);
+ }
+
+ @Override
+ public DOMStoreWriteTransaction newWriteOnlyTransaction() {
+ return new SnaphostBackedWriteTransaction(nextIdentifier(), snapshot.get(), this, operationTree);
+ }
+
+ @Override
+ public synchronized void onGlobalContextUpdated(final SchemaContext ctx) {
+ operationTree = SchemaAwareApplyOperationRoot.from(ctx);
+ schemaContext = ctx;
+ }
+
+ @Override
+ public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
+ final InstanceIdentifier path, final L listener, final DataChangeScope scope) {
+ LOG.debug("{}: Registering data change listener {} for {}",name,listener,path);
+ ListenerRegistrationNode listenerNode = listenerTree;
+ for(PathArgument arg : path.getPath()) {
+ listenerNode = listenerNode.ensureChild(arg);
+ }
+
+ /*
+ * Make sure commit is not occurring right now. Listener has to be registered and its
+ * state capture enqueued at a consistent point.
+ *
+ * FIXME: improve this to read-write lock, such that multiple listener registrations
+ * can occur simultaneously
+ */
+ final DataChangeListenerRegistration<L> reg;
+ synchronized (this) {
+ reg = listenerNode.registerDataChangeListener(path, listener, scope);
+
+ Optional<StoreMetadataNode> currentState = snapshot.get().read(path);
+ if (currentState.isPresent()) {
+ final NormalizedNode<?, ?> data = currentState.get().getData();
+
+ final DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder() //
+ .setAfter(data) //
+ .addCreated(path, data) //
+ .build();
+ executor.submit(new ChangeListenerNotifyTask(Collections.singletonList(reg), event));
+ }
+ }
+
+ return reg;
+ }
+
+ private synchronized DOMStoreThreePhaseCommitCohort submit(
+ final SnaphostBackedWriteTransaction writeTx) {
+ LOG.debug("Tx: {} is submitted. Modifications: {}",writeTx.getIdentifier(),writeTx.getMutatedView());
+ return new ThreePhaseCommitImpl(writeTx);
+ }
+
+ private Object nextIdentifier() {
+ return name + "-" + txCounter.getAndIncrement();
+ }
+
+ private void commit(final DataAndMetadataSnapshot currentSnapshot,
+ final StoreMetadataNode newDataTree, final Iterable<ChangeListenerNotifyTask> listenerTasks) {
+ LOG.debug("Updating Store snaphot version: {} with version:{}",currentSnapshot.getMetadataTree().getSubtreeVersion(),newDataTree.getSubtreeVersion());
+
+ if(LOG.isTraceEnabled()) {
+ LOG.trace("Data Tree is {}",StoreUtils.toStringTree(newDataTree));
+ }
+
+ final DataAndMetadataSnapshot newSnapshot = DataAndMetadataSnapshot.builder() //
+ .setMetadataTree(newDataTree) //
+ .setSchemaContext(schemaContext) //
+ .build();
+
+ /*
+ * The commit has to occur atomically with regard to listener registrations.
+ */
+ synchronized (this) {
+ final boolean success = snapshot.compareAndSet(currentSnapshot, newSnapshot);
+ checkState(success, "Store snapshot and transaction snapshot differ. This should never happen.");
+
+ for (ChangeListenerNotifyTask task : listenerTasks) {
+ executor.submit(task);
+ }
+ }
+ }
+
+ private static class SnapshotBackedReadTransaction implements DOMStoreReadTransaction {
+
+ private DataAndMetadataSnapshot stableSnapshot;
+ private final Object identifier;
+
+ public SnapshotBackedReadTransaction(final Object identifier, final DataAndMetadataSnapshot snapshot) {
+ this.identifier = identifier;
+ this.stableSnapshot = snapshot;
+ LOG.debug("ReadOnly Tx: {} allocated with snapshot {}",identifier,snapshot.getMetadataTree().getSubtreeVersion());
+
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public void close() {
+ stableSnapshot = null;
+ }
+
+ @Override
+ public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final InstanceIdentifier path) {
+ checkNotNull(path, "Path must not be null.");
+ checkState(stableSnapshot != null, "Transaction is closed");
+ return Futures.immediateFuture(NormalizedNodeUtils.findNode(stableSnapshot.getDataTree(), path));
+ }
+
+ @Override
+ public String toString() {
+ return "SnapshotBackedReadTransaction [id =" + identifier + "]";
+ }
+
+ }
+
+ private static class SnaphostBackedWriteTransaction implements DOMStoreWriteTransaction {
+
+ private MutableDataTree mutableTree;
+ private final Object identifier;
+ private InMemoryDOMDataStore store;
+
+ private boolean ready = false;
+
+ public SnaphostBackedWriteTransaction(final Object identifier, final DataAndMetadataSnapshot snapshot,
+ final InMemoryDOMDataStore store, final ModificationApplyOperation applyOper) {
+ this.identifier = identifier;
+ mutableTree = MutableDataTree.from(snapshot, applyOper);
+ this.store = store;
+ LOG.debug("Write Tx: {} allocated with snapshot {}",identifier,snapshot.getMetadataTree().getSubtreeVersion());
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public void close() {
+ this.mutableTree = null;
+ this.store = null;
+ }
+
+ @Override
+ public void write(final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ checkNotReady();
+ mutableTree.write(path, data);
+ }
+
+ @Override
+ public void delete(final InstanceIdentifier path) {
+ checkNotReady();
+ mutableTree.delete(path);
+ }
+
+ protected boolean isReady() {
+ return ready;
+ }
+
+ protected void checkNotReady() {
+ checkState(!ready, "Transaction is ready. No further modifications allowed.");
+ }
+
+ @Override
+ public synchronized DOMStoreThreePhaseCommitCohort ready() {
+ ready = true;
+ LOG.debug("Store transaction: {} : Ready", getIdentifier());
+ mutableTree.seal();
+ return store.submit(this);
+ }
+
+ protected MutableDataTree getMutatedView() {
+ return mutableTree;
+ }
+
+ @Override
+ public String toString() {
+ return "SnaphostBackedWriteTransaction [id=" + getIdentifier() + ", ready=" + isReady() + "]";
+ }
+
+ }
+
+ private static class SnapshotBackedReadWriteTransaction extends SnaphostBackedWriteTransaction implements
+ DOMStoreReadWriteTransaction {
+
+ protected SnapshotBackedReadWriteTransaction(final Object identifier, final DataAndMetadataSnapshot snapshot,
+ final InMemoryDOMDataStore store, final ModificationApplyOperation applyOper) {
+ super(identifier, snapshot, store, applyOper);
+ }
+
+ @Override
+ public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final InstanceIdentifier path) {
+ return Futures.immediateFuture(getMutatedView().read(path));
+ }
+
+ @Override
+ public String toString() {
+ return "SnapshotBackedReadWriteTransaction [id=" + getIdentifier() + ", ready=" + isReady() + "]";
+ }
+
+ }
+
+ private class ThreePhaseCommitImpl implements DOMStoreThreePhaseCommitCohort {
+
+ private final SnaphostBackedWriteTransaction transaction;
+ private final NodeModification modification;
+
+ private DataAndMetadataSnapshot storeSnapshot;
+ private Optional<StoreMetadataNode> proposedSubtree;
+ private Iterable<ChangeListenerNotifyTask> listenerTasks;
+
+ public ThreePhaseCommitImpl(final SnaphostBackedWriteTransaction writeTransaction) {
+ this.transaction = writeTransaction;
+ this.modification = transaction.getMutatedView().getRootModification();
+ }
+
+ @Override
+ public ListenableFuture<Boolean> canCommit() {
+ final DataAndMetadataSnapshot snapshotCapture = snapshot.get();
+ final ModificationApplyOperation snapshotOperation = operationTree;
+
+ return executor.submit(new Callable<Boolean>() {
+
+ @Override
+ public Boolean call() throws Exception {
+ boolean applicable = snapshotOperation.isApplicable(modification,
+ Optional.of(snapshotCapture.getMetadataTree()));
+ LOG.debug("Store Transcation: {} : canCommit : {}", transaction.getIdentifier(), applicable);
+ return applicable;
+ }
+ });
+ }
+
+ @Override
+ public ListenableFuture<Void> preCommit() {
+ storeSnapshot = snapshot.get();
+ if(modification.getModificationType() == ModificationType.UNMODIFIED) {
+ return Futures.immediateFuture(null);
+ }
+ return executor.submit(new Callable<Void>() {
+
+
+
+ @Override
+ public Void call() throws Exception {
+ StoreMetadataNode metadataTree = storeSnapshot.getMetadataTree();
+
+ proposedSubtree = operationTree.apply(modification, Optional.of(metadataTree),
+ increase(metadataTree.getSubtreeVersion()));
+
+ listenerTasks = DataChangeEventResolver.create() //
+ .setRootPath(PUBLIC_ROOT_PATH) //
+ .setBeforeRoot(Optional.of(metadataTree)) //
+ .setAfterRoot(proposedSubtree) //
+ .setModificationRoot(modification) //
+ .setListenerRoot(listenerTree) //
+ .resolve();
+
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public ListenableFuture<Void> abort() {
+ storeSnapshot = null;
+ proposedSubtree = null;
+ return Futures.<Void> immediateFuture(null);
+ }
+
+ @Override
+ public ListenableFuture<Void> commit() {
+ if(modification.getModificationType() == ModificationType.UNMODIFIED) {
+ return Futures.immediateFuture(null);
+ }
+
+ checkState(proposedSubtree != null,"Proposed subtree must be computed");
+ checkState(storeSnapshot != null,"Proposed subtree must be computed");
+ // return ImmediateFuture<>;
+ InMemoryDOMDataStore.this.commit(storeSnapshot, proposedSubtree.get(),listenerTasks);
+ return Futures.<Void> immediateFuture(null);
+ }
+
+ }
+
+ private static final class AlwaysFailOperation implements ModificationApplyOperation {
+
+ @Override
+ public Optional<StoreMetadataNode> apply(final NodeModification modification,
+ final Optional<StoreMetadataNode> storeMeta, final UnsignedLong subtreeVersion) {
+ throw new IllegalStateException("Schema Context is not available.");
+ }
+
+ @Override
+ public boolean isApplicable(final NodeModification modification, final Optional<StoreMetadataNode> storeMetadata) {
+ throw new IllegalStateException("Schema Context is not available.");
+ }
+
+ @Override
+ public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
+ throw new IllegalStateException("Schema Context is not available.");
+ }
+
+ @Override
+ public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
+ throw new IllegalStateException("Schema Context is not available.");
+ }
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreTreeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+
+import com.google.common.base.Optional;
+import com.google.common.primitives.UnsignedLong;
+
+/**
+ *
+ * Operation responsible for applying {@link NodeModification} on tree.
+ *
+ * Operation is composite - operation on top level node consists of
+ * suboperations on child nodes. This allows to walk operation hierarchy and
+ * invoke suboperations independently.
+ *
+ * <b>Implementation notes</b>
+ * <ul>
+ * <li>
+ * Implementations MUST expose all nested suboperations which operates on child
+ * nodes expose via {@link #getChild(PathArgument)} method.
+ * <li>Same suboperations SHOULD be used when invoked via
+ * {@link #apply(NodeModification, Optional)} if applicable.
+ *
+ *
+ * Hierarchical composite operation which is responsible for applying
+ * modification on particular subtree and creating updated subtree
+ *
+ *
+ */
+public interface ModificationApplyOperation extends StoreTreeNode<ModificationApplyOperation> {
+
+ /**
+ *
+ * Implementation of this operation must be stateless and must not change
+ * state of this object.
+ *
+ * @param modification
+ * NodeModification to be applied
+ * @param storeMeta
+ * Store Metadata Node on which NodeModification should be
+ * applied
+ * @param subtreeVersion New subtree version of parent node
+ * @throws IllegalArgumentException
+ * If it is not possible to apply Operation on provided Metadata
+ * node
+ * @return new {@link StoreMetadataNode} if operation resulted in updating
+ * node, {@link Optional#absent()} if {@link NodeModification}
+ * resulted in deletion of this node.
+ */
+ Optional<StoreMetadataNode> apply(NodeModification modification, Optional<StoreMetadataNode> storeMeta, UnsignedLong subtreeVersion);
+
+ /**
+ *
+ * Checks if provided node modification could be applied to current metadata node.
+ *
+ * @param modification Modification
+ * @param current Metadata Node to which modification should be applied
+ * @return true if modification is applicable
+ * false if modification is no applicable
+ */
+ boolean isApplicable(NodeModification modification, Optional<StoreMetadataNode> current);
+
+ /**
+ *
+ * Performs structural verification of NodeModification, such as writen values / types
+ * uses right structural elements.
+ *
+ * @param modification to be verified.
+ * @throws IllegalArgumentException If provided NodeModification does not adhere to the structure.
+ */
+ void verifyStructure(NodeModification modification) throws IllegalArgumentException;
+
+ /**
+ * Returns a suboperation for specified tree node
+ *
+ * @return Reference to suboperation for specified tree node, {@link Optional#absent()}
+ * if suboperation is not supported for specified tree node.
+ */
+ @Override
+ public Optional<ModificationApplyOperation> getChild(PathArgument child);
+
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.TreeNodeUtils;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+class MutableDataTree {
+
+ private static final Logger log = LoggerFactory.getLogger(MutableDataTree.class);
+
+ final DataAndMetadataSnapshot snapshot;
+ final NodeModification rootModification;
+ final ModificationApplyOperation strategyTree;
+
+ private boolean sealed = false;
+
+ private MutableDataTree(final DataAndMetadataSnapshot snapshot, final ModificationApplyOperation strategyTree) {
+ this.snapshot = snapshot;
+ this.strategyTree = strategyTree;
+ this.rootModification = NodeModification.createUnmodified(snapshot.getMetadataTree());
+ }
+
+ public void write(final InstanceIdentifier path, final NormalizedNode<?, ?> value) {
+ checkSealed();
+ resolveModificationFor(path).write(value);
+ }
+
+ public void delete(final InstanceIdentifier path) {
+ checkSealed();
+ resolveModificationFor(path).delete();
+ }
+
+ public Optional<NormalizedNode<?, ?>> read(final InstanceIdentifier path) {
+ Entry<InstanceIdentifier, NodeModification> modification = TreeNodeUtils.findClosestsOrFirstMatch(rootModification, path, NodeModification.IS_TERMINAL_PREDICATE);
+
+ Optional<StoreMetadataNode> result = resolveSnapshot(modification);
+ if (result.isPresent()) {
+ NormalizedNode<?, ?> data = result.get().getData();
+ return NormalizedNodeUtils.findNode(modification.getKey(), data, path);
+ }
+ return Optional.absent();
+
+ }
+
+ private Optional<StoreMetadataNode> resolveSnapshot(
+ final Entry<InstanceIdentifier, NodeModification> keyModification) {
+ InstanceIdentifier path = keyModification.getKey();
+ NodeModification modification = keyModification.getValue();
+ return resolveSnapshot(path, modification);
+ }
+
+ private Optional<StoreMetadataNode> resolveSnapshot(final InstanceIdentifier path,
+ final NodeModification modification) {
+ try {
+ Optional<Optional<StoreMetadataNode>> potentialSnapshot = modification.getSnapshotCache();
+ if(potentialSnapshot.isPresent()) {
+ return potentialSnapshot.get();
+ }
+ return resolveModificationStrategy(path).apply(modification, modification.getOriginal(),
+ StoreUtils.increase(snapshot.getMetadataTree().getSubtreeVersion()));
+ } catch (Exception e) {
+ log.error("Could not create snapshot for {}", path,e);
+ throw e;
+ }
+ }
+
+ private ModificationApplyOperation resolveModificationStrategy(final InstanceIdentifier path) {
+ log.trace("Resolving modification apply strategy for {}", path);
+ return TreeNodeUtils.findNodeChecked(strategyTree, path);
+ }
+
+ private OperationWithModification resolveModificationFor(final InstanceIdentifier path) {
+ NodeModification modification = rootModification;
+ // We ensure strategy is present.
+ ModificationApplyOperation operation = resolveModificationStrategy(path);
+ for (PathArgument pathArg : path.getPath()) {
+ modification = modification.modifyChild(pathArg);
+ }
+ return OperationWithModification.from(operation, modification);
+ }
+
+ public static MutableDataTree from(final DataAndMetadataSnapshot snapshot, final ModificationApplyOperation resolver) {
+ return new MutableDataTree(snapshot, resolver);
+ }
+
+ public void seal() {
+ sealed = true;
+ rootModification.seal();
+ }
+
+ private void checkSealed() {
+ checkState(!sealed, "Data Tree is sealed. No further modifications allowed.");
+ }
+
+ protected NodeModification getRootModification() {
+ return rootModification;
+ }
+
+ @Override
+ public String toString() {
+ return "MutableDataTree [modification=" + rootModification + "]";
+ }
+
+
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+import com.google.common.primitives.UnsignedLong;
+
+public class OperationWithModification {
+
+ private final NodeModification modification;
+ private final ModificationApplyOperation applyOperation;
+
+ private OperationWithModification(final ModificationApplyOperation op, final NodeModification mod) {
+ this.modification = mod;
+ this.applyOperation = op;
+ }
+
+ public OperationWithModification write(final NormalizedNode<?, ?> value) {
+ modification.write(value);
+ applyOperation.verifyStructure(modification);
+ return this;
+ }
+
+ public OperationWithModification delete() {
+ modification.delete();
+ return this;
+ }
+
+ public boolean isApplicable(final Optional<StoreMetadataNode> data) {
+ return applyOperation.isApplicable(modification, data);
+ }
+
+ public Optional<StoreMetadataNode> apply(final Optional<StoreMetadataNode> data, final UnsignedLong subtreeVersion) {
+ return applyOperation.apply(modification, data, subtreeVersion);
+ }
+
+ public static OperationWithModification from(final ModificationApplyOperation operation,
+ final NodeModification modification) {
+ return new OperationWithModification(operation, modification);
+
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreNodeCompositeBuilder;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.primitives.UnsignedLong;
+
+public abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
+
+ public static SchemaAwareApplyOperation from(final DataSchemaNode schemaNode) {
+ if (schemaNode instanceof ContainerSchemaNode) {
+ return new ContainerModificationStrategy((ContainerSchemaNode) schemaNode);
+ } else if (schemaNode instanceof ListSchemaNode) {
+ return new ListMapModificationStrategy((ListSchemaNode) schemaNode);
+ } else if (schemaNode instanceof ChoiceNode) {
+ return new ChoiceModificationStrategy((ChoiceNode) schemaNode);
+ } else if (schemaNode instanceof LeafListSchemaNode) {
+ return new LeafSetEntryModificationStrategy((LeafListSchemaNode) schemaNode);
+ } else if (schemaNode instanceof LeafSchemaNode) {
+ return new LeafModificationStrategy((LeafSchemaNode) schemaNode);
+ }
+ throw new IllegalArgumentException("Not supported schema node type for " + schemaNode.getClass());
+ }
+
+ public static SchemaAwareApplyOperation from(final DataNodeContainer resolvedTree,
+ final AugmentationTarget augSchemas, final AugmentationIdentifier identifier) {
+ AugmentationSchema augSchema = null;
+ allAugments : for (AugmentationSchema potential : augSchemas.getAvailableAugmentations()) {
+ boolean containsAll = true;
+ for(DataSchemaNode child : potential.getChildNodes()) {
+ if(identifier.getPossibleChildNames().contains(child.getQName())) {
+ augSchema = potential;
+ break allAugments;
+ }
+ }
+ }
+ if(augSchema != null) {
+ return new AugmentationModificationStrategy(augSchema,resolvedTree);
+ }
+ return null;
+ }
+
+
+
+ protected final ModificationApplyOperation resolveChildOperation(final PathArgument child) {
+ Optional<ModificationApplyOperation> potential = getChild(child);
+ checkArgument(potential.isPresent(), "Operation for child %s is not defined.", child);
+ return potential.get();
+ }
+
+ @Override
+ public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
+ if (modification.getModificationType() == ModificationType.WRITE) {
+ verifyWritenStructure(modification.getWritenValue());
+ }
+ }
+
+ protected abstract void verifyWritenStructure(NormalizedNode<?, ?> writenValue);
+
+ @Override
+ public boolean isApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
+ switch (modification.getModificationType()) {
+ case DELETE:
+ return isDeleteApplicable(modification, current);
+ case SUBTREE_MODIFIED:
+ return isSubtreeModificationApplicable(modification, current);
+ case WRITE:
+ return isWriteApplicable(modification, current);
+ case UNMODIFIED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected boolean isWriteApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
+ Optional<StoreMetadataNode> original = modification.getOriginal();
+ if (original.isPresent() && current.isPresent()) {
+ return isNotConflicting(original.get(), current.get());
+ } else if (current.isPresent()) {
+ return false;
+ }
+ return true;
+
+ }
+
+ protected final boolean isNotConflicting(final StoreMetadataNode original, final StoreMetadataNode current) {
+ return original.getNodeVersion().equals(current.getNodeVersion())
+ && original.getSubtreeVersion().equals(current.getSubtreeVersion());
+ }
+
+ protected abstract boolean isSubtreeModificationApplicable(final NodeModification modification,
+ final Optional<StoreMetadataNode> current);
+
+ private boolean isDeleteApplicable(final NodeModification modification, final Optional<StoreMetadataNode> current) {
+ // FiXME: Add delete conflict detection.
+ return true;
+ }
+
+ @Override
+ public final Optional<StoreMetadataNode> apply(final NodeModification modification,
+ final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
+
+ switch (modification.getModificationType()) {
+ case DELETE:
+ return modification.storeSnapshot(Optional.<StoreMetadataNode>absent());
+ case SUBTREE_MODIFIED:
+ return modification.storeSnapshot(Optional.of(applySubtreeChange(modification, currentMeta.get(), subtreeVersion)));
+ case WRITE:
+ return modification.storeSnapshot(Optional.of(applyWrite(modification, currentMeta, subtreeVersion)));
+ case UNMODIFIED:
+ return currentMeta;
+ default:
+ throw new IllegalArgumentException("Provided modification type is not supported.");
+ }
+ }
+
+ protected abstract StoreMetadataNode applyWrite(NodeModification modification,
+ Optional<StoreMetadataNode> currentMeta, UnsignedLong subtreeVersion);
+
+ protected abstract StoreMetadataNode applySubtreeChange(NodeModification modification,
+ StoreMetadataNode currentMeta, UnsignedLong subtreeVersion);
+
+ public static abstract class ValueNodeModificationStrategy<T extends DataSchemaNode> extends
+ SchemaAwareApplyOperation {
+
+ private final T schema;
+ private final Class<? extends NormalizedNode<?, ?>> nodeClass;
+
+ protected ValueNodeModificationStrategy(final T schema, final Class<? extends NormalizedNode<?, ?>> nodeClass) {
+ super();
+ this.schema = schema;
+ this.nodeClass = nodeClass;
+ }
+
+ @Override
+ protected void verifyWritenStructure(final NormalizedNode<?, ?> writenValue) {
+ checkArgument(nodeClass.isInstance(writenValue), "Node should must be of type %s", nodeClass);
+ }
+
+ @Override
+ public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
+ throw new UnsupportedOperationException("Node " + schema.getPath()
+ + "is leaf type node. Child nodes not allowed");
+ }
+
+ @Override
+ protected StoreMetadataNode applySubtreeChange(final NodeModification modification,
+ final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
+ throw new UnsupportedOperationException("Node " + schema.getPath()
+ + "is leaf type node. Subtree change is not allowed.");
+ }
+
+ @Override
+ protected StoreMetadataNode applyWrite(final NodeModification modification,
+ final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
+ UnsignedLong nodeVersion = subtreeVersion;
+ if (currentMeta.isPresent()) {
+ nodeVersion = StoreUtils.increase(currentMeta.get().getNodeVersion());
+ }
+
+ return StoreMetadataNode.builder().setNodeVersion(nodeVersion).setSubtreeVersion(subtreeVersion)
+ .setData(modification.getWritenValue()).build();
+ }
+
+ @Override
+ protected boolean isSubtreeModificationApplicable(final NodeModification modification,
+ final Optional<StoreMetadataNode> current) {
+ return false;
+ }
+
+ }
+
+ public static class LeafSetEntryModificationStrategy extends ValueNodeModificationStrategy<LeafListSchemaNode> {
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected LeafSetEntryModificationStrategy(final LeafListSchemaNode schema) {
+ super(schema, (Class) LeafSetEntryNode.class);
+ }
+ }
+
+ public static class LeafModificationStrategy extends ValueNodeModificationStrategy<LeafSchemaNode> {
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected LeafModificationStrategy(final LeafSchemaNode schema) {
+ super(schema, (Class) LeafNode.class);
+ }
+ }
+
+ public static abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareApplyOperation {
+
+ private final Class<? extends NormalizedNode<?, ?>> nodeClass;
+
+ protected NormalizedNodeContainerModificationStrategy(final Class<? extends NormalizedNode<?, ?>> nodeClass) {
+ this.nodeClass = nodeClass;
+ }
+
+ @Override
+ public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
+ if (modification.getModificationType() == ModificationType.WRITE) {
+
+ }
+ for (NodeModification childModification : modification.getModifications()) {
+ resolveChildOperation(childModification.getIdentifier()).verifyStructure(childModification);
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected void verifyWritenStructure(final NormalizedNode<?, ?> writenValue) {
+ checkArgument(nodeClass.isInstance(writenValue), "Node should must be of type %s", nodeClass);
+ checkArgument(writenValue instanceof NormalizedNodeContainer);
+ NormalizedNodeContainer writenCont = (NormalizedNodeContainer) writenValue;
+ for (Object child : writenCont.getValue()) {
+ checkArgument(child instanceof NormalizedNode);
+ NormalizedNode childNode = (NormalizedNode) child;
+ }
+ }
+
+ @Override
+ protected StoreMetadataNode applyWrite(final NodeModification modification,
+ final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
+ //
+ NormalizedNode<?, ?> newValue = modification.getWritenValue();
+
+ UnsignedLong nodeVersion = subtreeVersion;
+ if (currentMeta.isPresent()) {
+ nodeVersion = StoreUtils.increase(currentMeta.get().getNodeVersion());
+ }
+ StoreMetadataNode newValueMeta = StoreMetadataNode.createRecursivelly(newValue, nodeVersion, nodeVersion);
+
+ if (!modification.hasAdditionalModifications()) {
+ return newValueMeta;
+ }
+ @SuppressWarnings("rawtypes")
+ NormalizedNodeContainerBuilder dataBuilder = createBuilder(modification.getIdentifier());
+ StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.from(dataBuilder) //
+ .setNodeVersion(nodeVersion) //
+ .setSubtreeVersion(subtreeVersion);
+
+ Set<PathArgument> processedPreexisting = applyPreexistingChildren(modification, newValueMeta.getChildren(),
+ builder, nodeVersion);
+ applyNewChildren(modification, processedPreexisting, builder, nodeVersion);
+
+ return builder.build();
+
+ }
+
+ @Override
+ public StoreMetadataNode applySubtreeChange(final NodeModification modification,
+ final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
+
+ UnsignedLong updatedSubtreeVersion = StoreUtils.increase(currentMeta.getSubtreeVersion());
+ @SuppressWarnings("rawtypes")
+ NormalizedNodeContainerBuilder dataBuilder = createBuilder(modification.getIdentifier());
+ StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.from(dataBuilder)
+ .setIdentifier(modification.getIdentifier()).setNodeVersion(currentMeta.getNodeVersion())
+ .setSubtreeVersion(updatedSubtreeVersion);
+ // We process preexisting nodes
+ Set<PathArgument> processedPreexisting = applyPreexistingChildren(modification, currentMeta.getChildren(),
+ builder, updatedSubtreeVersion);
+ applyNewChildren(modification, processedPreexisting, builder, updatedSubtreeVersion);
+ return builder.build();
+ }
+
+ private void applyNewChildren(final NodeModification modification, final Set<PathArgument> ignore,
+ final StoreNodeCompositeBuilder builder, final UnsignedLong subtreeVersion) {
+ for (NodeModification childModification : modification.getModifications()) {
+ PathArgument childIdentifier = childModification.getIdentifier();
+ // We skip allready processed modifications
+ if (ignore.contains(childIdentifier)) {
+ continue;
+ }
+
+ builder.addIfPresent(resolveChildOperation(childIdentifier) //
+ .apply(childModification, Optional.<StoreMetadataNode> absent(), subtreeVersion));
+ }
+ }
+
+ private Set<PathArgument> applyPreexistingChildren(final NodeModification modification,
+ final Iterable<StoreMetadataNode> children, final StoreNodeCompositeBuilder nodeBuilder,
+ final UnsignedLong subtreeVersion) {
+ Builder<PathArgument> processedModifications = ImmutableSet.<PathArgument> builder();
+ for (StoreMetadataNode childMeta : children) {
+ PathArgument childIdentifier = childMeta.getIdentifier();
+ // We retrieve Child modification metadata
+ Optional<NodeModification> childModification = modification.getChild(childIdentifier);
+ // Node is modified
+ if (childModification.isPresent()) {
+ processedModifications.add(childIdentifier);
+ Optional<StoreMetadataNode> result = resolveChildOperation(childIdentifier) //
+ .apply(childModification.get(), Optional.of(childMeta), subtreeVersion);
+ nodeBuilder.addIfPresent(result);
+ } else {
+ // Child is unmodified - reuse existing metadata and data
+ // snapshot
+ nodeBuilder.add(childMeta);
+ }
+ }
+ return processedModifications.build();
+ }
+
+ @Override
+ protected boolean isSubtreeModificationApplicable(final NodeModification modification,
+ final Optional<StoreMetadataNode> current) {
+ if (false == current.isPresent()) {
+ return false;
+ }
+ boolean result = true;
+ StoreMetadataNode currentMeta = current.get();
+ for (NodeModification childMod : modification.getModifications()) {
+ PathArgument childId = childMod.getIdentifier();
+ Optional<StoreMetadataNode> childMeta = currentMeta.getChild(childId);
+ result &= resolveChildOperation(childId).isApplicable(childMod, childMeta);
+ }
+ return result;
+ }
+
+ @SuppressWarnings("rawtypes")
+ protected abstract NormalizedNodeContainerBuilder createBuilder(PathArgument identifier);
+ }
+
+ public static abstract class DataNodeContainerModificationStrategy<T extends DataNodeContainer> extends
+ NormalizedNodeContainerModificationStrategy {
+
+ private final T schema;
+ private final LoadingCache<PathArgument, ModificationApplyOperation> childCache = CacheBuilder.newBuilder().build(
+ CacheLoader.from(new Function<PathArgument, ModificationApplyOperation>() {
+
+ @Override
+ public ModificationApplyOperation apply(final PathArgument identifier) {
+ if (identifier instanceof AugmentationIdentifier && schema instanceof AugmentationTarget) {
+ return from(schema, (AugmentationTarget) schema, (AugmentationIdentifier) identifier);
+ }
+
+ DataSchemaNode child = schema.getDataChildByName(identifier.getNodeType());
+ if (child == null) {
+ return null;
+ }
+ return from(child);
+ }
+ }));
+
+ protected DataNodeContainerModificationStrategy(final T schema,
+ final Class<? extends NormalizedNode<?, ?>> nodeClass) {
+ super(nodeClass);
+ this.schema = schema;
+ }
+
+ protected T getSchema() {
+ return schema;
+ }
+
+ @Override
+ public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
+ try {
+ return Optional.<ModificationApplyOperation> fromNullable(childCache.get(identifier));
+ } catch (ExecutionException e) {
+ return Optional.absent();
+ }
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ protected abstract DataContainerNodeBuilder createBuilder(PathArgument identifier);
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [" + schema + "]";
+ }
+
+ }
+
+ public static class ContainerModificationStrategy extends
+ DataNodeContainerModificationStrategy<ContainerSchemaNode> {
+
+ public ContainerModificationStrategy(final ContainerSchemaNode schemaNode) {
+ super(schemaNode, ContainerNode.class);
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
+ // TODO Auto-generated method stub
+ checkArgument(identifier instanceof NodeIdentifier);
+ return ImmutableContainerNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
+ }
+
+ }
+
+ public static class AugmentationModificationStrategy extends
+ DataNodeContainerModificationStrategy<AugmentationSchema> {
+
+ protected AugmentationModificationStrategy(final AugmentationSchema schema, final DataNodeContainer resolved) {
+ super(schema, AugmentationNode.class);
+ // FIXME: Use resolved children instead of unresolved.
+
+ }
+
+
+ @Override
+ protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
+ return Builders.augmentationBuilder().withNodeIdentifier((AugmentationIdentifier) identifier);
+ }
+
+ }
+
+ public static class ChoiceModificationStrategy extends NormalizedNodeContainerModificationStrategy {
+
+ private final ChoiceNode schema;
+ private final Map<PathArgument,ModificationApplyOperation> childNodes;
+
+ public ChoiceModificationStrategy(final ChoiceNode schemaNode) {
+ super(org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode.class);
+ this.schema = schemaNode;
+ ImmutableMap.Builder<PathArgument, ModificationApplyOperation> child = ImmutableMap.builder();
+
+ for(ChoiceCaseNode caze : schemaNode.getCases()) {
+ for(DataSchemaNode cazeChild : caze.getChildNodes()) {
+ SchemaAwareApplyOperation childNode = from(cazeChild);
+ child.put(new NodeIdentifier(cazeChild.getQName()),childNode);
+ }
+ }
+ childNodes = child.build();
+ }
+
+ @Override
+ public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
+ return Optional.fromNullable(childNodes.get(child));
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
+ checkArgument(identifier instanceof NodeIdentifier);
+ return ImmutableChoiceNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
+ }
+
+ }
+
+ public static class ListEntryModificationStrategy extends DataNodeContainerModificationStrategy<ListSchemaNode> {
+
+ protected ListEntryModificationStrategy(final ListSchemaNode schema) {
+ super(schema, MapEntryNode.class);
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ protected final DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
+ return ImmutableMapEntryNodeBuilder.create().withNodeIdentifier((NodeIdentifierWithPredicates) identifier);
+ }
+
+ }
+
+ public static class LeafSetModificationStrategy extends NormalizedNodeContainerModificationStrategy {
+
+ private final Optional<ModificationApplyOperation> entryStrategy;
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected LeafSetModificationStrategy(final LeafListSchemaNode schema) {
+ super((Class) LeafSetNode.class);
+ entryStrategy = Optional.<ModificationApplyOperation> of(new LeafSetEntryModificationStrategy(schema));
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) {
+ return ImmutableLeafSetNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
+ }
+
+ @Override
+ public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
+ if (identifier instanceof NodeWithValue) {
+ return entryStrategy;
+ }
+ return Optional.absent();
+ }
+
+ }
+
+ public static class ListMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
+
+ private final Optional<ModificationApplyOperation> entryStrategy;
+
+ protected ListMapModificationStrategy(final ListSchemaNode schema) {
+ super(MapNode.class);
+ entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) {
+ return ImmutableMapNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
+ }
+
+ @Override
+ public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
+ if (identifier instanceof NodeIdentifierWithPredicates) {
+ return entryStrategy;
+ }
+ return Optional.absent();
+ }
+
+ @Override
+ public String toString() {
+ return "ListMapModificationStrategy [entry=" + entryStrategy + "]";
+ }
+ }
+
+ public void verifyIdentifier(final PathArgument identifier) {
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class SchemaAwareApplyOperationRoot extends SchemaAwareApplyOperation.DataNodeContainerModificationStrategy<ContainerSchemaNode> {
+
+ private final SchemaContext context;
+
+ public SchemaAwareApplyOperationRoot(final SchemaContext context) {
+ super(context,ContainerNode.class);
+ this.context = context;
+ }
+
+ @Override
+ protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
+ return ImmutableContainerNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
+ }
+
+ public SchemaContext getContext() {
+ return context;
+ }
+
+ @Override
+ public String toString() {
+ return "SchemaAwareApplyOperationRoot [context=" + context + "]";
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+import com.google.common.base.Function;
+import com.google.common.base.Strings;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.primitives.UnsignedLong;
+
+public final class StoreUtils {
+
+ private final static Function<Identifiable<Object>, Object> EXTRACT_IDENTIFIER = new Function<Identifiable<Object>, Object>() {
+
+ @Override
+ public Object apply(final Identifiable<Object> input) {
+ return input.getIdentifier();
+ }
+ };
+
+ public static final UnsignedLong increase(final UnsignedLong original) {
+ return original.plus(UnsignedLong.ONE);
+ }
+
+ public static final InstanceIdentifier append(final InstanceIdentifier parent, final PathArgument arg) {
+
+ return new InstanceIdentifier(ImmutableList.<PathArgument> builder().addAll(parent.getPath()).add(arg).build());
+ }
+
+ public static AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> initialChangeEvent(
+ final InstanceIdentifier path, final StoreMetadataNode data) {
+ return new InitialDataChangeEvent(path, data.getData());
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static <V> Function<Identifiable<V>, V> identifierExtractor() {
+ return (Function) EXTRACT_IDENTIFIER;
+ }
+
+ private static final class InitialDataChangeEvent implements
+ AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+ private final ImmutableMap<InstanceIdentifier, NormalizedNode<?, ?>> payload;
+ private final NormalizedNode<?, ?> data;
+
+ public InitialDataChangeEvent(final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ payload = ImmutableMap.<InstanceIdentifier, NormalizedNode<?, ?>> of(path, data);
+ this.data = data;
+ }
+
+ @Override
+ public Map<InstanceIdentifier, NormalizedNode<?, ?>> getCreatedData() {
+ return payload;
+ }
+
+ @Override
+ public Map<InstanceIdentifier, ? extends NormalizedNode<?, ?>> getOriginalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public NormalizedNode<?, ?> getOriginalSubtree() {
+ return null;
+ }
+
+ @Override
+ public Set<InstanceIdentifier> getRemovedPaths() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Map<InstanceIdentifier, NormalizedNode<?, ?>> getUpdatedData() {
+ return payload;
+ }
+
+ @Override
+ public NormalizedNode<?, ?> getUpdatedSubtree() {
+ return data;
+ }
+
+ }
+
+ public static <V> Set<V> toIdentifierSet(final Iterable<? extends Identifiable<V>> children) {
+ return FluentIterable.from(children).transform(StoreUtils.<V> identifierExtractor()).toSet();
+ }
+
+ public static String toStringTree(final StoreMetadataNode metaNode) {
+ StringBuilder builder = new StringBuilder();
+ toStringTree(builder, metaNode, 0);
+ return builder.toString();
+
+ }
+
+ private static void toStringTree(final StringBuilder builder, final StoreMetadataNode metaNode, final int offset) {
+ String prefix = Strings.repeat(" ", offset);
+ builder.append(prefix).append(toStringTree(metaNode.getIdentifier()));
+ NormalizedNode<?, ?> dataNode = metaNode.getData();
+ if (dataNode instanceof NormalizedNodeContainer<?, ?, ?>) {
+ builder.append(" {").append("\n");
+ for (StoreMetadataNode child : metaNode.getChildren()) {
+ toStringTree(builder, child, offset + 4);
+ }
+ builder.append(prefix).append("}");
+ } else {
+ builder.append(" ").append(dataNode.getValue());
+ }
+ builder.append("\n");
+ }
+
+ private static String toStringTree(final PathArgument identifier) {
+ if (identifier instanceof NodeIdentifierWithPredicates) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(identifier.getNodeType().getLocalName());
+ builder.append(((NodeIdentifierWithPredicates) identifier).getKeyValues().values());
+ return builder.toString();
+ } else if (identifier instanceof AugmentationIdentifier) {
+ return "augmentation";
+ }
+ return identifier.getNodeType().getLocalName();
+ }
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+public class ListenerRegistrationNode implements StoreTreeNode<ListenerRegistrationNode>, Identifiable<PathArgument> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ListenerRegistrationNode.class);
+
+ private final ListenerRegistrationNode parent;
+ private final Map<PathArgument, ListenerRegistrationNode> children;
+ private final PathArgument identifier;
+ private final HashSet<DataChangeListenerRegistration<?>> listeners;
+
+ private ListenerRegistrationNode(final PathArgument identifier) {
+ this(null, identifier);
+ }
+
+ private ListenerRegistrationNode(final ListenerRegistrationNode parent, final PathArgument identifier) {
+ this.parent = parent;
+ this.identifier = identifier;
+ children = new HashMap<>();
+ listeners = new HashSet<>();
+ }
+
+ public final static ListenerRegistrationNode createRoot() {
+ return new ListenerRegistrationNode(null);
+ }
+
+ @Override
+ public PathArgument getIdentifier() {
+ return identifier;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public Collection<org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration<?>> getListeners() {
+ // FIXME: this is not thread-safe and races with listener (un)registration!
+ return (Collection) listeners;
+ }
+
+ @Override
+ public synchronized Optional<ListenerRegistrationNode> getChild(final PathArgument child) {
+ return Optional.fromNullable(children.get(child));
+ }
+
+ public synchronized ListenerRegistrationNode ensureChild(final PathArgument child) {
+ ListenerRegistrationNode potential = (children.get(child));
+ if (potential == null) {
+ potential = new ListenerRegistrationNode(this, child);
+ children.put(child, potential);
+ }
+ return potential;
+ }
+
+ /**
+ *
+ * Registers listener on this node.
+ *
+ * @param path Full path on which listener is registered.
+ * @param listener Listener
+ * @param scope Scope of triggering event.
+ * @return
+ */
+ public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> DataChangeListenerRegistration<L> registerDataChangeListener(final InstanceIdentifier path,
+ final L listener, final DataChangeScope scope) {
+
+ DataChangeListenerRegistration<L> listenerReg = new DataChangeListenerRegistration<L>(path,listener, scope, this);
+ listeners.add(listenerReg);
+ return listenerReg;
+ }
+
+ private void removeListener(final DataChangeListenerRegistration<?> listener) {
+ listeners.remove(listener);
+ removeThisIfUnused();
+ }
+
+ private void removeThisIfUnused() {
+ if (parent != null && listeners.isEmpty() && children.isEmpty()) {
+ parent.removeChildIfUnused(this);
+ }
+ }
+
+ public boolean isUnused() {
+ return (listeners.isEmpty() && children.isEmpty()) || areChildrenUnused();
+ }
+
+ private boolean areChildrenUnused() {
+ for (ListenerRegistrationNode child : children.values()) {
+ if (!child.isUnused()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void removeChildIfUnused(final ListenerRegistrationNode listenerRegistrationNode) {
+ // FIXME Remove unnecessary
+ }
+
+ public static class DataChangeListenerRegistration<T extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>>
+ extends AbstractObjectRegistration<T> implements
+ org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration<T> {
+
+ private final DataChangeScope scope;
+ private ListenerRegistrationNode node;
+ private final InstanceIdentifier path;
+
+ public DataChangeListenerRegistration(final InstanceIdentifier path,final T listener, final DataChangeScope scope,
+ final ListenerRegistrationNode node) {
+ super(listener);
+ this.path = path;
+ this.scope = scope;
+ this.node = node;
+ }
+
+ @Override
+ public DataChangeScope getScope() {
+ return scope;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ node.removeListener(this);
+ node = null;
+ }
+
+ @Override
+ public InstanceIdentifier getPath() {
+ return 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.md.sal.dom.store.impl.tree;
+
+public enum ModificationType {
+
+ /**
+ *
+ * Node is unmodified
+ *
+ *
+ */
+ UNMODIFIED,
+ /**
+ *
+ * Child of tree node was modified
+ *
+ */
+ SUBTREE_MODIFIED,
+ /**
+ * Tree node was replaced with new value / subtree
+ *
+ */
+ WRITE,
+ /**
+ *
+ * Tree node is to be deleted.
+ *
+ */
+ DELETE
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.primitives.UnsignedLong;
+
+/**
+ * Node Modification Node and Tree
+ *
+ * Tree which structurally resembles data tree and captures client modifications
+ * to the data store tree.
+ *
+ * This tree is lazily created and populated via {@link #modifyChild(PathArgument)}
+ * and {@link StoreMetadataNode} which represents original state {@link #getOriginal()}.
+ *
+ */
+public class NodeModification implements StoreTreeNode<NodeModification>, Identifiable<PathArgument> {
+
+ public static final Predicate<NodeModification> IS_TERMINAL_PREDICATE = new Predicate<NodeModification>() {
+ @Override
+ public boolean apply(final NodeModification input) {
+ return input.getModificationType() == ModificationType.WRITE || input.getModificationType() == ModificationType.DELETE;
+ }
+ };
+ private final PathArgument identifier;
+ private ModificationType modificationType = ModificationType.UNMODIFIED;
+
+
+ private final Optional<StoreMetadataNode> original;
+
+ private NormalizedNode<?, ?> value;
+
+ private UnsignedLong subtreeVersion;
+ private Optional<StoreMetadataNode> snapshotCache;
+
+ private final Map<PathArgument, NodeModification> childModification;
+
+ private boolean sealed = false;
+
+ protected NodeModification(final PathArgument identifier, final Optional<StoreMetadataNode> original) {
+ this.identifier = identifier;
+ this.original = original;
+ childModification = new LinkedHashMap<>();
+ }
+
+ /**
+ *
+ *
+ * @return
+ */
+ public NormalizedNode<?, ?> getWritenValue() {
+ return value;
+ }
+
+ @Override
+ public PathArgument getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ *
+ * Returns original store metadata
+ * @return original store metadata
+ */
+ public final Optional<StoreMetadataNode> getOriginal() {
+ return original;
+ }
+
+ /**
+ * Returns modification type
+ *
+ * @return modification type
+ */
+ public final ModificationType getModificationType() {
+ return modificationType;
+ }
+
+ /**
+ *
+ * Returns child modification if child was modified
+ *
+ * @return Child modification if direct child or it's subtree
+ * was modified.
+ *
+ */
+ @Override
+ public Optional<NodeModification> getChild(final PathArgument child) {
+ return Optional.<NodeModification> fromNullable(childModification.get(child));
+ }
+
+ /**
+ *
+ * Returns child modification if child was modified, creates {@link NodeModification}
+ * for child otherwise.
+ *
+ * If this node's {@link ModificationType} is {@link ModificationType#UNMODIFIED}
+ * changes modification type to {@link ModificationType#SUBTREE_MODIFIED}
+ *
+ * @param child
+ * @return {@link NodeModification} for specified child, with {@link #getOriginal()}
+ * containing child metadata if child was present in original data.
+ */
+ public synchronized NodeModification modifyChild(final PathArgument child) {
+ checkSealed();
+ clearSnapshot();
+ if(modificationType == ModificationType.UNMODIFIED) {
+ updateModificationType(ModificationType.SUBTREE_MODIFIED);
+ }
+ final NodeModification potential = childModification.get(child);
+ if (potential != null) {
+ return potential;
+ }
+ Optional<StoreMetadataNode> currentMetadata = Optional.absent();
+ if(original.isPresent()) {
+ currentMetadata = original.get().getChild(child);
+ }
+ NodeModification newlyCreated = new NodeModification(child,currentMetadata);
+ childModification.put(child, newlyCreated);
+ return newlyCreated;
+ }
+
+ /**
+ *
+ * Returns all recorded direct child modification
+ *
+ * @return all recorded direct child modifications
+ */
+ public Iterable<NodeModification> getModifications() {
+ return childModification.values();
+ }
+
+
+ /**
+ *
+ * Records a delete for associated node.
+ *
+ */
+ public synchronized void delete() {
+ checkSealed();
+ clearSnapshot();
+ updateModificationType(ModificationType.DELETE);
+ childModification.clear();
+ this.value = null;
+ }
+
+ /**
+ *
+ * Records a write for associated node.
+ *
+ * @param value
+ */
+ public synchronized void write(final NormalizedNode<?, ?> value) {
+ checkSealed();
+ clearSnapshot();
+ updateModificationType(ModificationType.WRITE);
+ childModification.clear();
+ this.value = value;
+ }
+
+ private void checkSealed() {
+ checkState(!sealed, "Node Modification is sealed. No further changes allowed.");
+ }
+
+ public synchronized void seal() {
+ sealed = true;
+ clearSnapshot();
+ for(NodeModification child : childModification.values()) {
+ child.seal();
+ }
+ }
+
+ private void clearSnapshot() {
+ snapshotCache = null;
+ }
+
+ public Optional<StoreMetadataNode> storeSnapshot(final Optional<StoreMetadataNode> snapshot) {
+ snapshotCache = snapshot;
+ return snapshot;
+ }
+
+ public Optional<Optional<StoreMetadataNode>> getSnapshotCache() {
+ return Optional.fromNullable(snapshotCache);
+ }
+
+ public boolean hasAdditionalModifications() {
+ return !childModification.isEmpty();
+ }
+
+ public void updateModificationType(final ModificationType type) {
+ modificationType = type;
+ clearSnapshot();
+ }
+
+ @Override
+ public String toString() {
+ return "NodeModification [identifier=" + identifier + ", modificationType="
+ + modificationType + ", childModification=" + childModification + "]";
+ }
+
+ public static NodeModification createUnmodified(final StoreMetadataNode metadataTree) {
+ return new NodeModification(metadataTree.getIdentifier(), Optional.of(metadataTree));
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Map;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.primitives.UnsignedLong;
+
+public class StoreMetadataNode implements Immutable, Identifiable<PathArgument>, StoreTreeNode<StoreMetadataNode> {
+
+ private final UnsignedLong nodeVersion;
+ private final UnsignedLong subtreeVersion;
+ private final NormalizedNode<?, ?> data;
+
+ private final Map<PathArgument, StoreMetadataNode> children;
+
+ protected StoreMetadataNode(final NormalizedNode<?, ?> data, final UnsignedLong nodeVersion,
+ final UnsignedLong subtreeVersion, final Map<PathArgument, StoreMetadataNode> children) {
+ this.nodeVersion = nodeVersion;
+ this.subtreeVersion = subtreeVersion;
+ this.data = data;
+ this.children = ImmutableMap.copyOf(children);
+
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public UnsignedLong getNodeVersion() {
+ return this.nodeVersion;
+ }
+
+ @Override
+ public PathArgument getIdentifier() {
+ return data.getIdentifier();
+ }
+
+ public UnsignedLong getSubtreeVersion() {
+ return subtreeVersion;
+ }
+
+ public NormalizedNode<?, ?> getData() {
+ return this.data;
+ }
+
+ public Iterable<StoreMetadataNode> getChildren() {
+ return children.values();
+ }
+
+ @Override
+ public Optional<StoreMetadataNode> getChild(final PathArgument key) {
+ return Optional.fromNullable(children.get(key));
+ }
+
+ @Override
+ public String toString() {
+ return "StoreMetadataNode [identifier=" + getIdentifier() + ", nodeVersion=" + nodeVersion + "]";
+ }
+
+ public static Optional<UnsignedLong> getVersion(final Optional<StoreMetadataNode> currentMetadata) {
+ if (currentMetadata.isPresent()) {
+ return Optional.of(currentMetadata.get().getNodeVersion());
+ }
+ return Optional.absent();
+ }
+
+ public static Optional<StoreMetadataNode> getChild(final Optional<StoreMetadataNode> parent,
+ final PathArgument child) {
+ if (parent.isPresent()) {
+ return parent.get().getChild(child);
+ }
+ return Optional.absent();
+ }
+
+ public static final StoreMetadataNode createRecursivelly(final NormalizedNode<?, ?> node,
+ final UnsignedLong nodeVersion, final UnsignedLong subtreeVersion) {
+ Builder builder = builder() //
+ .setNodeVersion(nodeVersion) //
+ .setSubtreeVersion(subtreeVersion) //
+ .setData(node);
+ if (node instanceof NormalizedNodeContainer<?, ?, ?>) {
+
+ @SuppressWarnings("unchecked")
+ NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> nodeContainer = (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) node;
+ for (NormalizedNode<?, ?> subNode : nodeContainer.getValue()) {
+ builder.add(createRecursivelly(subNode, nodeVersion, subtreeVersion));
+ }
+ }
+ return builder.build();
+ }
+
+ public static class Builder {
+
+ private UnsignedLong nodeVersion;
+ private UnsignedLong subtreeVersion;
+ private NormalizedNode<?, ?> data;
+ private final ImmutableMap.Builder<PathArgument, StoreMetadataNode> children = ImmutableMap.builder();
+
+ private Builder() {}
+
+
+ public UnsignedLong getVersion() {
+ return nodeVersion;
+
+ }
+
+ public Builder setNodeVersion(final UnsignedLong version) {
+ this.nodeVersion = version;
+ return this;
+ }
+
+ public Builder setSubtreeVersion(final UnsignedLong version) {
+ this.subtreeVersion = version;
+ return this;
+ }
+
+ public Builder setData(final NormalizedNode<?, ?> data) {
+ this.data = data;
+ return this;
+ }
+
+ public Builder add(final StoreMetadataNode node) {
+ children.put(node.getIdentifier(), node);
+ return this;
+ }
+
+ public StoreMetadataNode build() {
+ checkState(data != null, "Data node should not be null.");
+ checkState(subtreeVersion.compareTo(nodeVersion) >= 0,
+ "Subtree version must be equals or greater than node version.");
+ return new StoreMetadataNode(data, nodeVersion, subtreeVersion, children.build());
+ }
+ }
+
+ public static StoreMetadataNode createRecursivelly(final NormalizedNode<?, ?> node, final UnsignedLong version) {
+ return createRecursivelly(node, version, version);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+
+import com.google.common.base.Optional;
+import com.google.common.primitives.UnsignedLong;
+
+/**
+ *
+ * Helper builder
+ *
+ *
+ */
+@SuppressWarnings("rawtypes")
+public class StoreNodeCompositeBuilder {
+
+ private final StoreMetadataNode.Builder metadata;
+
+ private final NormalizedNodeContainerBuilder data;
+
+ private StoreNodeCompositeBuilder(final NormalizedNodeContainerBuilder nodeBuilder) {
+ this.metadata = StoreMetadataNode.builder();
+ this.data = nodeBuilder;
+ }
+
+ @SuppressWarnings("unchecked")
+ public StoreNodeCompositeBuilder add(final StoreMetadataNode node) {
+ metadata.add(node);
+ data.addChild(node.getData());
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public StoreNodeCompositeBuilder addIfPresent(final Optional<StoreMetadataNode> potential) {
+ if (potential.isPresent()) {
+ StoreMetadataNode node = potential.get();
+ metadata.add(node);
+ data.addChild(node.getData());
+ }
+ return this;
+ }
+
+ public StoreMetadataNode build() {
+ return metadata.setData(data.build()).build();
+ }
+
+ public static StoreNodeCompositeBuilder from(final NormalizedNodeContainerBuilder nodeBuilder) {
+ return new StoreNodeCompositeBuilder(nodeBuilder);
+ }
+
+ @SuppressWarnings("unchecked")
+ public StoreNodeCompositeBuilder setIdentifier(final PathArgument identifier) {
+ data.withNodeIdentifier(identifier);
+ return this;
+ }
+
+ public StoreNodeCompositeBuilder setNodeVersion(final UnsignedLong nodeVersion) {
+ metadata.setNodeVersion(nodeVersion);
+ return this;
+ }
+
+ public StoreNodeCompositeBuilder setSubtreeVersion(final UnsignedLong updatedSubtreeVersion) {
+ metadata.setSubtreeVersion(updatedSubtreeVersion);
+ return this;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+
+import com.google.common.base.Optional;
+/**
+ *
+ * Tree node which contains references to it's leafs
+ *
+ * @param <C> Final node type
+ */
+public interface StoreTreeNode<C extends StoreTreeNode<C>> {
+
+ /**
+ *
+ * Returns direct child of the node
+ *
+ * @param child Identifier of child
+ * @return Optional with node if the child is existing, {@link Optional#absent()} otherwise.
+ */
+ Optional<C> getChild(PathArgument child);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+
+public class TreeNodeUtils {
+
+ /**
+ * Finds a node in tree
+ *
+ * @param tree Data Tree
+ * @param path Path to the node
+ * @return Optional with node if the node is present in tree, {@link Optional#absent()} otherwise.
+ *
+ */
+ public static <T extends StoreTreeNode<T>> Optional<T> findNode(final T tree, final InstanceIdentifier path) {
+ Optional<T> current = Optional.<T> of(tree);
+ Iterator<PathArgument> pathIter = path.getPath().iterator();
+ while (current.isPresent() && pathIter.hasNext()) {
+ current = current.get().getChild(pathIter.next());
+ }
+ return current;
+ }
+
+
+ public static <T extends StoreTreeNode<T>> T findNodeChecked(final T tree, final InstanceIdentifier path) {
+ T current = tree;
+ List<PathArgument> nested = new ArrayList<>(path.getPath());
+ for(PathArgument pathArg : path.getPath()) {
+ Optional<T> potential = current.getChild(pathArg);
+ nested.add(pathArg);
+ Preconditions.checkArgument(potential.isPresent(),"Child %s is not present in tree.",nested);
+ current = potential.get();
+ }
+ return current;
+ }
+
+ /**
+ * Finds a node or closest parent in the tree
+ *
+ * @param tree Data Tree
+ * @param path Path to the node
+ * @return Map.Entry Entry with key which is path to closest parent and value is parent node.
+ *
+ */
+ public static <T extends StoreTreeNode<T>> Map.Entry<InstanceIdentifier, T> findClosest(final T tree, final InstanceIdentifier path) {
+ return findClosestsOrFirstMatch(tree, path, Predicates.<T>alwaysFalse());
+ }
+
+ public static <T extends StoreTreeNode<T>> Map.Entry<InstanceIdentifier, T> findClosestsOrFirstMatch(final T tree, final InstanceIdentifier path, final Predicate<T> predicate) {
+ Optional<T> parent = Optional.<T>of(tree);
+ Optional<T> current = Optional.<T> of(tree);
+
+ int nesting = 0;
+ Iterator<PathArgument> pathIter = path.getPath().iterator();
+ while (current.isPresent() && pathIter.hasNext() && !predicate.apply(current.get())) {
+ parent = current;
+ current = current.get().getChild(pathIter.next());
+ nesting++;
+ }
+ if(current.isPresent()) {
+ final InstanceIdentifier currentPath = new InstanceIdentifier(path.getPath().subList(0, nesting));
+ return new SimpleEntry<InstanceIdentifier,T>(currentPath,current.get());
+ }
+ // Nesting minus one is safe, since current is allways present when nesting = 0
+ // so this prat of code is never triggered, in cases nesting == 0;
+ final InstanceIdentifier parentPath = new InstanceIdentifier(path.getPath().subList(0, nesting - 1));
+ return new SimpleEntry<InstanceIdentifier,T>(parentPath,parent.get());
+
+ }
+
+ public static <T extends StoreTreeNode<T>> Optional<T> getChild(final Optional<T> parent,final PathArgument child) {
+ if(parent.isPresent()) {
+ return parent.get().getChild(child);
+ }
+ return Optional.absent();
+ }
+
+}
import org.osgi.framework.ServiceRegistration
import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProviders
import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry
+import org.opendaylight.controller.md.sal.dom.broker.impl.compat.BackwardsCompatibleDataBroker
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMDataBrokerImpl
+import com.google.common.util.concurrent.MoreExecutors
+import com.google.common.collect.ImmutableMap
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType
+import org.opendaylight.controller.sal.core.spi.data.DOMStore
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore
+import java.util.concurrent.Executors
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker
class BrokerConfigActivator implements AutoCloseable {
private static val ROOT = InstanceIdentifier.builder().toInstance();
@Property
- private var DataBrokerImpl dataService;
+ private var DataProviderService dataService;
private var ServiceRegistration<DataBrokerService> dataReg;
private var ServiceRegistration<DataProviderService> dataProviderReg;
SchemaAwareDataStoreAdapter wrappedStore
- public def void start(BrokerImpl broker, DataStore store, BundleContext context) {
+ public def void start(BrokerImpl broker, DataStore store, DOMDataBroker asyncBroker,BundleContext context) {
val emptyProperties = new Hashtable<String, String>();
broker.setBundleContext(context);
schemaService = context.getService(serviceRef);
broker.setRouter(new SchemaAwareRpcBroker("/", SchemaContextProviders.fromSchemaService(schemaService)));
-
- dataService = new DataBrokerImpl();
- //dataService.setExecutor(broker.getExecutor());
-
- dataReg = context.registerService(DataBrokerService, dataService, emptyProperties);
- dataProviderReg = context.registerService(DataProviderService, dataService, emptyProperties);
-
- wrappedStore = new SchemaAwareDataStoreAdapter();
- wrappedStore.changeDelegate(store);
- wrappedStore.setValidationEnabled(false);
-
- context.registerService(SchemaServiceListener, wrappedStore, emptyProperties)
-
- dataService.registerConfigurationReader(ROOT, wrappedStore);
- dataService.registerCommitHandler(ROOT, wrappedStore);
- dataService.registerOperationalReader(ROOT, wrappedStore);
+
+
+ if(asyncBroker == null) {
+ dataService = new DataBrokerImpl();
+ dataProviderReg = context.registerService(DataProviderService, dataService, emptyProperties);
+
+ wrappedStore = new SchemaAwareDataStoreAdapter();
+ wrappedStore.changeDelegate(store);
+ wrappedStore.setValidationEnabled(false);
+ context.registerService(SchemaServiceListener, wrappedStore, emptyProperties)
+
+ dataService.registerConfigurationReader(ROOT, wrappedStore);
+ dataService.registerCommitHandler(ROOT, wrappedStore);
+ dataService.registerOperationalReader(ROOT, wrappedStore);
+ } else {
+ val compatibleDataBroker = new BackwardsCompatibleDataBroker(asyncBroker);
+ context.registerService(SchemaServiceListener,compatibleDataBroker,emptyProperties);
+ dataService = compatibleDataBroker;
+ }
+
+
+//
mountService = new MountPointManagerImpl();
- mountService.setDataBroker(dataService);
-
- mountReg = context.registerService(MountService, mountService, emptyProperties);
+ dataReg = context.registerService(DataBrokerService, dataService, emptyProperties);
+ mountReg = context.registerService(MountService, mountService, emptyProperties);
mountProviderReg = context.registerService(MountProvisionService, mountService, emptyProperties);
rpcProvisionRegistryReg = context.registerService(RpcProvisionRegistry, broker.getRouter(), emptyProperties);
private ListenerRegistry<SchemaServiceListener> listeners;
private BundleContext context;
- private BundleScanner scanner = new BundleScanner();
+ private final BundleScanner scanner = new BundleScanner();
private BundleTracker<ImmutableSet<Registration<URL>>> bundleTracker;
return listeners;
}
- public void setListeners(ListenerRegistry<SchemaServiceListener> listeners) {
+ public void setListeners(final ListenerRegistry<SchemaServiceListener> listeners) {
this.listeners = listeners;
}
return context;
}
- public void setContext(BundleContext context) {
+ public void setContext(final BundleContext context) {
this.context = context;
}
return getGlobalContext();
}
+ @Override
public SchemaContext getGlobalContext() {
return contextResolver.getSchemaContext().orNull();
}
@Override
- public void addModule(Module module) {
+ public void addModule(final Module module) {
throw new UnsupportedOperationException();
}
}
@Override
- public void removeModule(Module module) {
+ public void removeModule(final Module module) {
throw new UnsupportedOperationException();
}
@Override
- public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(SchemaServiceListener listener) {
+ public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(final SchemaServiceListener listener) {
+ Optional<SchemaContext> potentialCtx = contextResolver.getSchemaContext();
+ if(potentialCtx.isPresent()) {
+ listener.onGlobalContextUpdated(potentialCtx.get());
+ }
return listeners.register(listener);
}
}
- private void updateContext(SchemaContext snapshot) {
+ private void updateContext(final SchemaContext snapshot) {
Object[] services = listenerTracker.getServices();
if (services != null) {
for (Object rawListener : services) {
private class BundleScanner implements BundleTrackerCustomizer<ImmutableSet<Registration<URL>>> {
@Override
- public ImmutableSet<Registration<URL>> addingBundle(Bundle bundle, BundleEvent event) {
+ public ImmutableSet<Registration<URL>> addingBundle(final Bundle bundle, final BundleEvent event) {
if (bundle.getBundleId() == 0) {
return ImmutableSet.of();
}
@Override
- public void modifiedBundle(Bundle bundle, BundleEvent event, ImmutableSet<Registration<URL>> object) {
+ public void modifiedBundle(final Bundle bundle, final BundleEvent event, final ImmutableSet<Registration<URL>> object) {
logger.debug("Modified bundle {} {} {}", bundle, event, object);
}
*/
@Override
- public synchronized void removedBundle(Bundle bundle, BundleEvent event, ImmutableSet<Registration<URL>> urls) {
+ public synchronized void removedBundle(final Bundle bundle, final BundleEvent event, final ImmutableSet<Registration<URL>> urls) {
for (Registration<URL> url : urls) {
try {
url.close();
}
@Override
- public SchemaServiceListener addingService(ServiceReference<SchemaServiceListener> reference) {
+ public SchemaServiceListener addingService(final ServiceReference<SchemaServiceListener> reference) {
SchemaServiceListener listener = context.getService(reference);
SchemaContext _ctxContext = getGlobalContext();
}
@Override
- public void modifiedService(ServiceReference<SchemaServiceListener> reference, SchemaServiceListener service) {
+ public void modifiedService(final ServiceReference<SchemaServiceListener> reference, final SchemaServiceListener service) {
// NOOP
}
@Override
- public void removedService(ServiceReference<SchemaServiceListener> reference, SchemaServiceListener service) {
+ public void removedService(final ServiceReference<SchemaServiceListener> reference, final SchemaServiceListener service) {
context.ungetService(reference);
}
}
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Future;
import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import com.google.common.util.concurrent.ListenableFuture;
+
public class MountPointImpl implements MountProvisionInstance, SchemaContextProvider {
private final SchemaAwareRpcBroker rpcs;
@Override
- public Future<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input) {
+ public ListenableFuture<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input) {
return null;
}
import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
import org.opendaylight.controller.sal.dom.broker.spi.NotificationRouter;
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
public class NotificationRouterImpl implements NotificationRouter {
private static Logger log = LoggerFactory.getLogger(NotificationRouterImpl.class);
- private final Multimap<QName, Registration<NotificationListener>> listeners = Multimaps.synchronizedSetMultimap(HashMultimap.<QName, Registration<NotificationListener>>create());
+ private final Multimap<QName, ListenerRegistration> listeners = Multimaps.synchronizedSetMultimap(HashMultimap.<QName, ListenerRegistration>create());
// private Registration<NotificationListener> defaultListener;
private void sendNotification(CompositeNode notification) {
final QName type = notification.getNodeType();
- final Collection<Registration<NotificationListener>> toNotify = listeners.get(type);
+ final Collection<ListenerRegistration> toNotify = listeners.get(type);
log.trace("Publishing notification " + type);
if ((toNotify == null) || toNotify.isEmpty()) {
return;
}
- for (Registration<NotificationListener> listener : toNotify) {
+ for (ListenerRegistration listener : toNotify) {
try {
// FIXME: ensure that notification is immutable
listener.getInstance().onNotification(notification);
return ret;
}
- private class ListenerRegistration extends AbstractObjectRegistration<NotificationListener> {
+ private class ListenerRegistration extends AbstractListenerRegistration<NotificationListener> {
final QName type;
--- /dev/null
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.osgi.framework.ServiceReference;
+
+public class DOMDataBrokerProxy extends AbstractBrokerServiceProxy<DOMDataBroker> implements DOMDataBroker {
+
+ public DOMDataBrokerProxy(final ServiceReference<DOMDataBroker> ref, final DOMDataBroker delegate) {
+ super(ref, delegate);
+ }
+
+ @Override
+ public DOMDataReadTransaction newReadOnlyTransaction() {
+ return getDelegate().newReadOnlyTransaction();
+ }
+
+ @Override
+ public DOMDataReadWriteTransaction newReadWriteTransaction() {
+ return getDelegate().newReadWriteTransaction();
+ }
+
+ @Override
+ public DOMDataWriteTransaction newWriteOnlyTransaction() {
+ return getDelegate().newWriteOnlyTransaction();
+ }
+
+ @Override
+ public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+ final InstanceIdentifier path, final DOMDataChangeListener listener,
+ final org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope triggeringScope) {
+ return getDelegate().registerDataChangeListener(store, path, listener, triggeringScope);
+ }
+
+}
import org.opendaylight.controller.sal.core.api.model.SchemaService
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService
import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker
class ProxyFactory {
return createProxyImpl(serviceRef, service) as T;
}
+
private static def dispatch createProxyImpl(ServiceReference<?> ref, DataBrokerService service) {
new DataBrokerServiceProxy(ref as ServiceReference<DataBrokerService>, service);
}
private static def dispatch createProxyImpl(ServiceReference<?> ref, RpcProvisionRegistry service) {
new RpcProvisionRegistryProxy(ref as ServiceReference<RpcProvisionRegistry>, service);
}
+
+ private static def dispatch createProxyImpl(ServiceReference<?> ref, DOMDataBroker service) {
+ new DOMDataBrokerProxy(ref as ServiceReference<DOMDataBroker>, service)
+ }
+
private static def dispatch createProxyImpl(ServiceReference<?> reference, BrokerService service) {
throw new IllegalArgumentException("Not supported class");
config:java-name-prefix DomBrokerImpl;
}
+
+ identity dom-inmemory-data-broker {
+ base config:module-type;
+ config:provided-service sal:dom-async-data-broker;
+ }
+
identity hash-map-data-store {
base config:module-type;
config:provided-service sal:dom-data-store;
container data-store {
uses config:service-ref {
refine type {
- mandatory true;
+ mandatory false;
config:required-identity sal:dom-data-store;
}
}
}
+
+ container async-data-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory false;
+ config:required-identity sal:dom-async-data-broker;
+ }
+ }
+
+ }
+ }
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case dom-inmemory-data-broker {
+ when "/config:modules/config:module/config:type = 'dom-inmemory-data-broker'";
+
+ container schema-service {
+ uses config:service-ref {
+ refine type {
+ mandatory false;
+ config:required-identity sal:schema-service;
+ }
+ }
+
+ }
}
}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public class DOMBrokerPerformanceTest {
+
+ private static final Logger log = LoggerFactory.getLogger(DOMBrokerPerformanceTest.class);
+
+ private static NormalizedNode<?, ?> outerList(final int i) {
+ return ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i);
+ }
+
+ private static InstanceIdentifier outerListPath(final int i) {
+ return InstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)//
+ .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i) //
+ .build();
+ }
+
+ private SchemaContext schemaContext;
+ private DOMDataBrokerImpl domBroker;
+
+ private static <V> V measure(final String name, final Callable<V> callable) throws Exception {
+ // TODO Auto-generated method stub
+ log.debug("Measurement:{} Start", name);
+ long startNano = System.nanoTime();
+ try {
+ return callable.call();
+ } finally {
+ long endNano = System.nanoTime();
+ log.info("Measurement:\"{}\" Time:{} ms", name, (endNano - startNano) / 1000000.0d);
+ }
+ }
+
+ @Before
+ public void setupStore() {
+ InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
+ InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
+ schemaContext = TestModel.createTestContext();
+
+ operStore.onGlobalContextUpdated(schemaContext);
+ configStore.onGlobalContextUpdated(schemaContext);
+
+ ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore> builder() //
+ .put(CONFIGURATION, configStore) //
+ .put(OPERATIONAL, operStore) //
+ .build();
+ ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
+ domBroker = new DOMDataBrokerImpl(stores, executor);
+ }
+
+ @Test
+ public void testPerformance() throws Exception {
+ measure("Test Suite (all tests)", new Callable<Void>() {
+
+ @Override
+ public Void call() throws Exception {
+ smallTestSuite(10, 1000);
+ //smallTestSuite(10, 100);
+ smallTestSuite(100, 100);
+ //smallTestSuite(100, 100);
+ //smallTestSuite(1000, 10);
+ smallTestSuite(1000, 10);
+ //smallTestSuite(1000, 1000);
+ return null;
+ }
+ });
+ }
+
+ private void smallTestSuite(final int txNum, final int innerListWriteNum) throws Exception {
+ measure("TestSuite (Txs:" + txNum + " innerWrites:" + innerListWriteNum + ")", new Callable<Void>() {
+
+ @Override
+ public Void call() throws Exception {
+ measureOneTransactionTopContainer();
+ measureSeparateWritesOneLevel(txNum, innerListWriteNum);
+ return null;
+ }
+ });
+ }
+
+ private void measureSeparateWritesOneLevel(final int txNum, final int innerNum) throws Exception {
+ final List<DOMDataReadWriteTransaction> transactions = measure("Txs:"+ txNum + " Allocate",
+ new Callable<List<DOMDataReadWriteTransaction>>() {
+ @Override
+ public List<DOMDataReadWriteTransaction> call() throws Exception {
+ List<DOMDataReadWriteTransaction> builder = new ArrayList<>(txNum);
+ for (int i = 0; i < txNum; i++) {
+ DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction();
+ builder.add(writeTx);
+ }
+ return builder;
+ }
+ });
+ assertEquals(txNum, transactions.size());
+ measure("Txs:"+ txNum + " Writes:1", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ int i = 0;
+ for (DOMDataReadWriteTransaction writeTx :transactions) {
+ // Writes /test/outer-list/i in writeTx
+ writeTx.put(OPERATIONAL, outerListPath(i), outerList(i));
+ i++;
+ }
+ return null;
+ }
+ });
+
+ measure("Txs:"+ txNum + " Writes:" + innerNum, new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ int i = 0;
+ for (DOMDataReadWriteTransaction writeTx :transactions) {
+ // Writes /test/outer-list/i in writeTx
+ InstanceIdentifier path = InstanceIdentifier.builder(outerListPath(i))
+ .node(TestModel.INNER_LIST_QNAME).build();
+ writeTx.put(OPERATIONAL, path, ImmutableNodes.mapNodeBuilder(TestModel.INNER_LIST_QNAME).build());
+ for (int j = 0; j < innerNum; j++) {
+ InstanceIdentifier innerPath = InstanceIdentifier.builder(path)
+ .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, String.valueOf(j))
+ .build();
+ writeTx.put(
+ OPERATIONAL,
+ innerPath,
+ ImmutableNodes.mapEntry(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME,
+ String.valueOf(j)));
+ }
+ i++;
+ }
+ return null;
+ }
+ });
+
+ measure("Txs:" + txNum + " Submit, Finish", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ List<ListenableFuture<?>> allFutures = measure(txNum + " Submits",
+ new Callable<List<ListenableFuture<?>>>() {
+ @Override
+ public List<ListenableFuture<?>> call() throws Exception {
+ List<ListenableFuture<?>> builder = new ArrayList<>(txNum);
+ for (DOMDataReadWriteTransaction tx :transactions) {
+ builder.add(tx.commit());
+ }
+ return builder;
+ }
+ });
+ Futures.allAsList(allFutures).get();
+ return null;
+ }
+ });
+
+ final DOMDataReadTransaction readTx = measure("Txs:1 (ro), Allocate", new Callable<DOMDataReadTransaction>() {
+ @Override
+ public DOMDataReadTransaction call() throws Exception {
+ return domBroker.newReadOnlyTransaction();
+
+ }
+ });
+
+
+ measure("Txs:1 (ro) Reads:" + txNum + " (1-level)" , new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ for (int i = 0; i < txNum; i++) {
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> potential = readTx.read(OPERATIONAL,
+ outerListPath(i));
+ assertTrue("outerList/" + i, potential.get().isPresent());
+ }
+ return null;
+ }
+ });
+
+ measure("Txs:1 (ro) Reads:" + txNum * innerNum + " (2-level)", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ for (int i = 0; i < txNum; i++) {
+ for (int j = 0; j < innerNum; j++) {
+ InstanceIdentifier path = InstanceIdentifier
+ .builder(outerListPath(i))
+ //
+ .node(TestModel.INNER_LIST_QNAME)
+ .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, String.valueOf(j))
+ .build();
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> potential = readTx.read(OPERATIONAL, path);
+ assertTrue("outer-list/" + i + "/inner-list/" + j, potential.get().isPresent());
+ }
+ }
+ return null;
+ }
+ });
+ }
+
+ private void measureOneTransactionTopContainer() throws Exception {
+
+ final DOMDataReadWriteTransaction writeTx = measure("Txs:1 Allocate", new Callable<DOMDataReadWriteTransaction>() {
+ @Override
+ public DOMDataReadWriteTransaction call() throws Exception {
+ return domBroker.newReadWriteTransaction();
+ }
+ });
+
+ measure("Txs:1 Write", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+ writeTx.put(OPERATIONAL, TestModel.OUTER_LIST_PATH,
+ ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+ return null;
+ }
+ });
+
+ measure("Txs:1 Reads:1", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ // Reads /test in writeTx
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
+ TestModel.TEST_PATH);
+ assertTrue(writeTxContainer.get().isPresent());
+ return null;
+ }
+ });
+
+ measure("Txs:1 Reads:1", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ // Reads /test in writeTx
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
+ TestModel.TEST_PATH);
+ assertTrue(writeTxContainer.get().isPresent());
+ return null;
+ }
+ });
+
+ measure("Txs:1 Submit, Finish", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ measure("Txs:1 Submit", new Callable<ListenableFuture<?>>() {
+ @Override
+ public ListenableFuture<?> call() throws Exception {
+ return writeTx.commit();
+ }
+ }).get();
+ return null;
+ }
+ });
+ }
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public class DOMBrokerTest {
+
+ private SchemaContext schemaContext;
+ private DOMDataBrokerImpl domBroker;
+
+ @Before
+ public void setupStore() {
+ InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
+ InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
+ schemaContext = TestModel.createTestContext();
+
+ operStore.onGlobalContextUpdated(schemaContext);
+ configStore.onGlobalContextUpdated(schemaContext);
+
+ ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore> builder() //
+ .put(CONFIGURATION, configStore) //
+ .put(OPERATIONAL, operStore) //
+ .build();
+
+ ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
+ domBroker = new DOMDataBrokerImpl(stores, executor);
+ }
+
+ @Test
+ public void testTransactionIsolation() throws InterruptedException, ExecutionException {
+
+ assertNotNull(domBroker);
+
+ DOMDataReadTransaction readTx = domBroker.newReadOnlyTransaction();
+ assertNotNull(readTx);
+
+ DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction();
+ assertNotNull(writeTx);
+ /**
+ *
+ * Writes /test in writeTx
+ *
+ */
+ writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+ /**
+ *
+ * Reads /test from writeTx Read should return container.
+ *
+ */
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
+ TestModel.TEST_PATH);
+ assertTrue(writeTxContainer.get().isPresent());
+
+ /**
+ *
+ * Reads /test from readTx Read should return Absent.
+ *
+ */
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx
+ .read(OPERATIONAL, TestModel.TEST_PATH);
+ assertFalse(readTxContainer.get().isPresent());
+ }
+
+ @Test
+ public void testTransactionCommit() throws InterruptedException, ExecutionException {
+
+ DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction();
+ assertNotNull(writeTx);
+ /**
+ *
+ * Writes /test in writeTx
+ *
+ */
+ writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+ /**
+ *
+ * Reads /test from writeTx Read should return container.
+ *
+ */
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
+ TestModel.TEST_PATH);
+ assertTrue(writeTxContainer.get().isPresent());
+
+ writeTx.commit().get();
+
+ Optional<NormalizedNode<?, ?>> afterCommitRead = domBroker.newReadOnlyTransaction()
+ .read(OPERATIONAL, TestModel.TEST_PATH).get();
+ assertTrue(afterCommitRead.isPresent());
+ }
+
+
+
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class DataNormalizerTest {
+
+ private static final Short OUTER_LIST_ID = (short)10;
+
+ private static final InstanceIdentifier OUTER_LIST_PATH_LEGACY = InstanceIdentifier.builder(TestModel.TEST_QNAME)
+ .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME,OUTER_LIST_ID).build();
+
+ private static final InstanceIdentifier LEAF_TWO_PATH_LEGACY = InstanceIdentifier.builder(OUTER_LIST_PATH_LEGACY)
+ .node(TestModel.TWO_QNAME).build();
+
+ private static final ChoiceNode OUTER_CHOICE_ITEM = Builders.choiceBuilder()
+ .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_CHOICE_QNAME))
+ .withChild(ImmutableNodes.leafNode(TestModel.TWO_QNAME, "two"))
+ .withChild(ImmutableNodes.leafNode(TestModel.THREE_QNAME, "three"))
+ .build();
+
+ private static final MapEntryNode OUTER_LIST_WITHOUT_CHOICE = Builders.mapEntryBuilder()
+ .withNodeIdentifier(new NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME,OUTER_LIST_ID))
+ .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, OUTER_LIST_ID))
+ .build();
+
+ private static final MapEntryNode OUTER_LIST_WITH_CHOICE = Builders.mapEntryBuilder()
+ .withNodeIdentifier(new NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME,OUTER_LIST_ID))
+ .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, OUTER_LIST_ID))
+ .withChild(OUTER_CHOICE_ITEM)
+ .build();
+
+ @Test
+ public void test() {
+ SchemaContext testCtx = TestModel.createTestContext();
+ DataNormalizer normalizer = new DataNormalizer(testCtx);
+
+ InstanceIdentifier normalizedPath = normalizer.toNormalized(LEAF_TWO_PATH_LEGACY);
+
+ Node<?> outerListLegacy = normalizer.toLegacy(OUTER_LIST_WITH_CHOICE);
+ assertNotNull(outerListLegacy);
+
+
+
+
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.ExecutionException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public class InMemoryDataStoreTest {
+
+ private SchemaContext schemaContext;
+ private InMemoryDOMDataStore domStore;
+
+
+ @Before
+ public void setupStore() {
+ domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.sameThreadExecutor());
+ schemaContext = TestModel.createTestContext();
+ domStore.onGlobalContextUpdated(schemaContext);
+
+ }
+
+
+ @Test
+ public void testTransactionIsolation() throws InterruptedException, ExecutionException {
+
+ assertNotNull(domStore);
+
+
+ DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+ assertNotNull(readTx);
+
+ DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+ assertNotNull(writeTx);
+ /**
+ *
+ * Writes /test in writeTx
+ *
+ */
+ writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+ /**
+ *
+ * Reads /test from writeTx
+ * Read should return container.
+ *
+ */
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
+ assertTrue(writeTxContainer.get().isPresent());
+
+ /**
+ *
+ * Reads /test from readTx
+ * Read should return Absent.
+ *
+ */
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx.read(TestModel.TEST_PATH);
+ assertFalse(readTxContainer.get().isPresent());
+ }
+
+ @Test
+ public void testTransactionCommit() throws InterruptedException, ExecutionException {
+
+ DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+ assertNotNull(writeTx);
+ /**
+ *
+ * Writes /test in writeTx
+ *
+ */
+ writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+ /**
+ *
+ * Reads /test from writeTx
+ * Read should return container.
+ *
+ */
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
+ assertTrue(writeTxContainer.get().isPresent());
+
+ DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
+
+ assertThreePhaseCommit(cohort);
+
+ Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
+ assertTrue(afterCommitRead.isPresent());
+ }
+
+ @Test
+ public void testTransactionAbort() throws InterruptedException, ExecutionException {
+
+ DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+ assertNotNull(writeTx);
+
+ assertTestContainerWrite(writeTx);
+
+ DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
+
+ assertTrue(cohort.canCommit().get().booleanValue());
+ cohort.preCommit().get();
+ cohort.abort().get();
+
+ Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
+ assertFalse(afterCommitRead.isPresent());
+ }
+
+ @Test
+ public void testTransactionConflict() throws InterruptedException, ExecutionException {
+ DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
+ DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
+ assertTestContainerWrite(txOne);
+ assertTestContainerWrite(txTwo);
+
+ /**
+ * Commits transaction
+ */
+ assertThreePhaseCommit(txOne.ready());
+
+ /**
+ * Asserts that txTwo could not be commited
+ */
+ assertFalse(txTwo.ready().canCommit().get());
+ }
+
+
+
+ private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort) throws InterruptedException, ExecutionException {
+ assertTrue(cohort.canCommit().get().booleanValue());
+ cohort.preCommit().get();
+ cohort.commit().get();
+ }
+
+
+ private static Optional<NormalizedNode<?, ?>> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx) throws InterruptedException, ExecutionException {
+ /**
+ *
+ * Writes /test in writeTx
+ *
+ */
+ writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+ /**
+ *
+ * Reads /test from writeTx
+ * Read should return container.
+ *
+ */
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
+ assertTrue(writeTxContainer.get().isPresent());
+ return writeTxContainer.get();
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.ID_QNAME;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.INNER_LIST_QNAME;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.NAME_QNAME;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.OUTER_LIST_PATH;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.OUTER_LIST_QNAME;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.TEST_PATH;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.TEST_QNAME;
+import static org.opendaylight.controller.md.sal.dom.store.impl.TestModel.VALUE_QNAME;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+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.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+import com.google.common.primitives.UnsignedLong;
+
+/**
+ *
+ * Schema structure of document is
+ *
+ * <pre>
+ * container root {Â
+ * list list-a {
+ * key leaf-a;
+ * leaf leaf-a;
+ * choice choice-a {
+ * case one {
+ * leaf one;
+ * }
+ * case two-three {
+ * leaf two;
+ * leaf three;
+ * }
+ * }
+ * list list-b {
+ * key leaf-b;
+ * leaf leaf-b;
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ */
+public class ModificationMetadataTreeTest {
+
+ private static final Short ONE_ID = 1;
+ private static final Short TWO_ID = 2;
+ private static final String TWO_ONE_NAME = "one";
+ private static final String TWO_TWO_NAME = "two";
+
+ private static final InstanceIdentifier OUTER_LIST_1_PATH = InstanceIdentifier.builder(OUTER_LIST_PATH)
+ .nodeWithKey(OUTER_LIST_QNAME, ID_QNAME, ONE_ID) //
+ .build();
+
+ private static final InstanceIdentifier OUTER_LIST_2_PATH = InstanceIdentifier.builder(OUTER_LIST_PATH)
+ .nodeWithKey(OUTER_LIST_QNAME, ID_QNAME, TWO_ID) //
+ .build();
+
+ private static final InstanceIdentifier TWO_TWO_PATH = InstanceIdentifier.builder(OUTER_LIST_2_PATH)
+ .node(INNER_LIST_QNAME) //
+ .nodeWithKey(INNER_LIST_QNAME, NAME_QNAME, TWO_TWO_NAME) //
+ .build();
+
+ private static final InstanceIdentifier TWO_TWO_VALUE_PATH = InstanceIdentifier.builder(TWO_TWO_PATH)
+ .node(VALUE_QNAME) //
+ .build();
+
+ private static final MapEntryNode BAR_NODE = mapEntryBuilder(OUTER_LIST_QNAME, ID_QNAME, TWO_ID) //
+ .withChild(mapNodeBuilder(INNER_LIST_QNAME) //
+ .withChild(mapEntry(INNER_LIST_QNAME, NAME_QNAME, TWO_ONE_NAME)) //
+ .withChild(mapEntry(INNER_LIST_QNAME, NAME_QNAME, TWO_TWO_NAME)) //
+ .build()) //
+ .build();
+
+ private SchemaContext schemaContext;
+
+ @Before
+ public void prepare() {
+ schemaContext = TestModel.createTestContext();
+ assertNotNull("Schema context must not be null.", schemaContext);
+ }
+
+ /**
+ * Returns a test document
+ *
+ * <pre>
+ * test
+ * outer-list
+ * id 1
+ * outer-list
+ * id 2
+ * inner-list
+ * name "one"
+ * inner-list
+ * name "two"
+ *
+ * </pre>
+ *
+ * @return
+ */
+ public NormalizedNode<?, ?> createDocumentOne() {
+ return ImmutableContainerNodeBuilder
+ .create()
+ .withNodeIdentifier(new NodeIdentifier(schemaContext.getQName()))
+ .withChild(createTestContainer()).build();
+
+ }
+
+ private ContainerNode createTestContainer() {
+ return ImmutableContainerNodeBuilder
+ .create()
+ .withNodeIdentifier(new NodeIdentifier(TEST_QNAME))
+ .withChild(
+ mapNodeBuilder(OUTER_LIST_QNAME)
+ .withChild(mapEntry(OUTER_LIST_QNAME, ID_QNAME, ONE_ID))
+ .withChild(BAR_NODE).build()).build();
+ }
+
+ @Test
+ public void basicReadWrites() {
+ MutableDataTree modificationTree = MutableDataTree.from(
+ DataAndMetadataSnapshot.builder() //
+ .setMetadataTree(StoreMetadataNode.createRecursivelly(createDocumentOne(), UnsignedLong.valueOf(5))) //
+ .setSchemaContext(schemaContext) //
+ .build(), new SchemaAwareApplyOperationRoot(schemaContext));
+ Optional<NormalizedNode<?, ?>> originalBarNode = modificationTree.read(OUTER_LIST_2_PATH);
+ assertTrue(originalBarNode.isPresent());
+ assertSame(BAR_NODE, originalBarNode.get());
+
+ // writes node to /outer-list/1/inner_list/two/value
+ modificationTree.write(TWO_TWO_VALUE_PATH, ImmutableNodes.leafNode(VALUE_QNAME, "test"));
+
+ // reads node to /outer-list/1/inner_list/two/value
+ // and checks if node is already present
+ Optional<NormalizedNode<?, ?>> barTwoCModified = modificationTree.read(TWO_TWO_VALUE_PATH);
+ assertTrue(barTwoCModified.isPresent());
+ assertEquals(ImmutableNodes.leafNode(VALUE_QNAME, "test"), barTwoCModified.get());
+
+ // delete node to /outer-list/1/inner_list/two/value
+ modificationTree.delete(TWO_TWO_VALUE_PATH);
+ Optional<NormalizedNode<?, ?>> barTwoCAfterDelete = modificationTree.read(TWO_TWO_VALUE_PATH);
+ assertFalse(barTwoCAfterDelete.isPresent());
+ }
+
+
+ public MutableDataTree createEmptyModificationTree() {
+ /**
+ * Creates empty Snapshot with associated schema context.
+ */
+ DataAndMetadataSnapshot emptySnapshot = DataAndMetadataSnapshot.createEmpty(schemaContext);
+
+ /**
+ *
+ * Creates Mutable Data Tree based on provided snapshot and schema
+ * context.
+ *
+ */
+ MutableDataTree modificationTree = MutableDataTree.from(emptySnapshot, new SchemaAwareApplyOperationRoot(
+ schemaContext));
+ return modificationTree;
+ }
+
+ @Test
+ public void createFromEmptyState() {
+
+ MutableDataTree modificationTree = createEmptyModificationTree();
+ /**
+ * Writes empty container node to /test
+ *
+ */
+ modificationTree.write(TEST_PATH, ImmutableNodes.containerNode(TEST_QNAME));
+
+ /**
+ * Writes empty list node to /test/outer-list
+ */
+ modificationTree.write(OUTER_LIST_PATH, ImmutableNodes.mapNodeBuilder(OUTER_LIST_QNAME).build());
+
+ /**
+ * Reads list node from /test/outer-list
+ */
+ Optional<NormalizedNode<?, ?>> potentialOuterList = modificationTree.read(OUTER_LIST_PATH);
+ assertTrue(potentialOuterList.isPresent());
+
+ /**
+ * Reads container node from /test and verifies that it contains test
+ * node
+ */
+ Optional<NormalizedNode<?, ?>> potentialTest = modificationTree.read(TEST_PATH);
+ ContainerNode containerTest = assertPresentAndType(potentialTest, ContainerNode.class);
+
+ /**
+ *
+ * Gets list from returned snapshot of /test and verifies it contains
+ * outer-list
+ *
+ */
+ assertPresentAndType(containerTest.getChild(new NodeIdentifier(OUTER_LIST_QNAME)), MapNode.class);
+
+ }
+
+ @Test
+ public void writeSubtreeReadChildren() {
+ MutableDataTree modificationTree = createEmptyModificationTree();
+ modificationTree.write(TEST_PATH, createTestContainer());
+ Optional<NormalizedNode<?, ?>> potential = modificationTree.read(TWO_TWO_PATH);
+ MapEntryNode node = assertPresentAndType(potential, MapEntryNode.class);
+ }
+
+ @Test
+ public void writeSubtreeDeleteChildren() {
+ MutableDataTree modificationTree = createEmptyModificationTree();
+ modificationTree.write(TEST_PATH, createTestContainer());
+
+ // We verify data are present
+ Optional<NormalizedNode<?, ?>> potentialBeforeDelete = modificationTree.read(TWO_TWO_PATH);
+ MapEntryNode node = assertPresentAndType(potentialBeforeDelete, MapEntryNode.class);
+
+ modificationTree.delete(TWO_TWO_PATH);
+ Optional<NormalizedNode<?, ?>> potentialAfterDelete = modificationTree.read(TWO_TWO_PATH);
+ assertFalse(potentialAfterDelete.isPresent());
+
+ }
+
+ private static <T> T assertPresentAndType(final Optional<?> potential, final Class<T> type) {
+ assertNotNull(potential);
+ assertTrue(potential.isPresent());
+ assertTrue(type.isInstance(potential.get()));
+ return type.cast(potential.get());
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+public class TestModel {
+
+ public static final QName TEST_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13",
+ "test");
+ public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list");
+ public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list");
+ public static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME, "outer-choice");
+ public static final QName ID_QNAME = QName.create(TEST_QNAME, "id");
+ public static final QName NAME_QNAME = QName.create(TEST_QNAME, "name");
+ public static final QName VALUE_QNAME = QName.create(TEST_QNAME, "value");
+ private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang";
+
+ public static final InstanceIdentifier TEST_PATH = InstanceIdentifier.of(TEST_QNAME);
+ public static final InstanceIdentifier OUTER_LIST_PATH = InstanceIdentifier.builder(TEST_PATH).node(OUTER_LIST_QNAME).build();
+ public static final QName TWO_QNAME = QName.create(TEST_QNAME,"two");
+ public static final QName THREE_QNAME = QName.create(TEST_QNAME,"three");
+
+
+ public static final InputStream getDatastoreTestInputStream() {
+ return getInputStream(DATASTORE_TEST_YANG);
+ }
+
+ private static InputStream getInputStream(final String resourceName) {
+ return TestModel.class.getResourceAsStream(DATASTORE_TEST_YANG);
+ }
+
+ public static SchemaContext createTestContext() {
+ YangParserImpl parser = new YangParserImpl();
+ Set<Module> modules = parser.parseYangModelsFromStreams(Collections.singletonList(getDatastoreTestInputStream()));
+ return parser.resolveSchemaContext(modules);
+ }
+}
--- /dev/null
+module odl-datastore-test {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test";
+ prefix "store-test";
+
+ revision "2014-03-13" {
+ description "Initial revision.";
+ }
+
+ container test {
+ list outer-list {
+ key id;
+ leaf id {
+ type uint16;
+ }
+ choice outer-choice {
+ case one {
+ leaf one {
+ type string;
+ }
+ }
+ case two-three {
+ leaf two {
+ type string;
+ }
+ leaf three {
+ type string;
+ }
+ }
+ }
+ list inner-list {
+ key name;
+ leaf name {
+ type string;
+ }
+ leaf value {
+ type string;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /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.sal.core.spi.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMStore {
+
+ /**
+ *
+ * Creates a read only transaction
+ *
+ * @return
+ */
+ DOMStoreReadTransaction newReadOnlyTransaction();
+
+ /**
+ * Creates write only transaction
+ *
+ * @return
+ */
+ DOMStoreWriteTransaction newWriteOnlyTransaction();
+
+ /**
+ * Creates Read-Write transaction
+ *
+ * @return
+ */
+ DOMStoreReadWriteTransaction newReadWriteTransaction();
+
+ /**
+ * Registers {@link DataChangeListener} for Data Change callbacks
+ * which will be triggered on the change of provided subpath. What
+ * constitutes a change depends on the @scope parameter.
+ *
+ * Listener upon registration receives an initial callback
+ * {@link AsyncDataChangeListener#onDataChanged(org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent)}
+ * which contains stable view of data tree at the time of registration.
+ *
+ *Â @param path Path (subtree identifier) on which client listener will be invoked.
+ * @param listener Instance of listener which should be invoked on
+ * @param scope Scope of change which triggers callback.
+ * @return Listener Registration object, which client may use to close registration
+ * / interest on receiving data changes.
+ *
+ */
+ <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
+ InstanceIdentifier path, L listener, DataChangeScope scope);
+
+}
--- /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.sal.core.spi.data;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public interface DOMStoreReadTransaction extends DOMStoreTransaction {
+
+ /**
+ *
+ * Reads data from provided logical data store located at provided path
+ *
+ *
+ * @param path
+ * Path which uniquely identifies subtree which client want to
+ * read
+ * @return Listenable Future which contains read result
+ * <ul>
+ * <li>If data at supplied path exists the {@link Future#get()}
+ * returns Optional object containing data
+ * <li>If data at supplied path does not exists the
+ * {@link Future#get()} returns {@link Optional#absent()}.
+ * </ul>
+ */
+ ListenableFuture<Optional<NormalizedNode<?,?>>> read(InstanceIdentifier 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.sal.core.spi.data;
+
+/**
+ * Combination of a {@link DOMStoreReadTransaction} and {@link DOMStoreWriteTransaction}.
+ */
+public interface DOMStoreReadWriteTransaction extends DOMStoreReadTransaction, DOMStoreWriteTransaction {
+
+}
--- /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.sal.core.spi.data;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Interface implemented by the {@link DOMStore} and exposed for each {@link DOMStoreWriteTransaction}
+ * upon its transition to Ready state. The frontend (DOMStore user) uses this interface to drive the
+ * commit procedure across potentially multiple DOMStores using the Three-Phase-Commit (3PC) Protocol,
+ * as described in {@link https://en.wikipedia.org/wiki/Three-phase_commit}.
+ */
+public interface DOMStoreThreePhaseCommitCohort {
+
+ /**
+ * Sends transaction associated with this three phase commit instance to the
+ * participant, participant votes on the transaction, if the transaction
+ * should be committed or aborted.
+ *
+ * @return ListenableFuture with vote of the participant. Vote
+ * {@link ListenableFuture#get()} is following:
+ * <ul>
+ * <li>
+ * true if transaction is approved by data store.
+ * <li>false if the transaction is not approved by data store and
+ * should be aborted.
+ */
+ ListenableFuture<Boolean> canCommit();
+
+ /**
+ * Initiates a pre-commit phase of associated transaction on datastore.
+ *
+ * This message is valid only and only if and only if the participant responded
+ * on {@link #canCommit()} call with positive response.
+ *
+ * @return ListenableFuture representing acknowledgment for participant
+ * that pre-commit message was received and processed.
+ */
+ ListenableFuture<Void> preCommit();
+
+ /**
+ * Initiates a abort phase of associated transaction on data store.
+ *
+ * @return ListenableFuture representing acknowledgment for participant
+ * that abort message was received.
+ */
+ ListenableFuture<Void> abort();
+
+ /**
+ * Initiates a commit phase on of associated transaction on data store.
+ *
+ * @return ListenableFuture representing acknowledgment for participant
+ * that commit message was received and commit of transaction was
+ * processed.
+ */
+ ListenableFuture<Void> commit();
+}
--- /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.sal.core.spi.data;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+
+/**
+ * DOM Data Store transaction
+ *
+ * See {@link DOMStoreReadTransaction}, {@link DOMStoreWriteTransaction} and {@link DOMStoreReadWriteTransaction}
+ * for specific transaction types.
+ *
+ */
+public interface DOMStoreTransaction extends AutoCloseable, Identifiable<Object> {
+ /**
+ * Unique identifier of the transaction
+ */
+ @Override
+ public Object getIdentifier();
+
+ @Override
+ void close();
+}
--- /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.sal.core.spi.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMStoreWriteTransaction extends DOMStoreTransaction {
+
+ /**
+ * Store a provided data at specified path. This acts as a add / replace
+ * operation, which is to say that whole subtree will be replaced by
+ * specified path.
+ *
+ * If you need add or merge of current object with specified use
+ * {@link #merge(LogicalDatastoreType, Path, Object)}
+ *
+ *
+ * @param path
+ * @param data
+ * Data object to be written
+ *
+ * @throws IllegalStateException
+ * if the client code already sealed transaction and invoked
+ * {@link #ready()}
+ */
+ void write(InstanceIdentifier path, NormalizedNode<?, ?> data);
+
+ /**
+ *
+ * Deletes data and whole subtree located at provided path.
+ *
+ * @param path
+ * Path to delete
+ * @throws IllegalStateException
+ * if the client code already sealed transaction and invoked
+ * {@link #ready()}
+ */
+ void delete(InstanceIdentifier path);
+
+ /**
+ *
+ * Seals transaction, and returns three-phase commit cohort associated
+ * with this transaction and DOM Store to be coordinated by coordinator.
+ *
+ * @return Three Phase Commit Cohort instance for this transaction.
+ */
+ DOMStoreThreePhaseCommitCohort ready();
+
+}
--- /dev/null
+/*\r
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+package org.opendaylight.controller.sal.core.spi.data;
\ No newline at end of file
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-impl</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-broker-impl</artifactId>
</instructions>
</configuration>
</plugin>
+
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
</goals>
<configuration>
<codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ ${salGeneratorPath}
+ </outputBaseDir>
+ </generator>
<generator>
<codeGeneratorClass>
org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
</dependency>
</dependencies>
</plugin>
+
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<instructions>
<Import-Package>
*,
- com.google.common.collect,
!org.codehaus.enunciate.jaxrs
</Import-Package>
<Export-Package>
</plugins>
<pluginManagement>
<plugins>
- <!--This plugin's configuration is used to store Eclipse
+ <!--This plugin's configuration is used to store Eclipse
m2e settings only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-api</artifactId>
</dependency>
- <!--dependency> <groupId>org.opendaylight.yangtools</groupId> <artifactId>yang-data-impl</artifactId>
+ <!--dependency> <groupId>org.opendaylight.yangtools</groupId> <artifactId>yang-data-impl</artifactId>
<version>${yangtools.version}</version> </dependency -->
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
+ <artifactId>config-persister-file-xml-adapter</artifactId>
<version>${config.version}</version>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam</artifactId>
<version>${exam.version}</version>
- <!-- Compile scope here is intentional, it is used in TestHelper
- class which could be downloaded via nexus and reused in other integration
+ <!-- Compile scope here is intentional, it is used in TestHelper
+ class which could be downloaded via nexus and reused in other integration
tests. -->
<scope>compile</scope>
</dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-remote</artifactId>
+ </dependency>
<!-- Testing Dependencies -->
<dependency>
return callRpc(rpc, null)
}
- def resolveIdentifierInInvokeRpc(String identifier) {
+ private def resolveIdentifierInInvokeRpc(String identifier) {
if (identifier.indexOf("/") === -1) {
val identifierDecoded = identifier.urlPathArgDecode
val rpc = identifierDecoded.rpcDefinition
}
throw new ResponseException(NOT_FOUND, "RPC does not exist.");
}
- val slashErrorMsg = String.format("Identifier %n%s%ncan't contain slash character (/). +
- If slash is part of identifier name then use %2F placeholder.",identifier)
+ val slashErrorMsg = String.format(
+ "Identifier %n%s%ncan't contain slash character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier)
throw new ResponseException(NOT_FOUND, slashErrorMsg);
}
*/
package org.opendaylight.controller.sal.restconf.impl.test;
+import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import org.opendaylight.controller.sal.core.api.mount.MountInstance;
import org.opendaylight.controller.sal.core.api.mount.MountService;
import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import static junit.framework.Assert.assertNotNull;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
public class RestGetOperationTest extends JerseyTest {
assertEquals(200, get(uri, MediaType.APPLICATION_XML));
}
+ /**
+ * MountPoint test. URI represents mount point.
+ *
+ * Slashes in URI behind mount point. lst1 element with key
+ * GigabitEthernet0%2F0%2F0%2F0 (GigabitEthernet0/0/0/0) is requested via
+ * GET HTTP operation. It is tested whether %2F character is replaced with
+ * simple / in InstanceIdentifier parameter in method
+ * {@link BrokerFacade#readConfigurationDataBehindMountPoint(MountInstance, InstanceIdentifier)}
+ * which is called in method {@link RestconfImpl#readConfigurationData}
+ *
+ *
+ * @throws ParseException
+ */
+ @Test
+ public void getDataWithSlashesBehindMountPoint() throws UnsupportedEncodingException, URISyntaxException,
+ ParseException {
+ InstanceIdentifier awaitedInstanceIdentifier = prepareInstanceIdentifierForList();
+ when(
+ brokerFacade.readConfigurationDataBehindMountPoint(any(MountInstance.class),
+ eq(awaitedInstanceIdentifier))).thenReturn(prepareCnDataForMountPointTest());
+ MountInstance mountInstance = mock(MountInstance.class);
+ when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
+ MountService mockMountService = mock(MountService.class);
+ when(mockMountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
+
+ ControllerContext.getInstance().setMountService(mockMountService);
+
+ String uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont/lst1/GigabitEthernet0%2F0%2F0%2F0";
+ assertEquals(200, get(uri, MediaType.APPLICATION_XML));
+ }
+
+ private InstanceIdentifier prepareInstanceIdentifierForList() throws URISyntaxException, ParseException {
+ List<PathArgument> parameters = new ArrayList<>();
+
+ Date revision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-01-09");
+ URI uri = new URI("test:module");
+ QName qNameCont = QName.create(uri, revision, "cont");
+ QName qNameList = QName.create(uri, revision, "lst1");
+ QName qNameKeyList = QName.create(uri, revision, "lf11");
+
+ parameters.add(new InstanceIdentifier.NodeIdentifier(qNameCont));
+ parameters.add(new InstanceIdentifier.NodeIdentifierWithPredicates(qNameList, qNameKeyList,
+ "GigabitEthernet0/0/0/0"));
+ return new InstanceIdentifier(parameters);
+ }
+
@Test
public void getDataMountPointIntoHighestElement() throws UnsupportedEncodingException, URISyntaxException {
when(
response = target(uri).request("application/yang.api+xml").get();
validateModulesResponseXml(response);
}
+
// /streams/
@Test
public void getStreamsTest() throws UnsupportedEncodingException, FileNotFoundException {
assertTrue("Module2 in xml wasn't found", prepareXmlRegex("module2", "2014-01-02", "module:2", responseBody)
.find());
String[] split = responseBody.split("<module");
- assertEquals("<module element is returned more then once",2,split.length);
+ assertEquals("<module element is returned more then once", 2, split.length);
response = target(uri).request("application/yang.api+json").get();
assertEquals(200, response.getStatus());
assertTrue("Module2 in json wasn't found", prepareJsonRegex("module2", "2014-01-02", "module:2", responseBody)
.find());
split = responseBody.split("\"module\"");
- assertEquals("\"module\" element is returned more then once",2,split.length);
+ assertEquals("\"module\" element is returned more then once", 2, split.length);
}
prepareJsonRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point",
responseBody).find());
String[] split = responseBody.split("\"module\"");
- assertEquals("\"module\" element is returned more then once",2,split.length);
-
+ assertEquals("\"module\" element is returned more then once", 2, split.length);
response = target(uri).request("application/yang.api+xml").get();
assertEquals(200, response.getStatus());
prepareXmlRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", responseBody)
.find());
split = responseBody.split("<module");
- assertEquals("<module element is returned more then once",2,split.length);
-
-
-
+ assertEquals("<module element is returned more then once", 2, split.length);
}
type string;
}
}
- }
+ list lst1 {
+ key "lf11";
+ leaf lf11 {
+ type string;
+ }
+ }
+ }
rpc rpc-test {
--- /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>
+ <artifactId>sal-samples</artifactId>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <version>1.1-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <groupId>org.opendaylight.controller.samples.l2switch.md</groupId>
+ <artifactId>l2switch-impl</artifactId>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+
+ <instructions>
+ <Bundle-Activator>org.opendaylight.controller.sample.l2switch.md.L2SwitchProvider</Bundle-Activator>
+ </instructions>
+ <manifestLocation>${project.build.directory}/META-INF</manifestLocation>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>${junit.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples.l2switch.md</groupId>
+ <artifactId>l2switch-model</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-inventory</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-flow-service</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>net.sf.jung2</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-topology</artifactId>
+ <version>1.1-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
--- /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.sample.l2switch.md;
+
+import org.opendaylight.controller.sample.l2switch.md.addresstracker.AddressTracker;
+import org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService;
+import org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterServiceImpl;
+import org.opendaylight.controller.sample.l2switch.md.inventory.InventoryService;
+import org.opendaylight.controller.sample.l2switch.md.packet.PacketHandler;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphDijkstra;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService;
+import org.opendaylight.controller.sample.l2switch.md.topology.TopologyLinkDataChangeHandler;
+import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareConsumer;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * L2SwitchProvider serves as the Activator for our L2Switch OSGI bundle.
+ */
+public class L2SwitchProvider extends AbstractBindingAwareConsumer
+ implements AutoCloseable {
+
+ private final static Logger _logger = LoggerFactory.getLogger(L2SwitchProvider.class);
+
+ private Registration<NotificationListener> listenerRegistration;
+ private AddressTracker addressTracker;
+ private TopologyLinkDataChangeHandler topologyLinkDataChangeHandler;
+
+
+ /**
+ * Setup the L2Switch.
+ * @param consumerContext The context of the L2Switch.
+ */
+ @Override
+ public void onSessionInitialized(BindingAwareBroker.ConsumerContext consumerContext) {
+ DataBrokerService dataService = consumerContext.<DataBrokerService>getSALService(DataBrokerService.class);
+ addressTracker = new AddressTracker(dataService);
+
+ NetworkGraphService networkGraphService = new NetworkGraphDijkstra();
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataService, networkGraphService);
+
+ NotificationService notificationService =
+ consumerContext.<NotificationService>getSALService(NotificationService.class);
+ PacketProcessingService packetProcessingService =
+ consumerContext.<PacketProcessingService>getRpcService(PacketProcessingService.class);
+ PacketHandler packetHandler = new PacketHandler();
+ packetHandler.setAddressTracker(addressTracker);
+ packetHandler.setFlowWriterService(flowWriterService);
+ packetHandler.setPacketProcessingService(packetProcessingService);
+ packetHandler.setInventoryService(new InventoryService(dataService));
+
+ this.listenerRegistration = notificationService.registerNotificationListener(packetHandler);
+ this.topologyLinkDataChangeHandler = new TopologyLinkDataChangeHandler(dataService, networkGraphService);
+ topologyLinkDataChangeHandler.registerAsDataChangeListener();
+ }
+
+ /**
+ * Cleanup the L2Switch.
+ * @throws Exception occurs when the NotificationListener is closed
+ */
+ @Override
+ public void close() throws Exception {
+ if (listenerRegistration != null)
+ listenerRegistration.close();
+ }
+}
--- /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.sample.l2switch.md.addresstracker;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.L2Addresses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2AddressBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.address.tracker.rev140402.l2.addresses.L2AddressKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Future;
+
+/**
+ * AddressTracker manages the MD-SAL data tree for L2Address (mac, node connector pairings) information.
+ */
+public class AddressTracker {
+
+ private final static Logger _logger = LoggerFactory.getLogger(AddressTracker.class);
+ private DataBrokerService dataService;
+
+ /**
+ * Construct an AddressTracker with the specified inputs
+ * @param dataService The DataBrokerService for the AddressTracker
+ */
+ public AddressTracker(DataBrokerService dataService) {
+ this.dataService = dataService;
+ }
+
+ /**
+ * Get all the L2 Addresses in the MD-SAL data tree
+ * @return All the L2 Addresses in the MD-SAL data tree
+ */
+ public L2Addresses getAddresses() {
+ return (L2Addresses)dataService.readOperationalData(InstanceIdentifier.<L2Addresses>builder(L2Addresses.class).toInstance());
+ }
+
+ /**
+ * Get a specific L2 Address in the MD-SAL data tree
+ * @param macAddress A MacAddress associated with an L2 Address object
+ * @return The L2 Address corresponding to the specified macAddress
+ */
+ public L2Address getAddress(MacAddress macAddress) {
+ return (L2Address) dataService.readOperationalData(createPath(macAddress));
+ }
+
+ /**
+ * Add L2 Address into the MD-SAL data tree
+ * @param macAddress The MacAddress of the new L2Address object
+ * @param nodeConnectorRef The NodeConnectorRef of the new L2Address object
+ * @return Future containing the result of the add operation
+ */
+ public Future<RpcResult<TransactionStatus>> addAddress(MacAddress macAddress, NodeConnectorRef nodeConnectorRef) {
+ if(macAddress == null || nodeConnectorRef == null) {
+ return null;
+ }
+
+ // Create L2Address
+ final L2AddressBuilder builder = new L2AddressBuilder();
+ builder.setKey(new L2AddressKey(macAddress))
+ .setMac(macAddress)
+ .setNodeConnectorRef(nodeConnectorRef);
+
+ // Add L2Address to MD-SAL data tree
+ final DataModificationTransaction it = dataService.beginTransaction();
+ it.putOperationalData(createPath(macAddress), builder.build());
+ return it.commit();
+ }
+
+ /**
+ * Remove L2Address from the MD-SAL data tree
+ * @param macAddress The MacAddress of an L2Address object
+ * @return Future containing the result of the remove operation
+ */
+ public Future<RpcResult<TransactionStatus>> removeHost(MacAddress macAddress) {
+ final DataModificationTransaction it = dataService.beginTransaction();
+ it.removeOperationalData(createPath(macAddress));
+ return it.commit();
+ }
+
+ /**
+ * Create InstanceIdentifier path for an L2Address in the MD-SAL data tree
+ * @param macAddress The MacAddress of an L2Address object
+ * @return InstanceIdentifier of the L2Address corresponding to the specified macAddress
+ */
+ private InstanceIdentifier<L2Address> createPath(MacAddress macAddress) {
+ return InstanceIdentifier.<L2Addresses>builder(L2Addresses.class)
+ .<L2Address, L2AddressKey>child(L2Address.class, new L2AddressKey(macAddress)).toInstance();
+ }
+}
\ No newline at end of file
--- /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.sample.l2switch.md.flow;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+
+/**
+ * Service that adds packet forwarding flows to configuration data store.
+ */
+public interface FlowWriterService {
+
+ /**
+ * Writes a flow that forwards packets to destPort if destination mac in packet is destMac and
+ * source Mac in packet is sourceMac. If sourceMac is null then flow would not set any source mac,
+ * resulting in all packets with destMac being forwarded to destPort.
+ *
+ * @param sourceMac
+ * @param destMac
+ * @param destNodeConnectorRef
+ */
+ public void addMacToMacFlow(MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destNodeConnectorRef);
+
+ /**
+ * Writes mac-to-mac flow on all ports that are in the path between given source and destination ports.
+ * It uses path provided by NetworkGraphService{@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService} to find a links{@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * between given ports. And then writes appropriate flow on each port that is covered in that path.
+ *
+ * @param sourceMac
+ * @param sourceNodeConnectorRef
+ * @param destMac
+ * @param destNodeConnectorRef
+ */
+ public void addMacToMacFlowsUsingShortestPath(MacAddress sourceMac, NodeConnectorRef sourceNodeConnectorRef, MacAddress destMac, NodeConnectorRef destNodeConnectorRef);
+
+
+}
--- /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.sample.l2switch.md.flow;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
+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.FlowId;
+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.Match;
+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.ApplyActions;
+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.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+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.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
+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.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.util.List;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Implementation of FlowWriterService{@link org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService},
+ * that builds required flow and writes to configuration data store using provided DataBrokerService
+ * {@link org.opendaylight.controller.sal.binding.api.data.DataBrokerService}
+ */
+public class FlowWriterServiceImpl implements FlowWriterService {
+ private static final Logger _logger = LoggerFactory.getLogger(FlowWriterServiceImpl.class);
+ private final DataBrokerService dataBrokerService;
+ private final NetworkGraphService networkGraphService;
+ private AtomicLong flowIdInc = new AtomicLong();
+ private AtomicLong flowCookieInc = new AtomicLong(0x2a00000000000000L);
+
+
+ public FlowWriterServiceImpl(DataBrokerService dataBrokerService, NetworkGraphService networkGraphService) {
+ Preconditions.checkNotNull(dataBrokerService, "dataBrokerService should not be null.");
+ Preconditions.checkNotNull(networkGraphService, "networkGraphService should not be null.");
+ this.dataBrokerService = dataBrokerService;
+ this.networkGraphService = networkGraphService;
+ }
+
+ /**
+ * Writes a flow that forwards packets to destPort if destination mac in packet is destMac and
+ * source Mac in packet is sourceMac. If sourceMac is null then flow would not set any source mac,
+ * resulting in all packets with destMac being forwarded to destPort.
+ *
+ * @param sourceMac
+ * @param destMac
+ * @param destNodeConnectorRef
+ */
+ @Override
+ public void addMacToMacFlow(MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destNodeConnectorRef) {
+
+ Preconditions.checkNotNull(destMac, "Destination mac address should not be null.");
+ Preconditions.checkNotNull(destNodeConnectorRef, "Destination port should not be null.");
+
+
+ // do not add flow if both macs are same.
+ if(sourceMac != null && destMac.equals(sourceMac)) {
+ _logger.info("In addMacToMacFlow: No flows added. Source and Destination mac are same.");
+ return;
+ }
+
+ // get flow table key
+ TableKey flowTableKey = new TableKey((short) 0); //TODO: Hard coded Table Id 0, need to get it from Configuration data.
+
+ //build a flow path based on node connector to program flow
+ InstanceIdentifier<Flow> flowPath = buildFlowPath(destNodeConnectorRef, flowTableKey);
+
+ // build a flow that target given mac id
+ Flow flowBody = createMacToMacFlow(flowTableKey.getId(), 0, sourceMac, destMac, destNodeConnectorRef);
+
+ // commit the flow in config data
+ writeFlowToConfigData(flowPath, flowBody);
+ }
+
+ /**
+ * Writes mac-to-mac flow on all ports that are in the path between given source and destination ports.
+ * It uses path provided by NetworkGraphService
+ * {@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService} to find a links
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * between given ports. And then writes appropriate flow on each port that is covered in that path.
+ *
+ * @param sourceMac
+ * @param sourceNodeConnectorRef
+ * @param destMac
+ * @param destNodeConnectorRef
+ */
+ @Override
+ public void addMacToMacFlowsUsingShortestPath(MacAddress sourceMac,
+ NodeConnectorRef sourceNodeConnectorRef,
+ MacAddress destMac,
+ NodeConnectorRef destNodeConnectorRef) {
+ Preconditions.checkNotNull(sourceMac, "Source mac address should not be null.");
+ Preconditions.checkNotNull(sourceNodeConnectorRef, "Source port should not be null.");
+ Preconditions.checkNotNull(destMac, "Destination mac address should not be null.");
+ Preconditions.checkNotNull(destNodeConnectorRef, "Destination port should not be null.");
+
+ if(sourceNodeConnectorRef.equals(destNodeConnectorRef)) {
+ _logger.info("In addMacToMacFlowsUsingShortestPath: No flows added. Source and Destination ports are same.");
+ return;
+
+ }
+ NodeId sourceNodeId = new NodeId(sourceNodeConnectorRef.getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ NodeId destNodeId = new NodeId(destNodeConnectorRef.getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+
+ // add destMac-To-sourceMac flow on source port
+ addMacToMacFlow(destMac, sourceMac, sourceNodeConnectorRef);
+
+ // add sourceMac-To-destMac flow on destination port
+ addMacToMacFlow(sourceMac, destMac, destNodeConnectorRef);
+
+ if(!sourceNodeId.equals(destNodeId)) {
+ List<Link> linksInBeween = networkGraphService.getPath(sourceNodeId, destNodeId);
+
+ if(linksInBeween != null) {
+ // assumes the list order is maintained and starts with link that has source as source node
+ for(Link link : linksInBeween) {
+ // add sourceMac-To-destMac flow on source port
+ addMacToMacFlow(sourceMac, destMac, getSourceNodeConnectorRef(link));
+
+ // add destMac-To-sourceMac flow on destination port
+ addMacToMacFlow(destMac, sourceMac, getDestNodeConnectorRef(link));
+ }
+ }
+ }
+ }
+
+ private NodeConnectorRef getSourceNodeConnectorRef(Link link) {
+ InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
+ = InstanceIdentifierUtils.createNodeConnectorIdentifier(
+ link.getSource().getSourceNode().getValue(),
+ link.getSource().getSourceTp().getValue());
+ return new NodeConnectorRef(nodeConnectorInstanceIdentifier);
+ }
+
+ private NodeConnectorRef getDestNodeConnectorRef(Link link) {
+ InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
+ = InstanceIdentifierUtils.createNodeConnectorIdentifier(
+ link.getDestination().getDestNode().getValue(),
+ link.getDestination().getDestTp().getValue());
+
+ return new NodeConnectorRef(nodeConnectorInstanceIdentifier);
+ }
+
+ /**
+ * @param nodeConnectorRef
+ * @return
+ */
+ private InstanceIdentifier<Flow> buildFlowPath(NodeConnectorRef nodeConnectorRef, TableKey flowTableKey) {
+
+ // generate unique flow key
+ FlowId flowId = new FlowId(String.valueOf(flowIdInc.getAndIncrement()));
+ FlowKey flowKey = new FlowKey(flowId);
+
+ return InstanceIdentifierUtils.generateFlowInstanceIdentifier(nodeConnectorRef, flowTableKey, flowKey);
+ }
+
+ /**
+ * @param tableId
+ * @param priority
+ * @param sourceMac
+ * @param destMac
+ * @param destPort
+ * @return {@link org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder}
+ * builds flow that forwards all packets with destMac to given port
+ */
+ private Flow createMacToMacFlow(Short tableId, int priority,
+ MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destPort) {
+
+ // start building flow
+ FlowBuilder macToMacFlow = new FlowBuilder() //
+ .setTableId(tableId) //
+ .setFlowName("mac2mac");
+
+ // use its own hash code for id.
+ macToMacFlow.setId(new FlowId(Long.toString(macToMacFlow.hashCode())));
+
+ // create a match that has mac to mac ethernet match
+ EthernetMatchBuilder ethernetMatchBuilder = new EthernetMatchBuilder() //
+ .setEthernetDestination(new EthernetDestinationBuilder() //
+ .setAddress(destMac) //
+ .build());
+ // set source in the match only if present
+ if(sourceMac != null) {
+ ethernetMatchBuilder.setEthernetSource(new EthernetSourceBuilder()
+ .setAddress(sourceMac)
+ .build());
+ }
+ EthernetMatch ethernetMatch = ethernetMatchBuilder.build();
+ Match match = new MatchBuilder()
+ .setEthernetMatch(ethernetMatch)
+ .build();
+
+
+ Uri destPortUri = destPort.getValue().firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
+
+ Action outputToControllerAction = new ActionBuilder() //
+ .setAction(new OutputActionCaseBuilder() //
+ .setOutputAction(new OutputActionBuilder() //
+ .setMaxLength(new Integer(0xffff)) //
+ .setOutputNodeConnector(destPortUri) //
+ .build()) //
+ .build()) //
+ .build();
+
+ // Create an Apply Action
+ ApplyActions applyActions = new ApplyActionsBuilder().setAction(ImmutableList.of(outputToControllerAction))
+ .build();
+
+ // Wrap our Apply Action in an Instruction
+ Instruction applyActionsInstruction = new InstructionBuilder() //
+ .setInstruction(new ApplyActionsCaseBuilder()//
+ .setApplyActions(applyActions) //
+ .build()) //
+ .build();
+
+ // Put our Instruction in a list of Instructions
+ macToMacFlow
+ .setMatch(match) //
+ .setInstructions(new InstructionsBuilder() //
+ .setInstruction(ImmutableList.of(applyActionsInstruction)) //
+ .build()) //
+ .setPriority(priority) //
+ .setBufferId(0L) //
+ .setHardTimeout(0) //
+ .setIdleTimeout(0) //
+ .setCookie(BigInteger.valueOf(flowCookieInc.getAndIncrement()))
+ .setFlags(new FlowModFlags(false, false, false, false, false));
+
+ return macToMacFlow.build();
+ }
+
+ /**
+ * Starts and commits data change transaction which
+ * modifies provided flow path with supplied body.
+ *
+ * @param flowPath
+ * @param flowBody
+ * @return transaction commit
+ */
+ private Future<RpcResult<TransactionStatus>> writeFlowToConfigData(InstanceIdentifier<Flow> flowPath,
+ Flow flowBody) {
+ DataModificationTransaction addFlowTransaction = dataBrokerService.beginTransaction();
+ addFlowTransaction.putConfigurationData(flowPath, flowBody);
+ return addFlowTransaction.commit();
+ }
+}
--- /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.sample.l2switch.md.inventory;
+
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+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.NodeConnectorKey;
+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.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+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.topology.Link;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.*;
+
+/**
+ * InventoryService provides functions related to Nodes & NodeConnectors.
+ */
+public class InventoryService {
+ private DataBrokerService dataService;
+ // Key: SwitchId, Value: NodeConnectorRef that corresponds to NC between controller & switch
+ private HashMap<String, NodeConnectorRef> controllerSwitchConnectors;
+
+ /**
+ * Construct an InventoryService object with the specified inputs.
+ * @param dataService The DataBrokerService associated with the InventoryService.
+ */
+ public InventoryService(DataBrokerService dataService) {
+ this.dataService = dataService;
+ controllerSwitchConnectors = new HashMap<String, NodeConnectorRef>();
+ }
+
+ public HashMap<String, NodeConnectorRef> getControllerSwitchConnectors() {
+ return controllerSwitchConnectors;
+ }
+
+ // ToDo: Improve performance for thousands of switch ports
+ /**
+ * Get the External NodeConnectors of the network, which are the NodeConnectors connected to hosts.
+ * @return The list of external node connectors.
+ */
+ public List<NodeConnectorRef> getExternalNodeConnectors() {
+ // External NodeConnectors = All - Internal
+ ArrayList<NodeConnectorRef> externalNodeConnectors = new ArrayList<NodeConnectorRef>();
+ Set<String> internalNodeConnectors = new HashSet<>();
+
+ // Read Topology -- find list of switch-to-switch internal node connectors
+ NetworkTopology networkTopology =
+ (NetworkTopology)dataService.readOperationalData(
+ InstanceIdentifier.<NetworkTopology>builder(NetworkTopology.class).toInstance());
+
+ for (Topology topology : networkTopology.getTopology()) {
+ Topology completeTopology =
+ (Topology)dataService.readOperationalData(
+ InstanceIdentifierUtils.generateTopologyInstanceIdentifier(
+ topology.getTopologyId().getValue()));
+
+ for (Link link : completeTopology.getLink()) {
+ internalNodeConnectors.add(link.getDestination().getDestTp().getValue());
+ internalNodeConnectors.add(link.getSource().getSourceTp().getValue());
+ }
+ }
+
+ // Read Inventory -- contains list of all nodeConnectors
+ InstanceIdentifier.InstanceIdentifierBuilder<Nodes> nodesInsIdBuilder = InstanceIdentifier.<Nodes>builder(Nodes.class);
+ Nodes nodes = (Nodes)dataService.readOperationalData(nodesInsIdBuilder.toInstance());
+ if (nodes != null) {
+ for (Node node : nodes.getNode()) {
+ Node completeNode = (Node)dataService.readOperationalData(InstanceIdentifierUtils.createNodePath(node.getId()));
+ for (NodeConnector nodeConnector : completeNode.getNodeConnector()) {
+ // NodeConnector isn't switch-to-switch, so it must be controller-to-switch (internal) or external
+ if (!internalNodeConnectors.contains(nodeConnector.getId().getValue())) {
+ NodeConnectorRef ncRef = new NodeConnectorRef(
+ InstanceIdentifier.<Nodes>builder(Nodes.class).<Node, NodeKey>child(Node.class, node.getKey())
+ .<NodeConnector, NodeConnectorKey>child(NodeConnector.class, nodeConnector.getKey()).toInstance());
+
+ // External node connectors have "-" in their name for mininet, i.e. "s1-eth1"
+ if (nodeConnector.getAugmentation(FlowCapableNodeConnector.class).getName().contains("-")) {
+ externalNodeConnectors.add(ncRef);
+ }
+ // Controller-to-switch internal node connectors
+ else {
+ controllerSwitchConnectors.put(node.getId().getValue(), ncRef);
+ }
+ }
+ }
+ }
+ }
+
+ return externalNodeConnectors;
+ }
+}
\ No newline at end of file
--- /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.sample.l2switch.md.packet;
+
+import org.opendaylight.controller.sample.l2switch.md.addresstracker.AddressTracker;
+import org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService;
+import org.opendaylight.controller.sample.l2switch.md.inventory.InventoryService;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.LLDP;
+import org.opendaylight.controller.sal.packet.LinkEncap;
+import org.opendaylight.controller.sal.packet.Packet;
+import org.opendaylight.controller.sal.packet.RawPacket;
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.NetUtils;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.*;
+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.address.tracker.rev140402.l2.addresses.L2Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * PacketHandler examines Ethernet packets to find L2Addresses (mac, nodeConnector) pairings
+ * of the sender and learns them.
+ * It also forwards the data packets appropriately dependending upon whether it knows about the
+ * target or not.
+ */
+public class PacketHandler implements PacketProcessingListener {
+
+ private final static Logger _logger = LoggerFactory.getLogger(PacketHandler.class);
+
+ private PacketProcessingService packetProcessingService;
+ private AddressTracker addressTracker;
+ private FlowWriterService flowWriterService;
+ private InventoryService inventoryService;
+
+ public void setAddressTracker(AddressTracker addressTracker) {
+ this.addressTracker = addressTracker;
+ }
+
+ public void setPacketProcessingService(PacketProcessingService packetProcessingService) {
+ this.packetProcessingService = packetProcessingService;
+ }
+
+ public void setFlowWriterService(FlowWriterService flowWriterService) {
+ this.flowWriterService = flowWriterService;
+ }
+
+ public void setInventoryService(InventoryService inventoryService) {
+ this.inventoryService = inventoryService;
+ }
+
+ /**
+ * The handler function for all incoming packets.
+ * @param packetReceived The incoming packet.
+ */
+ @Override
+ public void onPacketReceived(PacketReceived packetReceived) {
+
+ if(packetReceived == null) return;
+
+ try {
+ byte[] payload = packetReceived.getPayload();
+ RawPacket rawPacket = new RawPacket(payload);
+ NodeConnectorRef ingress = packetReceived.getIngress();
+
+ Packet packet = decodeDataPacket(rawPacket);
+
+ if(!(packet instanceof Ethernet)) return;
+
+ handleEthernetPacket(packet, ingress);
+
+ } catch(Throwable _e) {
+ _e.printStackTrace();
+ }
+ }
+
+ /**
+ * The handler function for Ethernet packets.
+ * @param packet The incoming Ethernet packet.
+ * @param ingress The NodeConnector where the Ethernet packet came from.
+ */
+ private void handleEthernetPacket(Packet packet, NodeConnectorRef ingress) {
+ byte[] srcMac = ((Ethernet) packet).getSourceMACAddress();
+ byte[] destMac = ((Ethernet) packet).getDestinationMACAddress();
+
+ if (srcMac == null || srcMac.length == 0) return;
+
+ Object enclosedPacket = packet.getPayload();
+
+ if (enclosedPacket instanceof LLDP)
+ return; // LLDP packets are handled by OpenFlowPlugin
+
+ // get l2address by src mac
+ // if unknown, add l2address
+ MacAddress srcMacAddress = toMacAddress(srcMac);
+ L2Address src = addressTracker.getAddress(srcMacAddress);
+ boolean isSrcKnown = (src != null);
+ if (!isSrcKnown) {
+ addressTracker.addAddress(srcMacAddress, ingress);
+ }
+
+ // get host by dest mac
+ // if known set dest known to true
+ MacAddress destMacAddress = toMacAddress(destMac);
+ L2Address dest = addressTracker.getAddress(destMacAddress);
+ boolean isDestKnown = (dest != null);
+
+ byte[] payload = packet.getRawPayload();
+ // if (src and dest known)
+ // sendpacket to dest and add src<->dest flow
+ if(isSrcKnown & isDestKnown) {
+ flowWriterService.addMacToMacFlowsUsingShortestPath(srcMacAddress, src.getNodeConnectorRef(),
+ destMacAddress, dest.getNodeConnectorRef());
+ sendPacketOut(payload, getControllerNodeConnector(dest.getNodeConnectorRef()), dest.getNodeConnectorRef());
+ } else {
+ // if (dest unknown)
+ // sendpacket to external links minus ingress
+ floodExternalPorts(payload, ingress);
+ }
+ }
+
+ /**
+ * Floods the specified payload on external ports, which are ports not connected to switches.
+ * @param payload The payload to be flooded.
+ * @param ingress The NodeConnector where the payload came from.
+ */
+ private void floodExternalPorts(byte[] payload, NodeConnectorRef ingress) {
+ List<NodeConnectorRef> externalPorts = inventoryService.getExternalNodeConnectors();
+ externalPorts.remove(ingress);
+
+ for (NodeConnectorRef egress : externalPorts) {
+ sendPacketOut(payload, getControllerNodeConnector(egress), egress);
+ }
+ }
+
+ /**
+ * Sends the specified packet on the specified port.
+ * @param payload The payload to be sent.
+ * @param ingress The NodeConnector where the payload came from.
+ * @param egress The NodeConnector where the payload will go.
+ */
+ private void sendPacketOut(byte[] payload, NodeConnectorRef ingress, NodeConnectorRef egress) {
+ if (ingress == null || egress == null) return;
+ InstanceIdentifier<Node> egressNodePath = InstanceIdentifierUtils.getNodePath(egress.getValue());
+ TransmitPacketInput input = new TransmitPacketInputBuilder() //
+ .setPayload(payload) //
+ .setNode(new NodeRef(egressNodePath)) //
+ .setEgress(egress) //
+ .setIngress(ingress) //
+ .build();
+ packetProcessingService.transmitPacket(input);
+ }
+
+ /**
+ * Decodes an incoming packet.
+ * @param raw The raw packet to be decoded.
+ * @return The decoded form of the raw packet.
+ */
+ private Packet decodeDataPacket(RawPacket raw) {
+ if(raw == null) {
+ return null;
+ }
+ byte[] data = raw.getPacketData();
+ if(data.length <= 0) {
+ return null;
+ }
+ if(raw.getEncap().equals(LinkEncap.ETHERNET)) {
+ Ethernet res = new Ethernet();
+ try {
+ res.deserialize(data, 0, data.length * NetUtils.NumBitsInAByte);
+ res.setRawPayload(raw.getPacketData());
+ } catch(Exception e) {
+ _logger.warn("Failed to decode packet: {}", e.getMessage());
+ }
+ return res;
+ }
+ return null;
+ }
+
+ /**
+ * Creates a MacAddress object out of a byte array.
+ * @param dataLinkAddress The byte-array form of a MacAddress
+ * @return MacAddress of the specified dataLinkAddress.
+ */
+ private MacAddress toMacAddress(byte[] dataLinkAddress) {
+ return new MacAddress(HexEncode.bytesToHexStringFormat(dataLinkAddress));
+ }
+
+ /**
+ * Gets the NodeConnector that connects the controller & switch for a specified switch port/node connector.
+ * @param nodeConnectorRef The nodeConnector of a switch.
+ * @return The NodeConnector that that connects the controller & switch.
+ */
+ private NodeConnectorRef getControllerNodeConnector(NodeConnectorRef nodeConnectorRef) {
+ NodeConnectorRef controllerSwitchNodeConnector = null;
+ HashMap<String, NodeConnectorRef> controllerSwitchConnectors = inventoryService.getControllerSwitchConnectors();
+ InstanceIdentifier<Node> nodePath = InstanceIdentifierUtils.getNodePath(nodeConnectorRef.getValue());
+ if (nodePath != null) {
+ NodeKey nodeKey = InstanceIdentifierUtils.getNodeKey(nodePath);
+ if (nodeKey != null) {
+ controllerSwitchNodeConnector = controllerSwitchConnectors.get(nodeKey.getId().getValue());
+ }
+ }
+ return controllerSwitchNodeConnector;
+ }
+}
--- /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.sample.l2switch.md.topology;
+
+import com.google.common.base.Preconditions;
+import edu.uci.ics.jung.algorithms.shortestpath.DijkstraShortestPath;
+import edu.uci.ics.jung.graph.DirectedSparseGraph;
+import edu.uci.ics.jung.graph.Graph;
+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.network.topology.topology.Link;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Implementation of NetworkGraphService{@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService}.
+ * It uses Jung graph library internally to maintain a graph and optimum way to return shortest path using
+ * Dijkstra algorithm.
+ */
+public class NetworkGraphDijkstra implements NetworkGraphService {
+
+ private static final Logger _logger = LoggerFactory.getLogger(NetworkGraphDijkstra.class);
+
+ DijkstraShortestPath<NodeId, Link> shortestPath = null;
+ Graph<NodeId, Link> networkGraph = null;
+
+ /**
+ * Adds links to existing graph or creates new directed graph with given links if graph was not initialized.
+ * @param links
+ */
+ @Override
+ public synchronized void addLinks(List<Link> links) {
+ if(links == null || links.isEmpty()) {
+ _logger.info("In addLinks: No link added as links is null or empty.");
+ return;
+ }
+
+ if(networkGraph == null) {
+ networkGraph = new DirectedSparseGraph<>();
+ }
+
+ for(Link link : links) {
+ NodeId sourceNodeId = link.getSource().getSourceNode();
+ NodeId destinationNodeId = link.getDestination().getDestNode();
+ networkGraph.addVertex(sourceNodeId);
+ networkGraph.addVertex(destinationNodeId);
+ networkGraph.addEdge(link, sourceNodeId, destinationNodeId);
+ }
+ if(shortestPath == null) {
+ shortestPath = new DijkstraShortestPath<>(networkGraph);
+ } else {
+ shortestPath.reset();
+ }
+ }
+
+ /**
+ * removes links from existing graph.
+ * @param links
+ */
+ @Override
+ public synchronized void removeLinks(List<Link> links) {
+ Preconditions.checkNotNull(networkGraph, "Graph is not initialized, add links first.");
+
+ if(links == null || links.isEmpty()) {
+ _logger.info("In removeLinks: No link removed as links is null or empty.");
+ return;
+ }
+
+ for(Link link : links) {
+ networkGraph.removeEdge(link);
+ }
+
+ if(shortestPath == null) {
+ shortestPath = new DijkstraShortestPath<>(networkGraph);
+ } else {
+ shortestPath.reset();
+ }
+ }
+
+ /**
+ * returns a path between 2 nodes. Uses Dijkstra's algorithm to return shortest path.
+ * @param sourceNodeId
+ * @param destinationNodeId
+ * @return
+ */
+ @Override
+ public synchronized List<Link> getPath(NodeId sourceNodeId, NodeId destinationNodeId) {
+ Preconditions.checkNotNull(shortestPath, "Graph is not initialized, add links first.");
+
+ if(sourceNodeId == null || destinationNodeId == null) {
+ _logger.info("In getPath: returning null, as sourceNodeId or destinationNodeId is null.");
+ return null;
+ }
+
+ return shortestPath.getPath(sourceNodeId, destinationNodeId);
+ }
+
+ /**
+ * Clears the prebuilt graph, in case same service instance is required to process a new graph.
+ */
+ @Override
+ public synchronized void clear() {
+ networkGraph = null;
+ shortestPath = null;
+ }
+}
--- /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.sample.l2switch.md.topology;
+
+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.network.topology.topology.Link;
+
+import java.util.List;
+
+/**
+ * Service that allows to build a network graph using Topology links
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * and exposes operation that can be performed on such graph.
+ */
+public interface NetworkGraphService {
+
+ /**
+ * Adds links to existing graph or creates new graph with given links if graph was not initialized.
+ * @param links
+ */
+ public void addLinks(List<Link> links);
+
+ /**
+ * removes links from existing graph.
+ * @param links
+ */
+ public void removeLinks(List<Link> links);
+
+ /**
+ * returns a path between 2 nodes. Implementation should ideally return shortest path.
+ * @param sourceNodeId
+ * @param destinationNodeId
+ * @return
+ */
+ public List<Link> getPath(NodeId sourceNodeId, NodeId destinationNodeId);
+
+ /**
+ * Clears the prebuilt graph, in case same service instance is required to process a new graph.
+ */
+ public void clear();
+}
--- /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.sample.l2switch.md.topology;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+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.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.Link;
+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.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Listens to data change events on topology links
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * and maintains a topology graph using provided NetworkGraphService
+ * {@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService}.
+ * It refreshes the graph after a delay(default 10 sec) to accommodate burst of change events if they come in bulk.
+ * This is to avoid continuous refresh of graph on a series of change events in short time.
+ */
+public class TopologyLinkDataChangeHandler implements DataChangeListener {
+ private static final Logger _logger = LoggerFactory.getLogger(TopologyLinkDataChangeHandler.class);
+ private static final String DEFAULT_TOPOLOGY_ID = "flow:1";
+
+ private boolean networkGraphRefreshScheduled = false;
+ private final ScheduledExecutorService networkGraphRefreshScheduler = Executors.newScheduledThreadPool(1);
+ private final long DEFAULT_GRAPH_REFRESH_DELAY = 10;
+ private final long graphRefreshDelayInSec;
+
+ private final NetworkGraphService networkGraphService;
+ private final DataBrokerService dataBrokerService;
+
+ /**
+ * Uses default delay to refresh topology graph if this constructor is used.
+ * @param dataBrokerService
+ * @param networkGraphService
+ */
+ public TopologyLinkDataChangeHandler(DataBrokerService dataBrokerService, NetworkGraphService networkGraphService) {
+ Preconditions.checkNotNull(dataBrokerService, "dataBrokerService should not be null.");
+ Preconditions.checkNotNull(networkGraphService, "networkGraphService should not be null.");
+ this.dataBrokerService = dataBrokerService;
+ this.networkGraphService = networkGraphService;
+ this.graphRefreshDelayInSec = DEFAULT_GRAPH_REFRESH_DELAY;
+ }
+
+ /**
+ *
+ * @param dataBrokerService
+ * @param networkGraphService
+ * @param graphRefreshDelayInSec
+ */
+ public TopologyLinkDataChangeHandler(DataBrokerService dataBrokerService, NetworkGraphService networkGraphService,
+ long graphRefreshDelayInSec) {
+ Preconditions.checkNotNull(dataBrokerService, "dataBrokerService should not be null.");
+ Preconditions.checkNotNull(networkGraphService, "networkGraphService should not be null.");
+ this.dataBrokerService = dataBrokerService;
+ this.networkGraphService = networkGraphService;
+ this.graphRefreshDelayInSec = graphRefreshDelayInSec;
+ }
+
+ /**
+ * Based on if links have been added or removed in topology data store, schedules a refresh of network graph.
+ * @param dataChangeEvent
+ */
+ @Override
+ public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> dataChangeEvent) {
+ if(dataChangeEvent == null) {
+ _logger.info("In onDataChanged: No Processing done as dataChangeEvent is null.");
+ }
+ Map<InstanceIdentifier<?>, DataObject> linkOriginalData = dataChangeEvent.getOriginalOperationalData();
+ Map<InstanceIdentifier<?>, DataObject> linkUpdatedData = dataChangeEvent.getUpdatedOperationalData();
+ // change this logic, once MD-SAL start populating DeletedOperationData Set
+ if(linkOriginalData != null && linkUpdatedData != null
+ && (linkOriginalData.size() != 0 || linkUpdatedData.size() != 0)
+ && !networkGraphRefreshScheduled) {
+ networkGraphRefreshScheduled = linkOriginalData.size() != linkUpdatedData.size();
+ if(networkGraphRefreshScheduled) {
+ networkGraphRefreshScheduler.schedule(new NetworkGraphRefresher(), graphRefreshDelayInSec, TimeUnit.SECONDS);
+ }
+ }
+
+ }
+
+ /**
+ * Registers as a data listener to receive changes done to
+ * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
+ * under {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology}
+ * operation data root.
+ */
+
+ public void registerAsDataChangeListener() {
+ InstanceIdentifier<Link> linkInstance = InstanceIdentifier.builder(NetworkTopology.class)
+ .child(Topology.class, new TopologyKey(new TopologyId(DEFAULT_TOPOLOGY_ID))).child(Link.class).toInstance();
+ dataBrokerService.registerDataChangeListener(linkInstance, this);
+ }
+
+ /**
+ *
+ */
+ private class NetworkGraphRefresher implements Runnable {
+ /**
+ *
+ */
+ @Override
+ public void run() {
+ networkGraphRefreshScheduled = false;
+ //TODO: it should refer to changed links only from DataChangeEvent above.
+ List<Link> links = getLinksFromTopology(DEFAULT_TOPOLOGY_ID);
+ networkGraphService.clear();// can remove this once changed links are addressed
+ if(links != null && !links.isEmpty()) {
+ networkGraphService.addLinks(links);
+ }
+ }
+
+ /**
+ * @param topologyId
+ * @return
+ */
+ private List<Link> getLinksFromTopology(String topologyId) {
+ InstanceIdentifier<Topology> topologyInstanceIdentifier = InstanceIdentifierUtils.generateTopologyInstanceIdentifier(topologyId);
+ Topology topology = (Topology) dataBrokerService.readOperationalData(topologyInstanceIdentifier);
+ return topology.getLink();
+ }
+ }
+}
--- /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.sample.l2switch.md.util;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+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.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+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.NodeConnectorKey;
+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.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+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.yangtools.yang.binding.InstanceIdentifier;
+
+/* InstanceIdentifierUtils provides utility functions related to InstanceIdentifiers.
+ */
+public class InstanceIdentifierUtils {
+
+ /**
+ * Creates an Instance Identifier (path) for node with specified id
+ *
+ * @param nodeId
+ * @return
+ */
+ public static final InstanceIdentifier<Node> createNodePath(NodeId nodeId) {
+ return InstanceIdentifier.builder(Nodes.class) //
+ .child(Node.class, new NodeKey(nodeId)) //
+ .build();
+ }
+
+ /**
+ * Shorten's node child path to node path.
+ *
+ * @param nodeChild child of node, from which we want node path.
+ * @return
+ */
+ public static final InstanceIdentifier<Node> getNodePath(InstanceIdentifier<?> nodeChild) {
+ return nodeChild.firstIdentifierOf(Node.class);
+ }
+
+
+ /**
+ * Creates a table path by appending table specific location to node path
+ *
+ * @param nodePath
+ * @param tableKey
+ * @return
+ */
+ public static final InstanceIdentifier<Table> createTablePath(InstanceIdentifier<Node> nodePath, TableKey tableKey) {
+ return InstanceIdentifier.builder(nodePath)
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, tableKey)
+ .build();
+ }
+
+ /**
+ * Creates a path for particular flow, by appending flow-specific information
+ * to table path.
+ *
+ * @param table
+ * @param flowKey
+ * @return
+ */
+ public static InstanceIdentifier<Flow> createFlowPath(InstanceIdentifier<Table> table, FlowKey flowKey) {
+ return InstanceIdentifier.builder(table)
+ .child(Flow.class, flowKey)
+ .build();
+ }
+
+ /**
+ * Extract table id from table path.
+ *
+ * @param tablePath
+ * @return
+ */
+ public static Short getTableId(InstanceIdentifier<Table> tablePath) {
+ return tablePath.firstKeyOf(Table.class, TableKey.class).getId();
+ }
+
+ /**
+ * Extracts NodeConnectorKey from node connector path.
+ */
+ public static NodeConnectorKey getNodeConnectorKey(InstanceIdentifier<?> nodeConnectorPath) {
+ return nodeConnectorPath.firstKeyOf(NodeConnector.class, NodeConnectorKey.class);
+ }
+
+ /**
+ * Extracts NodeKey from node path.
+ */
+ public static NodeKey getNodeKey(InstanceIdentifier<?> nodePath) {
+ return nodePath.firstKeyOf(Node.class, NodeKey.class);
+ }
+
+
+ //
+ public static final InstanceIdentifier<NodeConnector> createNodeConnectorIdentifier(String nodeIdValue,
+ String nodeConnectorIdValue) {
+ return InstanceIdentifier.builder(createNodePath(new NodeId(nodeIdValue))) //
+ .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(nodeConnectorIdValue))) //
+ .build();
+ }
+
+ /**
+ * @param nodeConnectorRef
+ * @return
+ */
+ public static InstanceIdentifier<Node> generateNodeInstanceIdentifier(NodeConnectorRef nodeConnectorRef) {
+ return nodeConnectorRef.getValue().firstIdentifierOf(Node.class);
+ }
+
+ /**
+ * @param nodeConnectorRef
+ * @param flowTableKey
+ * @return
+ */
+ public static InstanceIdentifier<Table> generateFlowTableInstanceIdentifier(NodeConnectorRef nodeConnectorRef, TableKey flowTableKey) {
+ return InstanceIdentifier.builder(generateNodeInstanceIdentifier(nodeConnectorRef))
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, flowTableKey)
+ .build();
+ }
+
+ /**
+ * @param nodeConnectorRef
+ * @param flowTableKey
+ * @param flowKey
+ * @return
+ */
+ public static InstanceIdentifier<Flow> generateFlowInstanceIdentifier(NodeConnectorRef nodeConnectorRef,
+ TableKey flowTableKey,
+ FlowKey flowKey) {
+ return InstanceIdentifier.builder(generateFlowTableInstanceIdentifier(nodeConnectorRef, flowTableKey))
+ .child(Flow.class, flowKey)
+ .build();
+ }
+
+ public static InstanceIdentifier<Topology> generateTopologyInstanceIdentifier(String topologyId) {
+ return InstanceIdentifier.builder(NetworkTopology.class)
+ .child(Topology.class, new TopologyKey(new TopologyId(topologyId)))
+ .build();
+ }
+}
+
--- /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.sample.l2switch.md.flow;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+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.NodeConnectorKey;
+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.yang.binding.InstanceIdentifier;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ */
+public class FlowWriterServiceImplTest {
+ private DataBrokerService dataBrokerService;
+ private NodeConnectorRef srcNodeConnectorRef;
+ private NodeConnectorRef destNodeConnectorRef;
+ private MacAddress destMacAddress;
+ private MacAddress srcMacAddress;
+ private DataModificationTransaction dataModificationTransaction;
+ private NetworkGraphService networkGraphService;
+
+ @Before
+ public void init() {
+ dataBrokerService = mock(DataBrokerService.class);
+ networkGraphService = mock(NetworkGraphService.class);
+ //build source node connector ref
+ InstanceIdentifier<Nodes> srcNodesInstanceIdentifier
+ = InstanceIdentifier.builder(Nodes.class)
+ .build();
+ InstanceIdentifier<Node> srcNodeInstanceIdentifier
+ = InstanceIdentifier.builder(srcNodesInstanceIdentifier)
+ .child(Node.class, new NodeKey(new NodeId("openflow:1")))
+ .build();
+ InstanceIdentifier<NodeConnector> srcNodeConnectorInstanceIdentifier
+ = InstanceIdentifier.builder(srcNodeInstanceIdentifier)
+ .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("openflow:1:2")))
+ .build();
+ srcNodeConnectorRef = new NodeConnectorRef(srcNodeConnectorInstanceIdentifier);
+
+ //build dest node connector ref
+ InstanceIdentifier<Nodes> nodesInstanceIdentifier
+ = InstanceIdentifier.builder(Nodes.class)
+ .build();
+ InstanceIdentifier<Node> nodeInstanceIdentifier
+ = InstanceIdentifier.builder(nodesInstanceIdentifier)
+ .child(Node.class, new NodeKey(new NodeId("openflow:2")))
+ .build();
+ InstanceIdentifier<NodeConnector> nodeConnectorInstanceIdentifier
+ = InstanceIdentifier.builder(nodeInstanceIdentifier)
+ .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("openflow:2:2")))
+ .build();
+ destNodeConnectorRef = new NodeConnectorRef(nodeConnectorInstanceIdentifier);
+ destMacAddress = new MacAddress("00:0a:95:9d:68:16");
+ srcMacAddress = new MacAddress("00:0a:95:8c:97:24");
+ dataModificationTransaction = mock(DataModificationTransaction.class);
+ when(dataBrokerService.beginTransaction()).thenReturn(dataModificationTransaction);
+ }
+
+ @Test
+ public void testFlowWriterServiceImpl_NPEWhenDataBrokerServiceIsNull() throws Exception {
+ try {
+ new FlowWriterServiceImpl(null, networkGraphService);
+ fail("Expected null pointer exception.");
+ } catch(NullPointerException npe) {
+ assertEquals("dataBrokerService should not be null.", npe.getMessage());
+ }
+ }
+
+ @Test
+ public void testAddMacToMacFlow_NPEWhenNullSourceMacDestMacAndNodeConnectorRef() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ try {
+ flowWriterService.addMacToMacFlow(null, null, null);
+ fail("Expected null pointer exception.");
+ } catch(NullPointerException npe) {
+ assertEquals("Destination mac address should not be null.", npe.getMessage());
+ }
+ }
+
+ @Test
+ public void testAddMacToMacFlow_NPEWhenSourceMacNullMac() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ try {
+ flowWriterService.addMacToMacFlow(null, null, destNodeConnectorRef);
+ fail("Expected null pointer exception.");
+ } catch(NullPointerException npe) {
+ assertEquals("Destination mac address should not be null.", npe.getMessage());
+ }
+ }
+
+ @Test
+ public void testAddMacToMacFlow_NPEWhenNullSourceMacNodeConnectorRef() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ try {
+ flowWriterService.addMacToMacFlow(null, destMacAddress, null);
+ fail("Expected null pointer exception.");
+ } catch(NullPointerException npe) {
+ assertEquals("Destination port should not be null.", npe.getMessage());
+ }
+ }
+
+ @Test
+ public void testAddMacToMacFlow_WhenNullSourceMac() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ flowWriterService.addMacToMacFlow(null, destMacAddress, destNodeConnectorRef);
+ verify(dataBrokerService, times(1)).beginTransaction();
+ verify(dataModificationTransaction, times(1)).commit();
+ }
+
+ @Test
+ public void testAddMacToMacFlow_WhenSrcAndDestMacAreSame() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ flowWriterService.addMacToMacFlow(new MacAddress(destMacAddress.getValue()), destMacAddress, destNodeConnectorRef);
+ verify(dataBrokerService, never()).beginTransaction();
+ verify(dataModificationTransaction, never()).commit();
+
+ }
+
+ @Test
+ public void testAddMacToMacFlow_SunnyDay() throws Exception {
+ FlowWriterService flowWriterService = new FlowWriterServiceImpl(dataBrokerService, networkGraphService);
+ flowWriterService.addMacToMacFlow(srcMacAddress, destMacAddress, destNodeConnectorRef);
+ verify(dataBrokerService, times(1)).beginTransaction();
+ verify(dataModificationTransaction, times(1)).commit();
+ }
+
+}
--- /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.sample.l2switch.md.topology;
+
+import org.junit.Before;
+import org.junit.Test;
+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.link.attributes.Destination;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Source;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static junit.framework.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ */
+public class NetworkGraphDijkstraTest {
+ Link link1, link2, link3, link4, link5, link6, link7, link8, link9, link10,link11,link12;
+ Destination dest1, dest2, dest3, dest4, dest5, dest6,dest7,dest8,dest9,dest10,dest11,dest12;
+ Source src1, src2, src3, src4, src5, src6,src7,src8,src9,src10,src11,src12;
+ NodeId nodeId1 = new NodeId("openflow:1");
+ NodeId nodeId2 = new NodeId("openflow:2");
+ NodeId nodeId3 = new NodeId("openflow:3");
+ NodeId nodeId4 = new NodeId("openflow:4");
+ NodeId nodeId5 = new NodeId("openflow:5");
+ NodeId nodeId6 = new NodeId("openflow:6");
+ NodeId nodeId7 = new NodeId("openflow:7");
+ List<Link> links = new ArrayList<>();
+
+ @Before
+ public void init() {
+ link1 = mock(Link.class);
+ link2 = mock(Link.class);
+ link3 = mock(Link.class);
+ link4 = mock(Link.class);
+ link5 = mock(Link.class);
+ link6 = mock(Link.class);
+ link7 = mock(Link.class);
+ link8 = mock(Link.class);
+ link9 = mock(Link.class);
+ link10 = mock(Link.class);
+ link11 = mock(Link.class);
+ link12 = mock(Link.class);
+ dest1 = mock(Destination.class);
+ dest2 = mock(Destination.class);
+ dest3 = mock(Destination.class);
+ dest4 = mock(Destination.class);
+ dest5 = mock(Destination.class);
+ dest6 = mock(Destination.class);
+ dest7 = mock(Destination.class);
+ dest8 = mock(Destination.class);
+ dest9 = mock(Destination.class);
+ dest10 = mock(Destination.class);
+ dest11 = mock(Destination.class);
+ dest12 = mock(Destination.class);
+ src1 = mock(Source.class);
+ src2 = mock(Source.class);
+ src3 = mock(Source.class);
+ src4 = mock(Source.class);
+ src5 = mock(Source.class);
+ src6 = mock(Source.class);
+ src7 = mock(Source.class);
+ src8 = mock(Source.class);
+ src9 = mock(Source.class);
+ src10 = mock(Source.class);
+ src11 = mock(Source.class);
+ src12 = mock(Source.class);
+ when(link1.getSource()).thenReturn(src1);
+ when(link2.getSource()).thenReturn(src2);
+ when(link3.getSource()).thenReturn(src3);
+ when(link4.getSource()).thenReturn(src4);
+ when(link5.getSource()).thenReturn(src5);
+ when(link6.getSource()).thenReturn(src6);
+ when(link7.getSource()).thenReturn(src7);
+ when(link8.getSource()).thenReturn(src8);
+ when(link9.getSource()).thenReturn(src9);
+ when(link10.getSource()).thenReturn(src10);
+ when(link11.getSource()).thenReturn(src11);
+ when(link12.getSource()).thenReturn(src12);
+ when(link1.getDestination()).thenReturn(dest1);
+ when(link2.getDestination()).thenReturn(dest2);
+ when(link3.getDestination()).thenReturn(dest3);
+ when(link4.getDestination()).thenReturn(dest4);
+ when(link5.getDestination()).thenReturn(dest5);
+ when(link6.getDestination()).thenReturn(dest6);
+ when(link7.getDestination()).thenReturn(dest7);
+ when(link8.getDestination()).thenReturn(dest8);
+ when(link9.getDestination()).thenReturn(dest9);
+ when(link10.getDestination()).thenReturn(dest10);
+ when(link11.getDestination()).thenReturn(dest11);
+ when(link12.getDestination()).thenReturn(dest12);
+ when(src1.getSourceNode()).thenReturn(nodeId1);
+ when(dest1.getDestNode()).thenReturn(nodeId2);
+ when(src2.getSourceNode()).thenReturn(nodeId2);
+ when(dest2.getDestNode()).thenReturn(nodeId1);
+ when(src3.getSourceNode()).thenReturn(nodeId1);
+ when(dest3.getDestNode()).thenReturn(nodeId3);
+ when(src4.getSourceNode()).thenReturn(nodeId3);
+ when(dest4.getDestNode()).thenReturn(nodeId1);
+ when(src5.getSourceNode()).thenReturn(nodeId2);
+ when(dest5.getDestNode()).thenReturn(nodeId4);
+ when(src6.getSourceNode()).thenReturn(nodeId4);
+ when(dest6.getDestNode()).thenReturn(nodeId2);
+ when(src7.getSourceNode()).thenReturn(nodeId2);
+ when(dest7.getDestNode()).thenReturn(nodeId5);
+ when(src8.getSourceNode()).thenReturn(nodeId5);
+ when(dest8.getDestNode()).thenReturn(nodeId2);
+ when(src9.getSourceNode()).thenReturn(nodeId6);
+ when(dest9.getDestNode()).thenReturn(nodeId3);
+ when(src10.getSourceNode()).thenReturn(nodeId3);
+ when(dest10.getDestNode()).thenReturn(nodeId6);
+ when(src11.getSourceNode()).thenReturn(nodeId7);
+ when(dest11.getDestNode()).thenReturn(nodeId3);
+ when(src12.getSourceNode()).thenReturn(nodeId3);
+ when(dest12.getDestNode()).thenReturn(nodeId7);
+ links.add(link1);
+ links.add(link2);
+ links.add(link3);
+ links.add(link4);
+ links.add(link5);
+ links.add(link6);
+ links.add(link7);
+ links.add(link8);
+ links.add(link9);
+ links.add(link10);
+ links.add(link11);
+ links.add(link12);
+
+ }
+
+ @Test
+ public void testAddLinksAndGetPath() throws Exception {
+ NetworkGraphService networkGraphService = new NetworkGraphDijkstra();
+ networkGraphService.addLinks(links);
+ List<Link> path = networkGraphService.getPath(nodeId2, nodeId3);
+ assertEquals("path size is not as expected.", 2, path.size());
+ assertEquals("link source is not as expected.", nodeId2, path.get(0).getSource().getSourceNode());
+ assertEquals("link destination is not as expected.", nodeId1, path.get(0).getDestination().getDestNode());
+ path = networkGraphService.getPath(nodeId3, nodeId2);
+ assertEquals("path size is not as expected.", 2, path.size());
+ assertEquals("link source is not as expected.", nodeId3, path.get(0).getSource().getSourceNode());
+ assertEquals("link destination is not as expected.", nodeId1, path.get(0).getDestination().getDestNode());
+
+ path = networkGraphService.getPath(nodeId4, nodeId6);
+ assertEquals("path size is not as expected.", 4, path.size());
+ assertEquals("link source is not as expected.", nodeId4, path.get(0).getSource().getSourceNode());
+ assertEquals("link destination is not as expected.", nodeId2, path.get(0).getDestination().getDestNode());
+ }
+}
--- /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.sample.l2switch.md.topology;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sample.l2switch.md.util.InstanceIdentifierUtils;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+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.topology.Link;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ */
+public class TopologyLinkDataChangeHandlerTest {
+ NetworkGraphService networkGraphService;
+ DataBrokerService dataBrokerService;
+ DataChangeEvent dataChangeEvent;
+ Topology topology;
+ Link link;
+
+ @Before
+ public void init() {
+ networkGraphService = mock(NetworkGraphService.class);
+ dataBrokerService = mock(DataBrokerService.class);
+ dataChangeEvent = mock(DataChangeEvent.class);
+ link = mock(Link.class);
+ topology = mock(Topology.class);
+ }
+
+ @Test
+ public void testOnDataChange() throws Exception {
+ TopologyLinkDataChangeHandler topologyLinkDataChangeHandler = new TopologyLinkDataChangeHandler(dataBrokerService, networkGraphService, 2);
+ Map<InstanceIdentifier<?>, DataObject> original = new HashMap<InstanceIdentifier<?>, DataObject>();
+ InstanceIdentifier<?> instanceIdentifier = InstanceIdentifierUtils.generateTopologyInstanceIdentifier("flow:1");
+ DataObject dataObject = mock(DataObject.class);
+ Map<InstanceIdentifier<?>, DataObject> updated = new HashMap<InstanceIdentifier<?>, DataObject>();
+ updated.put(instanceIdentifier, dataObject);
+ when(dataChangeEvent.getUpdatedOperationalData()).thenReturn(updated);
+ when(dataChangeEvent.getOriginalOperationalData()).thenReturn(original);
+ List<Link> links = new ArrayList<>();
+ links.add(link);
+ when(dataBrokerService.readOperationalData(instanceIdentifier)).thenReturn(topology);
+ when(topology.getLink()).thenReturn(links);
+
+ topologyLinkDataChangeHandler.onDataChanged(dataChangeEvent);
+ Thread.sleep(2100);
+ verify(networkGraphService, times(1)).addLinks(links);
+ }
+}
--- /dev/null
+<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>
+ <artifactId>sal-samples</artifactId>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <version>1.1-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <groupId>org.opendaylight.controller.samples.l2switch.md</groupId>
+ <artifactId>l2switch-model</artifactId>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Import-Package>org.opendaylight.yangtools.yang.binding.annotations, *</Import-Package>
+ <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <version>${yangtools.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>src/main/yang</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ target/generated-sources/sal
+ </outputBaseDir>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>target/site/models</outputBaseDir>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.yang.wadl.generator.maven.WadlGenerator</codeGeneratorClass>
+ <outputBaseDir>target/site/models</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>maven-sal-api-gen-plugin</artifactId>
+ <version>${yangtools.version}</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ <version>${yangtools.version}</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-yang-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-inventory</artifactId>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+module l2-address-tracker {
+ yang-version 1;
+ namespace "urn:opendaylight:l2-address-tracker";
+ prefix l2-address-tracker;
+
+ import ietf-yang-types {
+ prefix yang;
+ revision-date 2010-09-24;
+ }
+ import opendaylight-inventory {
+ prefix inv;
+ revision-date 2013-08-19;
+ }
+
+ organization "Cisco Systems Inc";
+ contact
+ "Alex Fan <alefan@cisco.com>";
+ description
+ "YANG version of the L2 Address Tracker Data Model";
+
+ revision 2014-04-02 {
+ description
+ "L2 Address Tracker module draft.";
+ }
+
+ grouping l2-address {
+ leaf mac {
+ type yang:mac-address;
+ mandatory true;
+ description
+ "the mac address of the host.";
+ }
+ leaf node-connector-ref {
+ type inv:node-connector-ref;
+ }
+ }
+
+ container l2-addresses {
+ config false;
+ list l2-address {
+ key "mac";
+ uses l2-address;
+ }
+ }
+}
\ No newline at end of file
--- /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>
+
+
+ <artifactId>l2switch.aggregator</artifactId>
+ <groupId>org.opendaylight.controller.samples.l2switch</groupId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>model</module>
+ <module>implementation</module>
+ </modules>
+
+</project>
<module>toaster</module>
<module>toaster-consumer</module>
<module>toaster-provider</module>
+ <module>l2switch</module>
</modules>
<profiles>
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
import org.opendaylight.yangtools.concepts.Registration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
/**
*
*/
public final class ToasterConsumerModule extends org.opendaylight.controller.config.yang.config.toaster_consumer.impl.AbstractToasterConsumerModule
{
+ private static final Logger log = LoggerFactory.getLogger(ToasterConsumerModule.class);
public ToasterConsumerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
public void close() throws Exception {
runtimeRegistration.close();
notificationRegistration.close();
+ log.info("Toaster consumer (instance {}) torn down.", this);
}
@Override
}
}
- return new AutoCloseableToastConsumer();
+ AutoCloseable ret = new AutoCloseableToastConsumer();
+ log.info("Toaster consumer (instance {}) initialized.", ret);
+ return ret;
}
}
mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
systemProperty("osgi.bundles.defaultStartLevel").value("4"),
+ systemPackages("sun.nio.ch"),
toasterBundles(),
mdSalCoreBundles(),
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.Toaster;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterData;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
*
*/
public final class ToasterProviderModule extends org.opendaylight.controller.config.yang.config.toaster_provider.impl.AbstractToasterProviderModule
{
+ private static final Logger log = LoggerFactory.getLogger(ToasterProviderModule.class);
public ToasterProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
// Register to md-sal
opendaylightToaster.setNotificationProvider(getNotificationServiceDependency());
+ opendaylightToaster.setDataProvider(getDataBrokerDependency());
final BindingAwareBroker.RpcRegistration<ToasterService> rpcRegistration = getRpcRegistryDependency()
.addRpcImplementation(ToasterService.class, opendaylightToaster);
public void close() throws Exception {
rpcRegistration.close();
runtimeReg.close();
+ opendaylightToaster.close();
+ log.info("Toaster provider (instance {}) torn down.", this);
}
@Override
}
}
- return new AutoCloseableToaster();
+ AutoCloseable ret = new AutoCloseableToaster();
+ log.info("Toaster provider (instance {}) initialized.", ret);
+ return ret;
}
-
}
import java.util.Collections;
import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.opendaylight.controller.config.yang.config.toaster_provider.impl.ToasterProviderRuntimeMXBean;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.DisplayString;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.MakeToastInput;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterBuilder;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterData;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.slf4j.Logger;
import com.google.common.util.concurrent.Futures;
-public class OpendaylightToaster implements ToasterData, ToasterService, ToasterProviderRuntimeMXBean {
+public class OpendaylightToaster implements ToasterData, ToasterService, ToasterProviderRuntimeMXBean, AutoCloseable {
private static final Logger log = LoggerFactory.getLogger(OpendaylightToaster.class);
+ private static final InstanceIdentifier<Toaster> toasterIID = InstanceIdentifier.builder(Toaster.class).build();
private static final DisplayString toasterManufacturer = new DisplayString("Opendaylight");
private static final DisplayString toasterModelNumber = new DisplayString("Model 1 - Binding Aware");
- private final ToasterStatus toasterStatus;
private NotificationProviderService notificationProvider;
+ private DataBrokerService dataProvider;
private final ExecutorService executor;
private Future<RpcResult<Void>> currentTask;
public OpendaylightToaster() {
- toasterStatus = ToasterStatus.Down;
executor = Executors.newFixedThreadPool(1);
}
@Override
- public Toaster getToaster() {
+ public synchronized Toaster getToaster() {
ToasterBuilder tb = new ToasterBuilder();
tb //
.setToasterManufacturer(toasterManufacturer) //
- .setToasterModelNumber(toasterModelNumber) //
- .setToasterStatus(toasterStatus);
+ .setToasterModelNumber(toasterModelNumber) //
+ .setToasterStatus(currentTask == null ? ToasterStatus.Up : ToasterStatus.Down);
return tb.build();
}
@Override
- public Future<RpcResult<Void>> cancelToast() {
+ public synchronized Future<RpcResult<Void>> cancelToast() {
if (currentTask != null) {
cancelToastImpl();
}
}
@Override
- public Future<RpcResult<Void>> makeToast(MakeToastInput input) {
- // TODO Auto-generated method stub
- log.trace("makeToast - Received input for toast");
+ public synchronized Future<RpcResult<Void>> makeToast(MakeToastInput input) {
+ log.debug("makeToast - Received input for toast");
logToastInput(input);
if (currentTask != null) {
return inProgressError();
}
currentTask = executor.submit(new MakeToastTask(input));
+ updateStatus();
return currentTask;
}
this.notificationProvider = salService;
}
+ public void setDataProvider(DataBrokerService salDataProvider) {
+ this.dataProvider = salDataProvider;
+ updateStatus();
+ }
+
private void logToastInput(MakeToastInput input) {
String toastType = input.getToasterToastType().getName();
String toastDoneness = input.getToasterDoneness().toString();
return toastsMade.get();
}
+ private void updateStatus() {
+ if (dataProvider != null) {
+ final DataModificationTransaction t = dataProvider.beginTransaction();
+ t.removeOperationalData(toasterIID);
+ t.putOperationalData(toasterIID, getToaster());
+
+ try {
+ t.commit().get();
+ } catch (InterruptedException | ExecutionException e) {
+ log.warn("Failed to update toaster status, operational otherwise", e);
+ }
+ } else {
+ log.trace("No data provider configured, not updating status");
+ }
+ }
+
+ @Override
+ public void close() throws ExecutionException, InterruptedException {
+ if (dataProvider != null) {
+ final DataModificationTransaction t = dataProvider.beginTransaction();
+ t.removeOperationalData(toasterIID);
+ t.commit().get();
+ }
+ }
+
private class MakeToastTask implements Callable<RpcResult<Void>> {
final MakeToastInput toastRequest;
}
@Override
- public RpcResult<Void> call() throws Exception {
- Thread.sleep(1000);
+ public RpcResult<Void> call() throws InterruptedException {
+ Thread.sleep(1000 * toastRequest.getToasterDoneness());
ToastDoneBuilder notifyBuilder = new ToastDoneBuilder();
notifyBuilder.setToastStatus(ToastStatus.Done);
notificationProvider.publish(notifyBuilder.build());
- log.trace("Toast Done");
+ log.debug("Toast Done");
logToastInput(toastRequest);
- currentTask = null;
+ currentTask = null;
toastsMade.incrementAndGet();
+ updateStatus();
return Rpcs.<Void> getRpcResult(true, null, Collections.<RpcError> emptySet());
}
}
}
+ container data-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory false;
+ config:required-identity mdsal:binding-data-broker;
+ }
+ }
+ }
}
}
}
}
-}
\ No newline at end of file
+}
<groupId>org.eclipse.xtend</groupId>
<artifactId>org.eclipse.xtend.lib</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>junit</groupId>
</plugins>
<pluginManagement>
<plugins>
- <!--This plugin's configuration is used to store Eclipse
+ <!--This plugin's configuration is used to store Eclipse
m2e settings only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
- <version>4.0.10.Final</version>
+ <version>${netty.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<artifactId>jersey-core</artifactId>
<version>${jersey.version}</version>
</dependency>
- <!-- <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-servlet</artifactId>
+ <!-- <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-servlet</artifactId>
<version>${jersey.version}</version> </dependency> -->
<dependency>
<groupId>com.sun.jersey</groupId>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
+ <artifactId>config-persister-file-xml-adapter</artifactId>
<version>${config.version}</version>
</dependency>
<dependency>
InstanceIdentifier path = InstanceIdentifier.builder(InventoryUtils.INVENTORY_PATH)
.nodeWithKey(InventoryUtils.INVENTORY_NODE, InventoryUtils.INVENTORY_ID, "foo").toInstance();
-
+
InstanceIdentifier mountPointPath = path;
-
+
/** We retrive a mountpoint **/
MountProvisionInstance mountPoint = mountService.getMountPoint(mountPointPath);
CompositeNode data = mountPoint.readOperationalData(InstanceIdentifier.builder().node(CONFIG_MODULES)
mavenBundle(ODL, "yang-jmx-generator").versionAsInProject(),
mavenBundle(ODL, "logback-config").versionAsInProject(),
mavenBundle(ODL, "config-persister-api").versionAsInProject(),
- // mavenBundle(ODL,"config-persister-file-adapter").versionAsInProject(),
+ // mavenBundle(ODL,"config-persister-file-xml-adapter").versionAsInProject(),
mavenBundle(ODL, "protocol-framework").versionAsInProject(),
mavenBundle(ODL, "netconf-api").versionAsInProject(),
mavenBundle(ODL, "netconf-impl").versionAsInProject(),
<groupId>org.eclipse.xtend</groupId>
<artifactId>org.eclipse.xtend.lib</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<build>
import org.slf4j.LoggerFactory
import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareProvider
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext
+import org.osgi.framework.BundleContext;
class FlowCapableTopologyProvider extends AbstractBindingAwareProvider implements AutoCloseable {
LOG.info("FlowCapableTopologyProvider stopped.");
listenerRegistration?.close();
}
-
+
+ /**
+ * Gets called on start of a bundle.
+ * @param session
+ */
override onSessionInitiated(ProviderContext session) {
dataService = session.getSALService(DataProviderService)
notificationService = session.getSALService(NotificationProviderService)
exporter.start();
listenerRegistration = notificationService.registerNotificationListener(exporter);
}
+
+ /**
+ * Gets called during stop bundle
+ * @param context The execution context of the bundle being stopped.
+ */
+ override stopImpl(BundleContext context) {
+ close();
+ }
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
import static com.google.common.base.Preconditions.checkState;
public class Activator implements BundleActivator {
public void run() {
NetconfOperationServiceFactoryImpl factory = new NetconfOperationServiceFactoryImpl(yangStoreService);
logger.debug("Registering into OSGi");
- osgiRegistration = context.registerService(NetconfOperationServiceFactory.class, factory, null);
+ Dictionary<String, String> properties = new Hashtable<>();
+ properties.put("name", "config-netconf-connector");
+ osgiRegistration = context.registerService(NetconfOperationServiceFactory.class, factory, properties);
}
}
}
}
@Override
- public NetconfOperationServiceImpl createService(long netconfSessionId, String netconfSessionIdForReporting) {
+ public NetconfOperationServiceImpl createService(String netconfSessionIdForReporting) {
try {
return new NetconfOperationServiceImpl(yangStoreService, jmxClient, netconfSessionIdForReporting);
} catch (YangStoreException e) {
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
Preconditions.checkState(priority != HandlingPriority.CANNOT_HANDLE);
- final Document response = op.handle(request, NetconfOperationRouterImpl.EXECUTION_TERMINATION_POINT);
+ final Document response = op.handle(request, NetconfOperationChainedExecution.EXECUTION_TERMINATION_POINT);
logger.debug("Got response\n{}", XmlUtil.toString(response));
return response.getDocumentElement();
}
<groupId>${project.groupId}</groupId>
<artifactId>netconf-api</artifactId>
</dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>netconf-client</artifactId>
- </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-util</artifactId>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<instructions>
<Bundle-Activator>org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator
</Bundle-Activator>
- <Require-Capability>org.opendaylight.controller.config.persister.storage.adapter
- </Require-Capability>
- <Import-Package>
- com.google.common.base,
- com.google.common.collect,
- javax.management,
- javax.xml.parsers,
- org.opendaylight.controller.config.persist.api,
- org.opendaylight.controller.netconf.api,
- org.opendaylight.controller.netconf.api.jmx,
- org.opendaylight.controller.netconf.client,
- org.opendaylight.controller.netconf.util.osgi,
- org.opendaylight.controller.netconf.util.xml,
- org.opendaylight.controller.netconf.util.messages,
- io.netty.channel,
- io.netty.channel.nio,
- io.netty.util.concurrent,
- org.osgi.framework,
- org.slf4j,
- org.w3c.dom,
- org.xml.sax,
- javax.xml.namespace,
- javax.xml.xpath,
- org.opendaylight.controller.config.api,
- org.opendaylight.controller.netconf.util
- </Import-Package>
+ <Require-Capability>org.opendaylight.controller.config.persister.storage.adapter</Require-Capability>
<Export-Package>
</Export-Package>
</instructions>
* 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.persist.impl;
import com.google.common.annotations.VisibleForTesting;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
-import java.util.regex.Pattern;
-
-import static com.google.common.base.Preconditions.checkState;
+/**
+ * Inspects snapshot xml to be stored, remove all capabilities that are not referenced by it.
+ * Useful when persisting current configuration.
+ */
public class CapabilityStrippingConfigSnapshotHolder implements ConfigSnapshotHolder {
private static final Logger logger = LoggerFactory.getLogger(CapabilityStrippingConfigSnapshotHolder.class);
private final String configSnapshot;
private final StripCapabilitiesResult stripCapabilitiesResult;
- public CapabilityStrippingConfigSnapshotHolder(Element snapshot, Set<String> capabilities, Pattern ignoredMissingCapabilityRegex) {
+ public CapabilityStrippingConfigSnapshotHolder(Element snapshot, Set<String> capabilities) {
final XmlElement configElement = XmlElement.fromDomElement(snapshot);
configSnapshot = XmlUtil.toString(configElement.getDomElement());
- stripCapabilitiesResult = stripCapabilities(configElement, capabilities, ignoredMissingCapabilityRegex);
+ stripCapabilitiesResult = stripCapabilities(configElement, capabilities);
}
private static class StripCapabilitiesResult {
- private final SortedSet<String> requiredCapabilities, missingNamespaces;
+ private final SortedSet<String> requiredCapabilities, obsoleteCapabilities;
- private StripCapabilitiesResult(SortedSet<String> requiredCapabilities, SortedSet<String> missingNamespaces) {
+ private StripCapabilitiesResult(SortedSet<String> requiredCapabilities, SortedSet<String> obsoleteCapabilities) {
this.requiredCapabilities = Collections.unmodifiableSortedSet(requiredCapabilities);
- this.missingNamespaces = Collections.unmodifiableSortedSet(missingNamespaces);
+ this.obsoleteCapabilities = Collections.unmodifiableSortedSet(obsoleteCapabilities);
}
}
@VisibleForTesting
- static StripCapabilitiesResult stripCapabilities(XmlElement configElement, Set<String> allCapabilitiesFromHello,
- Pattern ignoredMissingCapabilityRegex) {
+ static StripCapabilitiesResult stripCapabilities(XmlElement configElement, Set<String> allCapabilitiesFromHello) {
// collect all namespaces
Set<String> foundNamespacesInXML = getNamespaces(configElement);
logger.trace("All capabilities {}\nFound namespaces in XML {}", allCapabilitiesFromHello, foundNamespacesInXML);
// required are referenced both in xml and hello
SortedSet<String> requiredCapabilities = new TreeSet<>();
// can be removed
- Set<String> obsoleteCapabilities = new HashSet<>();
- // are in xml but not in hello
- SortedSet<String> missingNamespaces = new TreeSet<>(foundNamespacesInXML);
+ SortedSet<String> obsoleteCapabilities = new TreeSet<>();
for (String capability : allCapabilitiesFromHello) {
String namespace = capability.replaceAll("\\?.*","");
if (foundNamespacesInXML.contains(namespace)) {
requiredCapabilities.add(capability);
- checkState(missingNamespaces.remove(namespace));
} else {
obsoleteCapabilities.add(capability);
}
logger.trace("Required capabilities {}, \nObsolete capabilities {}",
requiredCapabilities, obsoleteCapabilities);
- for(Iterator<String> iterator = missingNamespaces.iterator();iterator.hasNext(); ){
- String capability = iterator.next();
- if (ignoredMissingCapabilityRegex.matcher(capability).matches()){
- logger.trace("Ignoring missing capability {}", capability);
- iterator.remove();
- }
- }
- if (missingNamespaces.size() > 0) {
- logger.warn("Some capabilities are missing: {}", missingNamespaces);
- }
- return new StripCapabilitiesResult(requiredCapabilities, missingNamespaces);
+ return new StripCapabilitiesResult(requiredCapabilities, obsoleteCapabilities);
}
static Set<String> getNamespaces(XmlElement element){
result.add(attribute.getValue().getValue());
}
}
- //element.getAttributes()
for(XmlElement child: element.getChildElements()) {
result.addAll(getNamespaces(child));
}
}
@VisibleForTesting
- Set<String> getMissingNamespaces(){
- return stripCapabilitiesResult.missingNamespaces;
+ Set<String> getObsoleteCapabilities(){
+ return stripCapabilitiesResult.obsoleteCapabilities;
}
@Override
import javax.management.ObjectName;
import java.io.Closeable;
import java.io.IOException;
-import java.util.regex.Pattern;
/**
* Responsible for listening for notifications from netconf (via JMX) containing latest
public ConfigPersisterNotificationHandler(MBeanServerConnection mBeanServerConnection,
- Persister persisterAggregator, Pattern ignoredMissingCapabilityRegex) {
+ Persister persisterAggregator) {
this.mBeanServerConnection = mBeanServerConnection;
- listener = new ConfigPersisterNotificationListener(persisterAggregator, ignoredMissingCapabilityRegex);
+ listener = new ConfigPersisterNotificationListener(persisterAggregator);
registerAsJMXListener(mBeanServerConnection, listener);
}
private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationListener.class);
private final Persister persisterAggregator;
- private final Pattern ignoredMissingCapabilityRegex;
- ConfigPersisterNotificationListener(Persister persisterAggregator, Pattern ignoredMissingCapabilityRegex) {
+ ConfigPersisterNotificationListener(Persister persisterAggregator) {
this.persisterAggregator = persisterAggregator;
- this.ignoredMissingCapabilityRegex = ignoredMissingCapabilityRegex;
}
@Override
public void handleNotification(Notification notification, Object handback) {
- if (notification instanceof NetconfJMXNotification == false)
+ if (notification instanceof NetconfJMXNotification == false) {
return;
+ }
// Socket should not be closed at this point
// Activator unregisters this as JMX listener before close is called
logger.warn("Exception occured during notification handling: ", e);
throw e;
}
- } else
+ } else {
throw new IllegalStateException("Unknown config registry notification type " + notification);
+ }
}
private void handleAfterCommitNotification(final CommitJMXNotification notification) {
try {
persisterAggregator.persistConfig(new CapabilityStrippingConfigSnapshotHolder(notification.getConfigSnapshot(),
- notification.getCapabilities(), ignoredMissingCapabilityRegex));
+ notification.getCapabilities()));
logger.trace("Configuration persisted successfully");
} catch (IOException e) {
throw new RuntimeException("Unable to persist configuration snapshot", e);
package org.opendaylight.controller.netconf.persist.impl;
+import com.google.common.base.Function;
import com.google.common.base.Preconditions;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.Collections2;
import org.opendaylight.controller.config.api.ConflictingVersionException;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.client.NetconfClient;
-import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.util.NetconfUtil;
-import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import javax.annotation.concurrent.Immutable;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Collections;
+import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map.Entry;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
+import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
@Immutable
public class ConfigPusher {
private static final Logger logger = LoggerFactory.getLogger(ConfigPusher.class);
- private final ConfigPusherConfiguration configuration;
+ private final long maxWaitForCapabilitiesMillis;
+ private final long conflictingVersionTimeoutMillis;
+ private final NetconfOperationServiceFactory configNetconfConnector;
- public ConfigPusher(ConfigPusherConfiguration configuration) {
- this.configuration = configuration;
+ public ConfigPusher(NetconfOperationServiceFactory configNetconfConnector, long maxWaitForCapabilitiesMillis,
+ long conflictingVersionTimeoutMillis) {
+ this.configNetconfConnector = configNetconfConnector;
+ this.maxWaitForCapabilitiesMillis = maxWaitForCapabilitiesMillis;
+ this.conflictingVersionTimeoutMillis = conflictingVersionTimeoutMillis;
}
- public synchronized LinkedHashMap<ConfigSnapshotHolder, EditAndCommitResponseWithRetries> pushConfigs(
- List<ConfigSnapshotHolder> configs) throws InterruptedException {
+ public synchronized LinkedHashMap<ConfigSnapshotHolder, EditAndCommitResponse> pushConfigs(List<ConfigSnapshotHolder> configs) {
logger.debug("Last config snapshots to be pushed to netconf: {}", configs);
-
- // first just make sure we can connect to netconf, even if nothing is being pushed
- {
- NetconfClient netconfClient = makeNetconfConnection(Collections.<String>emptySet());
- Util.closeClientAndDispatcher(netconfClient);
- }
- LinkedHashMap<ConfigSnapshotHolder, EditAndCommitResponseWithRetries> result = new LinkedHashMap<>();
+ LinkedHashMap<ConfigSnapshotHolder, EditAndCommitResponse> result = new LinkedHashMap<>();
// start pushing snapshots:
for (ConfigSnapshotHolder configSnapshotHolder : configs) {
- EditAndCommitResponseWithRetries editAndCommitResponseWithRetries = pushSnapshotWithRetries(configSnapshotHolder);
+ EditAndCommitResponse editAndCommitResponseWithRetries = pushConfigWithConflictingVersionRetries(configSnapshotHolder);
logger.debug("Config snapshot pushed successfully: {}, result: {}", configSnapshotHolder, result);
result.put(configSnapshotHolder, editAndCommitResponseWithRetries);
}
}
/**
- * Checks for ConflictingVersionException and retries until optimistic lock succeeds or maximal
- * number of attempts is reached.
+ * First calls {@link #getOperationServiceWithRetries(java.util.Set, String)} in order to wait until
+ * expected capabilities are present, then tries to push configuration. If {@link ConflictingVersionException}
+ * is caught, whole process is retried - new service instance need to be obtained from the factory. Closes
+ * {@link NetconfOperationService} after each use.
*/
- private synchronized EditAndCommitResponseWithRetries pushSnapshotWithRetries(ConfigSnapshotHolder configSnapshotHolder)
- throws InterruptedException {
-
- ConflictingVersionException lastException = null;
- int maxAttempts = configuration.netconfPushConfigAttempts;
-
- for (int retryAttempt = 1; retryAttempt <= maxAttempts; retryAttempt++) {
- NetconfClient netconfClient = makeNetconfConnection(configSnapshotHolder.getCapabilities());
- logger.trace("Pushing following xml to netconf {}", configSnapshotHolder);
- try {
- EditAndCommitResponse editAndCommitResponse = pushLastConfig(configSnapshotHolder, netconfClient);
- return new EditAndCommitResponseWithRetries(editAndCommitResponse, retryAttempt);
+ private synchronized EditAndCommitResponse pushConfigWithConflictingVersionRetries(ConfigSnapshotHolder configSnapshotHolder) {
+ ConflictingVersionException lastException;
+ Stopwatch stopwatch = new Stopwatch().start();
+ do {
+ try (NetconfOperationService operationService = getOperationServiceWithRetries(configSnapshotHolder.getCapabilities(), configSnapshotHolder.toString())) {
+ return pushConfig(configSnapshotHolder, operationService);
} catch (ConflictingVersionException e) {
- logger.debug("Conflicting version detected, will retry after timeout");
lastException = e;
- Thread.sleep(configuration.netconfPushConfigDelayMs);
- } catch (RuntimeException e) {
- throw new IllegalStateException("Unable to load " + configSnapshotHolder, e);
- } finally {
- Util.closeClientAndDispatcher(netconfClient);
+ logger.debug("Conflicting version detected, will retry after timeout");
+ sleep();
}
- }
- throw new IllegalStateException("Maximum attempt count has been reached for pushing " + configSnapshotHolder,
+ } while (stopwatch.elapsed(TimeUnit.MILLISECONDS) < conflictingVersionTimeoutMillis);
+ throw new IllegalStateException("Max wait for conflicting version stabilization timeout after " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms",
lastException);
}
- /**
- * @param expectedCaps capabilities that server hello must contain. Will retry until all are found or throws RuntimeException.
- * If empty set is provided, will only make sure netconf client successfuly connected to the server.
- * @return NetconfClient that has all required capabilities from server.
- */
- private synchronized NetconfClient makeNetconfConnection(Set<String> expectedCaps) throws InterruptedException {
-
- // TODO think about moving capability subset check to netconf client
- // could be utilized by integration tests
+ private NetconfOperationService getOperationServiceWithRetries(Set<String> expectedCapabilities, String idForReporting) {
+ Stopwatch stopwatch = new Stopwatch().start();
+ NotEnoughCapabilitiesException lastException;
+ do {
+ try {
+ return getOperationService(expectedCapabilities, idForReporting);
+ } catch (NotEnoughCapabilitiesException e) {
+ logger.debug("Not enough capabilities: " + e.toString());
+ lastException = e;
+ sleep();
+ }
+ } while (stopwatch.elapsed(TimeUnit.MILLISECONDS) < maxWaitForCapabilitiesMillis);
+ throw new IllegalStateException("Max wait for capabilities reached." + lastException.getMessage(), lastException);
+ }
- final long pollingStartNanos = System.nanoTime();
- final long deadlineNanos = pollingStartNanos + TimeUnit.MILLISECONDS.toNanos(configuration.netconfCapabilitiesWaitTimeoutMs);
- int attempt = 0;
+ private static class NotEnoughCapabilitiesException extends Exception {
+ private NotEnoughCapabilitiesException(String message, Throwable cause) {
+ super(message, cause);
+ }
- NetconfHelloMessageAdditionalHeader additionalHeader = new NetconfHelloMessageAdditionalHeader("unknown",
- configuration.netconfAddress.getAddress().getHostAddress(),
- Integer.toString(configuration.netconfAddress.getPort()), "tcp", "persister");
+ private NotEnoughCapabilitiesException(String message) {
+ super(message);
+ }
+ }
- Set<String> latestCapabilities = null;
- while (System.nanoTime() < deadlineNanos) {
- attempt++;
- NetconfClientDispatcher netconfClientDispatcher = new NetconfClientDispatcher(configuration.eventLoopGroup,
- configuration.eventLoopGroup, additionalHeader, configuration.connectionAttemptTimeoutMs);
- NetconfClient netconfClient;
- try {
- netconfClient = new NetconfClient(this.toString(), configuration.netconfAddress, configuration.connectionAttemptDelayMs, netconfClientDispatcher);
- } catch (IllegalStateException e) {
- logger.debug("Netconf {} was not initialized or is not stable, attempt {}", configuration.netconfAddress, attempt, e);
- netconfClientDispatcher.close();
- Thread.sleep(configuration.connectionAttemptDelayMs);
- continue;
- }
- latestCapabilities = netconfClient.getCapabilities();
- if (Util.isSubset(netconfClient, expectedCaps)) {
- logger.debug("Hello from netconf stable with {} capabilities", latestCapabilities);
- logger.trace("Session id received from netconf server: {}", netconfClient.getClientSession());
- return netconfClient;
- }
- Set<String> allNotFound = computeNotFoundCapabilities(expectedCaps, latestCapabilities);
- logger.debug("Netconf server did not provide required capabilities. Attempt {}. " +
- "Expected but not found: {}, all expected {}, current {}",
- attempt, allNotFound, expectedCaps, latestCapabilities);
- Util.closeClientAndDispatcher(netconfClient);
- Thread.sleep(configuration.connectionAttemptDelayMs);
+ /**
+ * Get NetconfOperationService iif all required capabilities are present.
+ *
+ * @param expectedCapabilities that must be provided by configNetconfConnector
+ * @param idForReporting
+ * @return service if capabilities are present, otherwise absent value
+ */
+ private NetconfOperationService getOperationService(Set<String> expectedCapabilities, String idForReporting) throws NotEnoughCapabilitiesException {
+ NetconfOperationService serviceCandidate;
+ try {
+ serviceCandidate = configNetconfConnector.createService(idForReporting);
+ } catch(RuntimeException e) {
+ throw new NotEnoughCapabilitiesException("Netconf service not stable for " + idForReporting, e);
}
- if (latestCapabilities == null) {
- logger.error("Could not connect to the server in {} ms", configuration.netconfCapabilitiesWaitTimeoutMs);
- throw new RuntimeException("Could not connect to netconf server");
+ Set<String> notFoundDiff = computeNotFoundCapabilities(expectedCapabilities, serviceCandidate);
+ if (notFoundDiff.isEmpty()) {
+ return serviceCandidate;
+ } else {
+ serviceCandidate.close();
+ logger.trace("Netconf server did not provide required capabilities for {} " +
+ "Expected but not found: {}, all expected {}, current {}",
+ idForReporting, notFoundDiff, expectedCapabilities, serviceCandidate.getCapabilities()
+ );
+ throw new NotEnoughCapabilitiesException("Not enough capabilities for " + idForReporting + ". Expected but not found: " + notFoundDiff);
}
- Set<String> allNotFound = computeNotFoundCapabilities(expectedCaps, latestCapabilities);
- logger.error("Netconf server did not provide required capabilities. Expected but not found: {}, all expected {}, current {}",
- allNotFound, expectedCaps, latestCapabilities);
- throw new RuntimeException("Netconf server did not provide required capabilities. Expected but not found:" + allNotFound);
}
- private static Set<String> computeNotFoundCapabilities(Set<String> expectedCaps, Set<String> latestCapabilities) {
- Set<String> allNotFound = new HashSet<>(expectedCaps);
- allNotFound.removeAll(latestCapabilities);
+ private static Set<String> computeNotFoundCapabilities(Set<String> expectedCapabilities, NetconfOperationService serviceCandidate) {
+ Collection<String> actual = Collections2.transform(serviceCandidate.getCapabilities(), new Function<Capability, String>() {
+ @Override
+ public String apply(Capability input) {
+ return input.getCapabilityUri();
+ }
+ });
+ Set<String> allNotFound = new HashSet<>(expectedCapabilities);
+ allNotFound.removeAll(actual);
return allNotFound;
}
+
+ private void sleep() {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException(e);
+ }
+ }
+
/**
* Sends two RPCs to the netconf server: edit-config and commit.
*
* @param configSnapshotHolder
- * @param netconfClient
* @throws ConflictingVersionException if commit fails on optimistic lock failure inside of config-manager
* @throws java.lang.RuntimeException if edit-config or commit fails otherwise
*/
- private synchronized EditAndCommitResponse pushLastConfig(ConfigSnapshotHolder configSnapshotHolder, NetconfClient netconfClient)
+ private synchronized EditAndCommitResponse pushConfig(ConfigSnapshotHolder configSnapshotHolder, NetconfOperationService operationService)
throws ConflictingVersionException {
Element xmlToBePersisted;
throw new IllegalStateException("Cannot parse " + configSnapshotHolder);
}
logger.trace("Pushing last configuration to netconf: {}", configSnapshotHolder);
-
+ Stopwatch stopwatch = new Stopwatch().start();
NetconfMessage editConfigMessage = createEditConfigMessage(xmlToBePersisted);
- // sending message to netconf
- NetconfMessage editResponseMessage;
- try {
- editResponseMessage = sendRequestGetResponseCheckIsOK(editConfigMessage, netconfClient);
- } catch (IOException e) {
- throw new IllegalStateException("Edit-config failed on " + configSnapshotHolder, e);
- }
+ Document editResponseMessage = sendRequestGetResponseCheckIsOK(editConfigMessage, operationService,
+ "edit-config", configSnapshotHolder.toString());
- // commit
- NetconfMessage commitResponseMessage;
- try {
- commitResponseMessage = sendRequestGetResponseCheckIsOK(getCommitMessage(), netconfClient);
- } catch (IOException e) {
- throw new IllegalStateException("Edit commit succeeded, but commit failed on " + configSnapshotHolder, e);
- }
+ Document commitResponseMessage = sendRequestGetResponseCheckIsOK(getCommitMessage(), operationService,
+ "commit", configSnapshotHolder.toString());
if (logger.isTraceEnabled()) {
StringBuilder response = new StringBuilder("editConfig response = {");
- response.append(XmlUtil.toString(editResponseMessage.getDocument()));
+ response.append(XmlUtil.toString(editResponseMessage));
response.append("}");
response.append("commit response = {");
- response.append(XmlUtil.toString(commitResponseMessage.getDocument()));
+ response.append(XmlUtil.toString(commitResponseMessage));
response.append("}");
logger.trace("Last configuration loaded successfully");
logger.trace("Detailed message {}", response);
+ logger.trace("Total time spent {} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
return new EditAndCommitResponse(editResponseMessage, commitResponseMessage);
}
+ private NetconfOperation findOperation(NetconfMessage request, NetconfOperationService operationService) {
+ TreeMap<HandlingPriority, NetconfOperation> allOperations = new TreeMap<>();
+ Set<NetconfOperation> netconfOperations = operationService.getNetconfOperations();
+ if (netconfOperations.isEmpty()) {
+ throw new IllegalStateException("Possible code error: no config operations");
+ }
+ for (NetconfOperation netconfOperation : netconfOperations) {
+ HandlingPriority handlingPriority = netconfOperation.canHandle(request.getDocument());
+ allOperations.put(handlingPriority, netconfOperation);
+ }
+ Entry<HandlingPriority, NetconfOperation> highestEntry = allOperations.lastEntry();
+ if (highestEntry.getKey().isCannotHandle()) {
+ throw new IllegalStateException("Possible code error: operation with highest priority is CANNOT_HANDLE");
+ }
+ return highestEntry.getValue();
+ }
+
+ private Document sendRequestGetResponseCheckIsOK(NetconfMessage request, NetconfOperationService operationService,
+ String operationNameForReporting, String configIdForReporting)
+ throws ConflictingVersionException {
- private NetconfMessage sendRequestGetResponseCheckIsOK(NetconfMessage request, NetconfClient netconfClient)
- throws ConflictingVersionException, IOException {
+ NetconfOperation operation = findOperation(request, operationService);
+ Document response;
try {
- NetconfMessage netconfMessage = netconfClient.sendMessage(request,
- configuration.netconfSendMessageMaxAttempts, configuration.netconfSendMessageDelayMs);
- NetconfUtil.checkIsMessageOk(netconfMessage);
- return netconfMessage;
- } catch(ConflictingVersionException e) {
- logger.trace("conflicting version detected: {}", e.toString());
- throw e;
- } catch (RuntimeException | ExecutionException | InterruptedException | TimeoutException e) { // TODO: change NetconfClient#sendMessage to throw checked exceptions
- logger.debug("Error while executing netconf transaction {} to {}", request, netconfClient, e);
- throw new IOException("Failed to execute netconf transaction", e);
+ response = operation.handle(request.getDocument(), NetconfOperationChainedExecution.EXECUTION_TERMINATION_POINT);
+ } catch (NetconfDocumentedException | RuntimeException e) {
+ if (e instanceof NetconfDocumentedException && e.getCause() instanceof ConflictingVersionException) {
+ throw (ConflictingVersionException) e.getCause();
+ }
+ throw new IllegalStateException("Failed to send " + operationNameForReporting +
+ " for configuration " + configIdForReporting, e);
}
+ return NetconfUtil.checkIsMessageOk(response);
}
-
// load editConfig.xml template, populate /rpc/edit-config/config with parameter
private static NetconfMessage createEditConfigMessage(Element dataElement) {
String editConfigResourcePath = "/netconfOp/editConfig.xml";
return new NetconfMessage(doc);
} catch (IOException | SAXException e) {
// error reading the xml file bundled into the jar
- throw new RuntimeException("Error while opening local resource " + editConfigResourcePath, e);
+ throw new IllegalStateException("Error while opening local resource " + editConfigResourcePath, e);
}
}
return new NetconfMessage(XmlUtil.readXmlToDocument(stream));
} catch (SAXException | IOException e) {
// error reading the xml file bundled into the jar
- throw new RuntimeException("Error while opening local resource " + resource, e);
+ throw new IllegalStateException("Error while opening local resource " + resource, e);
}
}
static class EditAndCommitResponse {
- private final NetconfMessage editResponse, commitResponse;
+ private final Document editResponse, commitResponse;
- EditAndCommitResponse(NetconfMessage editResponse, NetconfMessage commitResponse) {
+ EditAndCommitResponse(Document editResponse, Document commitResponse) {
this.editResponse = editResponse;
this.commitResponse = commitResponse;
}
- public NetconfMessage getEditResponse() {
+ public Document getEditResponse() {
return editResponse;
}
- public NetconfMessage getCommitResponse() {
+ public Document getCommitResponse() {
return commitResponse;
}
'}';
}
}
-
-
- static class EditAndCommitResponseWithRetries {
- private final EditAndCommitResponse editAndCommitResponse;
- private final int retries;
-
- EditAndCommitResponseWithRetries(EditAndCommitResponse editAndCommitResponse, int retries) {
- this.editAndCommitResponse = editAndCommitResponse;
- this.retries = retries;
- }
-
- public int getRetries() {
- return retries;
- }
-
- public EditAndCommitResponse getEditAndCommitResponse() {
- return editAndCommitResponse;
- }
-
- @Override
- public String toString() {
- return "EditAndCommitResponseWithRetries{" +
- "editAndCommitResponse=" + editAndCommitResponse +
- ", retries=" + retries +
- '}';
- }
- }
-
}
+++ /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.persist.impl;
-
-import io.netty.channel.EventLoopGroup;
-
-import javax.annotation.concurrent.Immutable;
-import java.net.InetSocketAddress;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Configuration properties for ConfigPusher. Contains delays and timeouts for netconf
- * connection establishment, netconf capabilities stabilization and configuration push.
- */
-@Immutable
-public final class ConfigPusherConfiguration {
-
- public static final long DEFAULT_CONNECTION_ATTEMPT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
- public static final int DEFAULT_CONNECTION_ATTEMPT_DELAY_MS = 5000;
-
- public static final int DEFAULT_NETCONF_SEND_MESSAGE_MAX_ATTEMPTS = 20;
- public static final int DEFAULT_NETCONF_SEND_MESSAGE_DELAY_MS = 1000;
-
- public static final long DEFAULT_NETCONF_CAPABILITIES_WAIT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(2);
-
- public static final int DEFAULT_NETCONF_PUSH_CONFIG_ATTEMPTS = 30;
- public static final long DEFAULT_NETCONF_PUSH_CONFIG_DELAY_MS = TimeUnit.MINUTES.toMillis(1);
-
- final InetSocketAddress netconfAddress;
- final EventLoopGroup eventLoopGroup;
-
- /**
- * Total time to wait for capability stabilization
- */
- final long netconfCapabilitiesWaitTimeoutMs;
-
- /**
- * Delay between message send attempts
- */
- final int netconfSendMessageDelayMs;
- /**
- * Total number attempts to send a message
- */
- final int netconfSendMessageMaxAttempts;
-
- /**
- * Delay between connection establishment attempts
- */
- final int connectionAttemptDelayMs;
- /**
- * Total number of attempts to perform connection establishment
- */
- final long connectionAttemptTimeoutMs;
-
- /**
- * Total number of attempts to push configuration to netconf
- */
- final int netconfPushConfigAttempts;
- /**
- * Delay between configuration push attempts
- */
- final long netconfPushConfigDelayMs;
-
- ConfigPusherConfiguration(InetSocketAddress netconfAddress, long netconfCapabilitiesWaitTimeoutMs,
- int netconfSendMessageDelayMs, int netconfSendMessageMaxAttempts, int connectionAttemptDelayMs,
- long connectionAttemptTimeoutMs, EventLoopGroup eventLoopGroup, int netconfPushConfigAttempts,
- long netconfPushConfigDelayMs) {
- this.netconfAddress = netconfAddress;
- this.netconfCapabilitiesWaitTimeoutMs = netconfCapabilitiesWaitTimeoutMs;
- this.netconfSendMessageDelayMs = netconfSendMessageDelayMs;
- this.netconfSendMessageMaxAttempts = netconfSendMessageMaxAttempts;
- this.connectionAttemptDelayMs = connectionAttemptDelayMs;
- this.connectionAttemptTimeoutMs = connectionAttemptTimeoutMs;
- this.eventLoopGroup = eventLoopGroup;
- this.netconfPushConfigAttempts = netconfPushConfigAttempts;
- this.netconfPushConfigDelayMs = netconfPushConfigDelayMs;
- }
-}
+++ /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.persist.impl;
-
-import io.netty.channel.EventLoopGroup;
-
-import java.net.InetSocketAddress;
-
-public class ConfigPusherConfigurationBuilder {
- InetSocketAddress netconfAddress;
- EventLoopGroup eventLoopGroup;
-
- long netconfCapabilitiesWaitTimeoutMs = ConfigPusherConfiguration.DEFAULT_NETCONF_CAPABILITIES_WAIT_TIMEOUT_MS;
- int netconfSendMessageDelayMs = ConfigPusherConfiguration.DEFAULT_NETCONF_SEND_MESSAGE_DELAY_MS;
- int netconfSendMessageMaxAttempts = ConfigPusherConfiguration.DEFAULT_NETCONF_SEND_MESSAGE_MAX_ATTEMPTS;
- int connectionAttemptDelayMs = ConfigPusherConfiguration.DEFAULT_CONNECTION_ATTEMPT_DELAY_MS;
- long connectionAttemptTimeoutMs = ConfigPusherConfiguration.DEFAULT_CONNECTION_ATTEMPT_TIMEOUT_MS;
- int netconfPushConfigAttempts = ConfigPusherConfiguration.DEFAULT_NETCONF_PUSH_CONFIG_ATTEMPTS;
- long netconfPushConfigDelayMs = ConfigPusherConfiguration.DEFAULT_NETCONF_PUSH_CONFIG_DELAY_MS;
-
- private ConfigPusherConfigurationBuilder() {
- }
-
- public static ConfigPusherConfigurationBuilder aConfigPusherConfiguration() {
- return new ConfigPusherConfigurationBuilder();
- }
-
- public ConfigPusherConfigurationBuilder withNetconfAddress(InetSocketAddress netconfAddress) {
- this.netconfAddress = netconfAddress;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withNetconfCapabilitiesWaitTimeoutMs(long netconfCapabilitiesWaitTimeoutMs) {
- this.netconfCapabilitiesWaitTimeoutMs = netconfCapabilitiesWaitTimeoutMs;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withNetconfSendMessageDelayMs(int netconfSendMessageDelayMs) {
- this.netconfSendMessageDelayMs = netconfSendMessageDelayMs;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withNetconfSendMessageMaxAttempts(int netconfSendMessageMaxAttempts) {
- this.netconfSendMessageMaxAttempts = netconfSendMessageMaxAttempts;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withConnectionAttemptDelayMs(int connectionAttemptDelayMs) {
- this.connectionAttemptDelayMs = connectionAttemptDelayMs;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withConnectionAttemptTimeoutMs(long connectionAttemptTimeoutMs) {
- this.connectionAttemptTimeoutMs = connectionAttemptTimeoutMs;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withEventLoopGroup(EventLoopGroup eventLoopGroup) {
- this.eventLoopGroup = eventLoopGroup;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withNetconfPushConfigAttempts(int netconfPushConfigAttempts) {
- this.netconfPushConfigAttempts = netconfPushConfigAttempts;
- return this;
- }
-
- public ConfigPusherConfigurationBuilder withNetconfPushConfigDelayMs(long netconfPushConfigDelayMs) {
- this.netconfPushConfigDelayMs = netconfPushConfigDelayMs;
- return this;
- }
-
- public ConfigPusherConfiguration build() {
- ConfigPusherConfiguration configPusherConfiguration = new ConfigPusherConfiguration(netconfAddress,
- netconfCapabilitiesWaitTimeoutMs, netconfSendMessageDelayMs, netconfSendMessageMaxAttempts,
- connectionAttemptDelayMs, connectionAttemptTimeoutMs, eventLoopGroup, netconfPushConfigAttempts,
- netconfPushConfigDelayMs);
- return configPusherConfiguration;
- }
-}
netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.xml.XmlDirectoryStorageAdapter
netconf.config.persister.1.properties.fileStorage=configuration/initial/
- netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
+ netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter
netconf.config.persister.2.readonly=true
- netconf.config.persister.2.properties.fileStorage=configuration/current/controller.config.1.txt
+ netconf.config.persister.2.properties.fileStorage=configuration/current/controller.config.1.xml
- netconf.config.persister.3.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
- netconf.config.persister.3.properties.fileStorage=configuration/current/controller.config.2.txt
+ netconf.config.persister.3.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.xml.XmlFileStorageAdapter
+ netconf.config.persister.3.properties.fileStorage=configuration/current/controller.config.2.xml
netconf.config.persister.3.properties.numberOfBackups=3
</pre>
+++ /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.persist.impl;
-
-import org.opendaylight.controller.netconf.client.NetconfClient;
-import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Set;
-
-public final class Util {
- private static final Logger logger = LoggerFactory.getLogger(Util.class);
-
-
- public static boolean isSubset(NetconfClient netconfClient, Set<String> expectedCaps) {
- return isSubset(netconfClient.getCapabilities(), expectedCaps);
-
- }
-
- private static boolean isSubset(Set<String> currentCapabilities, Set<String> expectedCaps) {
- for (String exCap : expectedCaps) {
- if (currentCapabilities.contains(exCap) == false)
- return false;
- }
- return true;
- }
-
- public static void closeClientAndDispatcher(NetconfClient client) {
- NetconfClientDispatcher dispatcher = client.getNetconfClientDispatcher();
- Exception fromClient = null;
- try {
- client.close();
- } catch (Exception e) {
- fromClient = e;
- } finally {
- try {
- dispatcher.close();
- } catch (Exception e) {
- if (fromClient != null) {
- e.addSuppressed(fromClient);
- }
- throw new RuntimeException("Error closing temporary client ", e);
- }
- }
- }
-}
package org.opendaylight.controller.netconf.persist.impl.osgi;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
+import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler;
import org.opendaylight.controller.netconf.persist.impl.ConfigPusher;
-import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfiguration;
-import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfigurationBuilder;
import org.opendaylight.controller.netconf.persist.impl.PersisterAggregator;
-import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.MBeanServer;
import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.util.concurrent.ThreadFactory;
-import java.util.regex.Pattern;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
public class ConfigPersisterActivator implements BundleActivator {
private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterActivator.class);
- public static final String IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX = "ignoredMissingCapabilityRegex";
-
- public static final String MAX_WAIT_FOR_CAPABILITIES_MILLIS = "maxWaitForCapabilitiesMillis";
+ public static final String MAX_WAIT_FOR_CAPABILITIES_MILLIS_PROPERTY = "maxWaitForCapabilitiesMillis";
+ private static final long MAX_WAIT_FOR_CAPABILITIES_MILLIS_DEFAULT = TimeUnit.MINUTES.toMillis(2);
+ public static final String CONFLICTING_VERSION_TIMEOUT_MILLIS_PROPERTY = "conflictingVersionTimeoutMillis";
+ private static final long CONFLICTING_VERSION_TIMEOUT_MILLIS_DEFAULT = TimeUnit.SECONDS.toMillis(30);
public static final String NETCONF_CONFIG_PERSISTER = "netconf.config.persister";
public static final String STORAGE_ADAPTER_CLASS_PROP_SUFFIX = "storageAdapterClass";
- public static final String DEFAULT_IGNORED_REGEX = "^urn:ietf:params:xml:ns:netconf:base:1.0";
- private final MBeanServer platformMBeanServer;
+ private static final MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
- private final Optional<ConfigPusherConfiguration> initialConfigForPusher;
- private volatile ConfigPersisterNotificationHandler jmxNotificationHandler;
- private Thread initializationThread;
- private ThreadFactory initializationThreadFactory;
- private EventLoopGroup nettyThreadGroup;
- private PersisterAggregator persisterAggregator;
+ private List<AutoCloseable> autoCloseables;
- public ConfigPersisterActivator() {
- this(new ThreadFactory() {
- @Override
- public Thread newThread(Runnable initializationRunnable) {
- return new Thread(initializationRunnable, "ConfigPersister-registrator");
- }
- }, ManagementFactory.getPlatformMBeanServer(), null);
- }
-
- @VisibleForTesting
- protected ConfigPersisterActivator(ThreadFactory threadFactory, MBeanServer mBeanServer,
- ConfigPusherConfiguration initialConfigForPusher) {
- this.initializationThreadFactory = threadFactory;
- this.platformMBeanServer = mBeanServer;
- this.initialConfigForPusher = Optional.fromNullable(initialConfigForPusher);
- }
@Override
public void start(final BundleContext context) throws Exception {
logger.debug("ConfigPersister starting");
-
+ autoCloseables = new ArrayList<>();
PropertiesProviderBaseImpl propertiesProvider = new PropertiesProviderBaseImpl(context);
- final Pattern ignoredMissingCapabilityRegex = getIgnoredCapabilitiesProperty(propertiesProvider);
- persisterAggregator = PersisterAggregator.createFromProperties(propertiesProvider);
+ final PersisterAggregator persisterAggregator = PersisterAggregator.createFromProperties(propertiesProvider);
+ autoCloseables.add(persisterAggregator);
+ final long maxWaitForCapabilitiesMillis = getMaxWaitForCapabilitiesMillis(propertiesProvider);
+ final List<ConfigSnapshotHolder> configs = persisterAggregator.loadLastConfigs();
+ final long conflictingVersionTimeoutMillis = getConflictingVersionTimeoutMillis(propertiesProvider);
+ logger.trace("Following configs will be pushed: {}", configs);
+ ServiceTrackerCustomizer<NetconfOperationServiceFactory, NetconfOperationServiceFactory> configNetconfCustomizer = new ServiceTrackerCustomizer<NetconfOperationServiceFactory, NetconfOperationServiceFactory>() {
+ @Override
+ public NetconfOperationServiceFactory addingService(ServiceReference<NetconfOperationServiceFactory> reference) {
+ NetconfOperationServiceFactory service = reference.getBundle().getBundleContext().getService(reference);
+ final ConfigPusher configPusher = new ConfigPusher(service, maxWaitForCapabilitiesMillis, conflictingVersionTimeoutMillis);
+ logger.debug("Configuration Persister got %s", service);
+ final Thread pushingThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ configPusher.pushConfigs(configs);
+ logger.info("Configuration Persister initialization completed.");
+ ConfigPersisterNotificationHandler jmxNotificationHandler = new ConfigPersisterNotificationHandler(platformMBeanServer, persisterAggregator);
+ synchronized (ConfigPersisterActivator.this) {
+ autoCloseables.add(jmxNotificationHandler);
+ }
+ }
+ }, "config-pusher");
+ synchronized (ConfigPersisterActivator.this){
+ autoCloseables.add(new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ pushingThread.interrupt();
+ }
+ });
+ }
+ pushingThread.start();
+ return service;
+ }
- final ConfigPusher configPusher = new ConfigPusher(getConfigurationForPusher(context, propertiesProvider));
+ @Override
+ public void modifiedService(ServiceReference<NetconfOperationServiceFactory> reference, NetconfOperationServiceFactory service) {
+ }
- // offload initialization to another thread in order to stop blocking activator
- Runnable initializationRunnable = new Runnable() {
@Override
- public void run() {
- try {
- configPusher.pushConfigs(persisterAggregator.loadLastConfigs());
- jmxNotificationHandler = new ConfigPersisterNotificationHandler(platformMBeanServer, persisterAggregator,
- ignoredMissingCapabilityRegex);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- logger.error("Interrupted while waiting for netconf connection");
- // uncaught exception handler will deal with this failure
- throw new RuntimeException("Interrupted while waiting for netconf connection", e);
- }
- logger.info("Configuration Persister initialization completed.");
+ public void removedService(ServiceReference<NetconfOperationServiceFactory> reference, NetconfOperationServiceFactory service) {
}
};
- initializationThread = initializationThreadFactory.newThread(initializationRunnable);
- initializationThread.start();
- }
-
- private Pattern getIgnoredCapabilitiesProperty(PropertiesProviderBaseImpl propertiesProvider) {
- String regexProperty = propertiesProvider.getProperty(IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX);
- String regex;
- if (regexProperty != null) {
- regex = regexProperty;
- } else {
- regex = DEFAULT_IGNORED_REGEX;
- }
- return Pattern.compile(regex);
- }
+ Filter filter = context.createFilter(getFilterString());
- private Optional<Long> getMaxWaitForCapabilitiesProperty(PropertiesProviderBaseImpl propertiesProvider) {
- String timeoutProperty = propertiesProvider.getProperty(MAX_WAIT_FOR_CAPABILITIES_MILLIS);
- return Optional.fromNullable(timeoutProperty == null ? null : Long.valueOf(timeoutProperty));
+ ServiceTracker<NetconfOperationServiceFactory, NetconfOperationServiceFactory> tracker =
+ new ServiceTracker<>(context, filter, configNetconfCustomizer);
+ tracker.open();
}
- private ConfigPusherConfiguration getConfigurationForPusher(BundleContext context,
- PropertiesProviderBaseImpl propertiesProvider) {
-
- // If configuration was injected via constructor, use it
- if(initialConfigForPusher.isPresent())
- return initialConfigForPusher.get();
-
- Optional<Long> maxWaitForCapabilitiesMillis = getMaxWaitForCapabilitiesProperty(propertiesProvider);
- final InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context,
- "Netconf is not configured, persister is not operational", true);
-
- nettyThreadGroup = new NioEventLoopGroup();
- ConfigPusherConfigurationBuilder configPusherConfigurationBuilder = ConfigPusherConfigurationBuilder.aConfigPusherConfiguration();
+ @VisibleForTesting
+ public static String getFilterString() {
+ return "(&" +
+ "(" + Constants.OBJECTCLASS + "=" + NetconfOperationServiceFactory.class.getName() + ")" +
+ "(name" + "=" + "config-netconf-connector" + ")" +
+ ")";
+ }
- if(maxWaitForCapabilitiesMillis.isPresent())
- configPusherConfigurationBuilder.withNetconfCapabilitiesWaitTimeoutMs(maxWaitForCapabilitiesMillis.get());
+ private long getConflictingVersionTimeoutMillis(PropertiesProviderBaseImpl propertiesProvider) {
+ String timeoutProperty = propertiesProvider.getProperty(CONFLICTING_VERSION_TIMEOUT_MILLIS_PROPERTY);
+ return timeoutProperty == null ? CONFLICTING_VERSION_TIMEOUT_MILLIS_DEFAULT : Long.valueOf(timeoutProperty);
+ }
- return configPusherConfigurationBuilder
- .withEventLoopGroup(nettyThreadGroup)
- .withNetconfAddress(address)
- .build();
+ private long getMaxWaitForCapabilitiesMillis(PropertiesProviderBaseImpl propertiesProvider) {
+ String timeoutProperty = propertiesProvider.getProperty(MAX_WAIT_FOR_CAPABILITIES_MILLIS_PROPERTY);
+ return timeoutProperty == null ? MAX_WAIT_FOR_CAPABILITIES_MILLIS_DEFAULT : Long.valueOf(timeoutProperty);
}
@Override
- public void stop(BundleContext context) throws Exception {
- initializationThread.interrupt();
- if (jmxNotificationHandler != null) {
- jmxNotificationHandler.close();
+ public synchronized void stop(BundleContext context) throws Exception {
+ Exception lastException = null;
+ for (AutoCloseable autoCloseable : autoCloseables) {
+ try {
+ autoCloseable.close();
+ } catch (Exception e) {
+ if (lastException == null) {
+ lastException = e;
+ } else {
+ lastException.addSuppressed(e);
+ }
+ }
+ }
+ if (lastException != null) {
+ throw lastException;
}
- if(nettyThreadGroup!=null)
- nettyThreadGroup.shutdownGracefully();
- persisterAggregator.close();
}
}
import com.google.common.collect.Sets;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
-import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Element;
import java.io.IOException;
-import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
-import java.util.regex.Pattern;
import static org.junit.Assert.assertEquals;
public class CapabilityStrippingConfigSnapshotHolderTest {
@Test
- public void testCapabilityStripping() throws Exception {
+ public void testCapabilityStripping() throws Exception {
Set<String> allCapabilities = readLines("/capabilities-all.txt");
Set<String> expectedCapabilities = readLines("/capabilities-stripped.txt");
String snapshotAsString = readToString("/snapshot.xml");
Element element = XmlUtil.readXmlToElement(snapshotAsString);
- {
- CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder(
- element, allCapabilities, Pattern.compile(
- ConfigPersisterActivator.DEFAULT_IGNORED_REGEX
- ));
- assertEquals(expectedCapabilities, tested.getCapabilities());
- assertEquals(Collections.emptySet(), tested.getMissingNamespaces());
- }
- {
- // test regex
- CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder(
- element, allCapabilities, Pattern.compile(
- "^bar"
- ));
- assertEquals(expectedCapabilities, tested.getCapabilities());
- assertEquals(Sets.newHashSet(ConfigPersisterActivator.DEFAULT_IGNORED_REGEX.substring(1)),
- tested.getMissingNamespaces());
- }
+ CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder(
+ element, allCapabilities);
+ assertEquals(expectedCapabilities, tested.getCapabilities());
+
+ Set<String> obsoleteCapabilities = Sets.difference(allCapabilities, expectedCapabilities);
+
+ assertEquals(obsoleteCapabilities, tested.getObsoleteCapabilities());
}
private Set<String> readLines(String fileName) throws IOException {
*/
package org.opendaylight.controller.netconf.persist.impl.osgi;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeoutException;
-
-import javax.management.MBeanServer;
-
+import com.google.common.collect.Sets;
import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import org.junit.Before;
import org.junit.Test;
-import org.junit.matchers.JUnitMatchers;
import org.opendaylight.controller.config.api.ConflictingVersionException;
-import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
-import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfiguration;
-import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfigurationBuilder;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.persist.impl.osgi.MockedBundleContext.DummyAdapterWithInitialSnapshot;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
-import com.google.common.collect.Lists;
-import io.netty.channel.nio.NioEventLoopGroup;
+import javax.management.MBeanServer;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
public class ConfigPersisterTest {
+ private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterTest.class);
private MockedBundleContext ctx;
private ConfigPersisterActivator configPersisterActivator;
private static final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+ private TestingExceptionHandler handler;
- private static final String NETCONF_ADDRESS = "localhost";
- private static final String NETCONF_PORT = "18383";
- private static NioEventLoopGroup eventLoopGroup;
- private void setUpContextAndStartPersister(Thread.UncaughtExceptionHandler exHandler, String requiredCapability, ConfigPusherConfiguration configuration)
- throws Exception {
- MockedBundleContext.DummyAdapterWithInitialSnapshot.expectedCapability = requiredCapability;
- ctx = new MockedBundleContext(NETCONF_ADDRESS, NETCONF_PORT);
- configPersisterActivator = new ConfigPersisterActivator(getThreadFactory(exHandler), mBeanServer,
- configuration);
+ private void setUpContextAndStartPersister(String requiredCapability) throws Exception {
+ DummyAdapterWithInitialSnapshot.expectedCapability = requiredCapability;
+ ctx = new MockedBundleContext(1000, 1000);
+ configPersisterActivator = new ConfigPersisterActivator();
configPersisterActivator.start(ctx.getBundleContext());
}
- @BeforeClass
- public static void setUp() throws Exception {
- eventLoopGroup = new NioEventLoopGroup();
+ @Before
+ public void setUp() {
+ handler = new TestingExceptionHandler();
+ Thread.setDefaultUncaughtExceptionHandler(handler);
}
@After
public void tearDown() throws Exception {
+ Thread.setDefaultUncaughtExceptionHandler(null);
configPersisterActivator.stop(ctx.getBundleContext());
}
- @AfterClass
- public static void closeNettyGroup() throws Exception {
- eventLoopGroup.shutdownGracefully();
- }
-
- @Test
- public void testPersisterNetconfNotStarting() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
-
- setUpContextAndStartPersister(handler, "cap2", getConfiguration(100, 100).build());
-
- waitTestToFinish(2000);
-
- handler.assertException("connect to netconf endpoint", RuntimeException.class,
- "Could not connect to netconf server");
- }
-
@Test
public void testPersisterNotAllCapabilitiesProvided() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
- ConfigPusherConfiguration cfg = getConfiguration(500, 1000)
- .withNetconfCapabilitiesWaitTimeoutMs(1000).build();
-
- setUpContextAndStartPersister(handler, "required-cap", cfg);
-
- try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1")) {
-
- waitTestToFinish(2500);
+ setUpContextAndStartPersister("required-cap");
+ Thread.sleep(2000);
+ handler.assertException(IllegalStateException.class, "Max wait for capabilities reached.Not enough capabilities " +
+ "for <data><config-snapshot/></data>. Expected but not found: [required-cap]");
- handler.assertException("retrieve required capabilities from netconf endpoint", RuntimeException.class,
- "Expected but not found:[required-cap]");
- }
}
@Test
- public void testPersisterNoResponseFromNetconfAfterEdit() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
- ConfigPusherConfiguration cfg = getConfigurationWithOnePushAttempt();
-
- setUpContextAndStartPersister(handler, "cap1", cfg);
-
- try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1")) {
-
- waitTestToFinish(3000);
-
- handler.assertException("receive response from netconf endpoint", IllegalStateException.class,
- "Unable to load", TimeoutException.class,
- null, 3);
-
- assertEquals(1 + 2, endpoint.getReceivedMessages().size());
- assertHelloMessage(endpoint.getReceivedMessages().get(1));
- assertEditMessage(endpoint.getReceivedMessages().get(2));
- }
+ public void testPersisterSuccessfulPush() throws Exception {
+ setUpContextAndStartPersister("cap1");
+ NetconfOperationService service = getWorkingService(getOKDocument());
+ doReturn(service).when(ctx.serviceFactory).createService(anyString());
+ Thread.sleep(2000);
+ assertCannotRegisterAsJMXListener_pushWasSuccessful();
}
- private ConfigPusherConfiguration getConfigurationWithOnePushAttempt() {
- return getConfiguration(500, 1000)
- .withNetconfCapabilitiesWaitTimeoutMs(1000)
- .withNetconfPushConfigAttempts(1)
- .withNetconfPushConfigDelayMs(100)
- .withNetconfSendMessageMaxAttempts(3)
- .withNetconfSendMessageDelayMs(500).build();
+ // this means pushing of config was successful
+ public void assertCannotRegisterAsJMXListener_pushWasSuccessful() {
+ handler.assertException(RuntimeException.class, "Cannot register as JMX listener to netconf");
}
- @Test
- public void testPersisterSuccessfulPush() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
- ConfigPusherConfiguration cfg = getConfigurationForSuccess();
-
- setUpContextAndStartPersister(handler, "cap1", cfg);
-
- try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage,
- MockNetconfEndpoint.okMessage)) {
+ public NetconfOperationService getWorkingService(Document document) throws SAXException, IOException, NetconfDocumentedException {
+ NetconfOperationService service = mock(NetconfOperationService.class);
+ Capability capability = mock(Capability.class);
+ doReturn(Sets.newHashSet(capability)).when(service).getCapabilities();
+ doReturn("cap1").when(capability).getCapabilityUri();
- waitTestToFinish(4000);
- handler.assertException("register as JMX listener", RuntimeException.class,
- "Cannot register as JMX listener to netconf");
-
- assertEquals(1 + 3, endpoint.getReceivedMessages().size());
- assertCommitMessage(endpoint.getReceivedMessages().get(3));
- }
+ NetconfOperation mockedOperation = mock(NetconfOperation.class);
+ doReturn(Sets.newHashSet(mockedOperation)).when(service).getNetconfOperations();
+ doReturn(HandlingPriority.getHandlingPriority(1)).when(mockedOperation).canHandle(any(Document.class));
+ doReturn(document).when(mockedOperation).handle(any(Document.class), any(NetconfOperationChainedExecution.class));
+ doNothing().when(service).close();
+ return service;
}
- private ConfigPusherConfiguration getConfigurationForSuccess() {
- return getConfiguration(500, 1000)
- .withNetconfCapabilitiesWaitTimeoutMs(1000)
- .withNetconfPushConfigAttempts(3)
- .withNetconfPushConfigDelayMs(100)
- .withNetconfSendMessageMaxAttempts(3)
- .withNetconfSendMessageDelayMs(500).build();
+ private Document getOKDocument() throws SAXException, IOException {
+ return XmlUtil.readXmlToDocument(
+ "<rpc-reply message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+ "<ok/>\n" +
+ "</rpc-reply>"
+ );
}
- @Test
- public void testPersisterConflictingVersionException() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
- ConfigPusherConfiguration cfg = getConfigurationWithOnePushAttempt();
-
- setUpContextAndStartPersister(handler, "cap1", cfg);
-
- try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage,
- MockNetconfEndpoint.conflictingVersionErrorMessage); DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier();) {
-
- Thread.sleep(4000);
-
- handler.assertException("register as JMX listener", IllegalStateException.class,
- "Maximum attempt count has been reached for pushing", ConflictingVersionException.class, "Optimistic lock failed", 1);
-
- assertEquals(1 + 3, endpoint.getReceivedMessages().size());
- assertCommitMessage(endpoint.getReceivedMessages().get(3));
- }
- }
@Test
- public void testPersisterConflictingVersionExceptionThenSuccess() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
- ConfigPusherConfiguration cfg = getConfigurationForSuccess();
-
- setUpContextAndStartPersister(handler, "cap1", cfg);
-
- MockNetconfEndpoint.MessageSequence conflictingMessageSequence = new MockNetconfEndpoint.MessageSequence(
- MockNetconfEndpoint.okMessage, MockNetconfEndpoint.conflictingVersionErrorMessage);
- MockNetconfEndpoint.MessageSequence okMessageSequence = new MockNetconfEndpoint.MessageSequence(
- MockNetconfEndpoint.okMessage, MockNetconfEndpoint.okMessage);
-
- try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1",
- Lists.newArrayList(conflictingMessageSequence, okMessageSequence));
- DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier()) {
-
- Thread.sleep(4000);
-
- handler.assertNoException();
-
- assertEquals(1 + 3/*Hello + Edit + Commit*/ + 3/*Hello + Edit + Commit*/, endpoint.getReceivedMessages().size());
- assertCommitMessage(endpoint.getReceivedMessages().get(6));
+ public void testPersisterConflictingVersionException() throws Exception {
+ setUpContextAndStartPersister("cap1");
+
+ doReturn(getConflictingService()).when(ctx.serviceFactory).createService(anyString());
+ Thread.sleep(2000);
+ handler.assertException(IllegalStateException.class, "Max wait for conflicting version stabilization timeout");
+ }
+
+ private NetconfOperationService getConflictingService() throws Exception {
+ NetconfOperationService service = getWorkingService(getOKDocument());
+ ConflictingVersionException cve = new ConflictingVersionException("");
+ try {
+ NetconfDocumentedException.wrap(cve);
+ throw new AssertionError("Should throw an exception");
+ }catch(NetconfDocumentedException e) {
+ NetconfOperation mockedOperation = service.getNetconfOperations().iterator().next();
+ doThrow(e).when(mockedOperation).handle(any(Document.class), any(NetconfOperationChainedExecution.class));
+ return service;
}
}
@Test
- public void testPersisterSuccessfulPushAndSuccessfulJMXRegistration() throws Exception {
- final TestingExceptionHandler handler = new TestingExceptionHandler();
- ConfigPusherConfiguration cfg = getConfigurationForSuccess();
-
- setUpContextAndStartPersister(handler, "cap1", cfg);
-
- try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage,
- MockNetconfEndpoint.okMessage); DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier()) {
-
- Thread.sleep(2000);
-
- handler.assertNoException();
-
- assertEquals(1 + 3, endpoint.getReceivedMessages().size());
- }
+ public void testSuccessConflictingVersionException() throws Exception {
+ setUpContextAndStartPersister("cap1");
+ doReturn(getConflictingService()).when(ctx.serviceFactory).createService(anyString());
+ Thread.sleep(500);
+ // working service:
+ logger.info("Switching to working service **");
+ doReturn(getWorkingService(getOKDocument())).when(ctx.serviceFactory).createService(anyString());
+ Thread.sleep(1000);
+ assertCannotRegisterAsJMXListener_pushWasSuccessful();
}
- private ConfigPusherConfigurationBuilder getConfiguration(int connectionAttemptDelayMs, int connectionAttemptTimeoutMs) {
- return ConfigPusherConfigurationBuilder.aConfigPusherConfiguration()
- .withEventLoopGroup(eventLoopGroup)
- .withConnectionAttemptDelayMs(connectionAttemptDelayMs)
- .withConnectionAttemptTimeoutMs(connectionAttemptTimeoutMs)
- .withNetconfCapabilitiesWaitTimeoutMs(44)
- .withNetconfAddress(new InetSocketAddress(NETCONF_ADDRESS, Integer.valueOf(NETCONF_PORT)));
- }
-
- private void waitTestToFinish(int i) throws InterruptedException {
- Thread.sleep(i);
- }
-
-
- private DefaultCommitNotificationProducer startJMXCommitNotifier() {
- return new DefaultCommitNotificationProducer(mBeanServer);
- }
-
- private void assertEditMessage(String netconfMessage) {
- assertThat(netconfMessage,
- JUnitMatchers.containsString(MockedBundleContext.DummyAdapterWithInitialSnapshot.CONFIG_SNAPSHOT));
- }
-
- private void assertCommitMessage(String netconfMessage) {
- assertThat(netconfMessage, JUnitMatchers.containsString("<commit"));
- }
-
- private void assertHelloMessage(String netconfMessage) {
- assertThat(netconfMessage,
- JUnitMatchers.containsString("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"));
- assertThat(netconfMessage, JUnitMatchers.containsString("<capability>"));
- }
-
- private MockNetconfEndpoint startMockNetconfEndpoint(String capability, List<MockNetconfEndpoint.MessageSequence> messageSequences) {
- // Add first empty sequence for testing connection created by config persister at startup
- messageSequences.add(0, new MockNetconfEndpoint.MessageSequence(Collections.<String>emptyList()));
- return new MockNetconfEndpoint(capability, NETCONF_PORT, messageSequences);
- }
-
- private MockNetconfEndpoint startMockNetconfEndpoint(String capability, String... messages) {
- return startMockNetconfEndpoint(capability, Lists.newArrayList(new MockNetconfEndpoint.MessageSequence(messages)));
- }
-
- public ThreadFactory getThreadFactory(final Thread.UncaughtExceptionHandler exHandler) {
- return new ThreadFactory() {
- @Override
- public Thread newThread(Runnable r) {
- Thread thread = new Thread(r, "config-persister-testing-activator");
- thread.setUncaughtExceptionHandler(exHandler);
- return thread;
- }
- };
- }
}
+++ /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.persist.impl.osgi;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketTimeoutException;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
-
-import com.google.common.collect.Lists;
-
-class MockNetconfEndpoint implements AutoCloseable {
-
- public static final int READ_SOCKET_TIMEOUT = 3000;
-
- public static final String MSG_SEPARATOR = "]]>]]>\n";
-
- private final AtomicBoolean stopped = new AtomicBoolean(false);
- private List<String> receivedMessages = Lists.newCopyOnWriteArrayList();
- private Thread innerThread;
-
- MockNetconfEndpoint(String capability, String netconfPort, List<MessageSequence> messageSequence) {
- helloMessage = helloMessage.replace("capability_place_holder", capability);
- start(netconfPort, messageSequence);
- }
-
- private String helloMessage = "<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
- "<capabilities>\n" +
- "<capability>capability_place_holder</capability>\n" +
- "</capabilities>\n" +
- "<session-id>1</session-id>\n" +
- "</hello>\n" +
- MSG_SEPARATOR;
-
- public static String conflictingVersionErrorMessage;
- static {
- try {
- conflictingVersionErrorMessage = XmlUtil.toString(XmlFileLoader
- .xmlFileToDocument("netconfMessages/conflictingversion/conflictingVersionResponse.xml")) + MSG_SEPARATOR;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public static String okMessage = "<rpc-reply message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
- "<ok/>\n" +
- "</rpc-reply>" +
- MSG_SEPARATOR ;
-
- private void start(final String port, final List<MessageSequence> messagesToSend) {
- innerThread = new Thread(new Runnable() {
- @Override
- public void run() {
- int clientCounter = 0;
-
- while (stopped.get() == false) {
- try (ServerSocket s = new ServerSocket(Integer.valueOf(port))) {
- s.setSoTimeout(READ_SOCKET_TIMEOUT);
-
- Socket clientSocket = s.accept();
- clientCounter++;
- clientSocket.setSoTimeout(READ_SOCKET_TIMEOUT);
-
- PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
- BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
-
- // Negotiate
- sendMessage(out, helloMessage);
- receiveMessage(in);
-
- // Accept next message (edit-config)
- receiveMessage(in);
-
- for (String message : getMessageSequenceForClient(messagesToSend, clientCounter)) {
- sendMessage(out, message);
- receiveMessage(in);
- }
- } catch (SocketTimeoutException e) {
- // No more activity on netconf endpoint, close
- return;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- private Iterable<? extends String> getMessageSequenceForClient(List<MessageSequence> messagesToSend,
- int clientCounter) {
- if (messagesToSend.size() <= clientCounter) {
- return messagesToSend.get(messagesToSend.size() - 1).getMessages();
- } else {
- return messagesToSend.get(clientCounter - 1).getMessages();
- }
- }
-
- private void receiveMessage(BufferedReader in) throws Exception {
- String message = readMessage(in);
- if(message == null || message.equals(""))
- return;
- receivedMessages.add(message);
- }
-
- private String readMessage(BufferedReader in) throws IOException {
- int c;
- StringBuilder b = new StringBuilder();
-
- while((c = in.read()) != -1) {
- b.append((char)c);
- if(b.toString().endsWith("]]>]]>"))
- break;
- }
-
- return b.toString();
- }
-
- private void sendMessage(PrintWriter out, String message) throws InterruptedException {
- out.print(message);
- out.flush();
- }
-
- });
- innerThread.setName("Mocked-netconf-endpoint-inner-thread");
- innerThread.start();
- }
-
- public List<String> getReceivedMessages() {
- return receivedMessages;
- }
-
- public void close() throws IOException, InterruptedException {
- stopped.set(true);
- innerThread.join();
- }
-
- static class MessageSequence {
- private List<String> messages;
-
- MessageSequence(List<String> messages) {
- this.messages = messages;
- }
-
- MessageSequence(String... messages) {
- this(Lists.newArrayList(messages));
- }
-
- public Collection<String> getMessages() {
- return messages;
- }
- }
-}
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.persist.api.PropertiesProvider;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.persist.impl.DummyAdapter;
+import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
import java.io.IOException;
+import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
final class MockedBundleContext {
-
@Mock
private BundleContext context;
+ @Mock
+ private Filter filter;
+ @Mock
+ private ServiceReference<?> serviceReference;
+ @Mock
+ private Bundle bundle;
+ @Mock
+ NetconfOperationServiceFactory serviceFactory;
+ @Mock
+ private NetconfOperationService service;
- MockedBundleContext(String netconfAddress, String netconfPort) {
+ MockedBundleContext(long maxWaitForCapabilitiesMillis, long conflictingVersionTimeoutMillis) throws Exception {
MockitoAnnotations.initMocks(this);
- initContext(netconfAddress, netconfPort);
+ doReturn(null).when(context).getProperty(anyString());
+ initContext(maxWaitForCapabilitiesMillis, conflictingVersionTimeoutMillis);
+ doReturn(filter).when(context).createFilter(ConfigPersisterActivator.getFilterString());
+ String filterString = "filter";
+ doReturn(filterString).when(filter).toString();
+ doNothing().when(context).addServiceListener(any(ServiceListener.class), eq(filterString));
+ ServiceReference<?>[] toBeReturned = {serviceReference};
+ doReturn(toBeReturned).when(context).getServiceReferences((String) null, filterString);
+ doReturn(bundle).when(serviceReference).getBundle();
+ doReturn(context).when(bundle).getBundleContext();
+ doReturn("").when(serviceReference).toString();
+ doReturn(serviceFactory).when(context).getService(any(ServiceReference.class));
+ doReturn(service).when(serviceFactory).createService(anyString());
+ doReturn(Collections.emptySet()).when(service).getCapabilities();
+ doNothing().when(service).close();
}
public BundleContext getBundleContext() {
return context;
}
- private void initContext(String netconfAddress, String netconfPort) {
- initProp(context, ConfigPersisterActivator.IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX, null);
-
- initPropNoPrefix(context, "netconf.tcp.client.address", netconfAddress);
- initPropNoPrefix(context, "netconf.tcp.client.port", netconfPort);
-
+ private void initContext(long maxWaitForCapabilitiesMillis, long conflictingVersionTimeoutMillis) {
initProp(context, "active", "1");
initProp(context, "1." + ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX, DummyAdapterWithInitialSnapshot.class.getName());
initProp(context, "1." + "readonly", "false");
initProp(context, "1." + ".properties.fileStorage", "target/configuration-persister-test/initial/");
-
+ initProp(context, ConfigPersisterActivator.MAX_WAIT_FOR_CAPABILITIES_MILLIS_PROPERTY, String.valueOf(maxWaitForCapabilitiesMillis));
+ initProp(context, ConfigPersisterActivator.CONFLICTING_VERSION_TIMEOUT_MILLIS_PROPERTY, String.valueOf(conflictingVersionTimeoutMillis));
}
private void initProp(BundleContext context, String key, String value) {
@Override
public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
- return Lists.newArrayList(getConfigSnapshopt());
+ return Lists.newArrayList(getConfigSnapshot());
}
@Override
return this;
}
- public ConfigSnapshotHolder getConfigSnapshopt() {
+ public ConfigSnapshotHolder getConfigSnapshot() {
return new ConfigSnapshotHolder() {
@Override
public String getConfigSnapshot() {
this.t = e;
}
+ public void assertException(Class<? extends Exception> exType, String exMessageToContain) {
+ assertException(exMessageToContain, exType, exMessageToContain);
+ }
+
public void assertException(String failMessageSuffix, Class<? extends Exception> exType, String exMessageToContain) {
if(t == null) {
fail("Should fail to " + failMessageSuffix);
*/
package org.opendaylight.controller.netconf.impl.osgi;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
import org.opendaylight.controller.netconf.api.NetconfSession;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
public class NetconfOperationRouterImpl implements NetconfOperationRouter {
return sortedPriority;
}
- public static final NetconfOperationChainedExecution EXECUTION_TERMINATION_POINT = new NetconfOperationChainedExecution() {
- @Override
- public boolean isExecutionTermination() {
- return true;
- }
-
- @Override
- public Document execute(Document requestMessage) throws NetconfDocumentedException {
- throw new IllegalStateException("This execution represents the termination point in operation execution and cannot be executed itself");
- }
- };
-
private static class NetconfOperationExecution implements NetconfOperationChainedExecution {
private final NetconfOperation netconfOperation;
private NetconfOperationChainedExecution subsequentExecution;
package org.opendaylight.controller.netconf.impl.osgi;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
public class NetconfOperationServiceSnapshot implements AutoCloseable {
private static final Logger logger = LoggerFactory.getLogger(NetconfOperationServiceSnapshot.class);
Set<NetconfOperationService> services = new HashSet<>();
netconfSessionIdForReporting = getNetconfSessionIdForReporting(sessionId);
for (NetconfOperationServiceFactory factory : factories) {
- services.add(factory.createService(sessionId, netconfSessionIdForReporting));
+ services.add(factory.createService(netconfSessionIdForReporting));
}
this.services = Collections.unmodifiableSet(services);
}
private NetconfOperationServiceFactory mockOpF() {
return new NetconfOperationServiceFactory() {
@Override
- public NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting) {
+ public NetconfOperationService createService(String netconfSessionIdForReporting) {
return new NetconfOperationService() {
@Override
public Set<Capability> getCapabilities() {
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.config.persist.api.Persister;
import org.opendaylight.controller.config.spi.ModuleFactory;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification;
import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
import org.opendaylight.controller.netconf.client.NetconfClient;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
import java.util.Collection;
import java.util.List;
import java.util.Set;
-import java.util.regex.Pattern;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Matchers.any;
try (NetconfClient persisterClient = new NetconfClient("persister", tcpAddress, 4000, clientDispatcher)) {
try (ConfigPersisterNotificationHandler configPersisterNotificationHandler = new ConfigPersisterNotificationHandler(
- platformMBeanServer, mockedAggregator, Pattern.compile(""))) {
+ platformMBeanServer, mockedAggregator)) {
try (NetconfClient netconfClient = new NetconfClient("client", tcpAddress, 4000, clientDispatcher)) {
package org.opendaylight.controller.netconf.it;
-import static java.util.Collections.emptyList;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.Session;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
import io.netty.channel.ChannelFuture;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-import javax.management.ObjectName;
-import javax.xml.parsers.ParserConfigurationException;
-
import junit.framework.Assert;
-
+import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
import org.opendaylight.controller.config.yang.test.impl.DepTestImplModuleFactory;
import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleFactory;
import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleMXBean;
import org.opendaylight.controller.netconf.client.NetconfClient;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
-import ch.ethz.ssh2.Connection;
-import ch.ethz.ssh2.Session;
+import javax.management.ObjectName;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
+import static java.util.Collections.emptyList;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
public class NetconfITTest extends AbstractNetconfConfigTest {
// TODO refactor, pull common code up to AbstractNetconfITTest
- private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class);
+ private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class);
private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023);
private static final InetSocketAddress sshAddress = new InetSocketAddress("127.0.0.1", 10830);
private static final String PASSWORD = "netconf";
private NetconfMessage getConfig, getConfigCandidate, editConfig,
- closeSession, startExi, stopExi;
+ closeSession, startExi, stopExi;
private DefaultCommitNotificationProducer commitNot;
private NetconfServerDispatcher dispatch;
ChannelFuture s = dispatch.createServer(tcpAddress);
s.await();
- clientDispatcher = new NetconfClientDispatcher( nettyThreadgroup, nettyThreadgroup, 5000);
+ clientDispatcher = new NetconfClientDispatcher(nettyThreadgroup, nettyThreadgroup, 5000);
}
private NetconfServerDispatcher createDispatcher(NetconfOperationServiceFactoryListenerImpl factoriesListener) {
yangDependencies.add(resourceAsStream);
}
}
- assertEquals("Some yang files were not found",emptyList(), failedToFind);
+ assertEquals("Some yang files were not found", emptyList(), failedToFind);
return yangDependencies;
}
protected List<ModuleFactory> getModuleFactories() {
return getModuleFactoriesS();
}
+
static List<ModuleFactory> getModuleFactoriesS() {
return Lists.newArrayList(new TestImplModuleFactory(), new DepTestImplModuleFactory(),
new NetconfTestImplModuleFactory());
@Test
public void testTwoSessions() throws Exception {
- try (NetconfClient netconfClient = new NetconfClient("1", tcpAddress, 10000, clientDispatcher)) {
+ try (NetconfClient netconfClient = new NetconfClient("1", tcpAddress, 10000, clientDispatcher)) {
try (NetconfClient netconfClient2 = new NetconfClient("2", tcpAddress, 10000, clientDispatcher)) {
}
}
NetconfTestImplModuleMXBean proxy = configRegistryClient
.newMXBeanProxy(impl, NetconfTestImplModuleMXBean.class);
proxy.setTestingDep(dep);
- proxy.setSimpleShort((short)0);
+ proxy.setSimpleShort((short) 0);
transaction.commit();
return netconfClient;
}
- private void startSSHServer() throws Exception{
+ private void startSSHServer() throws Exception {
logger.info("Creating SSH server");
- StubUserManager um = new StubUserManager(USERNAME,PASSWORD);
- InputStream is = getClass().getResourceAsStream("/RSA.pk");
- AuthProvider ap = new AuthProvider(um, is);
- Thread sshServerThread = new Thread(NetconfSSHServer.start(10830,tcpAddress,ap));
+ StubUserManager um = new StubUserManager(USERNAME, PASSWORD);
+ String pem;
+ try (InputStream is = getClass().getResourceAsStream("/RSA.pk")) {
+ pem = IOUtils.toString(is);
+ }
+ AuthProvider ap = new AuthProvider(um, pem);
+ Thread sshServerThread = new Thread(NetconfSSHServer.start(10830, tcpAddress, ap));
sshServerThread.setDaemon(true);
sshServerThread.start();
logger.info("SSH server on");
public void sshTest() throws Exception {
startSSHServer();
logger.info("creating connection");
- Connection conn = new Connection(sshAddress.getHostName(),sshAddress.getPort());
+ Connection conn = new Connection(sshAddress.getHostName(), sshAddress.getPort());
Assert.assertNotNull(conn);
logger.info("connection created");
conn.connect();
- boolean isAuthenticated = conn.authenticateWithPassword(USERNAME,PASSWORD);
+ boolean isAuthenticated = conn.authenticateWithPassword(USERNAME, PASSWORD);
assertTrue(isAuthenticated);
logger.info("user authenticated");
final Session sess = conn.openSession();
logger.info("user authenticated");
sess.getStdin().write(XmlUtil.toString(this.getConfig.getDocument()).getBytes());
- new Thread(){
+ new Thread() {
@Override
- public void run(){
- while (true){
+ public void run() {
+ while (true) {
byte[] bytes = new byte[1024];
int c = 0;
try {
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
- logger.info("got data:"+bytes);
- if (c == 0) break;
+ logger.info("got data:" + bytes);
+ if (c == 0) {
+ break;
+ }
}
}
}.join();
import static org.opendaylight.controller.test.sal.binding.it.TestHelper.mdSalCoreBundles;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackages;
import static org.ops4j.pax.exam.CoreOptions.systemProperty;
@RunWith(PaxExam.class)
systemProperty("osgi.console").value("2401"),
systemProperty("osgi.bundles.defaultStartLevel").value("4"),
systemProperty("pax.exam.osgi.unresolved.fail").value("true"),
+ systemPackages("sun.nio.ch"),
testingModules(),
loggingModules(),
return getHandlingPriority(priority + priorityIncrease);
}
+ public boolean isCannotHandle() {
+ return this.equals(CANNOT_HANDLE);
+ }
+
@Override
public int compareTo(HandlingPriority o) {
if (this == o)
* Do not execute if this is termination point
*/
Document execute(Document requestMessage) throws NetconfDocumentedException;
+
+ public static final NetconfOperationChainedExecution EXECUTION_TERMINATION_POINT = new NetconfOperationChainedExecution() {
+ @Override
+ public boolean isExecutionTermination() {
+ return true;
+ }
+
+ @Override
+ public Document execute(Document requestMessage) throws NetconfDocumentedException {
+ throw new IllegalStateException("This execution represents the termination point in operation execution and cannot be executed itself");
+ }
+ };
+
+
}
*/
public interface NetconfOperationServiceFactory {
- NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting);
+ NetconfOperationService createService(String netconfSessionIdForReporting);
}
}
@Override
- public NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting) {
+ public NetconfOperationService createService(String netconfSessionIdForReporting) {
return operationService;
}
}
-<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">
+<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">
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
<groupId>org.opendaylight.controller</groupId>
<artifactId>usermanager</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
- <Bundle-Activator>org.opendaylight.controller.netconf.osgi.NetconfSSHActivator</Bundle-Activator>
+ <Bundle-Activator>org.opendaylight.controller.netconf.ssh.osgi.NetconfSSHActivator
+ </Bundle-Activator>
<Import-Package>
com.google.common.base,
ch.ethz.ssh2,
org.osgi.framework,
org.osgi.util.tracker,
org.slf4j,
+ org.bouncycastle.openssl
</Import-Package>
</instructions>
</configuration>
*/
package org.opendaylight.controller.netconf.ssh.authentication;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.commons.io.IOUtils;
import org.opendaylight.controller.sal.authorization.AuthResultEnum;
import org.opendaylight.controller.sal.authorization.UserLevel;
import org.opendaylight.controller.usermanager.IUserManager;
import org.opendaylight.controller.usermanager.UserConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
public class AuthProvider implements AuthProviderInterface {
- private static IUserManager um;
+ private static IUserManager um; //FIXME static mutable state, no locks
private static final String DEFAULT_USER = "netconf";
private static final String DEFAULT_PASSWORD = "netconf";
- private String PEM;
-
- private static final Logger logger = LoggerFactory.getLogger(AuthProvider.class);
-
- public AuthProvider(IUserManager ium,InputStream privateKeyFileInputStream) throws Exception {
+ private final String pem;
+ public AuthProvider(IUserManager ium, String pemCertificate) throws Exception {
+ checkNotNull(pemCertificate, "Parameter 'pemCertificate' is null");
AuthProvider.um = ium;
- if (AuthProvider.um == null){
+ if (AuthProvider.um == null) {
throw new Exception("No usermanager service available.");
}
List<String> roles = new ArrayList<String>(1);
roles.add(UserLevel.SYSTEMADMIN.toString());
- AuthProvider.um.addLocalUser(new UserConfig(DEFAULT_USER, DEFAULT_PASSWORD, roles));
-
- try {
- PEM = IOUtils.toString(privateKeyFileInputStream);
- } catch (IOException e) {
- logger.error("Error reading RSA key from file.");
- throw new IllegalStateException("Error reading RSA key from file.");
- }
+ AuthProvider.um.addLocalUser(new UserConfig(DEFAULT_USER, DEFAULT_PASSWORD, roles)); //FIXME hardcoded auth
+ pem = pemCertificate;
}
+
@Override
- public boolean authenticated(String username, String password) throws Exception {
- if (AuthProvider.um == null){
+ public boolean authenticated(String username, String password) throws Exception {
+ if (AuthProvider.um == null) {
throw new Exception("No usermanager service available.");
}
- AuthResultEnum authResult = AuthProvider.um.authenticate(username,password);
- if (authResult.equals(AuthResultEnum.AUTH_ACCEPT) || authResult.equals(AuthResultEnum.AUTH_ACCEPT_LOC)){
- return true;
- }
- return false;
+ AuthResultEnum authResult = AuthProvider.um.authenticate(username, password);
+ return authResult.equals(AuthResultEnum.AUTH_ACCEPT) || authResult.equals(AuthResultEnum.AUTH_ACCEPT_LOC);
}
@Override
- public char[] getPEMAsCharArray() throws Exception {
- if (null == PEM){
- logger.error("Missing RSA key string.");
- throw new Exception("Missing RSA key.");
- }
- return PEM.toCharArray();
+ public char[] getPEMAsCharArray() {
+ return pem.toCharArray();
}
@Override
public void addUserManagerService(IUserManager userManagerService) {
AuthProvider.um = userManagerService;
}
-
-
}
--- /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.ssh.authentication;
+
+import org.apache.commons.io.FileUtils;
+import org.bouncycastle.openssl.PEMWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+
+public class PEMGenerator {
+ private static final Logger logger = LoggerFactory.getLogger(PEMGenerator.class);
+ private static final int KEY_SIZE = 4096;
+
+ public static String generateTo(File privateFile) throws Exception {
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+ SecureRandom sr = new SecureRandom();
+ keyGen.initialize(KEY_SIZE, sr);
+ KeyPair keypair = keyGen.generateKeyPair();
+ logger.info("Generating private key to {}", privateFile.getAbsolutePath());
+ String privatePEM = toString(keypair.getPrivate());
+ FileUtils.write(privateFile, privatePEM);
+ return privatePEM;
+ }
+
+ private static String toString(Key key) throws IOException {
+ try (StringWriter writer = new StringWriter()) {
+ try (PEMWriter pemWriter = new PEMWriter(writer)) {
+ pemWriter.writeObject(key);
+ }
+ return writer.toString();
+ }
+ }
+}
* 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.osgi;
+package org.opendaylight.controller.netconf.ssh.osgi;
import com.google.common.base.Optional;
-import java.io.FileInputStream;
-import java.net.InetSocketAddress;
+import org.apache.commons.io.IOUtils;
import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
import org.opendaylight.controller.usermanager.IUserManager;
import org.osgi.framework.BundleActivator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
/**
* Activator for netconf SSH bundle which creates SSH bridge between netconf client and netconf server. Activator
* starts SSH Server in its own thread. This thread is closed when activator calls stop() method. Server opens socket
@Override
public void removedService(ServiceReference<IUserManager> reference, IUserManager service) {
logger.trace("Removing service {} from netconf SSH. " +
- "SSH won't authenticate users until IUserManeger service will be started.", reference);
+ "SSH won't authenticate users until IUserManager service will be started.", reference);
removeUserManagerService();
}
};
if (sshSocketAddressOptional.isPresent()){
String path = NetconfConfigUtil.getPrivateKeyPath(context);
- path = path.replace("\\", "/");
+ path = path.replace("\\", "/"); // FIXME: shouldn't this convert lines to system dependent path separator?
if (path.equals("")){
throw new Exception("Missing netconf.ssh.pk.path key in configuration file.");
}
- try (FileInputStream fis = new FileInputStream(path)){
- AuthProvider authProvider = new AuthProvider(iUserManager,fis);
- this.server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddress,authProvider);
+ File privateKeyFile = new File(path);
+ String privateKeyPEMString;
+ if (privateKeyFile.exists() == false) {
+ // generate & save to file
+ privateKeyPEMString = PEMGenerator.generateTo(privateKeyFile);
+ } else {
+ // read from file
+ try (FileInputStream fis = new FileInputStream(path)) {
+ privateKeyPEMString = IOUtils.toString(fis);
+ } catch (IOException e) {
+ logger.error("Error reading RSA key from file '{}'", path);
+ throw new IllegalStateException("Error reading RSA key from file " + path);
+ }
}
+ AuthProvider authProvider = new AuthProvider(iUserManager, privateKeyPEMString);
+ this.server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddress,authProvider);
Thread serverThread = new Thread(server,"netconf SSH server thread");
serverThread.setDaemon(true);
package org.opendaylight.controller.netconf.ssh.threads;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import ch.ethz.ssh2.AuthenticationResult;
import ch.ethz.ssh2.PtySettings;
import ch.ethz.ssh2.ServerAuthenticationCallback;
import ch.ethz.ssh2.ServerSession;
import ch.ethz.ssh2.ServerSessionCallback;
import ch.ethz.ssh2.SimpleServerSessionCallback;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
@ThreadSafe
public class SocketThread implements Runnable, ServerAuthenticationCallback, ServerConnectionCallback {
- private static final Logger logger = LoggerFactory.getLogger(SocketThread.class);
+ private static final Logger logger = LoggerFactory.getLogger(SocketThread.class);
private final Socket socket;
private final InetSocketAddress clientAddress;
public static void start(Socket socket,
InetSocketAddress clientAddress,
long sessionId,
- AuthProvider authProvider) throws IOException{
- Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket,clientAddress,sessionId,authProvider));
+ AuthProvider authProvider) throws IOException {
+ Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket, clientAddress, sessionId, authProvider));
netconf_ssh_socket_thread.setDaemon(true);
netconf_ssh_socket_thread.start();
}
+
private SocketThread(Socket socket,
InetSocketAddress clientAddress,
long sessionId,
this.socket = socket;
this.clientAddress = clientAddress;
this.sessionId = sessionId;
- this.remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replaceFirst("/","");
+ this.remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replaceFirst("/", "");
this.authProvider = authProvider;
}
public void run() {
conn = new ServerConnection(socket);
try {
- conn.setPEMHostKey(authProvider.getPEMAsCharArray(),"netconf");
+ conn.setPEMHostKey(authProvider.getPEMAsCharArray(), "netconf");
} catch (Exception e) {
logger.debug("Server authentication setup failed.");
}
try {
conn.connect();
} catch (IOException e) {
- logger.error("SocketThread error ",e);
+ logger.error("SocketThread error ", e);
}
}
+
@Override
- public ServerSessionCallback acceptSession(final ServerSession session)
- {
- SimpleServerSessionCallback cb = new SimpleServerSessionCallback()
- {
+ public ServerSessionCallback acceptSession(final ServerSession session) {
+ SimpleServerSessionCallback cb = new SimpleServerSessionCallback() {
@Override
- public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException
- {
- return new Runnable(){
+ public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException {
+ return new Runnable() {
@Override
- public void run()
- {
- if (subsystem.equals("netconf")){
+ public void run() {
+ if (subsystem.equals("netconf")) {
IOThread netconf_ssh_input = null;
- IOThread netconf_ssh_output = null;
+ IOThread netconf_ssh_output = null;
try {
String hostName = clientAddress.getHostName();
int portNumber = clientAddress.getPort();
logger.trace("echo socket created");
logger.trace("starting netconf_ssh_input thread");
- netconf_ssh_input = new IOThread(echoSocket.getInputStream(),ss.getStdin(),"input_thread_"+sessionId,ss,conn);
+ netconf_ssh_input = new IOThread(echoSocket.getInputStream(), ss.getStdin(), "input_thread_" + sessionId, ss, conn);
netconf_ssh_input.setDaemon(false);
netconf_ssh_input.start();
logger.trace("starting netconf_ssh_output thread");
- final String customHeader = "["+currentUser+";"+remoteAddressWithPort+";ssh;;;;;;]\n";
- netconf_ssh_output = new IOThread(ss.getStdout(),echoSocket.getOutputStream(),"output_thread_"+sessionId,ss,conn,customHeader);
+ final String customHeader = "[" + currentUser + ";" + remoteAddressWithPort + ";ssh;;;;;;]\n";
+ netconf_ssh_output = new IOThread(ss.getStdout(), echoSocket.getOutputStream(), "output_thread_" + sessionId, ss, conn, customHeader);
netconf_ssh_output.setDaemon(false);
netconf_ssh_output.start();
- } catch (Throwable t){
- logger.error("SSH bridge couldn't create echo socket",t.getMessage(),t);
+ } catch (Exception t) {
+ logger.error("SSH bridge could not create echo socket: {}", t.getMessage(), t);
try {
- if (netconf_ssh_input!=null){
+ if (netconf_ssh_input != null) {
netconf_ssh_input.join();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
- logger.error("netconf_ssh_input join error ",e);
+ logger.error("netconf_ssh_input join error ", e);
}
try {
- if (netconf_ssh_output!=null){
+ if (netconf_ssh_output != null) {
netconf_ssh_output.join();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
- logger.error("netconf_ssh_output join error ",e);
+ logger.error("netconf_ssh_output join error ", e);
}
-
}
} else {
- try {
- ss.getStdin().write("wrong subsystem requested - closing connection".getBytes());
- ss.close();
- } catch (IOException e) {
- logger.debug("excpetion while sending bad subsystem response",e);
- }
+ String reason = "Only netconf subsystem is supported, requested:" + subsystem;
+ closeSession(ss, reason);
}
}
};
}
+
+ public void closeSession(ServerSession ss, String reason) {
+ logger.trace("Closing session - {}", reason);
+ try {
+ ss.getStdin().write(reason.getBytes());
+ } catch (IOException e) {
+ logger.debug("Exception while closing session", e);
+ }
+ ss.close();
+ }
+
@Override
- public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException
- {
- return new Runnable()
- {
+ public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException {
+ return new Runnable() {
@Override
- public void run()
- {
- //noop
+ public void run() {
+ closeSession(ss, "PTY request not supported");
}
};
}
@Override
- public Runnable requestShell(final ServerSession ss) throws IOException
- {
- return new Runnable()
- {
+ public Runnable requestShell(final ServerSession ss) throws IOException {
+ return new Runnable() {
@Override
- public void run()
- {
- //noop
+ public void run() {
+ closeSession(ss, "Shell not supported");
}
};
}
}
@Override
- public String initAuthentication(ServerConnection sc)
- {
- logger.trace("Established connection with host {}",remoteAddressWithPort);
- return "Established connection with host "+remoteAddressWithPort+"\r\n";
+ public String initAuthentication(ServerConnection sc) {
+ logger.trace("Established connection with host {}", remoteAddressWithPort);
+ return "Established connection with host " + remoteAddressWithPort + "\r\n";
}
@Override
- public String[] getRemainingAuthMethods(ServerConnection sc)
- {
- return new String[] { ServerAuthenticationCallback.METHOD_PASSWORD };
+ public String[] getRemainingAuthMethods(ServerConnection sc) {
+ return new String[]{ServerAuthenticationCallback.METHOD_PASSWORD};
}
@Override
- public AuthenticationResult authenticateWithNone(ServerConnection sc, String username)
- {
+ public AuthenticationResult authenticateWithNone(ServerConnection sc, String username) {
return AuthenticationResult.FAILURE;
}
@Override
- public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password)
- {
+ public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password) {
try {
- if (authProvider.authenticated(username,password)){
+ if (authProvider.authenticated(username, password)) {
currentUser = username;
- logger.trace("user {}@{} authenticated",currentUser,remoteAddressWithPort);
+ logger.trace("user {}@{} authenticated", currentUser, remoteAddressWithPort);
return AuthenticationResult.SUCCESS;
}
- } catch (Exception e){
+ } catch (Exception e) {
logger.warn("Authentication failed due to :" + e.getLocalizedMessage());
}
return AuthenticationResult.FAILURE;
@Override
public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm,
- byte[] publickey, byte[] signature)
- {
+ byte[] publickey, byte[] signature) {
return AuthenticationResult.FAILURE;
}
--- /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;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
+import org.opendaylight.controller.usermanager.IUserManager;
+import org.opendaylight.controller.usermanager.UserConfig;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+
+// This test is intended to be verified using ssh
+@Ignore
+public class KeyGeneratorTest {
+
+ @Mock
+ private IUserManager iUserManager;
+ File tempFile;
+
+ @Before
+ public void setUp() throws IOException {
+ MockitoAnnotations.initMocks(this);
+ doReturn(null).when(iUserManager).addLocalUser(any(UserConfig.class));
+ tempFile = File.createTempFile("odltest", ".tmp");
+ tempFile.deleteOnExit();
+ }
+
+ @After
+ public void tearDown() {
+ assertTrue(tempFile.delete());
+ }
+
+ @Test
+ public void test() throws Exception {
+ String pem = PEMGenerator.generateTo(tempFile);
+
+ AuthProvider authProvider = new AuthProvider(iUserManager, pem);
+ InetSocketAddress inetSocketAddress = new InetSocketAddress(Inet4Address.getLoopbackAddress().getHostAddress(), 8383);
+ NetconfSSHServer server = NetconfSSHServer.start(1830, inetSocketAddress, authProvider);
+
+ Thread serverThread = new Thread(server,"netconf SSH server thread");
+ serverThread.start();
+ serverThread.join();
+ }
+}
package org.opendaylight.controller.netconf;
import ch.ethz.ssh2.Connection;
-import java.io.InputStream;
-import java.net.InetSocketAddress;
import junit.framework.Assert;
+import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+
public class SSHServerTest {
public void startSSHServer() throws Exception{
logger.info("Creating SSH server");
StubUserManager um = new StubUserManager(USER,PASSWORD);
- InputStream is = getClass().getResourceAsStream("/RSA.pk");
- AuthProvider ap = new AuthProvider(um, is);
+ String pem;
+ try(InputStream is = getClass().getResourceAsStream("/RSA.pk")) {
+ pem = IOUtils.toString(is);
+ }
+ AuthProvider ap = new AuthProvider(um, pem);
NetconfSSHServer server = NetconfSSHServer.start(PORT,tcpAddress,ap);
sshServerThread = new Thread(server);
sshServerThread.setDaemon(true);
package org.opendaylight.controller.netconf.util;
import com.google.common.base.Preconditions;
-import org.opendaylight.controller.config.api.ConflictingVersionException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpression;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
return (doc == null) ? null : new NetconfMessage(doc);
}
- public static void checkIsMessageOk(NetconfMessage responseMessage) throws ConflictingVersionException {
- XmlElement element = XmlElement.fromDomDocument(responseMessage.getDocument());
+ public static Document checkIsMessageOk(NetconfMessage responseMessage) {
+ return checkIsMessageOk(responseMessage.getDocument());
+ }
+
+ public static Document checkIsMessageOk(Document response) {
+ XmlElement element = XmlElement.fromDomDocument(response);
Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY));
element = element.getOnlyChildElement();
-
if (element.getName().equals(XmlNetconfConstants.OK)) {
- return;
+ return response;
}
-
- if (element.getName().equals(XmlNetconfConstants.RPC_ERROR)) {
- logger.warn("Can not load last configuration, operation failed");
- // is it ConflictingVersionException ?
- XPathExpression xPathExpression = XMLNetconfUtil.compileXPath("/netconf:rpc-reply/netconf:rpc-error/netconf:error-info/netconf:error");
- String error = (String) XmlUtil.evaluateXPath(xPathExpression, element.getDomElement(), XPathConstants.STRING);
- if (error!=null && error.contains(ConflictingVersionException.class.getCanonicalName())) {
- throw new ConflictingVersionException(error);
- }
- throw new IllegalStateException("Can not load last configuration, operation failed: "
- + XmlUtil.toString(responseMessage.getDocument()));
- }
-
logger.warn("Can not load last configuration. Operation failed.");
throw new IllegalStateException("Can not load last configuration. Operation failed: "
- + XmlUtil.toString(responseMessage.getDocument()));
+ + XmlUtil.toString(response));
}
}
package org.opendaylight.controller.netconf.util.mapping;
-import java.util.Map;
-
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
+import java.util.Map;
+
public abstract class AbstractNetconfOperation implements NetconfOperation {
private final String netconfSessionIdForReporting;
@Override
public String toString() {
- final StringBuffer sb = new StringBuffer("AbstractConfigNetconfOperation{");
- sb.append("name=").append(getOperationName());
+ final StringBuffer sb = new StringBuffer(getClass().getName());
+ try {
+ sb.append("{name=").append(getOperationName());
+ } catch(UnsupportedOperationException e) {
+ // no problem
+ }
sb.append(", namespace=").append(getOperationNamespace());
sb.append(", session=").append(netconfSessionIdForReporting);
sb.append('}');
package org.opendaylight.controller.netconf.util;
import org.junit.Test;
-import org.opendaylight.controller.config.api.ConflictingVersionException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Document;
try{
NetconfUtil.checkIsMessageOk(new NetconfMessage(document));
fail();
- }catch(ConflictingVersionException e){
+ }catch(IllegalStateException e){
assertThat(e.getMessage(), containsString("Optimistic lock failed. Expected parent version 21, was 18"));
}
}
<osgi.version>5.0.0</osgi.version>
<maven.bundle.version>2.4.0</maven.bundle.version>
<slf4j.version>1.7.2</slf4j.version>
- <netconf.netty.version>4.0.10.Final</netconf.netty.version>
<salGeneratorPath>${project.build.directory}/generated-sources/sal</salGeneratorPath>
</properties>
<artifactId>config-persister-api</artifactId>
<version>${config.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-adapter</artifactId>
- <version>${config.version}</version>
- </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-client</artifactId>
/*
- * Copyright IBM Corporation, 2013. All rights reserved.
+ * Copyright IBM Corporation and others, 2013. 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,
@XmlElement (name="tenant_id")
String tenantID;
+ @XmlElement (name="ipv6_address_mode", nillable=true)
+ String ipV6AddressMode;
+
+ @XmlElement (name="ipv6_ra_mode", nillable=true)
+ String ipV6RaMode;
+
/* stores the OpenStackPorts associated with an instance
* used to determine if that instance can be deleted.
*/
this.tenantID = tenantID;
}
+ public String getIpV6AddressMode() { return ipV6AddressMode; }
+
+ public void setIpV6AddressMode(String ipV6AddressMode) { this.ipV6AddressMode = ipV6AddressMode; }
+
+ public String getIpV6RaMode() { return ipV6RaMode; }
+
+ public void setIpV6RaMode(String ipV6RaMode) { this.ipV6RaMode = ipV6RaMode; }
+
/**
* This method copies selected fields from the object and returns them
* as a new object, suitable for marshaling.
if (s.equals("tenant_id")) {
ans.setTenantID(this.getTenantID());
}
+ if (s.equals("ipv6_address_mode")) {
+ ans.setIpV6AddressMode(this.getIpV6AddressMode());
+ }
+ if (s.equals("ipv6_ra_mode")) {
+ ans.setIpV6RaMode(this.getIpV6RaMode());
+ }
}
return ans;
}
+ ", ipVersion=" + ipVersion + ", cidr=" + cidr + ", gatewayIP=" + gatewayIP + ", dnsNameservers="
+ dnsNameservers + ", allocationPools=" + allocationPools + ", hostRoutes=" + hostRoutes
+ ", enableDHCP=" + enableDHCP + ", tenantID=" + tenantID + ", myPorts=" + myPorts
- + ", gatewayIPAssigned=" + gatewayIPAssigned + "]";
+ + ", gatewayIPAssigned=" + gatewayIPAssigned + ", ipv6AddressMode=" + ipV6AddressMode
+ + ", ipv6RaMode=" + ipV6RaMode + "]";
}
}
import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.networkconfig.bridgedomain.BridgeDomainConfigServiceException;
import org.opendaylight.controller.sal.networkconfig.bridgedomain.ConfigConstants;
import org.opendaylight.controller.sal.networkconfig.bridgedomain.IBridgeDomainConfigService;
import org.opendaylight.controller.sal.utils.ServiceHelper;
if (status.getCode().equals(StatusCode.SUCCESS)) {
return Response.status(Response.Status.CREATED).build();
}
- } catch (Throwable t) {
+ } catch (BridgeDomainConfigServiceException e) {
return Response.status(Response.Status.PRECONDITION_FAILED).build();
}
throw new ResourceNotFoundException(status.getDescription());
if (status.getCode().equals(StatusCode.SUCCESS)) {
return Response.status(Response.Status.OK).build();
}
- } catch (Throwable t) {
+ } catch (Exception t) {
return Response.status(Response.Status.PRECONDITION_FAILED).build();
}
throw new ResourceNotFoundException(status.getDescription());
if (status.getCode().equals(StatusCode.SUCCESS)) {
return Response.status(Response.Status.CREATED).build();
}
- } catch (Throwable t) {
+ } catch (Exception t) {
return Response.status(Response.Status.PRECONDITION_FAILED).build();
}
throw new ResourceNotFoundException(status.getDescription());
if (status.getCode().equals(StatusCode.SUCCESS)) {
return Response.status(Response.Status.OK).build();
}
- } catch (Throwable t) {
+ } catch (Exception t) {
return Response.status(Response.Status.PRECONDITION_FAILED).build();
}
throw new ResourceNotFoundException(status.getDescription());
/*
- * Copyright IBM Corporation, 2013. All rights reserved.
+ * Copyright IBM Corporation and others, 2013. 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,
@QueryParam("gateway_ip") String queryGatewayIP,
@QueryParam("enable_dhcp") String queryEnableDHCP,
@QueryParam("tenant_id") String queryTenantID,
+ @QueryParam("ipv6_address_mode") String queryIpV6AddressMode,
+ @QueryParam("ipv6_ra_mode") String queryIpV6RaMode,
// pagination
@QueryParam("limit") String limit,
@QueryParam("marker") String marker,
(queryCIDR == null || queryCIDR.equals(oSS.getCidr())) &&
(queryGatewayIP == null || queryGatewayIP.equals(oSS.getGatewayIP())) &&
(queryEnableDHCP == null || queryEnableDHCP.equals(oSS.getEnableDHCP())) &&
- (queryTenantID == null || queryTenantID.equals(oSS.getTenantID()))) {
+ (queryTenantID == null || queryTenantID.equals(oSS.getTenantID())) &&
+ (queryIpV6AddressMode == null || queryIpV6AddressMode.equals(oSS.getIpV6AddressMode())) &&
+ (queryIpV6RaMode == null || queryIpV6RaMode.equals(oSS.getIpV6RaMode()))){
if (fields.size() > 0) {
ans.add(extractFields(oSS,fields));
} else {
// switch
private ByteBuffer peerNetData; // encrypted message from the switch
private FileInputStream kfd = null, tfd = null;
+ private final String keyStoreFileDefault = "./configuration/tlsKeyStore";
+ private final String trustStoreFileDefault = "./configuration/tlsTrustStore";
+ private final String keyStorePasswordPropName = "controllerKeyStorePassword";
+ private final String trustStorePasswordPropName = "controllerTrustStorePassword";
+ private static String keyStorePassword = null;
+ private static String trustStorePassword = null;
public SecureMessageReadWriteService(SocketChannel socket, Selector selector)
throws Exception {
*/
private void createSecureChannel(SocketChannel socket) throws Exception {
String keyStoreFile = System.getProperty("controllerKeyStore");
- String keyStorePassword = System
- .getProperty("controllerKeyStorePassword");
String trustStoreFile = System.getProperty("controllerTrustStore");
- String trustStorePassword = System
- .getProperty("controllerTrustStorePassword");
+ String keyStorePasswordProp = System.getProperty(keyStorePasswordPropName);
+ String trustStorePasswordProp = System.getProperty(trustStorePasswordPropName);
if (keyStoreFile != null) {
keyStoreFile = keyStoreFile.trim();
+ } else {
+ keyStoreFile = keyStoreFileDefault;
}
if ((keyStoreFile == null) || keyStoreFile.isEmpty()) {
throw new FileNotFoundException("TLS KeyStore file not found.");
}
+
+ if ((keyStorePassword == null) || ((keyStorePasswordProp != null) && !keyStorePasswordProp.isEmpty())) {
+ keyStorePassword = keyStorePasswordProp;
+ }
if (keyStorePassword != null) {
keyStorePassword = keyStorePassword.trim();
+ System.setProperty(keyStorePasswordPropName, "");
}
if ((keyStorePassword == null) || keyStorePassword.isEmpty()) {
throw new FileNotFoundException("TLS KeyStore Password not provided.");
}
if (trustStoreFile != null) {
trustStoreFile = trustStoreFile.trim();
+ } else {
+ trustStoreFile = trustStoreFileDefault;
}
if ((trustStoreFile == null) || trustStoreFile.isEmpty()) {
throw new FileNotFoundException("TLS TrustStore file not found");
}
+
+ if ((trustStorePassword == null) || ((trustStorePasswordProp != null) && !trustStorePasswordProp.isEmpty())) {
+ trustStorePassword = trustStorePasswordProp;
+ }
if (trustStorePassword != null) {
trustStorePassword = trustStorePassword.trim();
+ System.setProperty(trustStorePasswordPropName, "");
}
if ((trustStorePassword == null) || trustStorePassword.isEmpty()) {
throw new FileNotFoundException("TLS TrustStore Password not provided.");
if (!src.getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
if (type == UpdateType.ADDED) {
edgeMap.put(dst, edge);
- } else {
+ } else if (type == UpdateType.REMOVED) {
edgeMap.remove(dst);
}
} else {
*/
if (type == UpdateType.ADDED) {
prodMap.put(dst, edge);
- } else {
+ } else if (type == UpdateType.REMOVED) {
prodMap.remove(dst);
}
}
list.add(SwPath.class);
list.add(HwPath.class);
list.add(Drop.class);
+ list.add(Flood.class);
+ list.add(FloodAll.class);
return list;
}
--- /dev/null
+package org.opendaylight.controller.sal.match.extensible;
+
+import java.util.Arrays;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class DlDst extends MatchField<byte[]> {
+ private static final long serialVersionUID = 1L;
+ public static final String TYPE = "DL_DST";
+ private byte[] address;
+
+ /**
+ * Creates a Match field for the destination data layer address
+ *
+ * @param address
+ * the data layer address. The constructor makes a copy of it
+ */
+ public DlDst(byte[] address) {
+ super(TYPE);
+ if (address != null) {
+ this.address = Arrays.copyOf(address, address.length);
+ }
+ }
+
+ // To satisfy JAXB
+ public DlDst() {
+ super(TYPE);
+ }
+
+ @Override
+ public byte[] getValue() {
+ return Arrays.copyOf(address, address.length);
+ }
+
+ @Override
+ @XmlElement(name = "value")
+ protected String getValueString() {
+ return HexEncode.bytesToHexStringFormat(address);
+ }
+
+ @Override
+ public byte[] getMask() {
+ return null;
+ }
+
+ @Override
+ protected String getMaskString() {
+ return null;
+ }
+
+ @Override
+ public boolean isValid() {
+ return address != null && address.length == NetUtils.MACAddrLengthInBytes;
+ }
+
+ @Override
+ public boolean hasReverse() {
+ return true;
+ }
+
+ @Override
+ public DlSrc getReverse() {
+ return new DlSrc(address);
+ }
+
+ @Override
+ public DlDst clone() {
+ return new DlDst(address);
+ }
+
+ @Override
+ public boolean isV6() {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(address);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof DlDst)) {
+ return false;
+ }
+ DlDst other = (DlDst) obj;
+ return Arrays.equals(address, other.address);
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.match.extensible;
+
+import java.util.Arrays;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.HexEncode;
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class DlSrc extends MatchField<byte[]> {
+ private static final long serialVersionUID = 1L;
+ public static final String TYPE = "DL_SRC";
+ private byte[] address;
+
+ /**
+ * Creates a Match field for the source datalayer address
+ *
+ * @param address
+ * the datalayer address. The constructor makes a copy of it
+ */
+ public DlSrc(byte[] address) {
+ super(TYPE);
+ if (address != null) {
+ this.address = Arrays.copyOf(address, address.length);
+ }
+ }
+
+ // To satisfy JAXB
+ private DlSrc() {
+ super(TYPE);
+ }
+
+ @Override
+ public byte[] getValue() {
+ return Arrays.copyOf(address, address.length);
+ }
+
+ @Override
+ @XmlElement(name = "value")
+ protected String getValueString() {
+ return HexEncode.bytesToHexStringFormat(address);
+ }
+
+ @Override
+ public byte[] getMask() {
+ return null;
+ }
+
+ @Override
+ protected String getMaskString() {
+ return null;
+ }
+
+ @Override
+ public boolean isValid() {
+ return address != null && address.length == NetUtils.MACAddrLengthInBytes;
+ }
+
+ @Override
+ public boolean hasReverse() {
+ return true;
+ }
+
+ @Override
+ public DlDst getReverse() {
+ return new DlDst(address);
+ }
+
+ @Override
+ public DlSrc clone() {
+ return new DlSrc(address);
+ }
+
+ @Override
+ public boolean isV6() {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(address);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof DlSrc)) {
+ return false;
+ }
+ DlSrc other = (DlSrc) obj;
+ return Arrays.equals(address, other.address);
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class DlType extends MatchField<Short> {
+ private static final long serialVersionUID = 1L;
+ public static final String TYPE = "DL_TYPE";
+ private short ethertype;
+
+ /**
+ * Creates a Match field for the data layer type
+ *
+ * @param address
+ * the data layer type
+ */
+ public DlType(short ethertype) {
+ super(TYPE);
+ this.ethertype = ethertype;
+ }
+
+ // To satisfy JAXB
+ private DlType() {
+ super(TYPE);
+ }
+
+ @Override
+ public Short getValue() {
+ return ethertype;
+ }
+
+ @Override
+ @XmlElement(name = "value")
+ protected String getValueString() {
+ return String.format("0X%s", Integer.toHexString(NetUtils.getUnsignedShort(ethertype)));
+ }
+
+ @Override
+ public Short getMask() {
+ return null;
+ }
+
+ @Override
+ protected String getMaskString() {
+ return null;
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public boolean hasReverse() {
+ return false;
+ }
+
+ @Override
+ public DlType getReverse() {
+ return this.clone();
+ }
+
+ @Override
+ public DlType clone() {
+ return new DlType(ethertype);
+ }
+
+ @Override
+ public boolean isV6() {
+ return this.ethertype == EtherTypes.IPv6.shortValue();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ethertype;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof DlType)) {
+ return false;
+ }
+ DlType other = (DlType) obj;
+ if (ethertype != other.ethertype) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class DlVlan extends MatchField<Short> {
+ private static final long serialVersionUID = 1L;
+ public static final String TYPE = "DL_VLAN";
+ private static final short MAX = 4095;
+ private short vlan;
+
+ /**
+ * Creates a Match field for the data layer type
+ *
+ * @param address
+ * the data layer type
+ */
+ public DlVlan(short vlan) {
+ super(TYPE);
+ this.vlan = vlan;
+ }
+
+ // To satisfy JAXB
+ private DlVlan() {
+ super(TYPE);
+ }
+
+ @Override
+ public Short getValue() {
+ return vlan;
+ }
+
+ @Override
+ @XmlElement(name = "value")
+ protected String getValueString() {
+ return String.valueOf(vlan);
+ }
+
+ @Override
+ public Short getMask() {
+ return null;
+ }
+
+ @Override
+ protected String getMaskString() {
+ return null;
+ }
+
+ @Override
+ public boolean isValid() {
+ return vlan >= 0 && vlan <= MAX;
+ }
+
+ @Override
+ public DlVlan getReverse() {
+ return this.clone();
+ }
+
+ @Override
+ public boolean hasReverse() {
+ return false;
+ }
+
+ @Override
+ public DlVlan clone() {
+ return new DlVlan(vlan);
+ }
+
+ @Override
+ public boolean isV6() {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + vlan;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof DlVlan)) {
+ return false;
+ }
+ DlVlan other = (DlVlan) obj;
+ if (vlan != other.vlan) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class DlVlanPriority extends MatchField<Byte> {
+ private static final long serialVersionUID = 1L;
+ public static final String TYPE = "DL_VLAN_PR";
+ private static final byte MAX = 7;
+ private byte vlanPriority;
+
+ /**
+ * Creates a Match field for the data layer type
+ *
+ * @param address
+ * the data layer type
+ */
+ public DlVlanPriority(byte vlanPriority) {
+ super(TYPE);
+ this.vlanPriority = vlanPriority;
+ }
+
+ // To satisfy JAXB
+ private DlVlanPriority() {
+ super(TYPE);
+ }
+
+ @Override
+ public Byte getValue() {
+ return vlanPriority;
+ }
+
+ @Override
+ @XmlElement(name = "mask")
+ protected String getValueString() {
+ return String.format("0X%s", Integer.toHexString(NetUtils.getUnsignedByte(vlanPriority)));
+ }
+
+ @Override
+ public Byte getMask() {
+ return null;
+ }
+
+ @Override
+ protected String getMaskString() {
+ return null;
+ }
+
+ @Override
+ public boolean isValid() {
+ return vlanPriority >= 0 && vlanPriority <= MAX;
+ }
+
+ @Override
+ public boolean hasReverse() {
+ return false;
+ }
+
+ @Override
+ public DlVlanPriority getReverse() {
+ return this.clone();
+ }
+
+ @Override
+ public DlVlanPriority clone() {
+ return new DlVlanPriority(vlanPriority);
+ }
+
+ @Override
+ public boolean isV6() {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + vlanPriority;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof DlVlanPriority)) {
+ return false;
+ }
+ DlVlanPriority other = (DlVlanPriority) obj;
+ if (vlanPriority != other.vlanPriority) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.core.NodeConnector;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class InPort extends MatchField<NodeConnector> {
+ private static final long serialVersionUID = 1L;
+ public static final String TYPE = "IN_PORT";
+ private NodeConnector port;
+
+ /**
+ * Creates a Match field for the input port
+ *
+ * @param port
+ * the input port
+ */
+ public InPort(NodeConnector port) {
+ super(TYPE);
+ this.port = port;
+ }
+
+ // To satisfy JAXB
+ private InPort() {
+ super(TYPE);
+ }
+
+ @Override
+ public NodeConnector getValue() {
+ return port;
+ }
+
+ @Override
+ @XmlElement(name = "value")
+ protected String getValueString() {
+ return port.toString();
+ }
+
+ @Override
+ public NodeConnector getMask() {
+ return null;
+ }
+
+ @Override
+ protected String getMaskString() {
+ return null;
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public boolean hasReverse() {
+ return false;
+ }
+
+ @Override
+ public InPort getReverse() {
+ return this.clone();
+ }
+
+ @Override
+ public InPort clone() {
+ return new InPort(port);
+ }
+
+ @Override
+ public boolean isV6() {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((port == null) ? 0 : port.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof InPort)) {
+ return false;
+ }
+ InPort other = (InPort) obj;
+ if (port == null) {
+ if (other.port != null) {
+ return false;
+ }
+ } else if (!port.equals(other.port)) {
+ return false;
+ }
+ return true;
+ }
+}
\ 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.sal.match.extensible;
+
+import java.io.Serializable;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+/**
+ * Represents the generic match criteria for a network frame/packet/message
+ * It contains a collection of individual field match
+ *
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class Match implements Cloneable, Serializable {
+ private static final long serialVersionUID = 1L;
+ private Map<String, MatchField<?>> fields;
+
+ public Match() {
+ fields = new HashMap<String, MatchField<?>>();
+ }
+
+ public Match(Match match) {
+ fields = new HashMap<String, MatchField<?>>(match.fields);
+ }
+
+ /**
+ * Generic setter for frame/packet/message's header field against which to match
+ *
+ * @param field the fields parameters as MAtchField object
+ */
+ public void setField(MatchField<?> field) {
+ if (field.isValid()) {
+ fields.put(field.getType(), field);
+ }
+ }
+
+ /**
+ * Generic method to clear a field from the match
+ */
+ public void clearField(String type) {
+ fields.remove(type);
+ }
+
+ /**
+ * Generic getter for fields against which the match is programmed
+ *
+ * @param type frame/packet/message's header field type
+ * @return
+ */
+ public MatchField<?> getField(String type) {
+ return fields.get(type);
+ }
+
+ /**
+ * Returns the list of MatchType fields the match is set for
+ *
+ * @return List of individual MatchType fields.
+ */
+ public List<String> getMatchesList() {
+ return new ArrayList<String>(fields.keySet());
+ }
+
+ /**
+ * Returns the list of MatchFields the match is set for
+ *
+ * @return List of individual MatchField values.
+ */
+ @XmlElement(name="matchField")
+ public List<MatchField<?>> getMatchFields() {
+ return new ArrayList<MatchField<?>>(fields.values());
+ }
+
+ /**
+ * Returns whether this match is for an IPv6 flow
+ */
+ public boolean isIPv6() {
+ if (isPresent(DlType.TYPE)) {
+ for (MatchField<?> field : fields.values()) {
+ if (!field.isV6()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns whether this match is for an IPv4 flow
+ */
+ public boolean isIPv4() {
+ return !isIPv6();
+ }
+
+ /**
+ * Returns whether for the specified field type the match is to be considered "any"
+ * Equivalent to say this match does not care about the value of the specified field
+ *
+ * @param type
+ * @return
+ */
+ public boolean isAny(String type) {
+ return !fields.containsKey(type);
+ }
+
+ /**
+ * Returns whether a match for the specified field type is configured
+ *
+ * @param type
+ * @return
+ */
+ public boolean isPresent(String type) {
+ return (fields.get(type) != null);
+ }
+
+ public boolean isEmpty() {
+ return fields.isEmpty();
+ }
+
+ @Override
+ public Match clone() {
+ Match cloned = null;
+ try {
+ cloned = (Match) super.clone();
+ cloned.fields = new HashMap<String, MatchField<?>>();
+ for (Entry<String, MatchField<?>> entry : this.fields.entrySet()) {
+ cloned.fields.put(entry.getKey(), entry.getValue().clone());
+ }
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException(e);
+ }
+ return cloned;
+ }
+
+ /**
+ * Returns a reversed version of this match
+ * For example, in the reversed version the network source and destination
+ * addresses will be exchanged. Non symmetric match field will not be
+ * copied over into the reversed match version, like input port.
+ *
+ * @return
+ */
+ public Match reverse() {
+ Match reverse = new Match();
+ for (MatchField<?> field : fields.values()) {
+ reverse.setField(field.hasReverse()? field.getReverse() : field.clone());
+ }
+
+ // Reset asymmetric fields
+ reverse.clearField(InPort.TYPE);
+
+ return reverse;
+ }
+
+ /**
+ * Check whether the current match conflicts with the passed filter match
+ * This match conflicts with the filter if for at least a MatchType defined
+ * in the filter match, the respective MatchFields differ or are not
+ * compatible
+ *
+ * In other words the function returns true if the set of packets described
+ * by one match and the set of packets described by the other match are
+ * disjoint. Equivalently, if the intersection of the two sets of packets
+ * described by the two org.opendaylight.controller.sal.matches is an empty.
+ *
+ * For example, Let's suppose the filter has the following MatchFields:
+ * DL_TYPE = 0x800
+ * NW_DST = 172.20.30.110/24
+ *
+ * while this match has the following MatchFields:
+ * DL_TYPE = 0x800
+ * NW_DST = 172.20.30.45/24
+ * TP_DST = 80
+ *
+ * Then the function would return false as the two Match are not
+ * conflicting.
+ *
+ * Note: the mask value is taken into account only for MatchType.NW_SRC and
+ * MatchType.NW_DST
+ *
+ * @param match
+ * the Match describing the filter
+ * @return true if the set of packets described by one match and the set of
+ * packets described by the other match are disjoint, false
+ * otherwise
+ */
+ public boolean conflictWithFilter(Match filter) {
+ return !this.intersetcs(filter);
+ }
+
+ /**
+ * Merge the current Match fields with the fields of the filter Match. A
+ * check is first run to see if this Match is compatible with the filter
+ * Match. If it is not, the merge is not attempted.
+ *
+ * The result is the match object representing the intersection of the set
+ * of packets described by this match with the set of packets described by
+ * the filter match. If the intersection of the two sets is empty, the
+ * return match will be null.
+ *
+ * @param filter
+ * the match with which attempting the merge
+ * @return a new Match object describing the set of packets represented by
+ * the intersection of this and the filter org.opendaylight.controller.sal.matches. null if the
+ * intersection is empty.
+ */
+ public Match mergeWithFilter(Match filter) {
+ return this.getIntersection(filter);
+ }
+
+ /**
+ * Return the match representing the intersection of the set of packets
+ * described by this match with the set of packets described by the other
+ * match. Such as m.getIntersection(m) == m, m.getIntersection(u) == m and
+ * m.getIntersection(o) == o where u is an empty match (universal set, all
+ * packets) and o is the null match (empty set).
+ *
+ * @param other
+ * the match with which computing the intersection
+ * @return a new Match object representing the intersection of the set of
+ * packets described by this match with the set of packets described
+ * by the other match. null when the intersection is the empty set.
+ */
+ public Match getIntersection(Match other) {
+ // If no intersection, return the empty set
+ if (!this.intersetcs(other)) {
+ return null;
+ }
+ // Check if any of the two is the universal match
+ if (this.isEmpty()) {
+ return other.clone();
+ }
+ if (other.isEmpty()) {
+ return this.clone();
+ }
+ // Get all the match types for both filters
+ Set<String> allTypes = new HashSet<String>(this.fields.keySet());
+ allTypes.addAll(new HashSet<String>(other.fields.keySet()));
+ // Derive the intersection
+ Match intersection = new Match();
+ for (String type : allTypes) {
+ if (this.isAny(type) && other.isAny(type)) {
+ continue;
+ }
+ if (this.isAny(type)) {
+ intersection.setField(other.getField(type).clone());
+ continue;
+ } else if (other.isAny(type)) {
+ intersection.setField(this.getField(type).clone());
+ continue;
+ }
+ // Either they are equal or it is about IP address
+ switch (type) {
+ // When it is about IP address, take the wider prefix address
+ // between the twos
+ case NwSrc.TYPE:
+ case NwDst.TYPE:
+ MatchField<?> thisField = this.getField(type);
+ MatchField<?> otherField = other.getField(type);
+ InetAddress thisAddress = (InetAddress) thisField.getValue();
+ InetAddress otherAddress = (InetAddress) otherField.getValue();
+ InetAddress thisMask = (InetAddress) thisField.getMask();
+ InetAddress otherMask = (InetAddress) otherField.getMask();
+
+ int thisMaskLen = (thisMask == null) ? ((thisAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
+ .getSubnetMaskLength(thisMask);
+ int otherMaskLen = (otherMask == null) ? ((otherAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
+ .getSubnetMaskLength(otherMask);
+
+ InetAddress subnetPrefix = null;
+ InetAddress subnetMask = null;
+ if (thisMaskLen < otherMaskLen) {
+ subnetPrefix = NetUtils.getSubnetPrefix(otherAddress, otherMaskLen);
+ subnetMask = otherMask;
+ } else {
+ subnetPrefix = NetUtils.getSubnetPrefix(thisAddress, thisMaskLen);
+ subnetMask = thisMask;
+ }
+ MatchField<?> field = (type.equals(NwSrc.TYPE)) ? new NwSrc(subnetPrefix, subnetMask) : new NwDst(
+ subnetPrefix, subnetMask);
+ intersection.setField(field);
+ break;
+ default:
+ // this and other match field are equal for this type, pick this
+ // match field
+ intersection.setField(this.getField(type).clone());
+ }
+ }
+ return intersection;
+ }
+
+ /**
+ * Checks whether the intersection of the set of packets described by this
+ * match with the set of packets described by the other match is non empty
+ *
+ * For example, if this match is: DL_SRC = 00:cc:bb:aa:11:22
+ *
+ * and the other match is: DL_TYPE = 0x800 NW_SRC = 1.2.3.4
+ *
+ * then their respective matching packets set intersection is non empty:
+ * DL_SRC = 00:cc:bb:aa:11:22 DL_TYPE = 0x800 NW_SRC = 1.2.3.4
+ *
+ * @param other
+ * the other match with which testing the intersection
+ * @return true if the intersection of the respective matching packets sets
+ * is non empty
+ */
+ public boolean intersetcs(Match other) {
+ // No intersection with the empty set
+ if (other == null) {
+ return false;
+ }
+ // Always intersection with the universal set
+ if (this.isEmpty() || other.isEmpty()) {
+ return true;
+ }
+
+ // Get all the match types for both filters
+ Set<String> allTypes = new HashSet<String>(this.fields.keySet());
+ allTypes.addAll(new HashSet<String>(other.fields.keySet()));
+
+ // Iterate through all the match types defined in the two filters
+ for (String type : allTypes) {
+ if (this.isAny(type) || other.isAny(type)) {
+ continue;
+ }
+
+ MatchField<?> thisField = this.getField(type);
+ MatchField<?> otherField = other.getField(type);
+
+ switch (type) {
+ case DlSrc.TYPE:
+ case DlDst.TYPE:
+ if (!Arrays.equals((byte[]) thisField.getValue(), (byte[]) otherField.getValue())) {
+ return false;
+ }
+ break;
+ case NwSrc.TYPE:
+ case NwDst.TYPE:
+ InetAddress thisAddress = (InetAddress) thisField.getValue();
+ InetAddress otherAddress = (InetAddress) otherField.getValue();
+ // Validity check
+ if (thisAddress instanceof Inet4Address && otherAddress instanceof Inet6Address
+ || thisAddress instanceof Inet6Address && otherAddress instanceof Inet4Address) {
+ return false;
+ }
+ InetAddress thisMask = (InetAddress) thisField.getMask();
+ InetAddress otherMask = (InetAddress) otherField.getMask();
+ if (NetUtils.inetAddressConflict(thisAddress, otherAddress, thisMask, otherMask)
+ && NetUtils.inetAddressConflict(otherAddress, thisAddress, otherMask, thisMask)) {
+ return false;
+ }
+ break;
+ default:
+ if (!thisField.getValue().equals(otherField.getValue())) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((fields == null) ? 0 : fields.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof Match)) {
+ return false;
+ }
+ Match other = (Match) obj;
+ if (fields == null) {
+ if (other.fields != null) {
+ return false;
+ }
+ } else if (!fields.equals(other.fields)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Match[" + fields.values() + "]";
+ }
+}
--- /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.sal.match.extensible;
+
+import java.io.Serializable;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Represents the generic matching field object
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public abstract class MatchField<T> implements Cloneable, Serializable {
+ private static final long serialVersionUID = 1L;
+ private String type;
+
+ // To satisfy JAXB
+ @SuppressWarnings("unused")
+ private MatchField() {
+ }
+
+ public MatchField(String type) {
+ this.type = type;
+ }
+
+ @XmlElement(name = "type")
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Returns the value set for this match field
+ *
+ * @return
+ */
+ public abstract T getValue();
+
+ @XmlElement(name = "value")
+ protected abstract String getValueString();
+
+ /**
+ * Returns the mask value set for this field match A null mask means this is
+ * a full match
+ *
+ * @return
+ */
+ public abstract T getMask();
+
+ @XmlElement(name = "mask")
+ protected abstract String getMaskString();
+
+ /**
+ * Returns whether the field match configuration is valid or not
+ *
+ * @return true if valid, false otherwise
+ */
+ public abstract boolean isValid();
+
+ public abstract boolean hasReverse();
+
+ /**
+ * Returns the reverse match field. For example for a MatchField matching on
+ * source ip 1.1.1.1 it will return a MatchField matching on destination IP
+ * 1.1.1.1. For not reversable MatchField, a copy of this MatchField will be
+ * returned
+ *
+ * @return the correspondent reverse MatchField object or a copy of this
+ * object if the field is not reversable
+ */
+ public abstract MatchField<T> getReverse();
+
+ /**
+ * Returns whether the match field is congruent with IPv6 frames
+ *
+ * @return true if congruent with IPv6 frames
+ */
+ public abstract boolean isV6();
+
+ @Override
+ public abstract MatchField<T> clone();
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof MatchField)) {
+ return false;
+ }
+ MatchField<?> other = (MatchField<?>) obj;
+ if (type == null) {
+ if (other.type != null) {
+ return false;
+ }
+ } else if (!type.equals(other.type)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return (getMask() == null) ? String.format("%s(%s)", getType(), getValueString()) :
+ String.format("%s(%s,%s)", getType(), getValueString(), getMaskString());
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.match.extensible;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class NwDst extends MatchField<InetAddress> {
+ private static final long serialVersionUID = 1L;
+ public static final String TYPE = "NW_DST";
+ private InetAddress address;
+ private InetAddress mask;
+
+ /**
+ * Creates a Match field for the network destination address
+ *
+ * @param address
+ * the network address
+ * @param mask
+ * the network mask
+ */
+ public NwDst(InetAddress address, InetAddress mask) {
+ super(TYPE);
+ this.address = address;
+ this.mask = mask;
+ }
+
+ // To satisfy JAXB
+ private NwDst() {
+ super(TYPE);
+ }
+
+ public NwDst(InetAddress address) {
+ super(TYPE);
+ this.address = address;
+ this.mask = null;
+ }
+
+ @Override
+ public InetAddress getValue() {
+ return address;
+ }
+
+ @Override
+ @XmlElement(name = "value")
+ protected String getValueString() {
+ return address.getHostAddress();
+ }
+
+ @Override
+ public InetAddress getMask() {
+ return mask;
+ }
+
+ @Override
+ @XmlElement(name = "mask")
+ protected String getMaskString() {
+ return (mask == null) ? "null" : mask.getHostAddress();
+ }
+
+ @Override
+ public boolean isValid() {
+ if (address != null) {
+ if (mask != null) {
+ return address instanceof Inet4Address && mask instanceof Inet4Address
+ || address instanceof Inet6Address && mask instanceof Inet6Address;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean hasReverse() {
+ return true;
+ }
+
+ @Override
+ public NwSrc getReverse() {
+ return new NwSrc(address, mask);
+ }
+
+ @Override
+ public NwDst clone() {
+ return new NwDst(address, mask);
+ }
+
+ @Override
+ public boolean isV6() {
+ return address instanceof Inet6Address;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((address == null) ? 0 : address.hashCode());
+ result = prime * result + ((mask == null) ? 0 : mask.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof NwDst)) {
+ return false;
+ }
+ NwDst other = (NwDst) obj;
+ // Equality to be checked against prefix addresses
+ int thisMaskLen = (this.mask == null) ? ((this.address instanceof Inet4Address) ? 32 : 128) : NetUtils
+ .getSubnetMaskLength(this.mask);
+ int otherMaskLen = (other.mask == null) ? ((other.address instanceof Inet4Address) ? 32 : 128) : NetUtils
+ .getSubnetMaskLength(other.mask);
+
+ return NetUtils.getSubnetPrefix(address, thisMaskLen).equals(
+ NetUtils.getSubnetPrefix(other.address, otherMaskLen));
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class NwProtocol extends MatchField<Byte> {
+ private static final long serialVersionUID = 1L;
+ public static final String TYPE = "NW_PROTO";
+ private static final short MAX = 255;
+ private byte protocol;
+
+ /**
+ * Creates a Match field for the network protocol
+ *
+ * @param protocol
+ * the protocol number
+ */
+ public NwProtocol(byte protocol) {
+ super(TYPE);
+ this.protocol = protocol;
+ }
+
+ public NwProtocol(int protocol) {
+ super(TYPE);
+ this.protocol = (byte) protocol;
+ }
+
+ public NwProtocol(short protocol) {
+ super(TYPE);
+ this.protocol = (byte) protocol;
+ }
+
+ // To satisfy JAXB
+ private NwProtocol() {
+ super(TYPE);
+ }
+
+ @Override
+ public Byte getValue() {
+ return protocol;
+ }
+
+ @Override
+ @XmlElement(name = "value")
+ protected String getValueString() {
+ return String.format("0X%s", Integer.toHexString(NetUtils.getUnsignedByte(protocol)));
+ }
+
+ @Override
+ public Byte getMask() {
+ return null;
+ }
+
+ @Override
+ protected String getMaskString() {
+ return null;
+ }
+
+ @Override
+ public boolean isValid() {
+ int intProtocol = NetUtils.getUnsignedByte(protocol);
+ return intProtocol >= 0 && intProtocol <= MAX;
+ }
+
+ @Override
+ public boolean hasReverse() {
+ return false;
+ }
+
+ @Override
+ public NwProtocol getReverse() {
+ return this.clone();
+ }
+
+ @Override
+ public NwProtocol clone() {
+ return new NwProtocol(protocol);
+ }
+
+ @Override
+ public boolean isV6() {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + protocol;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof NwProtocol)) {
+ return false;
+ }
+ NwProtocol other = (NwProtocol) obj;
+ if (protocol != other.protocol) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.match.extensible;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class NwSrc extends MatchField<InetAddress> {
+ private static final long serialVersionUID = 1L;
+ public static final String TYPE = "NW_SRC";
+ private InetAddress address;
+ private InetAddress mask;
+
+ /**
+ * Creates a Match field for the network source address
+ *
+ * @param address
+ * the network address
+ * @param mask
+ * the network mask
+ */
+ public NwSrc(InetAddress address, InetAddress mask) {
+ super(TYPE);
+ this.address = address;
+ this.mask = mask;
+ }
+
+ // To satisfy JAXB
+ private NwSrc() {
+ super(TYPE);
+ }
+
+ public NwSrc(InetAddress address) {
+ super(TYPE);
+ this.address = address;
+ this.mask = null;
+ }
+
+ @Override
+ public InetAddress getValue() {
+ return address;
+ }
+
+ @Override
+ @XmlElement(name = "value")
+ protected String getValueString() {
+ return address.toString();
+ }
+
+ @Override
+ public InetAddress getMask() {
+ return mask;
+ }
+
+ @Override
+ @XmlElement(name = "mask")
+ protected String getMaskString() {
+ return mask == null ? "null" : mask.toString();
+ }
+
+ @Override
+ public boolean isValid() {
+ if (address != null) {
+ if (mask != null) {
+ return address instanceof Inet4Address && mask instanceof Inet4Address
+ || address instanceof Inet6Address && mask instanceof Inet6Address;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean hasReverse() {
+ return true;
+ }
+
+ @Override
+ public NwDst getReverse() {
+ return new NwDst(address, mask);
+ }
+
+ @Override
+ public NwSrc clone() {
+ return new NwSrc(address, mask);
+ }
+
+ @Override
+ public boolean isV6() {
+ return address instanceof Inet6Address;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((address == null) ? 0 : address.hashCode());
+ result = prime * result + ((mask == null) ? 0 : mask.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof NwSrc)) {
+ return false;
+ }
+ NwSrc other = (NwSrc) obj;
+ // Equality to be checked against prefix addresses
+ int thisMaskLen = (this.mask == null) ? ((this.address instanceof Inet4Address) ? 32 : 128) : NetUtils
+ .getSubnetMaskLength(this.mask);
+ int otherMaskLen = (other.mask == null) ? ((other.address instanceof Inet4Address) ? 32 : 128) : NetUtils
+ .getSubnetMaskLength(other.mask);
+
+ return NetUtils.getSubnetPrefix(address, thisMaskLen).equals(
+ NetUtils.getSubnetPrefix(other.address, otherMaskLen));
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class NwTos extends MatchField<Byte> {
+ private static final long serialVersionUID = 1L;
+ public static final String TYPE = "NW_TOS";
+ private static final short MAX = 63;
+ private byte tos;
+
+ /**
+ * Creates a Match field for the network TOS
+ *
+ * @param address
+ * the network TOS
+ */
+ public NwTos(byte tos) {
+ super(TYPE);
+ this.tos = tos;
+ }
+
+ public NwTos(int tos) {
+ super(TYPE);
+ this.tos = (byte) tos;
+ }
+
+ public NwTos(short tos) {
+ super(TYPE);
+ this.tos = (byte) tos;
+ }
+
+ // To satisfy JAXB
+ private NwTos() {
+ super(TYPE);
+ }
+
+ @Override
+ public Byte getValue() {
+ return tos;
+ }
+
+ @Override
+ @XmlElement(name = "value")
+ protected String getValueString() {
+ return String.format("0X%s", Integer.toHexString(NetUtils.getUnsignedByte(tos)));
+ }
+
+ @Override
+ public Byte getMask() {
+ return null;
+ }
+
+ @Override
+ protected String getMaskString() {
+ return null;
+ }
+
+ @Override
+ public boolean isValid() {
+ return tos >= 0 && tos <= MAX;
+ }
+
+ @Override
+ public boolean hasReverse() {
+ return false;
+ }
+
+ @Override
+ public NwTos getReverse() {
+ return this.clone();
+ }
+
+ @Override
+ public NwTos clone() {
+ return new NwTos(tos);
+ }
+
+ @Override
+ public boolean isV6() {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + tos;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof NwTos)) {
+ return false;
+ }
+ NwTos other = (NwTos) obj;
+ if (tos != other.tos) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class TpDst extends MatchField<Short> {
+ private static final long serialVersionUID = 1L;
+ public static final String TYPE = "TP_DST";
+ private short port;
+
+ /**
+ * Creates a Match field for the transport destination port
+ *
+ * @param port
+ * the transport port
+ */
+ public TpDst(short port) {
+ super(TYPE);
+ this.port = port;
+ }
+
+ // To satisfy JAXB
+ private TpDst() {
+ super(TYPE);
+ }
+
+ @Override
+ public Short getValue() {
+ return port;
+ }
+
+ @Override
+ @XmlElement(name = "value")
+ protected String getValueString() {
+ return String.valueOf(NetUtils.getUnsignedShort(port));
+ }
+
+ @Override
+ public Short getMask() {
+ return null;
+ }
+
+ @Override
+ protected String getMaskString() {
+ return null;
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public TpSrc getReverse() {
+ return new TpSrc(port);
+ }
+
+ @Override
+ public boolean hasReverse() {
+ return true;
+ }
+
+ @Override
+ public TpDst clone() {
+ return new TpDst(port);
+ }
+
+ @Override
+ public boolean isV6() {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + port;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof TpDst)) {
+ return false;
+ }
+ TpDst other = (TpDst) obj;
+ if (port != other.port) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.match.extensible;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.opendaylight.controller.sal.utils.NetUtils;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class TpSrc extends MatchField<Short> {
+ private static final long serialVersionUID = 1L;
+ public static final String TYPE = "TP_SRC";
+ private short port;
+
+ /**
+ * Creates a Match field for the Transport source port
+ *
+ * @param port
+ * the transport port
+ */
+ public TpSrc(short port) {
+ super(TYPE);
+ this.port = port;
+ }
+
+ // To satisfy JAXB
+ private TpSrc() {
+ super(TYPE);
+ }
+
+ @Override
+ public Short getValue() {
+ return port;
+ }
+
+ @Override
+ @XmlElement(name = "value")
+ protected String getValueString() {
+ return String.valueOf(NetUtils.getUnsignedShort(port));
+ }
+
+ @Override
+ public Short getMask() {
+ return null;
+ }
+
+ @Override
+ protected String getMaskString() {
+ return null;
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public boolean hasReverse() {
+ return true;
+ }
+
+ @Override
+ public TpDst getReverse() {
+ return new TpDst(port);
+ }
+
+ @Override
+ public TpSrc clone() {
+ return new TpSrc(port);
+ }
+
+ @Override
+ public boolean isV6() {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + port;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof TpSrc)) {
+ return false;
+ }
+ TpSrc other = (TpSrc) obj;
+ if (port != other.port) {
+ return false;
+ }
+ return true;
+ }
+}
\ 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.sal.match;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.match.extensible.DlDst;
+import org.opendaylight.controller.sal.match.extensible.DlSrc;
+import org.opendaylight.controller.sal.match.extensible.DlType;
+import org.opendaylight.controller.sal.match.extensible.DlVlan;
+import org.opendaylight.controller.sal.match.extensible.DlVlanPriority;
+import org.opendaylight.controller.sal.match.extensible.InPort;
+import org.opendaylight.controller.sal.match.extensible.Match;
+import org.opendaylight.controller.sal.match.extensible.MatchField;
+import org.opendaylight.controller.sal.match.extensible.NwDst;
+import org.opendaylight.controller.sal.match.extensible.NwProtocol;
+import org.opendaylight.controller.sal.match.extensible.NwSrc;
+import org.opendaylight.controller.sal.match.extensible.NwTos;
+import org.opendaylight.controller.sal.match.extensible.TpDst;
+import org.opendaylight.controller.sal.match.extensible.TpSrc;
+import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.IPProtocols;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.controller.sal.utils.NodeCreator;
+
+public class MatchExtensibleTest {
+ @Test
+ public void testMatchCreation() {
+ Node node = NodeCreator.createOFNode(7L);
+ NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 6, node);
+ MatchField<?> field = new InPort(port);
+
+ Assert.assertTrue(field != null);
+ Assert.assertEquals(field.getType(), InPort.TYPE);
+ Assert.assertEquals(field.getValue(), port);
+ Assert.assertTrue(field.isValid());
+
+
+ byte mac[] = { (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd, (byte) 11, (byte) 22 };
+ field = null;
+ field = new DlSrc(mac);
+ Assert.assertNotNull(field.getValue());
+
+ field = null;
+ field = new NwTos((byte) 0x22);
+ Assert.assertNotNull(field.getValue());
+ }
+
+ @Test
+ public void testMatchSetGet() {
+ Match x = new Match();
+ short val = 2346;
+ NodeConnector inPort = NodeConnectorCreator.createOFNodeConnector(val, NodeCreator.createOFNode(1L));
+ x.setField(new InPort(inPort));
+ Assert.assertEquals(x.getField(InPort.TYPE).getValue(), inPort);
+ Assert.assertTrue((Short) ((NodeConnector) x.getField(InPort.TYPE).getValue()).getID() == val);
+ }
+
+ @Test
+ public void testMatchSetGetMAC() {
+ Match x = new Match();
+ byte mac[] = { (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd, (byte) 11, (byte) 22 };
+ byte mac2[] = { (byte) 0xaa, (byte) 0xbb, 0, 0, 0, (byte) 0xbb };
+
+ x.setField(new DlSrc(mac));
+ x.setField(new DlDst(mac2));
+ Assert.assertArrayEquals(mac, (byte[]) x.getField(DlSrc.TYPE).getValue());
+ Assert.assertFalse(Arrays.equals((byte[]) x.getField(DlSrc.TYPE).getValue(), (byte[]) x.getField(DlDst.TYPE)
+ .getValue()));
+
+ x.setField(new DlDst(mac.clone()));
+ Assert.assertArrayEquals((byte[]) x.getField(DlSrc.TYPE).getValue(), (byte[]) x.getField(DlDst.TYPE).getValue());
+ }
+
+ @Test
+ public void testMatchSetGetNWAddr() throws UnknownHostException {
+ Match x = new Match();
+ String ip = "172.20.231.23";
+ InetAddress address = InetAddress.getByName(ip);
+ InetAddress mask = InetAddress.getByName("255.255.0.0");
+
+ x.setField(new NwSrc(address, mask));
+ Assert.assertEquals(address, x.getField(NwSrc.TYPE).getValue());
+ Assert.assertEquals(x.getField(NwSrc.TYPE).getMask(), mask);
+ }
+
+ @Test
+ public void testMatchSetGetEtherType() throws UnknownHostException {
+ Match x = new Match();
+
+ x.setField(new DlType(EtherTypes.QINQ.shortValue()));
+ Assert.assertEquals(x.getField(DlType.TYPE).getValue(), EtherTypes.QINQ.shortValue());
+
+ x.setField(new DlType(EtherTypes.LLDP.shortValue()));
+ Assert.assertEquals(x.getField(DlType.TYPE).getValue(), EtherTypes.LLDP.shortValue());
+ Assert.assertFalse(x.getField(DlType.TYPE).equals(EtherTypes.LLDP.intValue()));
+ }
+
+ @Test
+ public void testSetGetNwTos() {
+ Match x = new Match();
+ x.setField(new NwTos((byte) 0xb));
+
+ Byte t = new Byte((byte) 0xb);
+
+ Object o = x.getField(NwTos.TYPE).getValue();
+ Assert.assertEquals(o, t);
+ Assert.assertEquals(o, Byte.valueOf((byte)0xb));
+ }
+
+ @Test
+ public void testSetGetNwProto() {
+ Match x = new Match();
+ Byte proto = (byte) 199;
+ x.setField(new NwProtocol(proto));
+
+ Byte o = (Byte) x.getField(NwProtocol.TYPE).getValue();
+ Assert.assertEquals(o, proto);
+ }
+
+ @Test
+ public void testSetTpSrc() {
+ // Minimum value validation.
+ Match match = new Match();
+ short tp_src = 0;
+ match.setField(new TpSrc(tp_src));
+
+ Object o = match.getField(TpSrc.TYPE).getValue();
+ Assert.assertEquals(o, tp_src);
+
+ // Maximum value validation.
+ match = new Match();
+ tp_src = (short) 0xffff;
+ match.setField(new TpSrc(tp_src));
+
+ o = match.getField(TpSrc.TYPE).getValue();
+ Assert.assertEquals(o, tp_src);
+ }
+
+ @Test
+ public void testSetTpDst() {
+ // Minimum value validation.
+ Match match = new Match();
+ short tp_dst = 0;
+ match.setField(new TpDst(tp_dst));
+
+ Object o = match.getField(TpDst.TYPE).getValue();
+ Assert.assertTrue(o.equals(tp_dst));
+
+ // Maximum value validation.
+ match = new Match();
+ tp_dst = (short) 0xffff;
+ match.setField(new TpDst(tp_dst));
+
+ o = match.getField(TpDst.TYPE).getValue();
+ Assert.assertEquals(o, tp_dst);
+ }
+
+ @Test
+ public void testEquality() throws Exception {
+ Node node = NodeCreator.createOFNode(7L);
+ NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 24, node);
+ NodeConnector port2 = NodeConnectorCreator.createOFNodeConnector((short) 24, node);
+ byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
+ byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
+ byte srcMac2[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
+ byte dstMac2[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
+ InetAddress srcIP = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+ InetAddress dstIP = InetAddress.getByName("2001:420:281:1004:e123:e688:d655:a1b0");
+ InetAddress ipMask = InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
+ InetAddress ipMaskd = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
+ InetAddress ipMask2 = InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
+ InetAddress ipMaskd2 = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
+ short ethertype = EtherTypes.IPv6.shortValue();
+ short ethertype2 = EtherTypes.IPv6.shortValue();
+ short vlan = (short) 27, vlan2 = (short) 27;
+ byte vlanPr = (byte) 3, vlanPr2 = (byte) 3;
+ Byte tos = 4, tos2 = 4;
+ byte proto = IPProtocols.UDP.byteValue(), proto2 = IPProtocols.UDP.byteValue();
+ short src = (short) 5500, src2 = (short) 5500;
+ short dst = 80, dst2 = 80;
+
+ /*
+ * Create a SAL Flow aFlow
+ */
+ Match match1 = new Match();
+ Match match2 = new Match();
+ match1.setField(new InPort(port));
+ match1.setField(new DlSrc(srcMac));
+ match1.setField(new DlDst(dstMac));
+ match1.setField(new DlType(ethertype));
+ match1.setField(new DlVlan(vlan));
+ match1.setField(new DlVlanPriority(vlanPr));
+ match1.setField(new NwSrc(srcIP, ipMask));
+ match1.setField(new NwDst(dstIP, ipMaskd));
+ match1.setField(new NwTos(tos));
+ match1.setField(new NwProtocol(proto));
+ match1.setField(new TpSrc(src));
+ match1.setField(new TpDst(dst));
+
+ match2.setField(new InPort(port2));
+ match2.setField(new DlSrc(srcMac2));
+ match2.setField(new DlDst(dstMac2));
+ match2.setField(new DlType(ethertype2));
+ match2.setField(new DlVlan(vlan2));
+ match2.setField(new DlVlanPriority(vlanPr2));
+ match2.setField(new NwSrc(srcIP, ipMask2));
+ match2.setField(new NwDst(dstIP, ipMaskd2));
+ match2.setField(new NwTos(tos2));
+ match2.setField(new NwProtocol(proto2));
+ match2.setField(new TpSrc(src2));
+ match2.setField(new TpDst(dst2));
+
+ Assert.assertTrue(match1.equals(match2));
+
+ Set<String> allFields = new HashSet<String>(match1.getMatchesList());
+ allFields.addAll(match2.getMatchesList());
+ // Make sure all values are equals
+ for (String type : allFields) {
+ if (match1.isPresent(type)) {
+ Assert.assertEquals(match1.getField(type), match2.getField(type));
+ }
+ }
+
+ // Make none of the fields couples are pointing to the same reference
+ MatchField<?> a = null, b = null;
+ for (String type : allFields) {
+ a = match1.getField(type);
+ b = match2.getField(type);
+ if (a != null && b != null) {
+ Assert.assertFalse(a == b);
+ }
+ }
+ }
+
+ @Test
+ public void testEqualityNetMask() throws Exception {
+
+ InetAddress srcIP = InetAddress.getByName("1.1.1.1");
+ InetAddress ipMask = InetAddress.getByName("255.255.255.255");
+ InetAddress srcIP2 = InetAddress.getByName("1.1.1.1");
+ InetAddress ipMask2 = null;
+ short ethertype = EtherTypes.IPv4.shortValue();
+ short ethertype2 = EtherTypes.IPv4.shortValue();
+
+ /*
+ * Create a SAL Flow aFlow
+ */
+ Match match1 = new Match();
+ Match match2 = new Match();
+
+ match1.setField(new DlType(ethertype));
+ match1.setField(new NwSrc(srcIP, ipMask));
+
+ match2.setField(new DlType(ethertype2));
+ match2.setField(new NwSrc(srcIP2, ipMask2));
+
+ Assert.assertTrue(match1.equals(match2));
+
+ ipMask2 = InetAddress.getByName("255.255.255.255");
+ match2.setField(new NwSrc(srcIP2, ipMask2));
+
+ srcIP = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+ srcIP2 = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+ ipMask = null;
+ ipMask2 = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ ethertype = EtherTypes.IPv6.shortValue();
+ ethertype2 = EtherTypes.IPv6.shortValue();
+
+ match1.setField(new DlType(ethertype));
+ match1.setField(new NwSrc(srcIP, ipMask));
+
+ match2.setField(new DlType(ethertype2));
+ match2.setField(new NwSrc(srcIP2, ipMask2));
+
+ Assert.assertEquals(match1, match2);
+ }
+
+ @Test
+ public void testHashCodeWithReverseMatch() throws Exception {
+ InetAddress srcIP1 = InetAddress.getByName("1.1.1.1");
+ InetAddress ipMask1 = InetAddress.getByName("255.255.255.255");
+ InetAddress srcIP2 = InetAddress.getByName("2.2.2.2");
+ InetAddress ipMask2 = InetAddress.getByName("255.255.255.255");
+ MatchField<?> field1 = new NwSrc(srcIP1, ipMask1);
+ MatchField<?> field2 = new NwDst(srcIP2, ipMask2);
+ Match match1 = new Match();
+ match1.setField(field1);
+ match1.setField(field2);
+ Match match2 = match1.reverse();
+ Assert.assertFalse(match1.hashCode() == match2.hashCode());
+ }
+
+ @Test
+ public void testHashCode() throws Exception {
+ byte srcMac1[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
+ byte srcMac2[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
+ byte dstMac1[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
+ byte dstMac2[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
+ short ethertype = EtherTypes.IPv4.shortValue();
+ short ethertype2 = EtherTypes.IPv4.shortValue();
+ InetAddress srcIP1 = InetAddress.getByName("1.1.1.1");
+ InetAddress ipMask1 = InetAddress.getByName("255.255.255.255");
+ InetAddress srcIP2 = InetAddress.getByName("1.1.1.1");
+ InetAddress ipMask2 = InetAddress.getByName("255.255.255.255");
+
+ Match match1 = new Match();
+ Match match2 = new Match();
+
+ MatchField<?> field1 = new DlSrc(srcMac1);
+ MatchField<?> field2 = new DlSrc(srcMac2);
+ Assert.assertTrue(field1.hashCode() == field2.hashCode());
+
+ match1.setField(field1);
+ match2.setField(field2);
+ Assert.assertTrue(match1.hashCode() == match2.hashCode());
+
+ MatchField<?> field3 = new DlDst(dstMac1);
+ MatchField<?> field4 = new DlDst(dstMac2);
+ Assert.assertTrue(field3.hashCode() == field4.hashCode());
+
+ match1.setField(field3);
+ match2.setField(field4);
+ Assert.assertTrue(match1.hashCode() == match2.hashCode());
+
+ MatchField<?> field5 = new DlType(ethertype);
+ MatchField<?> field6 = new DlType(ethertype2);
+ Assert.assertTrue(field5.hashCode() == field6.hashCode());
+
+ match1.setField(field5);
+ match2.setField(field6);
+ Assert.assertTrue(match1.hashCode() == match2 .hashCode());
+
+ MatchField<?> field7 = new NwSrc(srcIP1, ipMask1);
+ MatchField<?> field8 = new NwSrc(srcIP2, ipMask2);
+ Assert.assertTrue(field7.hashCode() == field8.hashCode());
+
+ match1.setField(field7);
+ match2.setField(field8);
+ Assert.assertTrue(match1.hashCode() == match2.hashCode());
+
+ }
+
+ @Test
+ public void testCloning() throws Exception {
+ Node node = NodeCreator.createOFNode(7L);
+ NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 24, node);
+ byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
+ byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
+ InetAddress srcIP = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+ InetAddress dstIP = InetAddress.getByName("2001:420:281:1004:e123:e688:d655:a1b0");
+ InetAddress ipMasks = InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
+ InetAddress ipMaskd = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
+ short ethertype = EtherTypes.IPv6.shortValue();
+ short vlan = (short) 27;
+ byte vlanPr = (byte) 3;
+ Byte tos = 4;
+ byte proto = IPProtocols.UDP.byteValue();
+ short src = (short) 5500;
+ short dst = 80;
+
+ /*
+ * Create a SAL Flow aFlow
+ */
+ Match match = new Match();
+ match.setField(new InPort(port));
+ match.setField(new DlSrc(srcMac));
+ match.setField(new DlDst(dstMac));
+ match.setField(new DlType(ethertype));
+ match.setField(new DlVlan(vlan));
+ match.setField(new DlVlanPriority(vlanPr));
+ match.setField(new NwSrc(srcIP, ipMasks));
+ match.setField(new NwDst(dstIP, ipMaskd));
+ match.setField(new NwTos(tos));
+ match.setField(new NwProtocol(proto));
+ match.setField(new TpSrc(src));
+ match.setField(new TpDst(dst));
+
+ Match cloned = match.clone();
+
+ // Make sure all values are equals
+ for (String type : match.getMatchesList()) {
+ if (match.isPresent(type)) {
+ if (!match.getField(type).equals(cloned.getField(type))) {
+ Assert.assertEquals(match.getField(type), cloned.getField(type));
+ }
+ }
+ }
+
+ // Make sure none of the fields couples are pointing to the same
+ // reference
+ MatchField<?> a = null, b = null;
+ for (String type : match.getMatchesList()) {
+ a = match.getField(type);
+ b = cloned.getField(type);
+ if (a != null && b != null) {
+ Assert.assertFalse(a == b);
+ }
+ }
+
+ Assert.assertTrue(match.equals(cloned));
+
+ Assert.assertEquals(match.getField(DlSrc.TYPE), cloned.getField(DlSrc.TYPE));
+ Assert.assertEquals(match.getField(NwDst.TYPE), cloned.getField(NwDst.TYPE));
+ Assert.assertEquals(match.getField(NwDst.TYPE).getMask(), cloned.getField(NwDst.TYPE).getMask());
+ Assert.assertEquals(match.hashCode(), cloned.hashCode());
+ }
+
+ @Test
+ public void testFlip() throws Exception {
+ Node node = NodeCreator.createOFNode(7L);
+ NodeConnector port = NodeConnectorCreator.createOFNodeConnector((short) 24, node);
+ byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, (byte) 0x9a, (byte) 0xbc };
+ byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d, (byte) 0x5e, (byte) 0x6f };
+ InetAddress srcIP = InetAddress.getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+ InetAddress dstIP = InetAddress.getByName("2001:420:281:1004:e123:e688:d655:a1b0");
+ InetAddress ipMasks = InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
+ InetAddress ipMaskd = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
+ short ethertype = EtherTypes.IPv6.shortValue();
+ short vlan = (short) 27;
+ byte vlanPr = (byte) 3;
+ Byte tos = 4;
+ byte proto = IPProtocols.UDP.byteValue();
+ short src = (short) 5500;
+ short dst = 80;
+
+ /*
+ * Create a SAL Flow aFlow
+ */
+ Match match = new Match();
+ match.setField(new InPort(port));
+ match.setField(new DlSrc(srcMac));
+ match.setField(new DlDst(dstMac));
+ match.setField(new DlType(ethertype));
+ match.setField(new DlVlan(vlan));
+ match.setField(new DlVlanPriority(vlanPr));
+ match.setField(new NwSrc(srcIP, ipMasks));
+ match.setField(new NwDst(dstIP, ipMaskd));
+ match.setField(new NwTos(tos));
+ match.setField(new NwProtocol(proto));
+ match.setField(new TpSrc(src));
+ match.setField(new TpDst(dst));
+
+ Match flipped = match.reverse();
+
+ Assert.assertEquals(match.getField(DlType.TYPE), flipped.getField(DlType.TYPE));
+ Assert.assertEquals(match.getField(DlVlan.TYPE), flipped.getField(DlVlan.TYPE));
+
+ Assert.assertArrayEquals((byte[]) match.getField(DlDst.TYPE).getValue(), (byte[]) flipped.getField(DlSrc.TYPE)
+ .getValue());
+
+ Assert.assertEquals(match.getField(NwDst.TYPE).getValue(), flipped.getField(NwSrc.TYPE).getValue());
+
+ Assert.assertEquals(match.getField(TpDst.TYPE).getValue(), flipped.getField(TpSrc.TYPE).getValue());
+
+ Match flipflip = flipped.reverse().reverse();
+ Assert.assertEquals(flipflip, flipped);
+
+ }
+
+ @Test
+ public void testVlanNone() throws Exception {
+ // The value 0 is used to indicate that no VLAN ID is set
+ short vlan = (short) 0;
+ MatchField<?> field = new DlVlan(vlan);
+
+ Assert.assertTrue(field != null);
+ Assert.assertEquals(field.getValue(), new Short(vlan));
+ Assert.assertTrue(field.isValid());
+ }
+
+ @Test
+ public void testIntersection() throws UnknownHostException {
+ Short ethType = Short.valueOf((short)0x800);
+ InetAddress ip1 = InetAddress.getByName("1.1.1.1");
+ InetAddress ip2 = InetAddress.getByName("1.1.1.0");
+ InetAddress ipm2 = InetAddress.getByName("255.255.255.0");
+ InetAddress ip3 = InetAddress.getByName("1.3.0.0");
+ InetAddress ipm3 = InetAddress.getByName("255.255.0.0");
+ InetAddress ip4 = InetAddress.getByName("1.3.4.4");
+ InetAddress ipm4 = InetAddress.getByName("255.255.255.0");
+
+ Match m1 = new Match();
+ m1.setField(new DlType(ethType));
+ m1.setField(new NwSrc(ip1));
+
+ Match m2 = new Match();
+ m2.setField(new DlType(ethType));
+ m2.setField(new NwSrc(ip2, ipm2));
+
+ Match m3 = new Match();
+ m3.setField(new DlType(ethType));
+ m3.setField(new NwSrc(ip3, ipm3));
+ m3.setField(new NwProtocol(IPProtocols.TCP.byteValue()));
+
+ Match m3r = m3.reverse();
+ Assert.assertTrue(m3.intersetcs(m3r));
+
+ Assert.assertTrue(m1.intersetcs(m2));
+ Assert.assertTrue(m2.intersetcs(m1));
+ Assert.assertFalse(m1.intersetcs(m3));
+ Assert.assertTrue(m1.intersetcs(m3r));
+ Assert.assertFalse(m3.intersetcs(m1));
+ Assert.assertTrue(m3.intersetcs(m1.reverse()));
+ Assert.assertFalse(m2.intersetcs(m3));
+ Assert.assertFalse(m3.intersetcs(m2));
+ Assert.assertTrue(m2.intersetcs(m3r));
+
+
+ Match i = m1.getIntersection(m2);
+ Assert.assertTrue(((Short)i.getField(DlType.TYPE).getValue()).equals(ethType));
+ // Verify intersection of IP addresses is correct
+ Assert.assertTrue(((InetAddress)i.getField(NwSrc.TYPE).getValue()).equals(ip1));
+ Assert.assertNull(i.getField(NwSrc.TYPE).getMask());
+
+ // Empty set
+ i = m2.getIntersection(m3);
+ Assert.assertNull(i);
+
+ Match m4 = new Match();
+ m4.setField(new DlType(ethType));
+ m4.setField(new NwProtocol(IPProtocols.TCP.byteValue()));
+ m3.setField(new NwSrc(ip4, ipm4));
+ Assert.assertTrue(m4.intersetcs(m3));
+
+ // Verify intersection of IP and IP mask addresses is correct
+ Match ii = m3.getIntersection(m4);
+ Assert.assertTrue(((InetAddress)ii.getField(NwSrc.TYPE).getValue()).equals(ip4));
+ Assert.assertTrue(((InetAddress)ii.getField(NwSrc.TYPE).getMask()).equals(ipm4));
+
+ Match m5 = new Match();
+ m5.setField(new DlType(ethType));
+ m3.setField(new NwSrc(ip3, ipm3));
+ m5.setField(new NwProtocol(IPProtocols.UDP.byteValue()));
+ Assert.assertFalse(m5.intersetcs(m3));
+ Assert.assertFalse(m5.intersetcs(m4));
+ Assert.assertTrue(m5.intersetcs(m5));
+ Assert.assertFalse(m3.intersetcs(m5));
+ Assert.assertFalse(m4.intersetcs(m5));
+
+
+ Match i2 = m4.getIntersection(m3);
+ Assert.assertFalse(i2.isEmpty());
+ Assert.assertFalse(i2.getMatchesList().isEmpty());
+ Assert.assertTrue(((InetAddress)i2.getField(NwSrc.TYPE).getValue()).equals(ip3));
+ Assert.assertTrue(((InetAddress)i2.getField(NwSrc.TYPE).getMask()).equals(ipm3));
+ Assert.assertTrue(((Byte)i2.getField(NwProtocol.TYPE).getValue()).equals(IPProtocols.TCP.byteValue()));
+
+ byte src[] = {(byte)0, (byte)0xab,(byte)0xbc,(byte)0xcd,(byte)0xde,(byte)0xef};
+ byte dst[] = {(byte)0x10, (byte)0x11,(byte)0x12,(byte)0x13,(byte)0x14,(byte)0x15};
+ Short srcPort = (short)1024;
+ Short dstPort = (short)80;
+
+ // Check identity
+ Match m6 = new Match();
+ m6.setField(new DlSrc(src));
+ m6.setField(new DlDst(dst));
+ m6.setField(new NwSrc(ip2, ipm2));
+ m6.setField(new NwDst(ip3, ipm3));
+ m6.setField(new NwProtocol(IPProtocols.UDP.byteValue()));
+ m6.setField(new TpSrc(srcPort));
+ m6.setField(new TpDst(dstPort));
+ Assert.assertTrue(m6.intersetcs(m6));
+ Assert.assertTrue(m6.getIntersection(m6).equals(m6));
+
+ // Empty match, represents the universal set (all packets)
+ Match u = new Match();
+ Assert.assertEquals(m6.getIntersection(u), m6);
+ Assert.assertEquals(u.getIntersection(m6), m6);
+
+ // No intersection with null match, empty set
+ Assert.assertNull(m6.getIntersection(null));
+ }
+}
--- /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.sal.networkconfig.bridgedomain;
+
+/**
+ * Exception thrown by IPluginInBridgeDomainConfigService implementations.
+ */
+public class BridgeDomainConfigServiceException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public BridgeDomainConfigServiceException(String message) {
+ super(message);
+ }
+
+ public BridgeDomainConfigServiceException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
+
* @note This method will return false if one or more of the supplied params is not supported by the
* protocol plugin that serves the Node.
*/
- public Status createBridgeDomain(Node node, String bridgeIdentifier, Map<ConfigConstants, Object> params) throws Throwable;
+ public Status createBridgeDomain(Node node, String bridgeIdentifier, Map<ConfigConstants, Object> params) throws BridgeDomainConfigServiceException;
/**
* Delete a Bridge Domain
import org.opendaylight.controller.sal.core.Node;
import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.networkconfig.bridgedomain.BridgeDomainConfigServiceException;
import org.opendaylight.controller.sal.networkconfig.bridgedomain.ConfigConstants;
import org.opendaylight.controller.sal.networkconfig.bridgedomain.IBridgeDomainConfigService;
import org.opendaylight.controller.sal.networkconfig.bridgedomain.IPluginInBridgeDomainConfigService;
public class BridgeDomainConfigService implements IBridgeDomainConfigService {
protected static final Logger logger = LoggerFactory
.getLogger(BridgeDomainConfigService.class);
- private ConcurrentMap<String, IPluginInBridgeDomainConfigService> pluginService =
+ private final ConcurrentMap<String, IPluginInBridgeDomainConfigService> pluginService =
new ConcurrentHashMap<String, IPluginInBridgeDomainConfigService>();
void setPluginInService (Map props, IPluginInBridgeDomainConfigService s) {
@Override
public Status createBridgeDomain(Node node, String bridgeIdentifier, Map<ConfigConstants, Object> params)
- throws Throwable {
+ throws BridgeDomainConfigServiceException {
if (pluginService != null) {
IPluginInBridgeDomainConfigService plugin = this.pluginService.get(node.getType());
if (plugin != null) {
import java.util.Hashtable;
import org.apache.felix.dm.Component;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import org.opendaylight.controller.clustering.services.IClusterContainerServices;
import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
import org.opendaylight.controller.hosttracker.IfIptoHost;
import org.opendaylight.controller.switchmanager.IInventoryListener;
import org.opendaylight.controller.switchmanager.ISwitchManager;
import org.opendaylight.controller.topologymanager.ITopologyManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class Activator extends ComponentActivatorAbstractBase {
protected static final Logger logger = LoggerFactory
* instantiated in order to get an fully working implementation
* Object
*/
+ @Override
public Object[] getImplementations() {
Object[] res = { SimpleForwardingImpl.class,
SimpleBroadcastHandlerImpl.class };
* also optional per-container different behavior if needed, usually
* should not be the case though.
*/
+ @Override
public void configureInstance(Component c, Object imp, String containerName) {
if (imp.equals(SimpleForwardingImpl.class)) {
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put("salListenerName", "simpleforwarding");
+
// export the service
c.setInterface(new String[] { IInventoryListener.class.getName(),
IfNewHostNotify.class.getName(),
- IListenRoutingUpdates.class.getName() }, null);
+ IListenRoutingUpdates.class.getName(),
+ IListenDataPacket.class.getName() }, props);
c.add(createContainerServiceDependency(containerName).setService(
IClusterContainerServices.class).setCallbacks(
c.add(createContainerServiceDependency(containerName).setService(
IRouting.class).setCallbacks("setRouting", "unsetRouting")
.setRequired(false));
- }else if (imp.equals(SimpleBroadcastHandlerImpl.class)) {
+ c.add(createContainerServiceDependency(containerName).setService(
+ IDataPacketService.class).setCallbacks("setDataPacketService",
+ "unsetDataPacketService").setRequired(false));
+
+ } else if (imp.equals(SimpleBroadcastHandlerImpl.class)) {
Dictionary<String, String> props = new Hashtable<String, String>();
props.put("salListenerName", "simplebroadcasthandler");
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-import org.opendaylight.controller.sal.packet.Ethernet;
-import org.opendaylight.controller.sal.packet.IDataPacketService;
-import org.opendaylight.controller.sal.packet.IListenDataPacket;
import org.opendaylight.controller.sal.core.ConstructionException;
import org.opendaylight.controller.sal.core.Node;
import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
import org.opendaylight.controller.sal.packet.Packet;
import org.opendaylight.controller.sal.packet.PacketResult;
import org.opendaylight.controller.sal.packet.RawPacket;
public PacketResult receiveDataPacket(RawPacket inPkt) {
/*
* note that this assumes that the protocol plugin will do appropriate
- * filtering to ensure that this only receives packets for it's
+ * filtering to ensure that this only receives packets for its
* container.
*/
lock.writeLock().unlock();
}
+ @Override
public void setMode(BroadcastMode m) {
lock.writeLock().lock();
mode = m;
package org.opendaylight.controller.samples.simpleforwarding.internal;
+import java.net.InetAddress;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import org.opendaylight.controller.sal.flowprogrammer.Flow;
import org.opendaylight.controller.sal.match.Match;
import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
+import org.opendaylight.controller.sal.packet.IPv4;
+import org.opendaylight.controller.sal.packet.Packet;
+import org.opendaylight.controller.sal.packet.PacketResult;
+import org.opendaylight.controller.sal.packet.RawPacket;
import org.opendaylight.controller.sal.routing.IListenRoutingUpdates;
import org.opendaylight.controller.sal.routing.IRouting;
import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.NetUtils;
import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
import org.opendaylight.controller.sal.utils.Status;
import org.opendaylight.controller.samples.simpleforwarding.HostNodePair;
* installs those rules using <tt>installPerHostRules()</tt>.
*/
public class SimpleForwardingImpl implements IfNewHostNotify,
- IListenRoutingUpdates, IInventoryListener {
- private static Logger log = LoggerFactory
- .getLogger(SimpleForwardingImpl.class);
+ IListenRoutingUpdates, IInventoryListener, IListenDataPacket {
+ private static Logger log = LoggerFactory.getLogger(SimpleForwardingImpl.class);
private static short DEFAULT_IPSWITCH_PRIORITY = 1;
- private static String FORWARDING_RULES_CACHE_NAME = "forwarding.ipswitch.rules";
+ static final String FORWARDING_RULES_CACHE_NAME = "forwarding.ipswitch.rules";
private IfIptoHost hostTracker;
private IForwardingRulesManager frm;
private ITopologyManager topologyManager;
private Map<Node, List<FlowEntry>> tobePrunedPos = new HashMap<Node, List<FlowEntry>>();
private IClusterContainerServices clusterContainerService = null;
private ISwitchManager switchManager;
+ private IDataPacketService dataPacketService;
/**
* Return codes from the programming of the perHost rules in HW
public enum RulesProgrammingReturnCode {
SUCCESS, FAILED_FEW_SWITCHES, FAILED_ALL_SWITCHES, FAILED_WRONG_PARAMS
}
+ public void setDataPacketService(IDataPacketService s) {
+ log.debug("Setting dataPacketService");
+ this.dataPacketService = s;
+ }
+
+ public void unsetDataPacketService(IDataPacketService s) {
+ if (this.dataPacketService == s) {
+ this.dataPacketService = null;
+ }
+ }
public void setRouting(IRouting routing) {
+ log.debug("Setting routing");
this.routing = routing;
}
}
}
- public ITopologyManager getTopologyManager() {
- return topologyManager;
- }
-
public void setTopologyManager(ITopologyManager topologyManager) {
log.debug("Setting topologyManager");
this.topologyManager = topologyManager;
*
* @return a return code that convey the programming status of the HW
*/
- private RulesProgrammingReturnCode uninstallPerHostRules(
- HostNodeConnector host) {
+ private RulesProgrammingReturnCode uninstallPerHostRules(HostNodeConnector host) {
RulesProgrammingReturnCode retCode = RulesProgrammingReturnCode.SUCCESS;
Map<NodeConnector, FlowEntry> pos;
FlowEntry po;
for (Node swId : switches) {
List<FlowEntry> pl = tobePrunedPos.get(swId);
if (pl != null) {
- log
- .debug(
- "Policies for Switch: {} in the list to be deleted: {}",
- swId, pl);
+ log.debug("Policies for Switch: {} in the list to be deleted: {}", swId, pl);
Iterator<FlowEntry> plIter = pl.iterator();
//for (Policy po: pl) {
while (plIter.hasNext()) {
FlowEntry po = plIter.next();
- log.error("Removing Policy, Switch: {} Policy: {}", swId,
- po);
+ log.error("Removing Policy, Switch: {} Policy: {}", swId, po);
this.frm.uninstallFlowEntry(po);
plIter.remove();
}
this.switchManager = null;
}
}
+
+ @Override
+ public PacketResult receiveDataPacket(RawPacket inPkt) {
+ if (inPkt == null) {
+ return PacketResult.IGNORED;
+ }
+ log.trace("Received a frame of size: {}", inPkt.getPacketData().length);
+ Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt);
+ if (formattedPak instanceof Ethernet) {
+ Object nextPak = formattedPak.getPayload();
+ if (nextPak instanceof IPv4) {
+ log.trace("Handle punted IP packet: {}", formattedPak);
+ handlePuntedIPPacket((IPv4) nextPak, inPkt.getIncomingNodeConnector());
+ }
+ }
+ return PacketResult.IGNORED;
+
+ }
+
+ private void handlePuntedIPPacket(IPv4 pkt, NodeConnector incomingNodeConnector) {
+ InetAddress dIP = NetUtils.getInetAddress(pkt.getDestinationAddress());
+ if (dIP == null || hostTracker == null) {
+ log.debug("Invalid param(s) in handlePuntedIPPacket.. DestIP: {}. hostTracker: {}", dIP, hostTracker);
+ return;
+ }
+ HostNodeConnector destHost = hostTracker.hostFind(dIP);
+ if (destHost != null
+ && (routing == null ||
+ routing.getRoute(incomingNodeConnector.getNode(), destHost.getnodeconnectorNode()) != null)) {
+
+ log.trace("Host {} is at {}", dIP, destHost.getnodeConnector());
+ HostNodePair key = new HostNodePair(destHost, incomingNodeConnector.getNode());
+
+ // If SimpleForwarding is aware of this host, it will try to install
+ // a path. Forward packet until it's done.
+ if (dataPacketService != null && this.rulesDB.containsKey(key)) {
+
+
+ /*
+ * if we know where the host is and there's a path from where this
+ * packet was punted to where the host is, then attempt best effort delivery to the host
+ */
+ NodeConnector nc = destHost.getnodeConnector();
+ log.trace("Forwarding punted IP received at {} to {}", incomingNodeConnector, nc);
+ // re-encode the Ethernet packet (the parent of the IPv4 packet)
+ RawPacket rp = this.dataPacketService.encodeDataPacket(pkt.getParent());
+ rp.setOutgoingNodeConnector(nc);
+ this.dataPacketService.transmitDataPacket(rp);
+ }
+
+ }
+ }
}
return result;
}
- @RequestMapping(value = "/connect/{nodeId}", method = RequestMethod.POST)
+ @RequestMapping(value = "/connect/{nodeId:.+}", method = RequestMethod.POST)
@ResponseBody
public Status addNode(HttpServletRequest request, @PathVariable("nodeId") String nodeId,
@RequestParam(required = true) String ipAddress, @RequestParam(required = true) String port,
return new Status(StatusCode.SUCCESS);
}
- @RequestMapping(value = "/disconnect/{nodeId}", method = RequestMethod.POST)
+ @RequestMapping(value = "/disconnect/{nodeId:.+}", method = RequestMethod.POST)
@ResponseBody
public Status removeNode(HttpServletRequest request, @PathVariable("nodeId") String nodeId,
@RequestParam(required = true) String nodeType) {
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.ActionType;
+import org.opendaylight.controller.sal.action.SupportedFlowActions;
import org.opendaylight.controller.sal.authorization.Privilege;
import org.opendaylight.controller.sal.authorization.UserLevel;
import org.opendaylight.controller.sal.core.Description;
return nodes;
}
-
@RequestMapping(value = "/flow", method = RequestMethod.POST)
@ResponseBody
public String actionFlow(@RequestParam(required = true) String action, @RequestParam(required = false) String body,
}
}
+ @RequestMapping(value = "/valid-flows/{nodeId}")
+ @ResponseBody
+ public Object getValidActions(HttpServletRequest request, @RequestParam(required = false) String container,
+ @PathVariable("nodeId") String nodeId) {
+ String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container;
+
+ // Authorization check
+ String userName = request.getUserPrincipal().getName();
+ if (DaylightWebUtil.getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) {
+ return "Operation not authorized";
+ }
+
+ ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this);
+ if (switchManager == null) {
+ return null;
+ }
+
+ Map<String, String> result = new TreeMap<String, String>();
+
+ Node node = Node.fromString(nodeId);
+ SupportedFlowActions supportedFlows = (SupportedFlowActions) switchManager.getNodeProp(node, "supportedFlowActions");
+ List<Class<? extends Action>> actions = supportedFlows.getActions();
+ for (Class<? extends Action> action : actions) {
+ if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Drop.class)) {
+ result.put(ActionType.DROP.toString(), "Drop");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Loopback.class)) {
+ result.put(ActionType.LOOPBACK.toString(), "Loopback");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Flood.class)) {
+ result.put(ActionType.FLOOD.toString(), "Flood");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.FloodAll.class)) {
+ result.put(ActionType.FLOOD_ALL.toString(), "Flood All");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Controller.class)) {
+ result.put(ActionType.CONTROLLER.toString(), "Controller");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SwPath.class)) {
+ result.put(ActionType.SW_PATH.toString(), "Software Path");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.HwPath.class)) {
+ result.put(ActionType.HW_PATH.toString(), "Hardware Path");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Output.class)) {
+ result.put(ActionType.OUTPUT.toString(), "Output");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Enqueue.class)) {
+ result.put(ActionType.ENQUEUE.toString(), "Enqueue");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlSrc.class)) {
+ result.put(ActionType.SET_DL_SRC.toString(), "Set Datalayer Source Address");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlDst.class)) {
+ result.put(ActionType.SET_DL_DST.toString(), "Set Datalayer Destination Address");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanId.class)) {
+ result.put(ActionType.SET_VLAN_ID.toString(), "Set VLAN ID");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanPcp.class)) {
+ result.put(ActionType.SET_VLAN_PCP.toString(), "Set VLAN Priority");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanCfi.class)) {
+ result.put(ActionType.SET_VLAN_CFI.toString(), "Set VLAN CFI");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.PopVlan.class)) {
+ result.put(ActionType.POP_VLAN.toString(), "Pop VLAN");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.PushVlan.class)) {
+ result.put(ActionType.PUSH_VLAN.toString(), "Push VLAN");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlType.class)) {
+ result.put(ActionType.SET_DL_TYPE.toString(), "Set EtherType");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwSrc.class)) {
+ result.put(ActionType.SET_NW_SRC.toString(), "Set Network Source Address");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwDst.class)) {
+ result.put(ActionType.SET_NW_DST.toString(), "Set Network Destination Address");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwTos.class)) {
+ result.put(ActionType.SET_NW_TOS.toString(), "Modify ToS Bits");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetTpSrc.class)) {
+ result.put(ActionType.SET_TP_SRC.toString(), "Modify Transport Source Port");
+ } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetTpDst.class)) {
+ result.put(ActionType.SET_TP_DST.toString(), "Modify Transport Destination Port");
+ }
+ }
+
+ return result;
+ }
+
+ private boolean actionCompare(String name, ActionType type) {
+ return name.equals(type.getId().toLowerCase());
+ }
+
private String getNodeDesc(Node node, ISwitchManager switchManager) {
Description desc = (Description) switchManager.getNodeProp(node, Description.propertyName);
String description = (desc == null) ? "" : desc.getValue();
// specify dashlets and layouts
one.f.dashlet = {
- flows : {
- id : 'flows',
- name : 'Flow Entries'
- },
- nodes : {
- id : 'nodes',
- name : 'Nodes'
- },
- detail : {
- id : 'detail',
- name : 'Flow Detail'
- }
+ flows : {
+ id : 'flows',
+ name : 'Flow Entries'
+ },
+ nodes : {
+ id : 'nodes',
+ name : 'Nodes'
+ },
+ detail : {
+ id : 'detail',
+ name : 'Flow Detail'
+ }
};
one.f.menu = {
- left : {
- top : [
- one.f.dashlet.flows
- ],
- bottom : [
- one.f.dashlet.nodes
- ]
- },
- right : {
- top : [],
- bottom : [
- one.f.dashlet.detail
- ]
- }
+ left : {
+ top : [
+ one.f.dashlet.flows
+ ],
+ bottom : [
+ one.f.dashlet.nodes
+ ]
+ },
+ right : {
+ top : [],
+ bottom : [
+ one.f.dashlet.detail
+ ]
+ }
};
one.f.address = {
- root : "/controller/web/flows",
- flows : {
- main : "/main",
- flows : "/node-flows",
- nodes : "/node-ports",
- flow : "/flow",
- modifyFlow : "/modifyFlow",
- deleteFlows:"/flow/deleteFlows"
- }
+ root : "/controller/web/flows",
+ flows : {
+ main : "/main",
+ flows : "/node-flows",
+ nodes : "/node-ports",
+ flow : "/flow",
+ modifyFlow : "/modifyFlow",
+ deleteFlows:"/flow/deleteFlows"
+ }
}
/** NODES **/
one.f.nodes = {
- id : {
- dashlet: {
- datagrid: "one_f_nodes_id_dashlet_datagrid"
- }
- },
- registry : {},
- dashlet : function($dashlet) {
- var $h4 = one.lib.dashlet.header("Nodes");
- $dashlet.append($h4);
-
- one.f.nodes.ajax.dashlet(function(data) {
- var $gridHTML = one.lib.dashlet.datagrid.init(one.f.nodes.id.dashlet.datagrid, {
- searchable: true,
- filterable: false,
- pagination: true,
- flexibleRowsPerPage: true
- }, "table-striped table-condensed");
- $dashlet.append($gridHTML);
- var dataSource = one.f.nodes.data.nodesDataGrid(data);
- $("#" + one.f.nodes.id.dashlet.datagrid).datagrid({dataSource: dataSource});
- });
- },
- ajax : {
- dashlet : function(callback) {
- $.getJSON(one.f.address.root+one.f.address.flows.flows, function(data) {
- callback(data);
- });
- }
- },
- data : {
- nodesDataGrid: function(data) {
- var gridData = [];
- $.each(data, function(nodeName, flow) {
- var nodeFlowObject = {};
- nodeFlowObject["nodeName"] = nodeName;
- nodeFlowObject["flows"] = flow;
- nodeFlowObject["rowData"] = nodeName + flow + "-foobar";
- gridData.push(nodeFlowObject);
- });
+ id : {
+ dashlet: {
+ datagrid: "one_f_nodes_id_dashlet_datagrid"
+ }
+ },
+ registry : {},
+ dashlet : function($dashlet) {
+ var $h4 = one.lib.dashlet.header("Nodes");
+ $dashlet.append($h4);
- var source = new StaticDataSource({
- columns: [
- {
- property: 'nodeName',
- label: 'Node',
- sortable: true
- },
- {
- property: 'flows',
- label: 'Flows',
- sortable: true
- }
- ],
- data: gridData,
- delay: 0
- });
- return source;
- }
- },
- body : {
- dashlet : function(body, callback) {
- var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed'];
- var $table = one.lib.dashlet.table.table(attributes);
+ one.f.nodes.ajax.dashlet(function(data) {
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.nodes.id.dashlet.datagrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, "table-striped table-condensed");
+ $dashlet.append($gridHTML);
+ var dataSource = one.f.nodes.data.nodesDataGrid(data);
+ $("#" + one.f.nodes.id.dashlet.datagrid).datagrid({dataSource: dataSource});
+ });
+ },
+ ajax : {
+ dashlet : function(callback) {
+ $.getJSON(one.f.address.root+one.f.address.flows.flows, function(data) {
+ callback(data);
+ });
+ }
+ },
+ data : {
+ nodesDataGrid: function(data) {
+ var gridData = [];
+ $.each(data, function(nodeName, flow) {
+ var nodeFlowObject = {};
+ nodeFlowObject["nodeName"] = nodeName;
+ nodeFlowObject["flows"] = flow;
+ nodeFlowObject["rowData"] = nodeName + flow + "-foobar";
+ gridData.push(nodeFlowObject);
+ });
+
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'nodeName',
+ label: 'Node',
+ sortable: true
+ },
+ {
+ property: 'flows',
+ label: 'Flows',
+ sortable: true
+ }
+ ],
+ data: gridData,
+ delay: 0
+ });
+ return source;
+ }
+ },
+ body : {
+ dashlet : function(body, callback) {
+ var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed'];
+ var $table = one.lib.dashlet.table.table(attributes);
- var headers = ['Node', 'Flows'];
- var $thead = one.lib.dashlet.table.header(headers);
- $table.append($thead);
+ var headers = ['Node', 'Flows'];
+ var $thead = one.lib.dashlet.table.header(headers);
+ $table.append($thead);
- var $tbody = one.lib.dashlet.table.body(body);
- $table.append($tbody);
+ var $tbody = one.lib.dashlet.table.body(body);
+ $table.append($tbody);
- return $table;
- }
+ return $table;
}
+ }
}
/** FLOW DETAIL **/
one.f.detail = {
- id : {},
- registry : {},
- dashlet : function($dashlet, details) {
- var $h4 = one.lib.dashlet.header("Flow Details");
- $dashlet.append($h4);
-
- // details
- if (details == undefined) {
- var $none = $(document.createElement('div'));
- $none.addClass('none');
- var $p = $(document.createElement('p'));
- $p.text('Please select a flow');
- $p.addClass('text-center').addClass('text-info');
+ id : {},
+ registry : {},
+ dashlet : function($dashlet, details) {
+ var $h4 = one.lib.dashlet.header("Flow Details");
+ $dashlet.append($h4);
- $dashlet.append($none)
- .append($p);
- }
- },
- data : {
- dashlet : function(data) {
- var body = [];
- var tr = {};
- var entry = [];
-
- entry.push(data['name']);
- entry.push(data['node']);
- entry.push(data['flow']['priority']);
- entry.push(data['flow']['hardTimeout']);
- entry.push(data['flow']['idleTimeout']);
-
- tr.entry = entry;
- body.push(tr);
- return body;
- },
- description : function(data) {
- var body = [];
- var tr = {};
- var entry = [];
- entry.push(data['flow']['ingressPort']);
- entry.push(data['flow']['etherType']);
- entry.push(data['flow']['vlanId']);
- entry.push(data['flow']['vlanPriority']);
- entry.push(data['flow']['srcMac']);
- entry.push(data['flow']['dstMac']);
- entry.push(data['flow']['srcIp']);
- entry.push(data['flow']['dstIp']);
- entry.push(data['flow']['tosBits']);
- entry.push(data['flow']['srcPort']);
- entry.push(data['flow']['dstPort']);
- entry.push(data['flow']['protocol']);
- entry.push(data['flow']['cookie']);
-
- tr.entry = entry;
- body.push(tr);
- return body;
- },
- actions : function(data) {
- var body = [];
- var tr = {};
- var entry = [];
- var actions = '';
-
- $(data['flow']['actions']).each(function(index, value) {
- var locEqualTo = value.indexOf("=");
- if ( locEqualTo == -1 ) {
- actions += value + ', ';
- } else {
- var action = value.substr(0,locEqualTo);
- if( action == "OUTPUT") {
- var portIds = value.substr(locEqualTo+1).split(",");
- actions += action + "=";
- var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.selectedNode]['ports'];
- for(var i =0; i < portIds.length ; i++) {
- var portName = allPorts[portIds[i]];
- actions += portName + ", ";
- }
- } else {
- actions += value + ', ';
- }
- }
- });
- actions = actions.slice(0,-2);
- entry.push(actions);
+ // details
+ if (details == undefined) {
+ var $none = $(document.createElement('div'));
+ $none.addClass('none');
+ var $p = $(document.createElement('p'));
+ $p.text('Please select a flow');
+ $p.addClass('text-center').addClass('text-info');
- tr.entry = entry;
- body.push(tr);
- return body;
+ $dashlet.append($none)
+ .append($p);
+ }
+ },
+ data : {
+ dashlet : function(data) {
+ var body = [];
+ var tr = {};
+ var entry = [];
+
+ entry.push(data['name']);
+ entry.push(data['node']);
+ entry.push(data['flow']['priority']);
+ entry.push(data['flow']['hardTimeout']);
+ entry.push(data['flow']['idleTimeout']);
+
+ tr.entry = entry;
+ body.push(tr);
+ return body;
+ },
+ description : function(data) {
+ var body = [];
+ var tr = {};
+ var entry = [];
+ entry.push(data['flow']['ingressPort']);
+ entry.push(data['flow']['etherType']);
+ entry.push(data['flow']['vlanId']);
+ entry.push(data['flow']['vlanPriority']);
+ entry.push(data['flow']['srcMac']);
+ entry.push(data['flow']['dstMac']);
+ entry.push(data['flow']['srcIp']);
+ entry.push(data['flow']['dstIp']);
+ entry.push(data['flow']['tosBits']);
+ entry.push(data['flow']['srcPort']);
+ entry.push(data['flow']['dstPort']);
+ entry.push(data['flow']['protocol']);
+ entry.push(data['flow']['cookie']);
+
+ tr.entry = entry;
+ body.push(tr);
+ return body;
+ },
+ actions : function(data) {
+ var body = [];
+ var tr = {};
+ var entry = [];
+ var actions = '';
+
+ $(data['flow']['actions']).each(function(index, value) {
+ var locEqualTo = value.indexOf("=");
+ if ( locEqualTo == -1 ) {
+ actions += value + ', ';
+ } else {
+ var action = value.substr(0,locEqualTo);
+ if( action == "OUTPUT") {
+ var portIds = value.substr(locEqualTo+1).split(",");
+ actions += action + "=";
+ var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.selectedNode]['ports'];
+ for(var i =0; i < portIds.length ; i++) {
+ var portName = allPorts[portIds[i]];
+ actions += portName + ", ";
+ }
+ } else {
+ actions += value + ', ';
+ }
}
+ });
+ actions = actions.slice(0,-2);
+ entry.push(actions);
+
+ tr.entry = entry;
+ body.push(tr);
+ return body;
+ }
+ },
+ body : {
+ dashlet : function(body) {
+ // create table
+ var header = ['Flow Name', 'Node', 'Priority', 'Hard Timeout', 'Idle Timeout'];
+ var $thead = one.lib.dashlet.table.header(header);
+ var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
+ var $table = one.lib.dashlet.table.table(attributes);
+ $table.append($thead);
+
+ var $tbody = one.lib.dashlet.table.body(body);
+ $table.append($tbody);
+
+ return $table;
},
- body : {
- dashlet : function(body) {
- // create table
- var header = ['Flow Name', 'Node', 'Priority', 'Hard Timeout', 'Idle Timeout'];
- var $thead = one.lib.dashlet.table.header(header);
- var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
- var $table = one.lib.dashlet.table.table(attributes);
- $table.append($thead);
-
- var $tbody = one.lib.dashlet.table.body(body);
- $table.append($tbody);
-
- return $table;
- },
- description : function(body) {
- var header = ['Input Port', 'Ethernet Type', 'VLAN ID', 'VLAN Priority', 'Source MAC', 'Dest MAC', 'Source IP', 'Dest IP', 'ToS', 'Source Port', 'Dest Port', 'Protocol', 'Cookie'];
- var $thead = one.lib.dashlet.table.header(header);
- var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
- var $table = one.lib.dashlet.table.table(attributes);
- $table.append($thead);
+ description : function(body) {
+ var header = ['Input Port', 'Ethernet Type', 'VLAN ID', 'VLAN Priority', 'Source MAC', 'Dest MAC', 'Source IP', 'Dest IP', 'ToS', 'Source Port', 'Dest Port', 'Protocol', 'Cookie'];
+ var $thead = one.lib.dashlet.table.header(header);
+ var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
+ var $table = one.lib.dashlet.table.table(attributes);
+ $table.append($thead);
- var $tbody = one.lib.dashlet.table.body(body);
- $table.append($tbody);
+ var $tbody = one.lib.dashlet.table.body(body);
+ $table.append($tbody);
- return $table;
- },
- actions : function(body) {
- var header = ['Actions'];
- var $thead = one.lib.dashlet.table.header(header);
- var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
- var $table = one.lib.dashlet.table.table(attributes);
- $table.append($thead);
+ return $table;
+ },
+ actions : function(body) {
+ var header = ['Actions'];
+ var $thead = one.lib.dashlet.table.header(header);
+ var attributes = ['table-striped', 'table-bordered', 'table-condensed'];
+ var $table = one.lib.dashlet.table.table(attributes);
+ $table.append($thead);
- var $tbody = one.lib.dashlet.table.body(body);
- $table.append($tbody);
+ var $tbody = one.lib.dashlet.table.body(body);
+ $table.append($tbody);
- return $table;
- }
+ return $table;
}
+ }
}
/** FLOW ENTRIES **/
one.f.flows = {
- id : {
- dashlet : {
- add : "one_f_flows_id_dashlet_add",
- removeMultiple : "one_f_flows_id_dashlet_removeMultiple",
- remove : "one_f_flows_id_dashlet_remove",
- toggle : "one_f_flows_id_dashlet_toggle",
- edit : "one_f_flows_id_dashlet_edit",
- datagrid : "one_f_flows_id_dashlet_datagrid",
- selectAllFlows : "one_f_flows_id_dashlet_selectAllFlows"
- },
+ id : {
+ dashlet : {
+ add : "one_f_flows_id_dashlet_add",
+ removeMultiple : "one_f_flows_id_dashlet_removeMultiple",
+ remove : "one_f_flows_id_dashlet_remove",
+ toggle : "one_f_flows_id_dashlet_toggle",
+ edit : "one_f_flows_id_dashlet_edit",
+ datagrid : "one_f_flows_id_dashlet_datagrid",
+ selectAllFlows : "one_f_flows_id_dashlet_selectAllFlows"
+ },
+ modal : {
+ install : "one_f_flows_id_modal_install",
+ edit : "one_f_flows_id_modal_edit",
+ add : "one_f_flows_id_modal_add",
+ close : "one_f_flows_id_modal_close",
+ modal : "one_f_flows_id_modal_modal",
+ dialog : {
+ modal : "one_f_flows_id_modal_dialog_modal",
+ remove : "one_f_flows_id_modal_dialog_remove",
+ close : "one_f_flows_id_modal_dialog_close"
+ },
+ action : {
+ button : "one_f_flows_id_modal_action_button",
+ modal : "one_f_flows_id_modal_action_modal",
+ add : "one_f_flows_id_modal_action_add",
+ close : "one_f_flows_id_modal_action_close",
+ table : "one_f_flows_id_modal_action_table",
+ addOutputPorts : "one_f_flows_id_modal_action_addOutputPorts",
+ setVlanId : "one_f_flows_id_modal_action_setVlanId",
+ setVlanPriority : "one_f_flows_id_modal_action_setVlanPriority",
+ modifyDatalayerSourceAddress : "one_f_flows_id_modal_action_modifyDatalayerSourceAddress",
+ modifyDatalayerDestinationAddress : "one_f_flows_id_modal_action_modifyDatalayerDestinationAddress",
+ modifyNetworkSourceAddress : "one_f_flows_modal_action_modifyNetworkSourceAddress",
+ modifyNetworkDestinationAddress : "one_f_flows_modal_action_modifyNetworkDestinationAddress",
+ modifyTosBits : "one_f_flows_modal_action_modifyTosBits",
+ modifyTransportSourcePort : "one_f_flows_modal_action_modifyTransportSourcePort",
+ modifyTransportDestinationPort : "one_f_flows_modal_action_modifyTransportDestinationPort",
+ enqueue : 'one-f-flows-modal-action-enqueue',
+ queue : 'one-f-flows-modal-action-queue',
+ setEthertype : 'one-f-flows-modal-action-setEthertype',
+ pushVlan : 'one-f-flows-modal-action-pushVlan',
+ setVlanCfi : 'one-f-flows-modal-action-setVlanCfi',
modal : {
- install : "one_f_flows_id_modal_install",
- edit : "one_f_flows_id_modal_edit",
- add : "one_f_flows_id_modal_add",
- close : "one_f_flows_id_modal_close",
- modal : "one_f_flows_id_modal_modal",
- dialog : {
- modal : "one_f_flows_id_modal_dialog_modal",
- remove : "one_f_flows_id_modal_dialog_remove",
- close : "one_f_flows_id_modal_dialog_close"
- },
- action : {
- button : "one_f_flows_id_modal_action_button",
- modal : "one_f_flows_id_modal_action_modal",
- add : "one_f_flows_id_modal_action_add",
- close : "one_f_flows_id_modal_action_close",
- table : "one_f_flows_id_modal_action_table",
- addOutputPorts : "one_f_flows_id_modal_action_addOutputPorts",
- setVlanId : "one_f_flows_id_modal_action_setVlanId",
- setVlanPriority : "one_f_flows_id_modal_action_setVlanPriority",
- modifyDatalayerSourceAddress : "one_f_flows_id_modal_action_modifyDatalayerSourceAddress",
- modifyDatalayerDestinationAddress : "one_f_flows_id_modal_action_modifyDatalayerDestinationAddress",
- modifyNetworkSourceAddress : "one_f_flows_modal_action_modifyNetworkSourceAddress",
- modifyNetworkDestinationAddress : "one_f_flows_modal_action_modifyNetworkDestinationAddress",
- modifyTosBits : "one_f_flows_modal_action_modifyTosBits",
- modifyTransportSourcePort : "one_f_flows_modal_action_modifyTransportSourcePort",
- modifyTransportDestinationPort : "one_f_flows_modal_action_modifyTransportDestinationPort",
- modal : {
- modal : "one_f_flows_modal_action_modal_modal",
- remove : "one_f_flows_modal_action_modal_remove",
- cancel : "one_f_flows_modal_action_modal_cancel"
- }
- },
- form : {
- name : "one_f_flows_id_modal_form_name",
- nodes : "one_f_flows_id_modal_form_nodes",
- port : "one_f_flows_id_modal_form_port",
- priority : "one_f_flows_id_modal_form_priority",
- hardTimeout : "one_f_flows_id_modal_form_hardTimeout",
- idleTimeout : "one_f_flows_id_modal_form_idleTimeout",
- cookie : "one_f_flows_id_modal_form_cookie",
- etherType : "one_f_flows_id_modal_form_etherType",
- vlanId : "one_f_flows_id_modal_form_vlanId",
- vlanPriority : "one_f_flows_id_modal_form_vlanPriority",
- srcMac : "one_f_flows_id_modal_form_srcMac",
- dstMac : "one_f_flows_id_modal_form_dstMac",
- srcIp : "one_f_flows_id_modal_form_srcIp",
- dstIp : "one_f_flows_id_modal_form_dstIp",
- tosBits : "one_f_flows_id_modal_form_tosBits",
- srcPort : "one_f_flows_id_modal_form_srcPort",
- dstPort : "one_f_flows_id_modal_form_dstPort",
- protocol : "one_f_flows_id_modal_form_protocol"
- }
+ modal : "one_f_flows_modal_action_modal_modal",
+ remove : "one_f_flows_modal_action_modal_remove",
+ cancel : "one_f_flows_modal_action_modal_cancel"
}
- },
- registry : {},
- dashlet : function($dashlet, callback) {
-
- // load body
- one.f.flows.ajax.dashlet(function(data) {
-
- var $h4 = one.lib.dashlet.header("Flow Entries");
-
- $dashlet.append($h4);
- if (one.f.flows.registry.privilege === 'WRITE') {
- var button = one.lib.dashlet.button.single("Add Flow Entry", one.f.flows.id.dashlet.add, "btn-primary", "btn-mini");
- var $button = one.lib.dashlet.button.button(button);
-
- $button.click(function() {
- var $modal = one.f.flows.modal.initialize();
- $modal.modal();
- });
- $dashlet.append($button);
- var button = one.lib.dashlet.button.single("Remove Flow Entry", one.f.flows.id.dashlet.removeMultiple, "btn-danger", "btn-mini");
- var $button = one.lib.dashlet.button.button(button);
-
- $button.click(function() {
- var checkedCheckBoxes = $('.flowEntry[type=checkbox]:checked');
- if (checkedCheckBoxes.size() === 0) {
- return false;
- }
-
- var requestData = [];
-
- checkedCheckBoxes.each(function(index, value) {
- var flowEntry = {};
- flowEntry['name'] = checkedCheckBoxes[index].name;
- flowEntry['node'] = checkedCheckBoxes[index].getAttribute("node");
- requestData.push(flowEntry);
- });
- one.f.flows.modal.removeMultiple.dialog(requestData);
- });
- $dashlet.append($button);
+ },
+ form : {
+ name : "one_f_flows_id_modal_form_name",
+ nodes : "one_f_flows_id_modal_form_nodes",
+ port : "one_f_flows_id_modal_form_port",
+ priority : "one_f_flows_id_modal_form_priority",
+ hardTimeout : "one_f_flows_id_modal_form_hardTimeout",
+ idleTimeout : "one_f_flows_id_modal_form_idleTimeout",
+ cookie : "one_f_flows_id_modal_form_cookie",
+ etherType : "one_f_flows_id_modal_form_etherType",
+ vlanId : "one_f_flows_id_modal_form_vlanId",
+ vlanPriority : "one_f_flows_id_modal_form_vlanPriority",
+ srcMac : "one_f_flows_id_modal_form_srcMac",
+ dstMac : "one_f_flows_id_modal_form_dstMac",
+ srcIp : "one_f_flows_id_modal_form_srcIp",
+ dstIp : "one_f_flows_id_modal_form_dstIp",
+ tosBits : "one_f_flows_id_modal_form_tosBits",
+ srcPort : "one_f_flows_id_modal_form_srcPort",
+ dstPort : "one_f_flows_id_modal_form_dstPort",
+ protocol : "one_f_flows_id_modal_form_protocol",
+ action : 'one-f-flows-id-modal-form-action'
+ }
+ }
+ },
+ registry : {},
+ dashlet : function($dashlet, callback) {
+ // load body
+ one.f.flows.ajax.dashlet(function(data) {
+ var $h4 = one.lib.dashlet.header("Flow Entries");
+ $dashlet.append($h4);
+ if (one.f.flows.registry.privilege === 'WRITE') {
+ var button = one.lib.dashlet.button.single("Add Flow Entry", one.f.flows.id.dashlet.add, "btn-primary", "btn-mini");
+ var $button = one.lib.dashlet.button.button(button);
+
+ $button.click(function() {
+ var $modal = one.f.flows.modal.initialize();
+ $modal.modal();
+ });
+ $dashlet.append($button);
+ var button = one.lib.dashlet.button.single("Remove Flow Entry", one.f.flows.id.dashlet.removeMultiple, "btn-danger", "btn-mini");
+ var $button = one.lib.dashlet.button.button(button);
+
+ $button.click(function() {
+ var checkedCheckBoxes = $('.flowEntry[type=checkbox]:checked');
+ if (checkedCheckBoxes.size() === 0) {
+ return false;
+ }
- }
+ var requestData = [];
- var $gridHTML = one.lib.dashlet.datagrid.init(one.f.flows.id.dashlet.datagrid, {
- searchable: true,
- filterable: false,
- pagination: true,
- flexibleRowsPerPage: true
- }, "table-striped table-condensed");
- $dashlet.append($gridHTML);
- var dataSource = one.f.flows.data.flowsDataGrid(data);
- $("#" + one.f.flows.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
- $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).click(function() {
- $("#" + one.f.flows.id.dashlet.datagrid).find(':checkbox').prop('checked',
- $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).is(':checked'));
- });
-
- $("#" + one.f.flows.id.dashlet.datagrid).find("tbody tr").each(function(index, tr) {
- $tr = $(tr);
- $span = $("td span", $tr);
- var flowstatus = $span.data("flowstatus");
- if($span.data("installinhw") != null) {
- var installInHw = $span.data("installinhw").toString();
- if(installInHw == "true" && flowstatus == "Success") {
- $tr.addClass("success");
- } else {
- $tr.addClass("warning");
- }
- }
- // attach mouseover to show pointer cursor
- $tr.mouseover(function() {
- $(this).css("cursor", "pointer");
- });
- // attach click event
- $tr.click(function() {
- var $td = $($(this).find("td")[1]);
- var id = $td.text();
- var node = $td.find("span").data("nodeid");
- one.f.flows.detail(id, node);
- });
- $(".flowEntry").click(function(e){
- if (!$('.flowEntry[type=checkbox]:not(:checked)').length) {
- $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
- .prop("checked",
- true);
- } else {
- $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
- .prop("checked",
- false);
- }
- e.stopPropagation();
- });
- });
- });
-
- // details callback
- if(callback != undefined) callback();
+ checkedCheckBoxes.each(function(index, value) {
+ var flowEntry = {};
+ flowEntry['name'] = checkedCheckBoxes[index].name;
+ flowEntry['node'] = checkedCheckBoxes[index].getAttribute("node");
+ requestData.push(flowEntry);
+ });
+ one.f.flows.modal.removeMultiple.dialog(requestData);
});
- },
- detail : function(id, node) {
- // clear flow details
- var $detailDashlet = one.main.dashlet.right.bottom;
- $detailDashlet.empty();
- var $h4 = one.lib.dashlet.header("Flow Overview");
- $detailDashlet.append($h4);
-
- // details
- var flows = one.f.flows.registry.flows;
- one.f.flows.registry['selectedId'] = id;
- one.f.flows.registry['selectedNode'] = node;
- var flow;
- $(flows).each(function(index, value) {
- if (value.name == id && value.nodeId == node) {
- flow = value;
- }
+ $dashlet.append($button);
+
+ }
+ var $gridHTML = one.lib.dashlet.datagrid.init(one.f.flows.id.dashlet.datagrid, {
+ searchable: true,
+ filterable: false,
+ pagination: true,
+ flexibleRowsPerPage: true
+ }, "table-striped table-condensed");
+ $dashlet.append($gridHTML);
+ var dataSource = one.f.flows.data.flowsDataGrid(data);
+ $("#" + one.f.flows.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() {
+ $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).click(function() {
+ $("#" + one.f.flows.id.dashlet.datagrid).find(':checkbox').prop('checked',
+ $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).is(':checked'));
});
- if (one.f.flows.registry.privilege === 'WRITE') {
- // remove button
- var button = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.dashlet.remove, "btn-danger", "btn-mini");
- var $button = one.lib.dashlet.button.button(button);
- $button.click(function() {
- var $modal = one.f.flows.modal.dialog.initialize(id, node);
- $modal.modal();
- });
- // edit button
- var editButton = one.lib.dashlet.button.single("Edit Flow", one.f.flows.id.dashlet.edit, "btn-primary", "btn-mini");
- var $editButton = one.lib.dashlet.button.button(editButton);
- $editButton.click(function() {
- var install = flow['flow']['installInHw'];
- var $modal = one.f.flows.modal.initialize(true,install);
- $modal.modal().on('shown',function(){
- var $port = $('#'+one.f.flows.id.modal.form.port);
- $('#'+one.f.flows.id.modal.form.nodes).trigger("change");
- });
- });
- // toggle button
- var toggle;
- if (flow['flow']['installInHw'] == 'true' && flow['flow']['status'] == 'Success') {
- toggle = one.lib.dashlet.button.single("Uninstall Flow", one.f.flows.id.dashlet.toggle, "btn-warning", "btn-mini");
- } else {
- toggle = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.dashlet.toggle, "btn-success", "btn-mini");
- }
- var $toggle = one.lib.dashlet.button.button(toggle);
- $toggle.click(function() {
- one.f.flows.modal.ajax.toggleflow(id, node, function(data) {
- if(data == "Success") {
- one.main.dashlet.right.bottom.empty();
- one.f.detail.dashlet(one.main.dashlet.right.bottom);
- one.main.dashlet.left.top.empty();
- one.f.flows.dashlet(one.main.dashlet.left.top, function() {
- // checks are backwards due to stale registry
- if(flow['flow']['installInHw'] == 'true') {
- one.lib.alert('Uninstalled Flow');
- } else {
- one.lib.alert('Installed Flow');
- }
- one.f.flows.detail(id, node)
- });
- } else {
- one.lib.alert('Cannot toggle flow: '+data);
- }
- });
- });
- $detailDashlet.append($button).append($editButton).append($toggle);
- }
- // append details
- var body = one.f.detail.data.dashlet(flow);
- var $body = one.f.detail.body.dashlet(body);
- $detailDashlet.append($body);
- var body = one.f.detail.data.description(flow);
- var $body = one.f.detail.body.description(body);
- $detailDashlet.append($body);
- var body = one.f.detail.data.actions(flow);
- var $body = one.f.detail.body.actions(body);
- $detailDashlet.append($body);
- },
- modal : {
- dialog : {
- initialize : function(id, node) {
- var h3 = "Remove Flow";
- var $p = one.f.flows.modal.dialog.body(id);
- var footer = one.f.flows.modal.dialog.footer();
- var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $p, footer);
- $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
- $modal.modal('hide');
- });
- $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(function() {
- one.f.flows.modal.ajax.removeflow(id, node, function(data) {
- if (data == "Success") {
- $modal.modal('hide');
- one.main.dashlet.right.bottom.empty();
- one.f.detail.dashlet(one.main.dashlet.right.bottom);
- one.main.dashlet.left.top.empty();
- one.f.flows.dashlet(one.main.dashlet.left.top);
- one.lib.alert('Flow removed');
- } else {
- one.lib.alert('Cannot remove flow: '+data);
- }
- });
- });
- return $modal;
- },
- footer : function() {
- var footer = [];
-
- var removeButton = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.modal.dialog.remove, "btn-danger", "");
- var $removeButton = one.lib.dashlet.button.button(removeButton);
- footer.push($removeButton);
-
- var closeButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.dialog.close, "", "");
- var $closeButton = one.lib.dashlet.button.button(closeButton);
- footer.push($closeButton);
-
- return footer;
- },
- body : function(id) {
- var $p = $(document.createElement('p'));
- $p.append('Remove flow '+id+'?');
- return $p;
+ $("#" + one.f.flows.id.dashlet.datagrid).find("tbody tr").each(function(index, tr) {
+ $tr = $(tr);
+ $span = $("td span", $tr);
+ var flowstatus = $span.data("flowstatus");
+ if($span.data("installinhw") != null) {
+ var installInHw = $span.data("installinhw").toString();
+ if(installInHw == "true" && flowstatus == "Success") {
+ $tr.addClass("success");
+ } else {
+ $tr.addClass("warning");
}
- },
- initialize : function(edit,install) {
- var h3;
- if(edit) {
- h3 = "Edit Flow Entry";
- var footer = one.f.flows.modal.footerEdit();
-
+ }
+ // attach mouseover to show pointer cursor
+ $tr.mouseover(function() {
+ $(this).css("cursor", "pointer");
+ });
+ // attach click event
+ $tr.click(function() {
+ var $td = $($(this).find("td")[1]);
+ var id = $td.text();
+ var node = $td.find("span").data("nodeid");
+ one.f.flows.detail(id, node);
+ });
+ $(".flowEntry").click(function(e){
+ if (!$('.flowEntry[type=checkbox]:not(:checked)').length) {
+ $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
+ .prop("checked",
+ true);
} else {
- h3 = "Add Flow Entry";
- var footer = one.f.flows.modal.footer();
+ $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows)
+ .prop("checked",
+ false);
}
-
- var $modal = one.lib.modal.spawn(one.f.flows.id.modal.modal, h3, "", footer);
-
- // bind close button
- $('#'+one.f.flows.id.modal.close, $modal).click(function() {
- $modal.modal('hide');
+ e.stopPropagation();
+ });
+ });
+ });
+
+ // details callback
+ if(callback != undefined) callback();
+ });
+ },
+ detail : function(id, node) {
+ // clear flow details
+ var $detailDashlet = one.main.dashlet.right.bottom;
+ $detailDashlet.empty();
+ var $h4 = one.lib.dashlet.header("Flow Overview");
+ $detailDashlet.append($h4);
+
+ // details
+ var flows = one.f.flows.registry.flows;
+ one.f.flows.registry['selectedId'] = id;
+ one.f.flows.registry['selectedNode'] = node;
+ var flow;
+ $(flows).each(function(index, value) {
+ if (value.name == id && value.nodeId == node) {
+ flow = value;
+ }
+ });
+ if (one.f.flows.registry.privilege === 'WRITE') {
+ // remove button
+ var button = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.dashlet.remove, "btn-danger", "btn-mini");
+ var $button = one.lib.dashlet.button.button(button);
+ $button.click(function() {
+ var $modal = one.f.flows.modal.dialog.initialize(id, node);
+ $modal.modal();
+ });
+ // edit button
+ var editButton = one.lib.dashlet.button.single("Edit Flow", one.f.flows.id.dashlet.edit, "btn-primary", "btn-mini");
+ var $editButton = one.lib.dashlet.button.button(editButton);
+ $editButton.click(function() {
+ var install = flow['flow']['installInHw'];
+ var $modal = one.f.flows.modal.initialize(true,install);
+ $modal.modal().on('shown',function(){
+ var $port = $('#'+one.f.flows.id.modal.form.port);
+ $('#'+one.f.flows.id.modal.form.nodes).trigger("change");
+ });
+ });
+ // toggle button
+ var toggle;
+ if (flow['flow']['installInHw'] == 'true' && flow['flow']['status'] == 'Success') {
+ toggle = one.lib.dashlet.button.single("Uninstall Flow", one.f.flows.id.dashlet.toggle, "btn-warning", "btn-mini");
+ } else {
+ toggle = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.dashlet.toggle, "btn-success", "btn-mini");
+ }
+ var $toggle = one.lib.dashlet.button.button(toggle);
+ $toggle.click(function() {
+ one.f.flows.modal.ajax.toggleflow(id, node, function(data) {
+ if(data == "Success") {
+ one.main.dashlet.right.bottom.empty();
+ one.f.detail.dashlet(one.main.dashlet.right.bottom);
+ one.main.dashlet.left.top.empty();
+ one.f.flows.dashlet(one.main.dashlet.left.top, function() {
+ // checks are backwards due to stale registry
+ if(flow['flow']['installInHw'] == 'true') {
+ one.lib.alert('Uninstalled Flow');
+ } else {
+ one.lib.alert('Installed Flow');
+ }
+ one.f.flows.detail(id, node)
});
+ } else {
+ one.lib.alert('Cannot toggle flow: '+data);
+ }
+ });
+ });
- if (edit) {
- // bind edit flow button
- $('#'+one.f.flows.id.modal.edit, $modal).click(function() {
- one.f.flows.modal.save($modal, install, true);
- });
+ $detailDashlet.append($button).append($editButton).append($toggle);
+ }
+ // append details
+ var body = one.f.detail.data.dashlet(flow);
+ var $body = one.f.detail.body.dashlet(body);
+ $detailDashlet.append($body);
+ var body = one.f.detail.data.description(flow);
+ var $body = one.f.detail.body.description(body);
+ $detailDashlet.append($body);
+ var body = one.f.detail.data.actions(flow);
+ var $body = one.f.detail.body.actions(body);
+ $detailDashlet.append($body);
+ },
+ modal : {
+ dialog : {
+ initialize : function(id, node) {
+ var h3 = "Remove Flow";
+ var $p = one.f.flows.modal.dialog.body(id);
+ var footer = one.f.flows.modal.dialog.footer();
+ var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $p, footer);
+ $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
+ $modal.modal('hide');
+ });
+ $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(function() {
+ one.f.flows.modal.ajax.removeflow(id, node, function(data) {
+ if (data == "Success") {
+ $modal.modal('hide');
+ one.main.dashlet.right.bottom.empty();
+ one.f.detail.dashlet(one.main.dashlet.right.bottom);
+ one.main.dashlet.left.top.empty();
+ one.f.flows.dashlet(one.main.dashlet.left.top);
+ one.lib.alert('Flow removed');
} else {
- // bind add flow button
- $('#'+one.f.flows.id.modal.add, $modal).click(function() {
- one.f.flows.modal.save($modal, 'false');
- });
-
- // bind install flow button
- $('#'+one.f.flows.id.modal.install, $modal).click(function() {
- one.f.flows.modal.save($modal, 'true');
- });
+ one.lib.alert('Cannot remove flow: '+data);
}
+ });
+ });
+ return $modal;
+ },
+ footer : function() {
+ var footer = [];
+
+ var removeButton = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.modal.dialog.remove, "btn-danger", "");
+ var $removeButton = one.lib.dashlet.button.button(removeButton);
+ footer.push($removeButton);
+
+ var closeButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.dialog.close, "", "");
+ var $closeButton = one.lib.dashlet.button.button(closeButton);
+ footer.push($closeButton);
+
+ return footer;
+ },
+ body : function(id) {
+ var $p = $(document.createElement('p'));
+ $p.append('Remove flow '+id+'?');
+ return $p;
+ }
+ },
+ initialize : function(edit,install) {
+ var h3;
+ if(edit) {
+ h3 = "Edit Flow Entry";
+ var footer = one.f.flows.modal.footerEdit();
+
+ } else {
+ h3 = "Add Flow Entry";
+ var footer = one.f.flows.modal.footer();
+ }
+
+ var $modal = one.lib.modal.spawn(one.f.flows.id.modal.modal, h3, "", footer);
+
+ // bind close button
+ $('#'+one.f.flows.id.modal.close, $modal).click(function() {
+ $modal.modal('hide');
+ });
+
+ if (edit) {
+ // bind edit flow button
+ $('#'+one.f.flows.id.modal.edit, $modal).click(function() {
+ one.f.flows.modal.save($modal, install, true);
+ });
+ } else {
+ // bind add flow button
+ $('#'+one.f.flows.id.modal.add, $modal).click(function() {
+ one.f.flows.modal.save($modal, 'false');
+ });
+ // bind install flow button
+ $('#'+one.f.flows.id.modal.install, $modal).click(function() {
+ one.f.flows.modal.save($modal, 'true');
+ });
+ }
- var nodes = one.f.flows.registry.nodes;
- var nodeports = one.f.flows.registry.nodeports;
- var $body = one.f.flows.modal.body(nodes, nodeports, edit);
- one.lib.modal.inject.body($modal, $body,edit);
-
- return $modal;
- },
- save : function($modal, install, edit) {
- var result = {};
-
- result['name'] = $('#'+one.f.flows.id.modal.form.name, $modal).val();
- result['ingressPort'] = $('#'+one.f.flows.id.modal.form.port, $modal).val();
- result['priority'] = $('#'+one.f.flows.id.modal.form.priority, $modal).val();
- result['hardTimeout'] = $('#'+one.f.flows.id.modal.form.hardTimeout, $modal).val();
- result['idleTimeout'] = $('#'+one.f.flows.id.modal.form.idleTimeout, $modal).val();
- result['cookie'] = $('#'+one.f.flows.id.modal.form.cookie, $modal).val();
- result['etherType'] = $('#'+one.f.flows.id.modal.form.etherType, $modal).val();
- result['vlanId'] = $('#'+one.f.flows.id.modal.form.vlanId, $modal).val();
- result['vlanPriority'] = $('#'+one.f.flows.id.modal.form.vlanPriority, $modal).val();
- result['dlSrc'] = $('#'+one.f.flows.id.modal.form.srcMac, $modal).val();
- result['dlDst'] = $('#'+one.f.flows.id.modal.form.dstMac, $modal).val();
- result['nwSrc'] = $('#'+one.f.flows.id.modal.form.srcIp, $modal).val();
- result['nwDst'] = $('#'+one.f.flows.id.modal.form.dstIp, $modal).val();
- result['tosBits'] = $('#'+one.f.flows.id.modal.form.tosBits, $modal).val();
- result['tpSrc'] = $('#'+one.f.flows.id.modal.form.srcPort, $modal).val();
- result['tpDst'] = $('#'+one.f.flows.id.modal.form.dstPort, $modal).val();
- result['protocol'] = $('#'+one.f.flows.id.modal.form.protocol, $modal).val();
- result['installInHw'] = install;
-
- var nodeId = $('#'+one.f.flows.id.modal.form.nodes, $modal).val();
-
- $.each(result, function(key, value) {
- if (value == "") delete result[key];
- });
+ var nodes = one.f.flows.registry.nodes;
+ var nodeports = one.f.flows.registry.nodeports;
+ var $body = one.f.flows.modal.body(nodes, nodeports, edit);
+ one.lib.modal.inject.body($modal, $body,edit);
- var action = [];
- var $table = $('#'+one.f.flows.id.modal.action.table, $modal);
- $($table.find('tbody').find('tr')).each(function(index, value) {
- if (!$(this).find('td').hasClass('empty')) {
- action.push($(value).data('action'));
- }
+ return $modal;
+ },
+ save : function($modal, install, edit) {
+ var result = {};
+
+ result['name'] = $('#'+one.f.flows.id.modal.form.name, $modal).val();
+ result['ingressPort'] = $('#'+one.f.flows.id.modal.form.port, $modal).val();
+ result['priority'] = $('#'+one.f.flows.id.modal.form.priority, $modal).val();
+ result['hardTimeout'] = $('#'+one.f.flows.id.modal.form.hardTimeout, $modal).val();
+ result['idleTimeout'] = $('#'+one.f.flows.id.modal.form.idleTimeout, $modal).val();
+ result['cookie'] = $('#'+one.f.flows.id.modal.form.cookie, $modal).val();
+ result['etherType'] = $('#'+one.f.flows.id.modal.form.etherType, $modal).val();
+ result['vlanId'] = $('#'+one.f.flows.id.modal.form.vlanId, $modal).val();
+ result['vlanPriority'] = $('#'+one.f.flows.id.modal.form.vlanPriority, $modal).val();
+ result['dlSrc'] = $('#'+one.f.flows.id.modal.form.srcMac, $modal).val();
+ result['dlDst'] = $('#'+one.f.flows.id.modal.form.dstMac, $modal).val();
+ result['nwSrc'] = $('#'+one.f.flows.id.modal.form.srcIp, $modal).val();
+ result['nwDst'] = $('#'+one.f.flows.id.modal.form.dstIp, $modal).val();
+ result['tosBits'] = $('#'+one.f.flows.id.modal.form.tosBits, $modal).val();
+ result['tpSrc'] = $('#'+one.f.flows.id.modal.form.srcPort, $modal).val();
+ result['tpDst'] = $('#'+one.f.flows.id.modal.form.dstPort, $modal).val();
+ result['protocol'] = $('#'+one.f.flows.id.modal.form.protocol, $modal).val();
+ result['installInHw'] = install;
+
+ var nodeId = $('#'+one.f.flows.id.modal.form.nodes, $modal).val();
+
+ $.each(result, function(key, value) {
+ if (value == "") delete result[key];
+ });
+
+ var action = [];
+ var $table = $('#'+one.f.flows.id.modal.action.table, $modal);
+ $($table.find('tbody').find('tr')).each(function(index, value) {
+ if (!$(this).find('td').hasClass('empty')) {
+ action.push($(value).data('action'));
+ }
+ });
+ result['actions'] = action;
+
+ // frontend validation
+ if (result['name'] == undefined) {
+ alert('Need flow name');
+ return;
+ }
+ if (nodeId == '') {
+ alert('Select node');
+ return;
+ }
+ if (action.length == 0) {
+ alert('Please specify an action');
+ return;
+ }
+
+ // package for ajax call
+ var resource = {};
+ resource['body'] = JSON.stringify(result);
+ if(edit){
+ resource['action'] = 'edit';
+ } else {
+ resource['action'] = 'add';
+ }
+
+ resource['nodeId'] = nodeId;
+
+ if (edit) {
+ one.f.flows.modal.ajax.saveflow(resource, function(data) {
+ if (data == "Success") {
+ $modal.modal('hide').on('hidden', function () {
+ one.f.flows.detail(result['name'], nodeId);
});
- result['actions'] = action;
-
- // frontend validation
- if (result['name'] == undefined) {
- alert('Need flow name');
- return;
- }
- if (nodeId == '') {
- alert('Select node');
- return;
- }
- if (action.length == 0) {
- alert('Please specify an action');
- return;
- }
-
- // package for ajax call
- var resource = {};
- resource['body'] = JSON.stringify(result);
- if(edit){
- resource['action'] = 'edit';
+ one.lib.alert('Flow Entry edited');
+ one.main.dashlet.left.top.empty();
+ one.f.flows.dashlet(one.main.dashlet.left.top);
+ } else {
+ alert('Could not edit flow: '+data);
+ }
+ });
+ } else {
+ one.f.flows.modal.ajax.saveflow(resource, function(data) {
+ if (data == "Success") {
+ $modal.modal('hide');
+ one.lib.alert('Flow Entry added');
+ one.main.dashlet.left.top.empty();
+ one.f.flows.dashlet(one.main.dashlet.left.top);
+ } else {
+ alert('Could not add flow: '+data);
+ }
+ });
+ }
+ },
+ ajax : {
+ nodes : function(successCallback) {
+ $.getJSON(one.f.address.root+one.f.address.flows.nodes, function(data) {
+ var nodes = one.f.flows.modal.data.nodes(data);
+ var nodeports = data;
+ one.f.flows.registry['nodes'] = nodes;
+ one.f.flows.registry['nodeports'] = nodeports;
+
+ successCallback(nodes, nodeports);
+ });
+ },
+ saveflow : function(resource, callback) {
+ $.post(one.f.address.root+one.f.address.flows.flow, resource, function(data) {
+ callback(data);
+ });
+ },
+ removeflow : function(id, node, callback) {
+ resource = {};
+ resource['action'] = 'remove';
+ $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
+ callback(data);
+ });
+ },
+ toggleflow : function(id, node, callback) {
+ resource = {};
+ resource['action'] = 'toggle';
+ $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
+ callback(data);
+ });
+ }
+ },
+ data : {
+ nodes : function(data) {
+ result = {};
+ $.each(data, function(key, value) {
+ result[key] = value['name'];
+ });
+ return result;
+ }
+ },
+ body : function(nodes, nodeports, edit) {
+ var $form = $(document.createElement('form'));
+ var $fieldset = $(document.createElement('fieldset'));
+ var existingFlow;
+ // flow description
+ var $legend = one.lib.form.legend("");
+ $legend.css('visibility', 'hidden');
+ $fieldset.append($legend);
+ // name
+ var $label = one.lib.form.label("Name");
+ var $input = one.lib.form.input("Flow Name");
+ $input.attr('id', one.f.flows.id.modal.form.name);
+ if(edit) {
+ $input.attr('disabled', 'disabled');
+ var flows = one.f.flows.registry.flows;
+ $(flows).each(function(index, value) {
+ if (value.name == one.f.flows.registry.selectedId && value.nodeId == one.f.flows.registry.selectedNode) {
+ existingFlow = value.flow;
+ }
+ });
+ $input.val(existingFlow.name);
+ }
+
+ $fieldset.append($label).append($input);
+ // node
+ var $label = one.lib.form.label("Node");
+ var $select = one.lib.form.select.create(nodes);
+ one.lib.form.select.prepend($select, { '' : 'Please Select a Node' });
+ $select.val($select.find("option:first").val());
+ $select.attr('id', one.f.flows.id.modal.form.nodes);
+ if(edit) {
+ $select.attr('disabled', 'disabled');
+ $select.val(existingFlow.node.type + "|"+ existingFlow.node.nodeIDString);
+ }
+
+ // bind onchange
+ $select.change(function() {
+ // retrieve port value
+ var node = $(this).find('option:selected').attr('value');
+ var $ports = $('#'+one.f.flows.id.modal.form.port);
+ if (node == '') {
+ one.lib.form.select.inject($ports, {});
+ return;
+ }
+ one.f.flows.registry['currentNode'] = node;
+ var ports = nodeports[node]['ports'];
+ one.lib.form.select.inject($ports, ports);
+ one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' });
+ $ports.val($ports.find("option:first").val());
+ if(edit) {
+ $ports.val( existingFlow.ingressPort );
+ }
+ $.getJSON(one.f.address.root+'/valid-flows/'+node, function(response) {
+ var $select = $('#'+one.f.flows.id.modal.form.action, $fieldset);
+ one.lib.form.select.inject($select, response);
+ one.lib.form.select.prepend($select, {'' : 'Please Select an Action'});
+ // when selecting an action
+ $select.change(function() {
+ var action = $(this).find('option:selected');
+ one.f.flows.modal.action.parse(action.attr('value'));
+ $select[0].selectedIndex = 0;
+ });
+ });
+ });
+
+ $fieldset.append($label).append($select);
+ // input port
+ var $label = one.lib.form.label("Input Port");
+ var $select = one.lib.form.select.create();
+
+ $select.attr('id', one.f.flows.id.modal.form.port);
+ $fieldset.append($label).append($select);
+ // priority
+ var $label = one.lib.form.label("Priority");
+ var $input = one.lib.form.input("Priority");
+ $input.attr('id', one.f.flows.id.modal.form.priority);
+ $input.val('500');
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.priority);
+ }
+ // hardTimeout
+ var $label = one.lib.form.label("Hard Timeout");
+ var $input = one.lib.form.input("Hard Timeout");
+ $input.attr('id', one.f.flows.id.modal.form.hardTimeout);
+ if(edit) {
+ $input.val(existingFlow.hardTimeout);
+ }
+ $fieldset.append($label).append($input);
+
+ // idleTimeout
+ var $label = one.lib.form.label("Idle Timeout");
+ var $input = one.lib.form.input("Idle Timeout");
+ $input.attr('id', one.f.flows.id.modal.form.idleTimeout);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.idleTimeout);
+ }
+ // cookie
+ var $label = one.lib.form.label("Cookie");
+ var $input = one.lib.form.input("Cookie");
+ $input.attr('id', one.f.flows.id.modal.form.cookie);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.cookie);
+ }
+
+ // layer 2
+ var $legend = one.lib.form.legend("Layer 2");
+ $fieldset.append($legend);
+ // etherType
+ var $label = one.lib.form.label("Ethernet Type");
+ var $input = one.lib.form.input("Ethernet Type");
+ $input.attr('id', one.f.flows.id.modal.form.etherType);
+ $input.val('0x800');
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.etherType);
+ }
+ // vlanId
+ var $label = one.lib.form.label("VLAN Identification Number");
+ var $input = one.lib.form.input("VLAN Identification Number");
+ $input.attr('id', one.f.flows.id.modal.form.vlanId);
+ var $help = one.lib.form.help("Range: 0 - 4095");
+ $fieldset.append($label).append($input).append($help);
+ if(edit) {
+ $input.val(existingFlow.vlanId);
+ }
+
+ // vlanPriority
+ var $label = one.lib.form.label("VLAN Priority");
+ var $input = one.lib.form.input("VLAN Priority");
+ $input.attr('id', one.f.flows.id.modal.form.vlanPriority);
+ var $help = one.lib.form.help("Range: 0 - 7");
+ $fieldset.append($label).append($input).append($help);
+ if(edit) {
+ $input.val(existingFlow.vlanPriority);
+ }
+
+ // srcMac
+ var $label = one.lib.form.label("Source MAC Address");
+ var $input = one.lib.form.input("3c:97:0e:75:c3:f7");
+ $input.attr('id', one.f.flows.id.modal.form.srcMac);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.srcMac);
+ }
+ // dstMac
+ var $label = one.lib.form.label("Destination MAC Address");
+ var $input = one.lib.form.input("7c:d1:c3:e8:e6:99");
+ $input.attr('id', one.f.flows.id.modal.form.dstMac);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.dstMac);
+ }
+ // layer 3
+ var $legend = one.lib.form.legend("Layer 3");
+ $fieldset.append($legend);
+
+ // srcIp
+ var $label = one.lib.form.label("Source IP Address");
+ var $input = one.lib.form.input("192.168.3.128");
+ $input.attr('id', one.f.flows.id.modal.form.srcIp);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.srcIp);
+ }
+ // dstIp
+ var $label = one.lib.form.label("Destination IP Address");
+ var $input = one.lib.form.input("2001:2334::0/32");
+ $input.attr('id', one.f.flows.id.modal.form.dstIp);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.dstIp);
+ }
+ // tosBits
+ var $label = one.lib.form.label("ToS Bits");
+ var $input = one.lib.form.input("ToS Bits");
+ $input.attr('id', one.f.flows.id.modal.form.tosBits);
+ var $help = one.lib.form.help("Range: 0 - 63");
+ $fieldset.append($label).append($input).append($help);
+ if(edit) {
+ $input.val(existingFlow.tosBits);
+ }
+
+ // layer 4
+ var $legend = one.lib.form.legend("Layer 4");
+ $fieldset.append($legend);
+ // srcPort
+ var $label = one.lib.form.label("Source Port");
+ var $input = one.lib.form.input("Source Port");
+ $input.attr('id', one.f.flows.id.modal.form.srcPort);
+ var $help = one.lib.form.help("Range: 0 - 65535");
+ $fieldset.append($label).append($input).append($help);
+ if(edit) {
+ $input.val(existingFlow.srcPort);
+ }
+ // dstPort
+ var $label = one.lib.form.label("Destination Port");
+ var $input = one.lib.form.input("Destination Port");
+ $input.attr('id', one.f.flows.id.modal.form.dstPort);
+ var $help = one.lib.form.help("Range: 0 - 65535");
+ $fieldset.append($label).append($input).append($help);
+ if(edit) {
+ $input.val(existingFlow.dstPort);
+ }
+ // protocol
+ var $label = one.lib.form.label("Protocol");
+ var $input = one.lib.form.input("Protocol");
+ $input.attr('id', one.f.flows.id.modal.form.protocol);
+ $fieldset.append($label).append($input);
+ if(edit) {
+ $input.val(existingFlow.protocol);
+ }
+ // actions
+ var $legend = one.lib.form.label("Actions");
+ $fieldset.append($legend);
+ // actions table
+ var tableAttributes = ["table-striped", "table-bordered", "table-condensed", "table-hover", "table-cursor"];
+ var $table = one.lib.dashlet.table.table(tableAttributes);
+ $table.attr('id', one.f.flows.id.modal.action.table);
+ var tableHeaders = ["Action", "Data"];
+ var $thead = one.lib.dashlet.table.header(tableHeaders);
+ var $tbody = one.lib.dashlet.table.body("", tableHeaders);
+ $table.append($thead).append($tbody);
+ // actions
+ var actions = {
+ "" : "Please Select an Action",
+ "DROP" : "Drop",
+ "LOOPBACK" : "Loopback",
+ "FLOOD" : "Flood",
+ "FLOOD_ALL" : "Flood All",
+ "CONTROLLER" : "Controller",
+ "SW_PATH" : "Software Path",
+ "HW_PATH" : "Hardware Path",
+ "OUTPUT" : "Add Output Ports",
+ "ENQUEUE" : "Enqueue",
+ "SET_VLAN_ID" : "Set VLAN ID",
+ "SET_VLAN_PCP" : "Set VLAN Priority",
+ "SET_VLAN_CFI" : "Set VLAN CFI",
+ "POP_VLAN" : "Strip VLAN Header",
+ "PUSH_VLAN" : "Push VLAN",
+ "SET_DL_SRC" : "Modify Datalayer Source Address",
+ "SET_DL_DST" : "Modify Datalayer Destination Address",
+ "SET_DL_TYPE" : "Set Ethertype",
+ "SET_NW_SRC" : "Modify Network Source Address",
+ "SET_NW_DST" :"Modify Network Destination Address",
+ "SET_NW_TOS" : "Modify ToS Bits",
+ "SET_TP_SRC" : "Modify Transport Source Port",
+ "SET_TP_DST" : "Modify Transport Destination Port"
+ };
+ var $select = one.lib.form.select.create(actions);
+ $select.attr('id', one.f.flows.id.modal.form.action);
+ // when selecting an action
+ $select.change(function() {
+ var action = $(this).find('option:selected');
+ one.f.flows.modal.action.parse(action.attr('value'));
+ $select[0].selectedIndex = 0;
+ });
+
+ if(edit) {
+ $(existingFlow.actions).each(function(index, value){
+ setTimeout(function(){
+ var locEqualTo = value.indexOf("=");
+ if ( locEqualTo == -1 ) {
+ one.f.flows.modal.action.add.add(actions[value], value);
} else {
- resource['action'] = 'add';
+ var action = value.substr(0,locEqualTo);
+ if( action == "OUTPUT") {
+ var portIds = value.substr(locEqualTo+1).split(",");
+ var ports = [];
+ var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
+ for(var i =0; i < portIds.length ; i++) {
+ var portName = allPorts[portIds[i]];
+ ports.push(portName);
+ }
+ one.f.flows.modal.action.add.addPortsToTable(ports.join(", "), portIds.join(","));
+ } else {
+ var val = value.substr(locEqualTo+1);
+ one.f.flows.modal.action.add.addDataToTable(actions[action], val, action)
+ }
}
+ }, 1000)
+ });
+ }
+ $fieldset.append($select).append($table);
- resource['nodeId'] = nodeId;
-
- if (edit) {
- one.f.flows.modal.ajax.saveflow(resource, function(data) {
- if (data == "Success") {
- $modal.modal('hide').on('hidden', function () {
- one.f.flows.detail(result['name'], nodeId);
- });
- one.lib.alert('Flow Entry edited');
- one.main.dashlet.left.top.empty();
- one.f.flows.dashlet(one.main.dashlet.left.top);
- } else {
- alert('Could not edit flow: '+data);
- }
- });
- } else {
- one.f.flows.modal.ajax.saveflow(resource, function(data) {
- if (data == "Success") {
- $modal.modal('hide');
- one.lib.alert('Flow Entry added');
- one.main.dashlet.left.top.empty();
- one.f.flows.dashlet(one.main.dashlet.left.top);
- } else {
- alert('Could not add flow: '+data);
- }
- });
- }
+ // return
+ $form.append($fieldset);
+ return $form;
+ },
+ action : {
+ parse : function(option) {
+ switch (option) {
+ case "OUTPUT" :
+ var h3 = "Add Output Port";
+ var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addOutputPorts, one.f.flows.modal.action.add.addOutputPorts);
+ $modal.modal();
+ break;
+ case "SET_VLAN_ID" :
+ var h3 = "Set VLAN ID";
+ var placeholder = "VLAN Identification Number";
+ var id = one.f.flows.id.modal.action.setVlanId;
+ var help = "Range: 0 - 4095";
+ var action = 'SET_VLAN_ID';
+ var name = "VLAN ID";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_VLAN_PCP" :
+ var h3 = "Set VLAN Priority";
+ var placeholder = "VLAN Priority";
+ var id = one.f.flows.id.modal.action.setVlanPriority;
+ var help = "Range: 0 - 7";
+ var action = 'SET_VLAN_PCP';
+ var name = "VLAN Priority";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_VLAN_CFI" :
+ var h3 = "Set VLAN CFI";
+ var placeholder = "VLAN CFI";
+ var id = one.f.flows.id.modal.action.setVlanCfi;
+ var help = "Range: 0 - 1";
+ var action = 'SET_VLAN_CFI';
+ var name = "VLAN CFI";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "POP_VLAN" :
+ var name = "Strip VLAN Header";
+ var action = 'POP_VLAN';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "PUSH_VLAN" :
+ var h3 = "Push VLAN";
+ var placeholder = "VLAN";
+ var id = one.f.flows.id.modal.action.pushVlan;
+ var help = "Range: 0 - 4095";
+ var action = 'PUSH_VLAN';
+ var name = "VLAN";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_DL_SRC" :
+ var h3 = "Set Source MAC Address";
+ var placeholder = "Source MAC Address";
+ var id = one.f.flows.id.modal.action.modifyDatalayerSourceAddress;
+ var help = "Example: 00:11:22:aa:bb:cc";
+ var action = 'SET_DL_SRC';
+ var name = "Source MAC";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_DL_DST" :
+ var h3 = "Set Destination MAC Address";
+ var placeholder = "Destination MAC Address";
+ var id = one.f.flows.id.modal.action.modifyDatalayerDestinationAddress;
+ var help = "Example: 00:11:22:aa:bb:cc";
+ var action = 'SET_DL_DST';
+ var name = "Destination MAC";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_DL_TYPE" :
+ var h3 = "Set Ethertype";
+ var placeholder = "Ethertype";
+ var id = one.f.flows.id.modal.action.setEthertype;
+ var help = "Range: 0 - 65535";
+ var action = 'SET_DL_TYPE';
+ var name = "Ethertype";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_NW_SRC" :
+ var h3 = "Set IP Source Address";
+ var placeholder = "Source IP Address";
+ var id = one.f.flows.id.modal.action.modifyNetworkSourceAddress;
+ var help = "Example: 127.0.0.1";
+ var action = 'SET_NW_SRC';
+ var name = "Source IP";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_NW_DST" :
+ var h3 = "Set IP Destination Address";
+ var placeholder = "Destination IP Address";
+ var id = one.f.flows.id.modal.action.modifyNetworkDestinationAddress;
+ var help = "Example: 127.0.0.1";
+ var action = 'SET_NW_DST';
+ var name = "Destination IP";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_NW_TOS" :
+ var h3 = "Set IPv4 ToS";
+ var placeholder = "IPv4 ToS";
+ var id = one.f.flows.id.modal.action.modifyTosBits;
+ var help = "Range: 0 - 63";
+ var action = 'SET_NW_TOS';
+ var name = "ToS Bits";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_TP_SRC" :
+ var h3 = "Set Transport Source Port";
+ var placeholder = "Transport Source Port";
+ var id = one.f.flows.id.modal.action.modifyTransportSourcePort;
+ var help = "Range: 1 - 65535";
+ var action = 'SET_TP_SRC';
+ var name = "Source Port";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "SET_TP_DST" :
+ var h3 = "Set Transport Destination Port";
+ var placeholder = "Transport Destination Port";
+ var id = one.f.flows.id.modal.action.modifyTransportDestinationPort;
+ var help = "Range: 1 - 65535";
+ var action = 'SET_TP_DST';
+ var name = "Destination Port";
+ var body = function() {
+ return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
+ };
+ var add = function($modal) {
+ one.f.flows.modal.action.add.set(name, id, action, $modal);
+ };
+ var $modal = one.f.flows.modal.action.initialize(h3, body, add);
+ $modal.modal();
+ break;
+ case "ENQUEUE" :
+ var h3 = "Enqueue";
+ var placeholder = "Enqueue";
+ var id = one.f.flows.id.modal.action.enqueue;
+ var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addEnqueue, one.f.flows.modal.action.add.addEnqueue);
+ $modal.modal();
+ break;
+ case "DROP" :
+ var name = "Drop";
+ var action = 'DROP';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "LOOPBACK" :
+ var name = "Loopback";
+ var action = 'LOOPBACK';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "FLOOD" :
+ var name = "Flood";
+ var action = 'FLOOD';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "FLOOD_ALL" :
+ var name = "Flood All";
+ var action = 'FLOOD_ALL';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "SW_PATH" :
+ var name = "Software Path";
+ var action = 'SW_PATH';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "HW_PATH" :
+ var name = "Hardware Path";
+ var action = 'HW_PATH';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ case "CONTROLLER" :
+ var name = "Controller";
+ var action = 'CONTROLLER';
+ one.f.flows.modal.action.add.add(name, action);
+ break;
+ }
+ },
+ initialize : function(h3, bodyCallback, addCallback) {
+ var footer = one.f.flows.modal.action.footer();
+ var $body = bodyCallback();
+ var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal, h3, $body, footer);
+ // bind close button
+ $('#'+one.f.flows.id.modal.action.close, $modal).click(function() {
+ $modal.modal('hide');
+ });
+ // bind add flow button
+ $('#'+one.f.flows.id.modal.action.add, $modal).click(function() {
+ addCallback($modal);
+ });
+ return $modal;
+ },
+ add : {
+ addOutputPorts : function($modal) {
+ var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected');
+ var ports = '';
+ var pid = '';
+ $options.each(function(index, value) {
+ ports = ports+$(value).text()+", ";
+ pid = pid+$(value).attr('value')+",";
+ });
+ ports = ports.slice(0,-2);
+ pid = pid.slice(0,-1);
+ one.f.flows.modal.action.add.addPortsToTable(ports, pid);
+ $modal.modal('hide');
},
- ajax : {
- nodes : function(successCallback) {
- $.getJSON(one.f.address.root+one.f.address.flows.nodes, function(data) {
- var nodes = one.f.flows.modal.data.nodes(data);
- var nodeports = data;
- one.f.flows.registry['nodes'] = nodes;
- one.f.flows.registry['nodeports'] = nodeports;
-
- successCallback(nodes, nodeports);
- });
- },
- saveflow : function(resource, callback) {
- $.post(one.f.address.root+one.f.address.flows.flow, resource, function(data) {
- callback(data);
- });
- },
- removeflow : function(id, node, callback) {
- resource = {};
- resource['action'] = 'remove';
- $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
- callback(data);
- });
- },
- toggleflow : function(id, node, callback) {
- resource = {};
- resource['action'] = 'toggle';
- $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) {
- callback(data);
- });
- }
+ addEnqueue : function($modal) {
+ var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected');
+ var ports = '';
+ var pid = '';
+ $options.each(function(index, value) {
+ ports = ports+$(value).text()+", ";
+ pid = pid+$(value).attr('value')+",";
+ });
+ var $input = $('#'+one.f.flows.id.modal.action.queue);
+ var queue = $input.val();
+ ports = ports.slice(0,-2);
+ pid = pid.slice(0,-1);
+ one.f.flows.modal.action.add.addEnqueueToTable(ports, pid, queue);
+ $modal.modal('hide');
},
- data : {
- nodes : function(data) {
- result = {};
- $.each(data, function(key, value) {
- result[key] = value['name'];
- });
- return result;
- }
+ addEnqueueToTable : function(ports, pid, queue) {
+ if (queue !== '' && queue >= 0) {
+ ports += ':'+queue;
+ }
+ var $tr = one.f.flows.modal.action.table.add("Enqueue", ports);
+ $tr.attr('id', 'ENQUEUE');
+ if (queue !== '' && queue >= 0) {
+ $tr.data('action', 'ENQUEUE='+pid+':'+queue);
+ } else {
+ $tr.data('action', 'ENQUEUE='+pid+':0'); // default queue to 0
+ }
+ $tr.click(function() {
+ one.f.flows.modal.action.add.modal.initialize(this);
+ });
+ one.f.flows.modal.action.table.append($tr);
},
- body : function(nodes, nodeports, edit) {
- var $form = $(document.createElement('form'));
- var $fieldset = $(document.createElement('fieldset'));
- var existingFlow;
- // flow description
- var $legend = one.lib.form.legend("");
- $legend.css('visibility', 'hidden');
- $fieldset.append($legend);
- // name
- var $label = one.lib.form.label("Name");
- var $input = one.lib.form.input("Flow Name");
- $input.attr('id', one.f.flows.id.modal.form.name);
- if(edit) {
- $input.attr('disabled', 'disabled');
- var flows = one.f.flows.registry.flows;
- $(flows).each(function(index, value) {
- if (value.name == one.f.flows.registry.selectedId && value.nodeId == one.f.flows.registry.selectedNode) {
- existingFlow = value.flow;
- }
- });
- $input.val(existingFlow.name);
- }
-
- $fieldset.append($label).append($input);
- // node
- var $label = one.lib.form.label("Node");
- var $select = one.lib.form.select.create(nodes);
- one.lib.form.select.prepend($select, { '' : 'Please Select a Node' });
- $select.val($select.find("option:first").val());
- $select.attr('id', one.f.flows.id.modal.form.nodes);
- if(edit) {
- $select.attr('disabled', 'disabled');
- $select.val(existingFlow.node.type + "|"+ existingFlow.node.nodeIDString);
- }
+ addPortsToTable : function(ports, pid){
+ var $tr = one.f.flows.modal.action.table.add("Add Output Ports", ports);
+ $tr.attr('id', 'OUTPUT');
+ $tr.data('action', 'OUTPUT='+pid);
+ $tr.click(function() {
+ one.f.flows.modal.action.add.modal.initialize(this);
+ });
+ one.f.flows.modal.action.table.append($tr);
+ },
+ add : function(name, action) {
+ var $tr = one.f.flows.modal.action.table.add(name);
+ $tr.attr('id', action);
+ $tr.data('action', action);
+ $tr.click(function() {
+ one.f.flows.modal.action.add.modal.initialize(this);
+ });
+ one.f.flows.modal.action.table.append($tr);
+ },
+ set : function(name, id, action, $modal) {
+ var $input = $('#'+id);
+ var value = $input.val();
+ one.f.flows.modal.action.add.addDataToTable(name,value,action)
+ $modal.modal('hide');
+ },
+ addDataToTable : function(name,value,action) {
+ var $tr = one.f.flows.modal.action.table.add(name, value);
+ $tr.attr('id', action);
+ $tr.data('action', action+'='+value);
+ $tr.click(function() {
+ one.f.flows.modal.action.add.modal.initialize(this);
+ });
+ one.f.flows.modal.action.table.append($tr);
+ },
+ remove : function(that) {
+ $(that).remove();
+ var $table = $('#'+one.f.flows.id.modal.action.table);
+ if ($table.find('tbody').find('tr').size() == 0) {
+ var $tr = $(document.createElement('tr'));
+ var $td = $(document.createElement('td'));
+ $td.attr('colspan', '3');
+ $tr.addClass('empty');
+ $td.text('No data available');
+ $tr.append($td);
+ $table.find('tbody').append($tr);
+ }
+ },
+ modal : {
+ initialize : function(that) {
+ var h3 = "Remove Action";
+ var footer = one.f.flows.modal.action.add.modal.footer();
+ var $body = one.f.flows.modal.action.add.modal.body();
+ var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal.modal, h3, $body, footer);
+
+ // bind cancel button
+ $('#'+one.f.flows.id.modal.action.modal.cancel, $modal).click(function() {
+ $modal.modal('hide');
+ });
- // bind onchange
- $select.change(function() {
- // retrieve port value
- var node = $(this).find('option:selected').attr('value');
- var $ports = $('#'+one.f.flows.id.modal.form.port);
- if (node == '') {
- one.lib.form.select.inject($ports, {});
- return;
- }
- one.f.flows.registry['currentNode'] = node;
- var ports = nodeports[node]['ports'];
- one.lib.form.select.inject($ports, ports);
- one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' });
- $ports.val($ports.find("option:first").val());
- if(edit) {
- $ports.val( existingFlow.ingressPort );
- }
+ // bind remove button
+ $('#'+one.f.flows.id.modal.action.modal.remove, $modal).click(function() {
+ one.f.flows.modal.action.add.remove(that);
+ $modal.modal('hide');
});
- $fieldset.append($label).append($select);
- // input port
- var $label = one.lib.form.label("Input Port");
- var $select = one.lib.form.select.create();
-
- $select.attr('id', one.f.flows.id.modal.form.port);
- $fieldset.append($label).append($select);
- // priority
- var $label = one.lib.form.label("Priority");
- var $input = one.lib.form.input("Priority");
- $input.attr('id', one.f.flows.id.modal.form.priority);
- $input.val('500');
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.priority);
- }
- // hardTimeout
- var $label = one.lib.form.label("Hard Timeout");
- var $input = one.lib.form.input("Hard Timeout");
- $input.attr('id', one.f.flows.id.modal.form.hardTimeout);
- if(edit) {
- $input.val(existingFlow.hardTimeout);
- }
- $fieldset.append($label).append($input);
-
- // idleTimeout
- var $label = one.lib.form.label("Idle Timeout");
- var $input = one.lib.form.input("Idle Timeout");
- $input.attr('id', one.f.flows.id.modal.form.idleTimeout);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.idleTimeout);
- }
- // cookie
- var $label = one.lib.form.label("Cookie");
- var $input = one.lib.form.input("Cookie");
- $input.attr('id', one.f.flows.id.modal.form.cookie);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.cookie);
- }
+ $modal.modal();
+ },
+ body : function() {
+ var $p = $(document.createElement('p'));
+ $p.append("Remove this action?");
+ return $p;
+ },
+ footer : function() {
+ var footer = [];
- // layer 2
- var $legend = one.lib.form.legend("Layer 2");
- $fieldset.append($legend);
- // etherType
- var $label = one.lib.form.label("Ethernet Type");
- var $input = one.lib.form.input("Ethernet Type");
- $input.attr('id', one.f.flows.id.modal.form.etherType);
- $input.val('0x800');
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.etherType);
- }
- // vlanId
- var $label = one.lib.form.label("VLAN Identification Number");
- var $input = one.lib.form.input("VLAN Identification Number");
- $input.attr('id', one.f.flows.id.modal.form.vlanId);
- var $help = one.lib.form.help("Range: 0 - 4095");
- $fieldset.append($label).append($input).append($help);
- if(edit) {
- $input.val(existingFlow.vlanId);
- }
+ var removeButton = one.lib.dashlet.button.single("Remove Action", one.f.flows.id.modal.action.modal.remove, "btn-danger", "");
+ var $removeButton = one.lib.dashlet.button.button(removeButton);
+ footer.push($removeButton);
- // vlanPriority
- var $label = one.lib.form.label("VLAN Priority");
- var $input = one.lib.form.input("VLAN Priority");
- $input.attr('id', one.f.flows.id.modal.form.vlanPriority);
- var $help = one.lib.form.help("Range: 0 - 7");
- $fieldset.append($label).append($input).append($help);
- if(edit) {
- $input.val(existingFlow.vlanPriority);
- }
+ var cancelButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.action.modal.cancel, "", "");
+ var $cancelButton = one.lib.dashlet.button.button(cancelButton);
+ footer.push($cancelButton);
- // srcMac
- var $label = one.lib.form.label("Source MAC Address");
- var $input = one.lib.form.input("3c:97:0e:75:c3:f7");
- $input.attr('id', one.f.flows.id.modal.form.srcMac);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.srcMac);
- }
- // dstMac
- var $label = one.lib.form.label("Destination MAC Address");
- var $input = one.lib.form.input("7c:d1:c3:e8:e6:99");
- $input.attr('id', one.f.flows.id.modal.form.dstMac);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.dstMac);
- }
- // layer 3
- var $legend = one.lib.form.legend("Layer 3");
- $fieldset.append($legend);
-
- // srcIp
- var $label = one.lib.form.label("Source IP Address");
- var $input = one.lib.form.input("192.168.3.128");
- $input.attr('id', one.f.flows.id.modal.form.srcIp);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.srcIp);
- }
- // dstIp
- var $label = one.lib.form.label("Destination IP Address");
- var $input = one.lib.form.input("2001:2334::0/32");
- $input.attr('id', one.f.flows.id.modal.form.dstIp);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.dstIp);
- }
- // tosBits
- var $label = one.lib.form.label("ToS Bits");
- var $input = one.lib.form.input("ToS Bits");
- $input.attr('id', one.f.flows.id.modal.form.tosBits);
- var $help = one.lib.form.help("Range: 0 - 63");
- $fieldset.append($label).append($input).append($help);
- if(edit) {
- $input.val(existingFlow.tosBits);
- }
+ return footer;
+ }
+ }
+ },
+ table : {
+ add : function(action, data) {
+ var $tr = $(document.createElement('tr'));
+ var $td = $(document.createElement('td'));
+ $td.append(action);
+ $tr.append($td);
+ var $td = $(document.createElement('td'));
+ if (data != undefined) $td.append(data);
+ $tr.append($td);
+ return $tr;
+ },
+ append : function($tr) {
+ var $table = $('#'+one.f.flows.id.modal.action.table);
+ var $empty = $table.find('.empty').parent();
+ if ($empty.size() > 0) $empty.remove();
+ $table.append($tr);
+ }
+ },
+ body : {
+ common : function() {
+ var $form = $(document.createElement('form'));
+ var $fieldset = $(document.createElement('fieldset'));
+ return [$form, $fieldset];
+ },
+ addOutputPorts : function() {
+ var common = one.f.flows.modal.action.body.common();
+ var $form = common[0];
+ var $fieldset = common[1];
+ // output port
+ $label = one.lib.form.label("Select Output Ports");
+ if (one.f.flows.registry.currentNode == undefined){
+ return; //Selecting Output ports without selecting node throws an exception
+ }
+ var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
+ $select = one.lib.form.select.create(ports, true);
+ $select.attr('id', one.f.flows.id.modal.action.addOutputPorts);
+ $fieldset.append($label).append($select);
+ $form.append($fieldset);
+ return $form;
+ },
+ addEnqueue : function() {
+ var common = one.f.flows.modal.action.body.common();
+ var $form = common[0];
+ var $fieldset = common[1];
+ // output port
+ $label = one.lib.form.label("Select Output Ports");
+ if (one.f.flows.registry.currentNode == undefined){
+ return; //Selecting Output ports without selecting node throws an exception
+ }
+ var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
+ $select = one.lib.form.select.create(ports);
+ $select.attr('id', one.f.flows.id.modal.action.addOutputPorts);
+ $fieldset.append($label).append($select);
+ $label = one.lib.form.label('Queue (Optional)');
+ $input = one.lib.form.input('Queue')
+ .attr('id', one.f.flows.id.modal.action.queue);
+ $help = one.lib.form.help('Range: 1 - 2147483647');
+ $fieldset.append($label).append($input).append($help);
+ $form.append($fieldset);
+ return $form;
+ },
+ set : function(label, placeholder, id, help) {
+ var common = one.f.flows.modal.action.body.common();
+ var $form = common[0];
+ var $fieldset = common[1];
+ // input
+ $label = one.lib.form.label(label);
+ $input = one.lib.form.input(placeholder);
+ $input.attr('id', id);
+ $help = one.lib.form.help(help);
+ // append
+ $fieldset.append($label).append($input).append($help);
+ $form.append($fieldset);
+ return $form;
+ }
+ },
+ footer : function() {
+ var footer = [];
+ var addButton = one.lib.dashlet.button.single("Add Action", one.f.flows.id.modal.action.add, "btn-primary", "");
+ var $addButton = one.lib.dashlet.button.button(addButton);
+ footer.push($addButton);
+
+ var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.action.close, "", "");
+ var $closeButton = one.lib.dashlet.button.button(closeButton);
+ footer.push($closeButton);
+
+ return footer;
+ }
+ },
+ footer : function() {
+ var footer = [];
- // layer 4
- var $legend = one.lib.form.legend("Layer 4");
- $fieldset.append($legend);
- // srcPort
- var $label = one.lib.form.label("Source Port");
- var $input = one.lib.form.input("Source Port");
- $input.attr('id', one.f.flows.id.modal.form.srcPort);
- var $help = one.lib.form.help("Range: 0 - 65535");
- $fieldset.append($label).append($input).append($help);
- if(edit) {
- $input.val(existingFlow.srcPort);
- }
- // dstPort
- var $label = one.lib.form.label("Destination Port");
- var $input = one.lib.form.input("Destination Port");
- $input.attr('id', one.f.flows.id.modal.form.dstPort);
- var $help = one.lib.form.help("Range: 0 - 65535");
- $fieldset.append($label).append($input).append($help);
- if(edit) {
- $input.val(existingFlow.dstPort);
- }
- // protocol
- var $label = one.lib.form.label("Protocol");
- var $input = one.lib.form.input("Protocol");
- $input.attr('id', one.f.flows.id.modal.form.protocol);
- $fieldset.append($label).append($input);
- if(edit) {
- $input.val(existingFlow.protocol);
- }
- // actions
- var $legend = one.lib.form.label("Actions");
- $fieldset.append($legend);
- // actions table
- var tableAttributes = ["table-striped", "table-bordered", "table-condensed", "table-hover", "table-cursor"];
- var $table = one.lib.dashlet.table.table(tableAttributes);
- $table.attr('id', one.f.flows.id.modal.action.table);
- var tableHeaders = ["Action", "Data"];
- var $thead = one.lib.dashlet.table.header(tableHeaders);
- var $tbody = one.lib.dashlet.table.body("", tableHeaders);
- $table.append($thead).append($tbody);
- // actions
- var actions = {
- "" : "Please Select an Action",
- "DROP" : "Drop",
- "LOOPBACK" : "Loopback",
- "FLOOD" : "Flood",
- "SW_PATH" : "Software Path",
- "HW_PATH" : "Hardware Path",
- "CONTROLLER" : "Controller",
- "OUTPUT" : "Add Output Ports",
- "SET_VLAN_ID" : "Set VLAN ID",
- "SET_VLAN_PCP" : "Set VLAN Priority",
- "POP_VLAN" : "Strip VLAN Header",
- "SET_DL_SRC" : "Modify Datalayer Source Address",
- "SET_DL_DST" : "Modify Datalayer Destination Address",
- "SET_NW_SRC" : "Modify Network Source Address",
- "SET_NW_DST" :"Modify Network Destination Address",
- "SET_NW_TOS" : "Modify ToS Bits",
- "SET_TP_SRC" : "Modify Transport Source Port",
- "SET_TP_DST" : "Modify Transport Destination Port"
- };
- var $select = one.lib.form.select.create(actions);
- // when selecting an action
- $select.change(function() {
- var action = $(this).find('option:selected');
- one.f.flows.modal.action.parse(action.attr('value'));
- $select[0].selectedIndex = 0;
- });
+ var installButton = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.modal.install, "btn-success", "");
+ var $installButton = one.lib.dashlet.button.button(installButton);
+ footer.push($installButton);
- if(edit) {
- $(existingFlow.actions).each(function(index, value){
- setTimeout(function(){
- var locEqualTo = value.indexOf("=");
- if ( locEqualTo == -1 ) {
- one.f.flows.modal.action.add.add(actions[value], value);
- } else {
- var action = value.substr(0,locEqualTo);
- if( action == "OUTPUT") {
- var portIds = value.substr(locEqualTo+1).split(",");
- var ports = [];
- var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
- for(var i =0; i < portIds.length ; i++) {
- var portName = allPorts[portIds[i]];
- ports.push(portName);
- }
- one.f.flows.modal.action.add.addPortsToTable(ports.join(", "), portIds.join(","));
- } else {
- var val = value.substr(locEqualTo+1);
- one.f.flows.modal.action.add.addDataToTable(actions[action], val, action)
- }
- }
- }, 1000)
- });
- }
- $fieldset.append($select).append($table);
+ var addButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.add, "btn-primary", "");
+ var $addButton = one.lib.dashlet.button.button(addButton);
+ footer.push($addButton);
- // return
- $form.append($fieldset);
- return $form;
- },
- action : {
- parse : function(option) {
- switch (option) {
- case "OUTPUT" :
- var h3 = "Add Output Port";
- var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addOutputPorts, one.f.flows.modal.action.add.addOutputPorts);
- $modal.modal();
- break;
- case "SET_VLAN_ID" :
- var h3 = "Set VLAN ID";
- var placeholder = "VLAN Identification Number";
- var id = one.f.flows.id.modal.action.setVlanId;
- var help = "Range: 0 - 4095";
- var action = 'SET_VLAN_ID';
- var name = "VLAN ID";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_VLAN_PCP" :
- var h3 = "Set VLAN Priority";
- var placeholder = "VLAN Priority";
- var id = one.f.flows.id.modal.action.setVlanPriority;
- var help = "Range: 0 - 7";
- var action = 'SET_VLAN_PCP';
- var name = "VLAN Priority";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "POP_VLAN" :
- var name = "Strip VLAN Header";
- var action = 'POP_VLAN';
- one.f.flows.modal.action.add.add(name, action);
- break;
- case "SET_DL_SRC" :
- var h3 = "Set Source MAC Address";
- var placeholder = "Source MAC Address";
- var id = one.f.flows.id.modal.action.modifyDatalayerSourceAddress;
- var help = "Example: 00:11:22:aa:bb:cc";
- var action = 'SET_DL_SRC';
- var name = "Source MAC";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_DL_DST" :
- var h3 = "Set Destination MAC Address";
- var placeholder = "Destination MAC Address";
- var id = one.f.flows.id.modal.action.modifyDatalayerDestinationAddress;
- var help = "Example: 00:11:22:aa:bb:cc";
- var action = 'SET_DL_DST';
- var name = "Destination MAC";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_NW_SRC" :
- var h3 = "Set IP Source Address";
- var placeholder = "Source IP Address";
- var id = one.f.flows.id.modal.action.modifyNetworkSourceAddress;
- var help = "Example: 127.0.0.1";
- var action = 'SET_NW_SRC';
- var name = "Source IP";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_NW_DST" :
- var h3 = "Set IP Destination Address";
- var placeholder = "Destination IP Address";
- var id = one.f.flows.id.modal.action.modifyNetworkDestinationAddress;
- var help = "Example: 127.0.0.1";
- var action = 'SET_NW_DST';
- var name = "Destination IP";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_NW_TOS" :
- var h3 = "Set IPv4 ToS";
- var placeholder = "IPv4 ToS";
- var id = one.f.flows.id.modal.action.modifyTosBits;
- var help = "Range: 0 - 63";
- var action = 'SET_NW_TOS';
- var name = "ToS Bits";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_TP_SRC" :
- var h3 = "Set Transport Source Port";
- var placeholder = "Transport Source Port";
- var id = one.f.flows.id.modal.action.modifyTransportSourcePort;
- var help = "Range: 1 - 65535";
- var action = 'SET_TP_SRC';
- var name = "Source Port";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "SET_TP_DST" :
- var h3 = "Set Transport Destination Port";
- var placeholder = "Transport Destination Port";
- var id = one.f.flows.id.modal.action.modifyTransportDestinationPort;
- var help = "Range: 1 - 65535";
- var action = 'SET_TP_DST';
- var name = "Destination Port";
- var body = function() {
- return one.f.flows.modal.action.body.set(h3, placeholder, id, help);
- };
- var add = function($modal) {
- one.f.flows.modal.action.add.set(name, id, action, $modal);
- };
- var $modal = one.f.flows.modal.action.initialize(h3, body, add);
- $modal.modal();
- break;
- case "DROP" :
- var name = "Drop";
- var action = 'DROP';
- one.f.flows.modal.action.add.add(name, action);
- break;
- case "LOOPBACK" :
- var name = "Loopback";
- var action = 'LOOPBACK';
- one.f.flows.modal.action.add.add(name, action);
- break;
- case "FLOOD" :
- var name = "Flood";
- var action = 'FLOOD';
- one.f.flows.modal.action.add.add(name, action);
- break;
- case "SW_PATH" :
- var name = "Software Path";
- var action = 'SW_PATH';
- one.f.flows.modal.action.add.add(name, action);
- break;
- case "HW_PATH" :
- var name = "Hardware Path";
- var action = 'HW_PATH';
- one.f.flows.modal.action.add.add(name, action);
- break;
- case "CONTROLLER" :
- var name = "Controller";
- var action = 'CONTROLLER';
- one.f.flows.modal.action.add.add(name, action);
- break;
- }
- },
- initialize : function(h3, bodyCallback, addCallback) {
- var footer = one.f.flows.modal.action.footer();
- var $body = bodyCallback();
- var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal, h3, $body, footer);
- // bind close button
- $('#'+one.f.flows.id.modal.action.close, $modal).click(function() {
- $modal.modal('hide');
- });
- // bind add flow button
- $('#'+one.f.flows.id.modal.action.add, $modal).click(function() {
- addCallback($modal);
- });
- return $modal;
- },
- add : {
- addOutputPorts : function($modal) {
- var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected');
- var ports = '';
- var pid = '';
- $options.each(function(index, value) {
- ports = ports+$(value).text()+", ";
- pid = pid+$(value).attr('value')+",";
- });
- ports = ports.slice(0,-2);
- pid = pid.slice(0,-1);
- one.f.flows.modal.action.add.addPortsToTable(ports, pid);
- $modal.modal('hide');
- },
- addPortsToTable : function(ports, pid){
- var $tr = one.f.flows.modal.action.table.add("Add Output Ports", ports);
- $tr.attr('id', 'OUTPUT');
- $tr.data('action', 'OUTPUT='+pid);
- $tr.click(function() {
- one.f.flows.modal.action.add.modal.initialize(this);
- });
- one.f.flows.modal.action.table.append($tr);
- },
- add : function(name, action) {
- var $tr = one.f.flows.modal.action.table.add(name);
- $tr.attr('id', action);
- $tr.data('action', action);
- $tr.click(function() {
- one.f.flows.modal.action.add.modal.initialize(this);
- });
- one.f.flows.modal.action.table.append($tr);
- },
- set : function(name, id, action, $modal) {
- var $input = $('#'+id);
- var value = $input.val();
- one.f.flows.modal.action.add.addDataToTable(name,value,action)
- $modal.modal('hide');
- },
- addDataToTable : function(name,value,action) {
- var $tr = one.f.flows.modal.action.table.add(name, value);
- $tr.attr('id', action);
- $tr.data('action', action+'='+value);
- $tr.click(function() {
- one.f.flows.modal.action.add.modal.initialize(this);
- });
- one.f.flows.modal.action.table.append($tr);
- },
- remove : function(that) {
- $(that).remove();
- var $table = $('#'+one.f.flows.id.modal.action.table);
- if ($table.find('tbody').find('tr').size() == 0) {
- var $tr = $(document.createElement('tr'));
- var $td = $(document.createElement('td'));
- $td.attr('colspan', '3');
- $tr.addClass('empty');
- $td.text('No data available');
- $tr.append($td);
- $table.find('tbody').append($tr);
- }
- },
- modal : {
- initialize : function(that) {
- var h3 = "Remove Action";
- var footer = one.f.flows.modal.action.add.modal.footer();
- var $body = one.f.flows.modal.action.add.modal.body();
- var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal.modal, h3, $body, footer);
-
- // bind cancel button
- $('#'+one.f.flows.id.modal.action.modal.cancel, $modal).click(function() {
- $modal.modal('hide');
- });
-
- // bind remove button
- $('#'+one.f.flows.id.modal.action.modal.remove, $modal).click(function() {
- one.f.flows.modal.action.add.remove(that);
- $modal.modal('hide');
- });
-
- $modal.modal();
- },
- body : function() {
- var $p = $(document.createElement('p'));
- $p.append("Remove this action?");
- return $p;
- },
- footer : function() {
- var footer = [];
-
- var removeButton = one.lib.dashlet.button.single("Remove Action", one.f.flows.id.modal.action.modal.remove, "btn-danger", "");
- var $removeButton = one.lib.dashlet.button.button(removeButton);
- footer.push($removeButton);
-
- var cancelButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.action.modal.cancel, "", "");
- var $cancelButton = one.lib.dashlet.button.button(cancelButton);
- footer.push($cancelButton);
-
- return footer;
- }
- }
- },
- table : {
- add : function(action, data) {
- var $tr = $(document.createElement('tr'));
- var $td = $(document.createElement('td'));
- $td.append(action);
- $tr.append($td);
- var $td = $(document.createElement('td'));
- if (data != undefined) $td.append(data);
- $tr.append($td);
- return $tr;
- },
- append : function($tr) {
- var $table = $('#'+one.f.flows.id.modal.action.table);
- var $empty = $table.find('.empty').parent();
- if ($empty.size() > 0) $empty.remove();
- $table.append($tr);
- }
- },
- body : {
- common : function() {
- var $form = $(document.createElement('form'));
- var $fieldset = $(document.createElement('fieldset'));
- return [$form, $fieldset];
- },
- addOutputPorts : function() {
- var common = one.f.flows.modal.action.body.common();
- var $form = common[0];
- var $fieldset = common[1];
- // output port
- $label = one.lib.form.label("Select Output Ports");
- if (one.f.flows.registry.currentNode == undefined){
- return; //Selecting Output ports without selecting node throws an exception
- }
- var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports'];
- $select = one.lib.form.select.create(ports, true);
- $select.attr('id', one.f.flows.id.modal.action.addOutputPorts);
- $fieldset.append($label).append($select);
- $form.append($fieldset);
- return $form;
- },
- set : function(label, placeholder, id, help) {
- var common = one.f.flows.modal.action.body.common();
- var $form = common[0];
- var $fieldset = common[1];
- // input
- $label = one.lib.form.label(label);
- $input = one.lib.form.input(placeholder);
- $input.attr('id', id);
- $help = one.lib.form.help(help);
- // append
- $fieldset.append($label).append($input).append($help);
- $form.append($fieldset);
- return $form;
- }
- },
- footer : function() {
- var footer = [];
- var addButton = one.lib.dashlet.button.single("Add Action", one.f.flows.id.modal.action.add, "btn-primary", "");
- var $addButton = one.lib.dashlet.button.button(addButton);
- footer.push($addButton);
-
- var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.action.close, "", "");
- var $closeButton = one.lib.dashlet.button.button(closeButton);
- footer.push($closeButton);
-
- return footer;
- }
- },
- footer : function() {
- var footer = [];
+ var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
+ var $closeButton = one.lib.dashlet.button.button(closeButton);
+ footer.push($closeButton);
- var installButton = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.modal.install, "btn-success", "");
- var $installButton = one.lib.dashlet.button.button(installButton);
- footer.push($installButton);
+ return footer;
+ },
+ footerEdit : function() {
+ var footer = [];
- var addButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.add, "btn-primary", "");
- var $addButton = one.lib.dashlet.button.button(addButton);
- footer.push($addButton);
+ var editButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.edit, "btn-success", "");
+ var $editButton = one.lib.dashlet.button.button(editButton);
+ footer.push($editButton);
- var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
- var $closeButton = one.lib.dashlet.button.button(closeButton);
- footer.push($closeButton);
+ var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
+ var $closeButton = one.lib.dashlet.button.button(closeButton);
+ footer.push($closeButton);
- return footer;
- },
- footerEdit : function() {
- var footer = [];
+ return footer;
+ },
+ removeMultiple: {
+ dialog: function(flows) {
+ var h3 = 'Remove Flow Entry';
+ var flowList = [];
+ for (var i = 0; i < flows.length; i++) {
+ flowList.push(flows[i]["name"]);
+ }
+ var footer = one.f.flows.modal.removeMultiple.footer();
+ var $body = one.f.flows.modal.removeMultiple.body(flowList);
+ var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $body, footer);
- var editButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.edit, "btn-success", "");
- var $editButton = one.lib.dashlet.button.button(editButton);
- footer.push($editButton);
+ // bind close button
+ $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
+ $modal.modal('hide');
+ });
- var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", "");
- var $closeButton = one.lib.dashlet.button.button(closeButton);
- footer.push($closeButton);
+ // bind remove rule button
+ $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(this, function(e) {
+ var resource = {};
+ resource['body'] = JSON.stringify(flows);
- return footer;
- },
- removeMultiple: {
- dialog: function(flows) {
- var h3 = 'Remove Flow Entry';
- var flowList = [];
- for (var i = 0; i < flows.length; i++) {
- flowList.push(flows[i]["name"]);
- }
- var footer = one.f.flows.modal.removeMultiple.footer();
- var $body = one.f.flows.modal.removeMultiple.body(flowList);
- var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $body, footer);
-
- // bind close button
- $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() {
- $modal.modal('hide');
- });
-
- // bind remove rule button
- $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(this, function(e) {
- var resource = {};
- resource['body'] = JSON.stringify(flows);
-
- $.post(one.f.address.root+one.f.address.flows.deleteFlows, resource, function(response) {
- $modal.modal('hide');
- if(response == "Success") {
- one.lib.alert("Flow Entry(s) successfully removed");
- } else {
- one.lib.alert(response);
- }
- one.main.dashlet.right.bottom.empty();
- one.f.detail.dashlet(one.main.dashlet.right.bottom);
- one.main.dashlet.left.top.empty();
- one.f.flows.dashlet(one.main.dashlet.left.top);
- });
- });
- $modal.modal();
- },
- footer : function() {
- var footer = [];
- var remove = one.lib.dashlet.button.single('Remove Flow Entry',one.f.flows.id.modal.dialog.remove, 'btn-danger', '');
- var $remove = one.lib.dashlet.button.button(remove);
- footer.push($remove);
-
- var cancel = one.lib.dashlet.button.single('Cancel', one.f.flows.id.modal.dialog.close, '', '');
- var $cancel = one.lib.dashlet.button.button(cancel);
- footer.push($cancel);
-
- return footer;
- },
- body : function (flows) {
- var $p = $(document.createElement('p'));
- var p = 'Remove the following Flow Entry(s)?';
- //creata a BS label for each rule and append to list
- $(flows).each(function(){
- var $span = $(document.createElement('span'));
- $span.append(this);
- p += '<br/>' + $span[0].outerHTML;
- });
- $p.append(p);
- return $p;
+ $.post(one.f.address.root+one.f.address.flows.deleteFlows, resource, function(response) {
+ $modal.modal('hide');
+ if(response == "Success") {
+ one.lib.alert("Flow Entry(s) successfully removed");
+ } else {
+ one.lib.alert(response);
}
- }
- },
- ajax : {
- dashlet : function(callback) {
- $.getJSON(one.f.address.root+one.f.address.flows.main, function(data) {
- one.f.flows.registry['flows'] = data.flows;
- one.f.flows.registry['privilege'] = data.privilege;
- one.f.flows.modal.ajax.nodes(function(){/*Empty function. Do nothing. */})
+ one.main.dashlet.right.bottom.empty();
+ one.f.detail.dashlet(one.main.dashlet.right.bottom);
+ one.main.dashlet.left.top.empty();
+ one.f.flows.dashlet(one.main.dashlet.left.top);
+ });
+ });
+ $modal.modal();
+ },
+ footer : function() {
+ var footer = [];
+ var remove = one.lib.dashlet.button.single('Remove Flow Entry',one.f.flows.id.modal.dialog.remove, 'btn-danger', '');
+ var $remove = one.lib.dashlet.button.button(remove);
+ footer.push($remove);
+
+ var cancel = one.lib.dashlet.button.single('Cancel', one.f.flows.id.modal.dialog.close, '', '');
+ var $cancel = one.lib.dashlet.button.button(cancel);
+ footer.push($cancel);
+
+ return footer;
+ },
+ body : function (flows) {
+ var $p = $(document.createElement('p'));
+ var p = 'Remove the following Flow Entry(s)?';
+ //creata a BS label for each rule and append to list
+ $(flows).each(function(){
+ var $span = $(document.createElement('span'));
+ $span.append(this);
+ p += '<br/>' + $span[0].outerHTML;
+ });
+ $p.append(p);
+ return $p;
+ }
+ }
+ },
+ ajax : {
+ dashlet : function(callback) {
+ $.getJSON(one.f.address.root+one.f.address.flows.main, function(data) {
+ one.f.flows.registry['flows'] = data.flows;
+ one.f.flows.registry['privilege'] = data.privilege;
+ one.f.flows.modal.ajax.nodes(function(){/*Empty function. Do nothing. */})
+
+ callback(data);
+ });
+ }
+ },
+ data : {
+ flowsDataGrid: function(data) {
+ var source = new StaticDataSource({
+ columns: [
+ {
+ property: 'selector',
+ label: "<input type='checkbox' id='"+one.f.flows.id.dashlet.datagrid.selectAllFlows+"'/>",
+ sortable: false
+ },
+ {
+ property: 'name',
+ label: 'Flow Name',
+ sortable: true
+ },
+ {
+ property: 'node',
+ label: 'Node',
+ sortable: true
+ }
+ ],
+ data: data.flows,
+ formatter: function(items) {
+ $.each(items, function(index, item) {
+ var $checkbox = document.createElement("input");
+ $checkbox.setAttribute("type", "checkbox");
+ $checkbox.setAttribute("name", item.name);
+ $checkbox.setAttribute("node", item.nodeId);
+ $checkbox.setAttribute('class','flowEntry')
+ item.selector = $checkbox.outerHTML;
+ item["name"] = '<span data-installInHw=' + item["flow"]["installInHw"] +
+ ' data-flowstatus=' + item["flow"]["status"] +
+ ' data-nodeId=' + item["nodeId"] + '>' + item["name"] + '</span>';
+ });
- callback(data);
- });
- }
- },
- data : {
- flowsDataGrid: function(data) {
- var source = new StaticDataSource({
- columns: [
- {
- property: 'selector',
- label: "<input type='checkbox' id='"+one.f.flows.id.dashlet.datagrid.selectAllFlows+"'/>",
- sortable: false
- },
- {
- property: 'name',
- label: 'Flow Name',
- sortable: true
- },
- {
- property: 'node',
- label: 'Node',
- sortable: true
- }
- ],
- data: data.flows,
- formatter: function(items) {
- $.each(items, function(index, item) {
- var $checkbox = document.createElement("input");
- $checkbox.setAttribute("type", "checkbox");
- $checkbox.setAttribute("name", item.name);
- $checkbox.setAttribute("node", item.nodeId);
- $checkbox.setAttribute('class','flowEntry')
- item.selector = $checkbox.outerHTML;
- item["name"] = '<span data-installInHw=' + item["flow"]["installInHw"] +
- ' data-flowstatus=' + item["flow"]["status"] +
- ' data-nodeId=' + item["nodeId"] + '>' + item["name"] + '</span>';
- });
-
- },
- delay: 0
- });
- return source;
- },
- dashlet : function(data) {
- var body = [];
- $(data).each(function(index, value) {
- var tr = {};
- var entry = [];
-
-
- entry.push(value['name']);
- entry.push(value['node']);
- if (value['flow']['installInHw'] == 'true' && value['flow']['status'] == 'Success')
- tr['type'] = ['success'];
- else if (value['flow']['installInHw'] == 'false' && value['flow']['status'] == 'Success')
- tr['type'] = ['warning'];
- else
- tr['type'] = ['warning'];
- tr['entry'] = entry;
- tr['id'] = value['nodeId'];
-
- body.push(tr);
- });
- return body;
- }
+ },
+ delay: 0
+ });
+ return source;
},
- body : {
- dashlet : function(body, callback) {
- var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed', 'table-cursor'];
- var $table = one.lib.dashlet.table.table(attributes);
-
- var headers = ['Flow Name', 'Node'];
-
- var $thead = one.lib.dashlet.table.header(headers);
- $table.append($thead);
-
- var $tbody = one.lib.dashlet.table.body(body);
- $table.append($tbody);
- return $table;
- }
+ dashlet : function(data) {
+ var body = [];
+ $(data).each(function(index, value) {
+ var tr = {};
+ var entry = [];
+
+
+ entry.push(value['name']);
+ entry.push(value['node']);
+ if (value['flow']['installInHw'] == 'true' && value['flow']['status'] == 'Success')
+ tr['type'] = ['success'];
+ else if (value['flow']['installInHw'] == 'false' && value['flow']['status'] == 'Success')
+ tr['type'] = ['warning'];
+ else
+ tr['type'] = ['warning'];
+ tr['entry'] = entry;
+ tr['id'] = value['nodeId'];
+
+ body.push(tr);
+ });
+ return body;
}
+ },
+ body : {
+ dashlet : function(body, callback) {
+ var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed', 'table-cursor'];
+ var $table = one.lib.dashlet.table.table(attributes);
+
+ var headers = ['Flow Name', 'Node'];
+
+ var $thead = one.lib.dashlet.table.header(headers);
+ $table.append($thead);
+
+ var $tbody = one.lib.dashlet.table.body(body);
+ $table.append($tbody);
+ return $table;
+ }
+ }
}
/** INIT **/
// populate nav tabs
$(one.f.menu.left.top).each(function(index, value) {
- var $nav = $(".nav", "#left-top");
- one.main.page.dashlet($nav, value);
+ var $nav = $(".nav", "#left-top");
+ one.main.page.dashlet($nav, value);
});
$(one.f.menu.left.bottom).each(function(index, value) {
- var $nav = $(".nav", "#left-bottom");
- one.main.page.dashlet($nav, value);
+ var $nav = $(".nav", "#left-bottom");
+ one.main.page.dashlet($nav, value);
});
$(one.f.menu.right.bottom).each(function(index, value) {
- var $nav = $(".nav", "#right-bottom");
- one.main.page.dashlet($nav, value);
+ var $nav = $(".nav", "#right-bottom");
+ one.main.page.dashlet($nav, value);
});
one.f.populate = function($dashlet, header) {
- var $h4 = one.lib.dashlet.header(header);
- $dashlet.append($h4);
+ var $h4 = one.lib.dashlet.header(header);
+ $dashlet.append($h4);
};
// bind dashlet nav
$('.dash .nav a', '#main').click(function() {
- // de/activation
- var $li = $(this).parent();
- var $ul = $li.parent();
- one.lib.nav.unfocus($ul);
- $li.addClass('active');
- // clear respective dashlet
- var $dashlet = $ul.parent().find('.dashlet');
- one.lib.dashlet.empty($dashlet);
- // callback based on menu
- var id = $(this).attr('id');
- var menu = one.f.dashlet;
- switch (id) {
- case menu.flows.id:
- one.f.flows.dashlet($dashlet);
- break;
- case menu.nodes.id:
- one.f.nodes.dashlet($dashlet);
- break;
- case menu.detail.id:
- one.f.detail.dashlet($dashlet);
- break;
- };
+ // de/activation
+ var $li = $(this).parent();
+ var $ul = $li.parent();
+ one.lib.nav.unfocus($ul);
+ $li.addClass('active');
+ // clear respective dashlet
+ var $dashlet = $ul.parent().find('.dashlet');
+ one.lib.dashlet.empty($dashlet);
+ // callback based on menu
+ var id = $(this).attr('id');
+ var menu = one.f.dashlet;
+ switch (id) {
+ case menu.flows.id:
+ one.f.flows.dashlet($dashlet);
+ break;
+ case menu.nodes.id:
+ one.f.nodes.dashlet($dashlet);
+ break;
+ case menu.detail.id:
+ one.f.detail.dashlet($dashlet);
+ break;
+ };
});
// activate first tab on each dashlet
$('.dash .nav').each(function(index, value) {
- $($(value).find('li')[0]).find('a').click();
+ $($(value).find('li')[0]).find('a').click();
});