<relativePath>../../opendaylight/commons/opendaylight</relativePath>
</parent>
<artifactId>base-features</artifactId>
- <packaging>kar</packaging>
+ <packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>Base Features POM</description>
<properties>
</resource>
</resources>
<plugins>
- <plugin>
- <groupId>org.apache.karaf.tooling</groupId>
- <artifactId>karaf-maven-plugin</artifactId>
- <version>${karaf.version}</version>
- <extensions>true</extensions>
- <executions>
- <execution>
- <id>features-create-kar</id>
- <goals>
- <goal>features-create-kar</goal>
- </goals>
- <configuration>
- <featuresFile>${project.build.directory}/classes/${features.file}</featuresFile>
- </configuration>
- </execution>
- </executions>
- <!-- There is no useful configuration for the kar mojo. The features-generate-descriptor mojo configuration may be useful -->
- </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<bundle start="true" start-level="35">mvn:orbit/org.apache.juli.extras/7.0.32.v201211081135</bundle>
<bundle start="true" start-level="35">mvn:orbit/org.apache.tomcat.api/7.0.32.v201211081135</bundle>
<bundle start="true" start-level="35">mvn:orbit/org.apache.tomcat.util/7.0.32.v201211201952</bundle>
- <bundle start="true" start-level="35">wrap:mvn:virgomirror/org.eclipse.jdt.core.compiler.batch/3.8.0.I20120518-2145</bundle>
</feature>
<feature name="base-spring" description="Opendaylight Spring Support" version="${spring.version}">
<bundle>mvn:org.ow2.asm/asm-all/${asm.version}</bundle>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project>
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.2-SNAPSHOT</version>
+ <relativePath>../../opendaylight/commons/opendaylight</relativePath>
+ </parent>
+ <artifactId>controller-features</artifactId>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+ <description>Features POM</description>
+ <properties>
+ <features.file>features.xml</features.file>
+ </properties>
+ <build>
+ <resources>
+ <resource>
+ <filtering>true</filtering>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>filter</id>
+ <goals>
+ <goal>resources</goal>
+ </goals>
+ <phase>generate-resources</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/${features.file}</file>
+ <type>xml</type>
+ <classifier>features</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<features name="controller-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
+
+ <feature name="odl-hosttracker" description="Controller Service: Host Tracker">
+ <feature>odl-clustering</feature>
+ <feature>odl-managers</feature>
+ <feature>odl-sal</feature>
+ <bundle>mvn:org.opendaylight.controller/hosttracker/${hosttracker.api.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/hosttracker.implementation/${hosttracker.implementation.version}</bundle>
+ </feature>
+ <feature name="odl-sal" description="Service Abstraction Layer"
+ version="${sal.version}">
+ <feature>base-felix-dm</feature>
+ <bundle start="true" start-level="35">mvn:org.apache.commons/commons-lang3/${commons.lang.version}</bundle>
+ <bundle>mvn:org.osgi/org.osgi.compendium/${osgi.compendium.version}</bundle>
+ <bundle>mvn:org.apache.felix/org.apache.felix.dependencymanager/${felix.dependencymanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/sal/${sal.version}</bundle>
+ <!-- The SAL Implementation doesn't follow API versioning, should be revisited in the bundle -->
+ <bundle>mvn:org.opendaylight.controller/sal.implementation/${sal.implementation.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/sal.networkconfiguration/${sal.networkconfiguration.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/sal.networkconfiguration.implementation/${sal.networkconfiguration.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/sal.connection/${sal.connection.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/sal.connection.implementation/${sal.connection.version}</bundle>
+ </feature>
+ <feature name="odl-clustering" description="Controller Service: Clustering">
+ <feature>transaction</feature>
+ <feature>base-felix-dm</feature>
+ <feature>base-eclipselink-persistence</feature>
+ <feature>odl-sal</feature>
+ <bundle>mvn:org.opendaylight.controller/clustering.services/${clustering.services.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/clustering.services-implementation/${clustering.services_implementation.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/clustering.stub/${clustering.stub.version}</bundle>
+ </feature>
+ <feature name="odl-legacy-configuration">
+ <feature>odl-sal</feature>
+ <bundle>mvn:org.opendaylight.controller/configuration/${configuration.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/configuration.implementation/${configuration.implementation.version}</bundle>
+ </feature>
+ <feature name="odl-configuration" description="Controller Service: Configuration">
+ <!-- org.opendaylight.controller.config.yangjmxgenerator is missing -->
+ <bundle>mvn:org.opendaylight.controller/config-api/${config.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-manager/${config.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-netconf-connector/${netconf.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-persister-api/${config.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-persister-directory-xml-adapter/${config.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-persister-file-xml-adapter/${config.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-persister-impl/${netconf.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/yang-jmx-generator/${yang-jmx-generator.version}</bundle>
+ </feature>
+ <feature name="odl-managers" description="Big boss">
+ <feature>odl-legacy-configuration</feature>
+ <feature>base-spring-security</feature>
+ <feature>base-felix-dm</feature>
+ <feature>odl-clustering</feature>
+ <bundle>mvn:org.opendaylight.controller.thirdparty/net.sf.jung2/2.0.1</bundle>
+ <bundle>mvn:org.opendaylight.controller/appauth/${appauth.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/hosttracker/${hosttracker.api.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/hosttracker.implementation/${hosttracker.implementation.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/switchmanager/${switchmanager.api.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/switchmanager.implementation/${switchmanager.implementation.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/statisticsmanager/${statisticsmanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/statisticsmanager.implementation/${statisticsmanager.implementation.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/forwardingrulesmanager/${forwardingrulesmanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/forwardingrulesmanager.implementation/${forwardingrulesmanager.implementation.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/usermanager/${usermanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/usermanager.implementation/${usermanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/containermanager/${containermanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/containermanager.implementation/${containermanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/topologymanager/${topologymanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/forwarding.staticrouting</bundle>
+ <bundle>mvn:org.opendaylight.controller/routing.dijkstra_implementation</bundle>
+ <bundle>mvn:org.opendaylight.controller/connectionmanager</bundle>
+ <bundle>mvn:org.opendaylight.controller/connectionmanager.implementation</bundle>
+ </feature>
+</features>
</prerequisites>
<properties>
+ <akka.version>2.3.2</akka.version>
<aopalliance.version>1.0.0</aopalliance.version>
<appauth.version>0.4.2-SNAPSHOT</appauth.version>
+ <archetype-app-northbound>0.0.1-SNAPSHOT</archetype-app-northbound>
<aries.util.version>1.1.0</aries.util.version>
<!-- Controller Modules Versions -->
<arphandler.version>0.5.2-SNAPSHOT</arphandler.version>
+ <arphandler.version>0.5.2-SNAPSHOT</arphandler.version>
<asm.version>4.1</asm.version>
<!-- Plugin Versions -->
<bouncycastle.version>1.50</bouncycastle.version>
<bundle.plugin.version>2.4.0</bundle.plugin.version>
+ <bundlescanner.api.version>0.4.2-SNAPSHOT</bundlescanner.api.version>
+ <bundlescanner.implementation.version>0.4.2-SNAPSHOT</bundlescanner.implementation.version>
<bundlescanner.version>0.4.2-SNAPSHOT</bundlescanner.version>
<checkstyle.version>2.10</checkstyle.version>
<clustering.services.version>0.5.1-SNAPSHOT</clustering.services.version>
<clustering.stub.version>0.4.2-SNAPSHOT</clustering.stub.version>
<clustering.test.version>0.4.2-SNAPSHOT</clustering.test.version>
<commmons.northbound.version>0.4.2-SNAPSHOT</commmons.northbound.version>
+ <commons.checkstyle.version>0.0.3-SNAPSHOT</commons.checkstyle.version>
<!-- Third Party Versions -->
<commons.codec.version>1.7</commons.codec.version>
<commons.fileupload.version>1.2.2</commons.fileupload.version>
<commons.httpclient.version>0.1.2-SNAPSHOT</commons.httpclient.version>
<commons.io.version>2.4</commons.io.version>
<commons.lang.version>3.1</commons.lang.version>
+ <commons.logback_settings.version>0.0.2-SNAPSHOT</commons.logback_settings.version>
<commons.net.version>3.0.1</commons.net.version>
+ <commons.opendaylight.commons.httpclient>0.1.2-SNAPSHOT</commons.opendaylight.commons.httpclient>
+ <commons.opendaylight.concepts.version>0.5.2-SNAPSHOT</commons.opendaylight.concepts.version>
+ <commons.opendaylight.version>1.4.2-SNAPSHOT</commons.opendaylight.version>
+ <commons.parent.version>1.0.2-SNAPSHOT</commons.parent.version>
<compiler.version>2.3.2</compiler.version>
<concepts.version>0.5.2-SNAPSHOT</concepts.version>
<config.version>0.2.5-SNAPSHOT</config.version>
<connectionmanager.version>0.1.2-SNAPSHOT</connectionmanager.version>
<containermanager.it.version>0.5.2-SNAPSHOT</containermanager.it.version>
<containermanager.northbound.version>0.4.2-SNAPSHOT</containermanager.northbound.version>
+ <containermanager.shell.version>0.5.2-SNAPSHOT</containermanager.shell.version>
<containermanager.version>0.5.2-SNAPSHOT</containermanager.version>
<controllermanager.northbound.version>0.0.2-SNAPSHOT</controllermanager.northbound.version>
<corsfilter.version>7.0.42</corsfilter.version>
<ctrie.version>0.2.0</ctrie.version>
<devices.web.version>0.4.2-SNAPSHOT</devices.web.version>
+ <dummy-console.version>1.1.0-SNAPSHOT</dummy-console.version>
<eclipse.persistence.version>2.5.0</eclipse.persistence.version>
<!-- enforcer version -->
<enforcer.version>1.3.1</enforcer.version>
<hosttracker.implementation.version>0.5.2-SNAPSHOT</hosttracker.implementation.version>
<hosttracker.northbound.version>0.4.2-SNAPSHOT</hosttracker.northbound.version>
<hosttracker_new.api.version>0.4.2-SNAPSHOT</hosttracker_new.api.version>
+ <hosttracker_new.implementation.version>0.4.2-SNAPSHOT</hosttracker_new.implementation.version>
+ <httpservice-bridge.northbound.version>0.0.2-SNAPSHOT</httpservice-bridge.northbound.version>
<ietf-inet-types.version>2010.09.24.4-SNAPSHOT</ietf-inet-types.version>
<ietf-restconf.version>2013.10.19.1-SNAPSHOT</ietf-restconf.version>
<ietf-topology.version>2013.10.21.2-SNAPSHOT</ietf-topology.version>
<jersey2.version>2.8</jersey2.version>
<jettison.version>1.3.3</jettison.version>
<jmxGeneratorPath>src/main/yang-gen-config</jmxGeneratorPath>
+ <jolokia-bridge.version>0.0.2-SNAPSHOT</jolokia-bridge.version>
<jolokia.version>1.1.4</jolokia.version>
<jsr305.api.version>2.0.1</jsr305.api.version>
<jsr311.api.version>1.1.1</jsr311.api.version>
<jsr311.v2.api.version>2.0</jsr311.v2.api.version>
<junit.version>4.8.1</junit.version>
<karaf.branding.version>1.0.0-SNAPSHOT</karaf.branding.version>
+ <karaf.shell.version>3.0.0</karaf.shell.version>
<karaf.version>3.0.1</karaf.version>
<logback.version>1.0.9</logback.version>
<logging.bridge.version>0.4.2-SNAPSHOT</logging.bridge.version>
<networkconfig.neutron.version>0.4.2-SNAPSHOT</networkconfig.neutron.version>
<!-- ODL repository / plugin repository -->
<nexusproxy>http://nexus.opendaylight.org/content</nexusproxy>
+ <northbound.commons.version>0.4.2-SNAPSHOT</northbound.commons.version>
+ <northbound.hosttracker.version>1.4.2-SNAPSHOT</northbound.hosttracker.version>
+ <northbound.jolokia.version>1.4.2-SNAPSHOT</northbound.jolokia.version>
<opendaylight-l2-types.version>2013.08.27.4-SNAPSHOT</opendaylight-l2-types.version>
<org.json.version>20080701</org.json.version>
<osgi-brandfragment.web.version>0.0.2-SNAPSHOT</osgi-brandfragment.web.version>
<samples.loadbalancer.northbound.version>0.4.2-SNAPSHOT</samples.loadbalancer.northbound.version>
<samples.simpleforwarding.version>0.4.2-SNAPSHOT</samples.simpleforwarding.version>
<sanitytest.version>0.4.2-SNAPSHOT</sanitytest.version>
+ <scala.version>2.11</scala.version>
<security.version>0.4.2-SNAPSHOT</security.version>
<sitedeploy>dav:http://nexus.opendaylight.org/content/sites/site</sitedeploy>
<siteplugin>3.2</siteplugin>
<xtend.dstdir>src/main/xtend-gen</xtend.dstdir>
<xtend.version>2.4.3</xtend.version>
<yang-ext.version>2013.09.07.4-SNAPSHOT</yang-ext.version>
+ <yang-jmx-generator.version>1.0.0-SNAPSHOT</yang-jmx-generator.version>
<yangtools.version>0.6.2-SNAPSHOT</yangtools.version>
</properties>
<artifactId>jersey-core</artifactId>
<version>${jersey.version}</version>
</dependency>
+
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>${jersey.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-actor_${scala.version}</artifactId>
+ <version>${akka.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-cluster_${scala.version}</artifactId>
+ <version>${akka.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-persistence-experimental_${scala.version}</artifactId>
+ <version>${akka.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-remote_${scala.version}</artifactId>
+ <version>${akka.version}</version>
+ </dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<artifactId>containermanager.northbound</artifactId>
<version>${containermanager.northbound.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager.shell</artifactId>
+ <version>${containermanager.shell.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>controllermanager.northbound</artifactId>
<artifactId>chameleon-mbeans</artifactId>
<version>1.0.0</version>
</dependency>
+ <dependency>
+ <groupId>org.scala-lang</groupId>
+ <artifactId>scala-library</artifactId>
+ <version>${scala.version}.1</version>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<sourceDirectory>${project.basedir}</sourceDirectory>
<includes>**\/*.java,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat</includes>
- <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/</excludes>
+ <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/,**\/xtend-gen\/</excludes>
</configuration>
<dependencies>
<dependency>
--- /dev/null
+package org.opendaylight.controller.containermanager;
+
+import java.util.List;
+
+public interface IContainerManagerShell {
+ public List<String> psc();
+ public List<String> pfc();
+ public List<String> psd();
+ public List<String> psp();
+ public List<String> psm();
+ public List<String> addContainer(String arg1, String arg2);
+ public List<String> createContainer(String arg1, String arg2);
+ public List<String> removeContainerShell(String arg1);
+ public List<String> addContainerEntry(String arg1, String arg2, String arg3);
+ public List<String> removeContainerEntry(String arg1, String arg2, String arg3);
+ public List<String> addContainerFlow(String arg1, String arg2, String arg3);
+ public List<String> removeContainerFlow(String arg1, String arg2);
+ public List<String> containermgrGetRoles();
+ public List<String> containermgrGetAuthorizedGroups(String arg1);
+ public List<String> containermgrGetAuthorizedResources(String arg1);
+ public List<String> containermgrGetResourcesForGroup(String arg1);
+ public List<String> containermgrGetUserLevel(String arg1);
+ public List<String> containermgrGetUserResources(String arg1);
+ public List<String> saveConfig();
+}
\ No newline at end of file
package org.opendaylight.controller.containermanager.internal;
import org.eclipse.osgi.framework.console.CommandProvider;
+
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Set;
import java.util.Hashtable;
+
import org.opendaylight.controller.containermanager.IContainerManager;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
import org.apache.felix.dm.Component;
import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
CommandProvider.class.getName(),
IContainerInternal.class.getName(),
IContainerAuthorization.class.getName(),
- ICacheUpdateAware.class.getName()}, props);
+ ICacheUpdateAware.class.getName(),
+ IContainerManagerShell.class.getName()}, props);
c.add(createServiceDependency()
.setService(IClusterGlobalServices.class)
import org.opendaylight.controller.containermanager.ContainerFlowConfig;
import org.opendaylight.controller.containermanager.IContainerAuthorization;
import org.opendaylight.controller.containermanager.IContainerManager;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
import org.opendaylight.controller.containermanager.NodeConnectorsChangeEvent;
import org.opendaylight.controller.sal.authorization.AppRoleLevel;
import org.opendaylight.controller.sal.authorization.Privilege;
public class ContainerManager extends Authorization<String> implements IContainerManager, IObjectReader,
CommandProvider, ICacheUpdateAware<String, Object>, IContainerInternal, IContainerAuthorization,
- IConfigurationAware {
+ IConfigurationAware, IContainerManagerShell {
private static final Logger logger = LoggerFactory.getLogger(ContainerManager.class);
private static String CONTAINERS_FILE_NAME = "containers.conf";
private static final String allContainersGroup = "allContainers";
public boolean inContainerMode() {
return this.containerConfigs.size() > 0;
}
+
+ public List<String> psc() {
+ List<String> result = new ArrayList<String>();
+ for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
+ ContainerConfig sc = entry.getValue();
+ result.add(String.format("%s: %s", sc.getContainerName(), sc.toString()));
+ }
+ result.add("Total number of containers: " + containerConfigs.entrySet().size());
+ return result;
+ }
+
+ public List<String> pfc() {
+ List<String> result = new ArrayList<String>();
+ for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
+ ContainerConfig sc = entry.getValue();
+ result.add(String.format("%s: %s", sc.getContainerName(), sc.getContainerFlowConfigs()));
+ }
+ return result;
+ }
+
+ public List<String> psd() {
+ List<String> result = new ArrayList<String>();
+ for (String containerName : containerData.keySet()) {
+ ContainerData sd = containerData.get(containerName);
+ for (Node sid : sd.getSwPorts().keySet()) {
+ Set<NodeConnector> s = sd.getSwPorts().get(sid);
+ result.add("\t" + sid + " : " + s);
+ }
+
+ for (ContainerFlow s : sd.getContainerFlowSpecs()) {
+ result.add("\t" + s.toString());
+ }
+ }
+ return result;
+ }
+
+ public List<String> psp() {
+ List<String> result = new ArrayList<String>();
+ for (NodeConnector sp : nodeConnectorToContainers.keySet()) {
+ result.add(nodeConnectorToContainers.get(sp).toString());
+ }
+ return result;
+ }
+
+ public List<String> psm() {
+ List<String> result = new ArrayList<String>();
+ for (Node sp : nodeToContainers.keySet()) {
+ result.add(nodeToContainers.get(sp).toString());
+ }
+ return result;
+ }
+
+ public List<String> addContainer(String arg1, String arg2) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ String staticVlan = arg2;
+ ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, null, null);
+ result.add((this.addRemoveContainer(containerConfig, false)).toString());
+ return result;
+ }
+
+ public List<String> createContainer(String arg1, String arg2) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ String staticVlan = arg2;
+ if (staticVlan == null) {
+ result.add("Static Vlan not specified");
+ return result;
+ }
+ List<String> ports = new ArrayList<String>();
+ for (long l = 1L; l < 10L; l++) {
+ ports.add(NodeConnectorCreator.createOFNodeConnector((short) 1, NodeCreator.createOFNode(l)).toString());
+ }
+ List<ContainerFlowConfig> cFlowList = new ArrayList<ContainerFlowConfig>();
+ cFlowList.add(this.createSampleContainerFlowConfig("tcp", true));
+ ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, ports, cFlowList);
+ result.add((this.addRemoveContainer(containerConfig, false)).toString());
+ return result;
+ }
+
+ public List<String> removeContainerShell(String arg1) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ ContainerConfig containerConfig = new ContainerConfig(containerName, "", null, null);
+ result.add((this.addRemoveContainer(containerConfig, true)).toString());
+ return result;
+ }
+
+ public List<String> addContainerEntry(String arg1, String arg2, String arg3) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ String nodeId = arg2;
+ if (nodeId == null) {
+ result.add("Node Id not specified");
+ return result;
+ }
+ String portId = arg3;
+ if (portId == null) {
+ result.add("Port not specified");
+ return result;
+ }
+ Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
+ Short port = Short.valueOf(portId);
+ NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
+ List<String> portList = new ArrayList<String>(1);
+ portList.add(nc.toString());
+ result.add((this.addRemoveContainerEntries(containerName, portList, false)).toString());
+ return result;
+ }
+
+ public List<String> removeContainerEntry(String arg1, String arg2, String arg3) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ String nodeId = arg2;
+ if (nodeId == null) {
+ result.add("Node Id not specified");
+ return result;
+ }
+ String portId = arg3;
+ if (portId == null) {
+ result.add("Port not specified");
+ return result;
+ }
+ Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
+ Short port = Short.valueOf(portId);
+ NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
+ List<String> portList = new ArrayList<String>(1);
+ portList.add(nc.toString());
+ result.add((this.addRemoveContainerEntries(containerName, portList, true)).toString());
+ return result;
+ }
+ public List<String> addContainerFlow(String arg1, String arg2, String arg3) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ String cflowName = arg2;
+ if (cflowName == null) {
+ result.add("cflowName not specified");
+ return result;
+ }
+ String unidirectional = arg3;
+ boolean boolUnidirectional = Boolean.parseBoolean(unidirectional);
+ List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
+ list.add(createSampleContainerFlowConfig(cflowName, boolUnidirectional));
+ result.add((this.addRemoveContainerFlow(containerName, list, false)).toString());
+ return result;
+ }
+
+ public List<String> removeContainerFlow(String arg1, String arg2) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ String cflowName = arg2;
+ if (cflowName == null) {
+ result.add("cflowName not specified");
+ return result;
+ }
+ Set<String> set = new HashSet<String>(1);
+ set.add(cflowName);
+ result.add((this.removeContainerFlows(containerName, set)).toString());
+ return result;
+ }
+
+ public List<String> containermgrGetRoles() {
+ List<String> result = new ArrayList<String>();
+ result.add("Configured roles for Container Mgr:");
+ List<String> list = this.getRoles();
+ for (String role : list) {
+ result.add(role + "\t" + roles.get(role));
+ }
+ return result;
+ }
+
+ public List<String> containermgrGetAuthorizedGroups(String arg1) {
+ List<String> result = new ArrayList<String>();
+ String roleName = arg1;
+ if (roleName == null || roleName.trim().isEmpty()) {
+ result.add("Invalid argument");
+ result.add("mmGetAuthorizedGroups <role_name>");
+ return result;
+ }
+ result.add("Resource Groups associated to role " + roleName + ":");
+ List<ResourceGroup> list = this.getAuthorizedGroups(roleName);
+ for (ResourceGroup group : list) {
+ result.add(group.toString());
+ }
+ return result;
+ }
+ public List<String> containermgrGetAuthorizedResources(String arg1) {
+ List<String> result = new ArrayList<String>();
+ String roleName = arg1;
+ if (roleName == null || roleName.trim().isEmpty()) {
+ result.add("Invalid argument");
+ result.add("mmGetAuthorizedResources <role_name>");
+ return result;
+ }
+ result.add("Resource associated to role " + roleName + ":");
+ List<Resource> list = this.getAuthorizedResources(roleName);
+ for (Resource resource : list) {
+ result.add(resource.toString());
+ }
+ return result;
+ }
+ public List<String> containermgrGetResourcesForGroup(String arg1) {
+ List<String> result = new ArrayList<String>();
+ String groupName = arg1;
+ if (groupName == null || groupName.trim().isEmpty()) {
+ result.add("Invalid argument");
+ result.add("containermgrResourcesForGroup <group_name>");
+ return result;
+ }
+ result.add("Group " + groupName + " contains the following resources:");
+ List<Object> resources = this.getResources(groupName);
+ for (Object resource : resources) {
+ result.add(resource.toString());
+ }
+ return result;
+ }
+ public List<String> containermgrGetUserLevel(String arg1) {
+ List<String> result = new ArrayList<String>();
+ String userName = arg1;
+ if (userName == null || userName.trim().isEmpty()) {
+ result.add("Invalid argument");
+ result.add("containermgrGetUserLevel <user_name>");
+ return result;
+ }
+ result.add("User " + userName + " has level: " + this.getUserLevel(userName));
+ return result;
+ }
+ public List<String> containermgrGetUserResources(String arg1) {
+ List<String> result = new ArrayList<String>();
+ String userName = arg1;
+ if (userName == null || userName.trim().isEmpty()) {
+ result.add("Invalid argument");
+ result.add("containermgrGetUserResources <user_name>");
+ return result;
+ }
+ result.add("User " + userName + " owns the following resources: ");
+ Set<Resource> resources = this.getAllResourcesforUser(userName);
+ for (Resource resource : resources) {
+ result.add(resource.toString());
+ }
+ return result;
+ }
+ public List<String> saveConfig() {
+ List<String> result = new ArrayList<String>();
+ Status status = new Status(StatusCode.NOSERVICE, "Configuration service not reachable");
+
+ IConfigurationService configService = (IConfigurationService) ServiceHelper.getGlobalInstance(
+ IConfigurationService.class, this);
+ if (configService != null) {
+ status = configService.saveConfigurations();
+ }
+ result.add(status.toString());
+ return result;
+ }
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.2-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+ <artifactId>containermanager.shell</artifactId>
+ <version>${containermanager.shell.version}</version>
+ <packaging>bundle</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ <version>${karaf.shell.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager</artifactId>
+ <version>${containermanager.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>${bundle.plugin.version}</version>
+ <configuration>
+ <instructions>
+ <Import-Package>org.apache.felix.service.command,
+ org.apache.karaf.shell.commands,
+ org.apache.karaf.shell.console,
+ *</Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "addcontainer", description="Add Container")
+public class AddContainer extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Argument(index=1, name="staticVlan", description="staticVlan", required=true, multiValued=false)
+ String staticVlan = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.addContainer(containerName, staticVlan)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "AddContainerEntry", description="add container entry")
+public class AddContainerEntry extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Argument(index=1, name="nodeId", description="node ID", required=true, multiValued=false)
+ String nodeId = null;
+
+ @Argument(index=2, name="portId", description="portId", required=true, multiValued=false)
+ String portId = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.addContainerEntry(containerName, nodeId, portId)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "addContainerFlow", description="adds container flow")
+public class AddContainerFlow extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Argument(index=1, name="cflowName", description="c Flow name", required=true, multiValued=false)
+ String cflowName = null;
+
+ @Argument(index=2, name="unidirectional", description="unidirectional", required=true, multiValued=false)
+ String unidirectional = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.addContainerFlow(containerName, cflowName, unidirectional)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "containermgrGetAuthorizedGroups", description="Get authorized groups")
+public class ContainermgrGetAuthorizedGroups extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="roleName", description="role name", required=true, multiValued=false)
+ String roleName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.containermgrGetAuthorizedGroups(roleName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "containermgrGetAuthorizedResources", description="Get authorized resources")
+public class ContainermgrGetAuthorizedResources extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="roleName", description="role name", required=true, multiValued=false)
+ String roleName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.containermgrGetAuthorizedResources(roleName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "containermgrGetResourcesForGroup", description="Get resources for group")
+public class ContainermgrGetResourcesForGroup extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="groupName", description="group name", required=true, multiValued=false)
+ String groupName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.containermgrGetResourcesForGroup(groupName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "containermgrGetRoles", description="Get container mgr roles")
+public class ContainermgrGetRoles extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.containermgrGetRoles()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "containermgrGetUserLevel", description="Get user level")
+public class ContainermgrGetUserLevel extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="userName", description="user name", required=true, multiValued=false)
+ String userName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.containermgrGetUserLevel(userName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "containermgrGetUserResources", description="Get user resources")
+public class ContainermgrGetUserResources extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="userName", description="user name", required=true, multiValued=false)
+ String userName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.containermgrGetUserResources(userName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "createcontainer", description="create container")
+public class CreateContainer extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Argument(index=1, name="staticVlan", description="staticVlan", required=true, multiValued=false)
+ String staticVlan = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.createContainer(containerName, staticVlan)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "pfc", description="Display pfc")
+public class Pfc extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.pfc()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "psc", description="Display ")
+public class Psc extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.psc()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "psd", description="Display psd")
+public class Psd extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.psd()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "psm", description="Display psm")
+public class Psm extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.psm()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "psp", description="Display psp")
+public class Psp extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.psp()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "removecontainer", description="remove container")
+public class RemoveContainer extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.removeContainerShell(containerName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "removeContainerEntry", description="remove container entry")
+public class RemoveContainerEntry extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Argument(index=1, name="nodeId", description="node ID", required=true, multiValued=false)
+ String nodeId = null;
+
+ @Argument(index=2, name="portId", description="portId", required=true, multiValued=false)
+ String portId = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.removeContainerEntry(containerName, nodeId, portId)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "removeContainerFlow", description="removes container flow")
+public class RemoveContainerFlow extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Argument(index=1, name="cflowName", description="c Flow name", required=true, multiValued=false)
+ String cflowName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.removeContainerFlow(containerName, cflowName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "saveConfig", description="Save config")
+public class SaveConfig extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.saveConfig()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <reference id="containerManagerRef" interface="org.opendaylight.controller.containermanager.IContainerManagerShell"/>
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.AddContainer">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.AddContainerEntry">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.AddContainerFlow">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.ContainermgrGetAuthorizedGroups">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.ContainermgrGetAuthorizedResources">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.ContainermgrGetResourcesForGroup">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.ContainermgrGetRoles">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.ContainermgrGetUserLevel">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.ContainermgrGetUserResources">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.CreateContainer">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.Pfc">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.Psc">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.Psd">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.Psm">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.Psp">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.RemoveContainer">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.RemoveContainerEntry">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.RemoveContainerFlow">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.SaveConfig">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ </command-bundle>
+
+
+</blueprint>
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.lang.reflect.Field;
+
+import org.junit.Assert;
+import org.junit.Test;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+
+public class ContainerManagerShellTest {
+ private IContainerManagerShell containerManager;
+
+ @Test
+ public void testAddContainer() throws Exception {
+ String containerName = "test", staticVlan = "1234";
+ AddContainer addConTest = new AddContainer();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ List<String> result2 = new ArrayList<String>(Arrays.asList("Container Name not specified"));
+ when(containerManager.addContainer(containerName, staticVlan)).thenReturn(result);
+ when(containerManager.addContainer(null, null)).thenReturn(result2);
+
+ Field cNField = addConTest.getClass().getDeclaredField("containerName");
+ cNField.setAccessible(true);
+ Field sVField = addConTest.getClass().getDeclaredField("staticVlan");
+ sVField.setAccessible(true);
+
+ cNField.set(addConTest, "test");
+ sVField.set(addConTest, "1234");
+
+ addConTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ addConTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ baos.reset();
+
+ cNField.set(addConTest, null);
+ sVField.set(addConTest, null);
+ addConTest.doExecute();
+ Assert.assertEquals("Container Name not specified\n", baos.toString());
+ }
+
+ @Test
+ public void testAddContainerEntry() throws Exception {
+ String containerName = "test", nodeId = "1234", portId = "5678";
+ AddContainerEntry addConEntTest = new AddContainerEntry();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.addContainerEntry(containerName, nodeId, portId)).thenReturn(result);
+
+ Field cNField = addConEntTest.getClass().getDeclaredField("containerName");
+ cNField.setAccessible(true);
+ Field nIField = addConEntTest.getClass().getDeclaredField("nodeId");
+ nIField.setAccessible(true);
+ Field pIField = addConEntTest.getClass().getDeclaredField("portId");
+ pIField.setAccessible(true);
+
+ cNField.set(addConEntTest, "test");
+ nIField.set(addConEntTest, "1234");
+ pIField.set(addConEntTest, "5678");
+
+ addConEntTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ addConEntTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testAddContainerFlow() throws Exception {
+ String containerName = "test", cflowName = "1234", unidirectional = "5678";
+ AddContainerFlow addConFlowTest = new AddContainerFlow();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.addContainerFlow(containerName, cflowName, unidirectional)).thenReturn(result);
+
+ Field cNField = addConFlowTest.getClass().getDeclaredField("containerName");
+ cNField.setAccessible(true);
+ Field cfField = addConFlowTest.getClass().getDeclaredField("cflowName");
+ cfField.setAccessible(true);
+ Field unField = addConFlowTest.getClass().getDeclaredField("unidirectional");
+ unField.setAccessible(true);
+
+ cNField.set(addConFlowTest, "test");
+ cfField.set(addConFlowTest, "1234");
+ unField.set(addConFlowTest, "5678");
+
+ addConFlowTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ addConFlowTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testContainermgrGetAuthorizedGroups() throws Exception {
+ String roleName = "test";
+ ContainermgrGetAuthorizedGroups contmgrGTest = new ContainermgrGetAuthorizedGroups();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.containermgrGetAuthorizedGroups(roleName)).thenReturn(result);
+
+ Field rNField = contmgrGTest.getClass().getDeclaredField("roleName");
+ rNField.setAccessible(true);
+
+ rNField.set(contmgrGTest, "test");
+
+ contmgrGTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ contmgrGTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testContainermgrGetAuthorizedResources() throws Exception {
+ String roleName = "test";
+ ContainermgrGetAuthorizedResources contmgrRTest = new ContainermgrGetAuthorizedResources();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.containermgrGetAuthorizedResources(roleName)).thenReturn(result);
+
+ Field rNField = contmgrRTest.getClass().getDeclaredField("roleName");
+ rNField.setAccessible(true);
+
+ rNField.set(contmgrRTest, "test");
+
+ contmgrRTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ contmgrRTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testContainermgrGetResourcesForGroup() throws Exception {
+ String groupName = "test";
+ ContainermgrGetResourcesForGroup contmgrRTest = new ContainermgrGetResourcesForGroup();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.containermgrGetResourcesForGroup(groupName)).thenReturn(result);
+
+ Field gNField = contmgrRTest.getClass().getDeclaredField("groupName");
+ gNField.setAccessible(true);
+
+ gNField.set(contmgrRTest, groupName);
+
+ contmgrRTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ contmgrRTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testContainermgrGetRoles() throws Exception {
+ ContainermgrGetRoles contmgrRTest = new ContainermgrGetRoles();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.containermgrGetRoles()).thenReturn(result);
+
+ contmgrRTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ contmgrRTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testContainermgrGetUserLevel() throws Exception {
+ String userName = "test";
+ ContainermgrGetUserLevel contmgrUTest = new ContainermgrGetUserLevel();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.containermgrGetUserLevel(userName)).thenReturn(result);
+
+ Field gNField = contmgrUTest.getClass().getDeclaredField("userName");
+ gNField.setAccessible(true);
+
+ gNField.set(contmgrUTest, userName);
+
+ contmgrUTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ contmgrUTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testContainermgrGetUserResources() throws Exception {
+ String userName = "test";
+ ContainermgrGetUserResources contmgrUTest = new ContainermgrGetUserResources();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.containermgrGetUserResources(userName)).thenReturn(result);
+
+ Field gNField = contmgrUTest.getClass().getDeclaredField("userName");
+ gNField.setAccessible(true);
+
+ gNField.set(contmgrUTest, userName);
+
+ contmgrUTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ contmgrUTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testPfc() throws Exception {
+ Pfc pfc = new Pfc();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.pfc()).thenReturn(result);
+
+ pfc.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ pfc.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testPsc() throws Exception {
+ Psc psc = new Psc();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.psc()).thenReturn(result);
+
+ psc.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ psc.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testPsd() throws Exception {
+ Psd psd = new Psd();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.psd()).thenReturn(result);
+
+ psd.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ psd.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testPsm() throws Exception {
+ Psm psm = new Psm();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.psm()).thenReturn(result);
+
+ psm.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ psm.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testPsp() throws Exception {
+ Psp psp = new Psp();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.psp()).thenReturn(result);
+
+ psp.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ psp.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testRemoveContainer() throws Exception {
+ String containerName = "test";
+ RemoveContainer remConTest = new RemoveContainer();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.removeContainerShell(containerName)).thenReturn(result);
+
+ Field cNField = remConTest.getClass().getDeclaredField("containerName");
+ cNField.setAccessible(true);
+ cNField.set(remConTest, "test");
+
+ remConTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ remConTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testRemoveContainerEntry() throws Exception {
+ String containerName = "test", nodeId = "1234", portId = "5678";
+ RemoveContainerEntry remConEntTest = new RemoveContainerEntry();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.removeContainerEntry(containerName, nodeId, portId)).thenReturn(result);
+
+ Field cNField = remConEntTest.getClass().getDeclaredField("containerName");
+ cNField.setAccessible(true);
+ Field nIField = remConEntTest.getClass().getDeclaredField("nodeId");
+ nIField.setAccessible(true);
+ Field pIField = remConEntTest.getClass().getDeclaredField("portId");
+ pIField.setAccessible(true);
+
+ cNField.set(remConEntTest, "test");
+ nIField.set(remConEntTest, "1234");
+ pIField.set(remConEntTest, "5678");
+
+ remConEntTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ remConEntTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testRemoveContainerFlow() throws Exception {
+ String containerName = "test", cflowName = "1234";
+ RemoveContainerFlow remConFlowTest = new RemoveContainerFlow();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.removeContainerFlow(containerName, cflowName)).thenReturn(result);
+
+ Field cNField = remConFlowTest.getClass().getDeclaredField("containerName");
+ cNField.setAccessible(true);
+ Field cfField = remConFlowTest.getClass().getDeclaredField("cflowName");
+ cfField.setAccessible(true);
+
+ cNField.set(remConFlowTest, "test");
+ cfField.set(remConFlowTest, "1234");
+
+ remConFlowTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ remConFlowTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testSaveConfig() throws Exception {
+ SaveConfig saveConfig = new SaveConfig();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.saveConfig()).thenReturn(result);
+
+ saveConfig.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ saveConfig.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+}
\ No newline at end of file
<type>xml</type>
<scope>runtime</scope>
</dependency>
+ <!-- scope is compile so all features (there is only one) are installed
+ into startup.properties and the feature repo itself is not installed -->
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>base-features</artifactId>
<version>${project.version}</version>
- <type>kar</type>
+ <type>pom</type>
<scope>runtime</scope>
</dependency>
<dependency>
# Extra packages to import from the boot class loader
org.osgi.framework.system.packages.extra=org.apache.karaf.branding,sun.reflect,sun.reflect.misc,sun.misc,sun.nio.ch
-# https://bugs.eclipse.org/bugs/show_bug.cgi?id=325578
-# Extend the framework to avoid the resources to be presented with
-# a URL of type bundleresource: but to be presented as file:
-osgi.hook.configurators.include=org.eclipse.virgo.kernel.equinox.extensions.hooks.ExtensionsHookConfigurator
# Embedded Tomcat configuration File
org.eclipse.gemini.web.tomcat.config.path=configuration/tomcat-server.xml
org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true
-# Use Equinox as default OSGi Framework Implementation
-karaf.framework=equinox
# Netconf startup configuration
netconf.tcp.address=127.0.0.1
mvn\:org.ops4j.pax.url/pax-url-maven-commons/1.6.0 = 5
mvn\:org.ops4j.pax.url/pax-url-aether/1.6.0 = 5
mvn\:org.ops4j.pax.url/pax-url-wrap/1.6.0 = 5
-mvn\:javax.annotation/javax.annotation-api/1.2 = 5
+#mvn\:javax.annotation/javax.annotation-api/1.2 = 5
mvn\:org.ops4j.pax.logging/pax-logging-api/1.7.2 = 8
mvn\:org.ops4j.pax.logging/pax-logging-service/1.7.2 = 8
mvn\:org.apache.karaf.service/org.apache.karaf.service.guard/3.0.1 = 10
--- /dev/null
+#
+# The properties defined in this file will be made available through system
+# properties at the very beginning of the Karaf's boot process.
+#
+
+# Use Equinox as default OSGi Framework Implementation
+karaf.framework=equinox
+
+# https://bugs.eclipse.org/bugs/show_bug.cgi?id=325578
+# Extend the framework to avoid the resources to be presented with
+# a URL of type bundleresource: but to be presented as file:
+osgi.hook.configurators.include=org.eclipse.virgo.kernel.equinox.extensions.hooks.ExtensionsHookConfigurator
+
+
+# Log level when the pax-logging service is not available
+# This level will only be used while the pax-logging service bundle
+# is not fully available.
+# To change log levels, please refer to the org.ops4j.pax.logging.cfg file
+# instead.
+org.ops4j.pax.logging.DefaultServiceLog.level = ERROR
+
+#
+# Name of this Karaf instance.
+#
+karaf.name = root
+
+#
+# Default repository where bundles will be loaded from before using
+# other Maven repositories. For the full Maven configuration, see
+# the org.ops4j.pax.url.mvn.cfg file.
+#
+karaf.default.repository = system
+
+#
+# Location of a shell script that will be run when starting a shell
+# session. This script can be used to create aliases and define
+# additional commands.
+#
+karaf.shell.init.script = ${karaf.etc}/shell.init.script
+
+#
+# Sets the maximum size of the shell command history. If not set,
+# defaults to 500 entries. Setting to 0 will disable history.
+#
+# karaf.shell.history.maxSize = 0
+
+#
+# Deletes the entire karaf.data directory at every start
+#
+karaf.clean.all = false
+
+#
+# Deletes the karaf.data/cache directory at every start
+#
+karaf.clean.cache = false
+
+#
+# Roles to use when logging into a local Karaf console.
+#
+# The syntax is the following:
+# [classname:]principal
+# where classname is the class name of the principal object
+# (defaults to org.apache.karaf.jaas.modules.RolePrincipal)
+# and principal is the name of the principal of that class
+# (defaults to instance).
+#
+karaf.local.roles = admin,manager,viewer
+
+#
+# Set this empty property to avoid errors when validating xml documents.
+#
+xml.catalog.files =
+
+#
+# Suppress the bell in the console when hitting backspace too many times
+# for example
+#
+jline.nobell = true
+
+#
+# ServiceMix specs options
+#
+org.apache.servicemix.specs.debug = false
+org.apache.servicemix.specs.timeout = 0
+
+#
+# Settings for the OSGi 4.3 Weaving
+# By default, we will not weave any classes. Change this setting to include classes
+# that you application needs to have woven.
+#
+org.apache.aries.proxy.weaving.enabled = none
+# Classes not to weave - Aries default + Xerces which is known to have issues.
+org.apache.aries.proxy.weaving.disabled = org.objectweb.asm.*,org.slf4j.*,org.apache.log4j.*,javax.*,org.apache.xerces.*
+
+#
+# By default, only Karaf shell commands are secured, but additional services can be
+# secured by expanding this filter
+#
+karaf.secured.services = (&(osgi.command.scope=*)(osgi.command.function=*))
+
+#
+# Security properties
+#
+# To enable OSGi security, uncomment the properties below,
+# install the framework-security feature and restart.
+#
+#java.security.policy=${karaf.etc}/all.policy
+#org.osgi.framework.security=osgi
+#org.osgi.framework.trust.repositories=${karaf.etc}/trustStore.ks
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
- <plugin>
- <!-- FIXME: BUG-272: remove this configuration override -->
- <!-- replaced with new configuration -->
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-checkstyle-plugin</artifactId>
- <version>2.12</version>
- <configuration>
- <failsOnError>false</failsOnError>
- <failOnViolation>false</failOnViolation>
- <configLocation>checkstyle-logging.xml</configLocation>
- <consoleOutput>true</consoleOutput>
- <includeTestSourceDirectory>true</includeTestSourceDirectory>
- <sourceDirectory>${project.basedir}</sourceDirectory>
- <includes>**\/*.java,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat,**\/*.yang</includes>
- <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/</excludes>
- </configuration>
- <dependencies>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>checkstyle-logging</artifactId>
- <version>${yangtools.version}</version>
- </dependency>
- </dependencies>
- <executions>
- <execution>
- <goals>
- <goal>check</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
--- /dev/null
+package org.opendaylight.controller.md.sal.common.api.data;
+
+/**
+*
+* Failure of asynchronous transaction commit caused by failure
+* of optimistic locking.
+*
+* This exception is raised and returned when transaction commit
+* failed, because other transaction finished successfully
+* and modified same data as failed transaction.
+*
+* Clients may recover from this error condition by
+* retrieving current state and submitting new updated
+* transaction.
+*
+*/
+public class OptimisticLockFailedException extends TransactionCommitFailedException {
+
+ private static final long serialVersionUID = 1L;
+
+ protected OptimisticLockFailedException(final String message, final Throwable cause, final boolean enableSuppression,
+ final boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+ public OptimisticLockFailedException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+ public OptimisticLockFailedException(final String message) {
+ super(message);
+ }
+
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-distributed-datastore</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-actor_${scala.version}</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-cluster_${scala.version}</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-persistence-experimental_${scala.version}</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-remote_${scala.version}</artifactId>
+ </dependency>
+
+ <!-- SAL Dependencies -->
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</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-common-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-spi</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
+
+ <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</groupId>
+ <artifactId>yang-data-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+
+ <!-- AKKA Dependencies -->
+ <dependency>
+ <groupId>org.scala-lang</groupId>
+ <artifactId>scala-library</artifactId>
+ </dependency>
+
+ <!-- Test Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>${slf4j.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <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>
+ <Export-package></Export-package>
+ <Private-Package></Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <configuration>
+ <includes>
+ <include>org.opendaylight.controller.*</include>
+ </includes>
+ <check>false</check>
+ </configuration>
+ <executions>
+ <execution>
+ <id>pre-test</id>
+ <goals>
+ <goal>prepare-agent</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>post-test</id>
+ <goals>
+ <goal>report</goal>
+ </goals>
+ <phase>test</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Architecture:Clustering</url>
+ </scm>
+</project>
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+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.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ *
+ */
+public class DistributedDataStore implements DOMStore {
+
+ @Override
+ public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(InstanceIdentifier path, L listener, AsyncDataBroker.DataChangeScope scope) {
+ return new ListenerRegistrationProxy();
+ }
+
+ @Override
+ public DOMStoreTransactionChain createTransactionChain() {
+ return new TransactionChainProxy();
+ }
+
+ @Override
+ public DOMStoreReadTransaction newReadOnlyTransaction() {
+ return new TransactionProxy();
+ }
+
+ @Override
+ public DOMStoreWriteTransaction newWriteOnlyTransaction() {
+ return new TransactionProxy();
+ }
+
+ @Override
+ public DOMStoreReadWriteTransaction newReadWriteTransaction() {
+ return new TransactionProxy();
+ }
+}
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * ListenerRegistrationProxy acts as a proxy for a ListenerRegistration that was done on a remote shard
+ *
+ * Registering a DataChangeListener on the Data Store creates a new instance of the ListenerRegistrationProxy
+ * The ListenerRegistrationProxy talks to a remote ListenerRegistration actor.
+ */
+public class ListenerRegistrationProxy implements ListenerRegistration {
+ @Override
+ public Object getInstance() {
+ throw new UnsupportedOperationException("getInstance");
+ }
+
+ @Override
+ public void close() {
+ throw new UnsupportedOperationException("close");
+ }
+}
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+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.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+
+/**
+ * TransactionChainProxy acts as a proxy for a DOMStoreTransactionChain created on a remote shard
+ */
+public class TransactionChainProxy implements DOMStoreTransactionChain{
+ @Override
+ public DOMStoreReadTransaction newReadOnlyTransaction() {
+ throw new UnsupportedOperationException("newReadOnlyTransaction");
+ }
+
+ @Override
+ public DOMStoreReadWriteTransaction newReadWriteTransaction() {
+ throw new UnsupportedOperationException("newReadWriteTransaction");
+ }
+
+ @Override
+ public DOMStoreWriteTransaction newWriteOnlyTransaction() {
+ throw new UnsupportedOperationException("newWriteOnlyTransaction");
+ }
+
+ @Override
+ public void close() {
+ throw new UnsupportedOperationException("close");
+ }
+}
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+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.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * TransactionProxy acts as a proxy for one or more transactions that were created on a remote shard
+ *
+ * Creating a transaction on the consumer side will create one instance of a transaction proxy. If during
+ * the transaction reads and writes are done on data that belongs to different shards then a separate transaction will
+ * be created on each of those shards by the TransactionProxy
+ *
+ * The TransactionProxy does not make any guarantees about atomicity or order in which the transactions on the various
+ * shards will be executed.
+ *
+ */
+public class TransactionProxy implements DOMStoreReadWriteTransaction {
+ @Override
+ public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(InstanceIdentifier path) {
+ throw new UnsupportedOperationException("read");
+ }
+
+ @Override
+ public void write(InstanceIdentifier path, NormalizedNode<?, ?> data) {
+ throw new UnsupportedOperationException("write");
+ }
+
+ @Override
+ public void merge(InstanceIdentifier path, NormalizedNode<?, ?> data) {
+ throw new UnsupportedOperationException("merge");
+ }
+
+ @Override
+ public void delete(InstanceIdentifier path) {
+ throw new UnsupportedOperationException("delete");
+ }
+
+ @Override
+ public DOMStoreThreePhaseCommitCohort ready() {
+ throw new UnsupportedOperationException("ready");
+ }
+
+ @Override
+ public Object getIdentifier() {
+ throw new UnsupportedOperationException("getIdentifier");
+ }
+
+ @Override
+ public void close() {
+ throw new UnsupportedOperationException("close");
+ }
+}
--- /dev/null
+package org.opendaylight.controller.config.yang.config.distributed_datastore_provider;
+
+import org.opendaylight.controller.cluster.datastore.DistributedDataStore;
+
+public class DistributedDataStoreProviderModule extends org.opendaylight.controller.config.yang.config.distributed_datastore_provider.AbstractDistributedDataStoreProviderModule {
+ public DistributedDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public DistributedDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.config.distributed_datastore_provider.DistributedDataStoreProviderModule oldModule, java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ new DistributedDataStore();
+
+ final class AutoCloseableDistributedDataStore implements AutoCloseable {
+
+ @Override
+ public void close() throws Exception {
+
+ }
+ }
+
+ return new AutoCloseableDistributedDataStore();
+ }
+
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: distributed-datastore-provider yang module local name: distributed-datastore-provider
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Thu Jun 12 15:23:43 PDT 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.distributed_datastore_provider;
+public class DistributedDataStoreProviderModuleFactory extends org.opendaylight.controller.config.yang.config.distributed_datastore_provider.AbstractDistributedDataStoreProviderModuleFactory {
+
+}
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module distributed-datastore-provider {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:config:distributed-datastore-provider";
+ prefix "distributed-datastore-provider";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import rpc-context { prefix rpcx; revision-date 2013-06-17; }
+
+ description
+ "This module contains the base YANG definitions for
+ the distributed datastore provider implementation";
+
+ revision "2014-06-12" {
+ description
+ "Initial revision.";
+ }
+
+ // This is the definition of the service implementation as a module identity.
+ identity distributed-datastore-provider {
+ base config:module-type;
+
+ // Specifies the prefix for generated java classes.
+ config:java-name-prefix DistributedDataStoreProvider;
+ }
+
+ // Augments the 'configuration' choice node under modules/module.
+ augment "/config:modules/config:module/config:configuration" {
+ case distributed-datastore-provider {
+ when "/config:modules/config:module/config:type = 'distributed-datastore-provider'";
+ }
+ }
+}
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+import junit.framework.Assert;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+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.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class DistributedDataStoreTest {
+
+ private DistributedDataStore distributedDataStore;
+
+ @org.junit.Before
+ public void setUp() throws Exception {
+ distributedDataStore = new DistributedDataStore();
+ }
+
+ @org.junit.After
+ public void tearDown() throws Exception {
+
+ }
+
+ @org.junit.Test
+ public void testRegisterChangeListener() throws Exception {
+ ListenerRegistration registration =
+ distributedDataStore.registerChangeListener(InstanceIdentifier.builder().build(), new AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>() {
+ @Override
+ public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> change) {
+ throw new UnsupportedOperationException("onDataChanged");
+ }
+ }, AsyncDataBroker.DataChangeScope.BASE);
+
+ Assert.assertNotNull(registration);
+ }
+
+ @org.junit.Test
+ public void testCreateTransactionChain() throws Exception {
+ final DOMStoreTransactionChain transactionChain = distributedDataStore.createTransactionChain();
+ Assert.assertNotNull(transactionChain);
+ }
+
+ @org.junit.Test
+ public void testNewReadOnlyTransaction() throws Exception {
+ final DOMStoreReadTransaction transaction = distributedDataStore.newReadOnlyTransaction();
+ Assert.assertNotNull(transaction);
+ }
+
+ @org.junit.Test
+ public void testNewWriteOnlyTransaction() throws Exception {
+ final DOMStoreWriteTransaction transaction = distributedDataStore.newWriteOnlyTransaction();
+ Assert.assertNotNull(transaction);
+ }
+
+ @org.junit.Test
+ public void testNewReadWriteTransaction() throws Exception {
+ final DOMStoreReadWriteTransaction transaction = distributedDataStore.newReadWriteTransaction();
+ Assert.assertNotNull(transaction);
+ }
+}
/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
+ * 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.md.sal.common.api.data.TransactionChainFactory;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
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 {
+/**
+ * Data Broker which provides data transaction and data change listener fuctionality
+ * using {@link NormalizedNode} data format.
+ *
+ * This interface is type capture of generic interfaces and returns type captures
+ * of results for client-code convenience.
+ *
+ */
+public interface DOMDataBroker extends
+ AsyncDataBroker<InstanceIdentifier, NormalizedNode<?, ?>, DOMDataChangeListener>,
+ TransactionChainFactory<InstanceIdentifier, NormalizedNode<?, ?>>, BrokerService {
+
+ /**
+ * {@inheritDoc}
+ */
@Override
DOMDataReadTransaction newReadOnlyTransaction();
+ /**
+ * {@inheritDoc}
+ */
@Override
DOMDataReadWriteTransaction newReadWriteTransaction();
+ /**
+ * {@inheritDoc}
+ */
@Override
DOMDataWriteTransaction newWriteOnlyTransaction();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ DOMTransactionChain createTransactionChain(TransactionChainListener listener);
}
--- /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.TransactionChain;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A chain of DOM Data 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.
+ *
+ * <p>
+ * This interface is type capture of {@link TransactionChain} for DOM Data Contracts.
+ */
+public interface DOMTransactionChain extends TransactionChain<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+ @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.broker.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Composite DOM Transaction backed by {@link DOMStoreTransaction}.
+ *
+ * Abstract base for composite transaction, which provides access only to common
+ * functionality as retrieval of subtransaction, close method and retrieval of
+ * identifier.
+ *
+ * @param <K>
+ * Subtransaction distinguisher
+ * @param <T>
+ * Subtransaction type
+ */
+abstract class AbstractDOMForwardedCompositeTransaction<K, T extends DOMStoreTransaction> implements
+ AsyncTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+ private final ImmutableMap<K, T> backingTxs;
+ private final Object identifier;
+
+ /**
+ *
+ * Creates new composite Transactions.
+ *
+ * @param identifier
+ * Identifier of transaction.
+ * @param backingTxs
+ * Key,value map of backing transactions.
+ */
+ protected AbstractDOMForwardedCompositeTransaction(final Object identifier, final ImmutableMap<K, T> backingTxs) {
+ this.identifier = Preconditions.checkNotNull(identifier, "Identifier should not be null");
+ this.backingTxs = Preconditions.checkNotNull(backingTxs, "Backing transactions should not be null");
+ }
+
+ /**
+ * Returns subtransaction associated with supplied key.
+ *
+ * @param key
+ * @return
+ * @throws NullPointerException
+ * if key is null
+ * @throws IllegalArgumentException
+ * if no subtransaction is associated with key.
+ */
+ protected final T getSubtransaction(final K key) {
+ Preconditions.checkNotNull(key, "key must not be null.");
+ Preconditions.checkArgument(backingTxs.containsKey(key), "No subtransaction associated with %s", key);
+ return backingTxs.get(key);
+ }
+
+ /**
+ * Returns immutable Iterable of all subtransactions.
+ *
+ */
+ protected Iterable<T> getSubtransactions() {
+ return backingTxs.values();
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public void close() {
+ /*
+ * We share one exception for all failures, which are added
+ * as supressedExceptions to it.
+ *
+ */
+ IllegalStateException failure = null;
+ for (T subtransaction : backingTxs.values()) {
+ try {
+ subtransaction.close();
+ } catch (Exception e) {
+ // If we did not allocated failure we allocate it
+ if(failure == null) {
+ failure = new IllegalStateException("Uncaught exception occured during closing transaction.", e);
+ } else {
+ // We update it with addotional exceptions, which occured during error.
+ failure.addSuppressed(e);
+ }
+ }
+ }
+ // If we have failure, we throw it at after all attempts to close.
+ if(failure != null) {
+ throw failure;
+ }
+ }
+}
\ 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.md.sal.dom.broker.impl;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.annotation.concurrent.GuardedBy;
+
+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.api.DOMDataWriteTransaction;
+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.DOMStoreTransactionFactory;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ *
+ * Abstract composite transaction factory.
+ *
+ * Provides an convenience common implementation for composite DOM Transactions,
+ * where subtransaction is identified by {@link LogicalDatastoreType} type and
+ * implementation of subtransaction is provided by
+ * {@link DOMStoreTransactionFactory}.
+ *
+ * <b>Note:</b>This class does not have thread-safe implementation of {@link #close()},
+ * implementation may allow accessing and allocating new transactions during closing
+ * this instance.
+ *
+ * @param <T>
+ * Type of {@link DOMStoreTransactionFactory} factory.
+ */
+public abstract class AbstractDOMForwardedTransactionFactory<T extends DOMStoreTransactionFactory> implements DOMDataCommitImplementation, AutoCloseable {
+
+ private final ImmutableMap<LogicalDatastoreType, T> storeTxFactories;
+
+ private boolean closed;
+
+ protected AbstractDOMForwardedTransactionFactory(final Map<LogicalDatastoreType, ? extends T> txFactories) {
+ this.storeTxFactories = ImmutableMap.copyOf(txFactories);
+ }
+
+ /**
+ * Implementations must return unique identifier for each and every call of
+ * this method;
+ *
+ * @return new Unique transaction identifier.
+ */
+ protected abstract Object newTransactionIdentifier();
+
+ /**
+ * Creates a new composite read-only transaction
+ *
+ * Creates a new composite read-only transaction backed by one transaction
+ * per factory in {@link #getTxFactories()}.
+ *
+ * Subtransaction for reading is selected by supplied
+ * {@link LogicalDatastoreType} as parameter for
+ * {@link DOMDataReadTransaction#read(LogicalDatastoreType,org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)}
+ * .
+ *
+ * Id of returned transaction is retrieved via
+ * {@link #newTransactionIdentifier()}.
+ *
+ * @return New composite read-only transaction.
+ */
+ public DOMDataReadTransaction newReadOnlyTransaction() {
+ checkNotClosed();
+ ImmutableMap.Builder<LogicalDatastoreType, DOMStoreReadTransaction> builder = ImmutableMap.builder();
+ for (Entry<LogicalDatastoreType, T> store : storeTxFactories.entrySet()) {
+ builder.put(store.getKey(), store.getValue().newReadOnlyTransaction());
+ }
+ return new DOMForwardedReadOnlyTransaction(newTransactionIdentifier(), builder.build());
+ }
+
+
+
+ /**
+ * Creates a new composite write-only transaction
+ *
+ * <p>
+ * Creates a new composite write-only transaction backed by one write-only
+ * transaction per factory in {@link #getTxFactories()}.
+ *
+ * <p>
+ * Implementation of composite Write-only transaction is following:
+ *
+ * <ul>
+ * <li>
+ * {@link DOMDataWriteTransaction#put(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#write(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * is invoked on selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#merge(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#merge(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * is invoked on selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#delete(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#delete(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)} is invoked on
+ * selected subtransaction.
+ * <li> {@link DOMDataWriteTransaction#commit()} - results in invoking
+ * {@link DOMStoreWriteTransaction#ready()}, gathering all resulting cohorts
+ * and then invoking finalized implementation callback
+ * {@link #commit(DOMDataWriteTransaction, Iterable)} with transaction which
+ * was commited and gathered results.
+ * </ul>
+ *
+ * Id of returned transaction is generated via
+ * {@link #newTransactionIdentifier()}.
+ *
+ * @return New composite write-only transaction associated with this
+ * factory.
+ */
+ public DOMDataWriteTransaction newWriteOnlyTransaction() {
+ checkNotClosed();
+ ImmutableMap.Builder<LogicalDatastoreType, DOMStoreWriteTransaction> builder = ImmutableMap.builder();
+ for (Entry<LogicalDatastoreType, T> store : storeTxFactories.entrySet()) {
+ builder.put(store.getKey(), store.getValue().newWriteOnlyTransaction());
+ }
+ return new DOMForwardedWriteTransaction<DOMStoreWriteTransaction>(newTransactionIdentifier(), builder.build(),
+ this);
+ }
+
+ /**
+ * Creates a new composite write-only transaction
+ *
+ * <p>
+ * Creates a new composite write-only transaction backed by one write-only
+ * transaction per factory in {@link #getTxFactories()}.
+ * <p>
+ * Implementation of composite Write-only transaction is following:
+ *
+ * <ul>
+ * <li>
+ * {@link DOMDataWriteTransaction#read(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#read(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)} is invoked on
+ * selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#put(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#write(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * is invoked on selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#merge(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#merge(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * is invoked on selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#delete(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#delete(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)} is invoked on
+ * selected subtransaction.
+ * <li> {@link DOMDataWriteTransaction#commit()} - results in invoking
+ * {@link DOMStoreWriteTransaction#ready()}, gathering all resulting cohorts
+ * and then invoking finalized implementation callback
+ * {@link #commit(DOMDataWriteTransaction, Iterable)} with transaction which
+ * was commited and gathered results.
+ * <li>
+ * </ul>
+ *
+ * Id of returned transaction is generated via
+ * {@link #newTransactionIdentifier()}.
+ *
+ * @return New composite read-write transaction associated with this
+ * factory.
+ *
+ */
+ public DOMDataReadWriteTransaction newReadWriteTransaction() {
+ checkNotClosed();
+ ImmutableMap.Builder<LogicalDatastoreType, DOMStoreReadWriteTransaction> builder = ImmutableMap.builder();
+ for (Entry<LogicalDatastoreType, T> store : storeTxFactories.entrySet()) {
+ builder.put(store.getKey(), store.getValue().newReadWriteTransaction());
+ }
+ return new DOMForwardedReadWriteTransaction(newTransactionIdentifier(), builder.build(), this);
+ }
+
+ /**
+ * Convenience accessor of backing factories intended to be used only by
+ * finalization of this class.
+ *
+ * <b>Note:</b>
+ * Finalization of this class may want to access other functionality of
+ * supplied Transaction factories.
+ *
+ * @return Map of backing transaction factories.
+ */
+ protected final Map<LogicalDatastoreType, T> getTxFactories() {
+ return storeTxFactories;
+ }
+
+ /**
+ *
+ * Checks if instance is not closed.
+ *
+ * @throws IllegalStateException If instance of this class was closed.
+ *
+ */
+ @GuardedBy("this")
+ protected synchronized void checkNotClosed() {
+ Preconditions.checkState(!closed,"Transaction factory was closed. No further operations allowed.");
+ }
+
+ @Override
+ @GuardedBy("this")
+ public synchronized void close() {
+ closed = true;
+ }
+
+}
*/
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.common.api.data.TransactionChainListener;
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.md.sal.dom.api.DOMTransactionChain;
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.controller.sal.core.spi.data.DOMStoreTransactionChain;
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 {
+public class DOMDataBrokerImpl extends AbstractDOMForwardedTransactionFactory<DOMStore> 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 DOMDataCommitCoordinatorImpl coordinator;
private final AtomicLong txNum = new AtomicLong();
+ private final AtomicLong chainNum = new AtomicLong();
public DOMDataBrokerImpl(final ImmutableMap<LogicalDatastoreType, DOMStore> datastores,
final ListeningExecutorService executor) {
- super();
- this.datastores = datastores;
- this.executor = executor;
+ super(datastores);
+ this.coordinator = new DOMDataCommitCoordinatorImpl(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() {
+ protected 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);
+ DOMStore potentialStore = getTxFactories().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 synchronized 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) {
- getSubtransaction(store).merge(path,data);
- }
-
- @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);
- }
- }
-
- 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 DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+ ImmutableMap.Builder<LogicalDatastoreType, DOMStoreTransactionChain> backingChainsBuilder = ImmutableMap
+ .builder();
+ for (Entry<LogicalDatastoreType, DOMStore> entry : getTxFactories().entrySet()) {
+ backingChainsBuilder.put(entry.getKey(), entry.getValue().createTransactionChain());
+ }
+ long chainId = chainNum.getAndIncrement();
+ ImmutableMap<LogicalDatastoreType, DOMStoreTransactionChain> backingChains = backingChainsBuilder.build();
+ LOG.debug("Transactoin chain {} created with listener {}, backing store chains {}", chainId, listener,
+ backingChains);
+ return new DOMDataBrokerTransactionChainImpl(chainId, backingChains, coordinator, listener);
}
@Override
- public void close() throws Exception {
-
+ public ListenableFuture<RpcResult<TransactionStatus>> commit(final DOMDataWriteTransaction transaction,
+ final Iterable<DOMStoreThreePhaseCommitCohort> cohorts) {
+ LOG.debug("Transaction: {} submitted with cohorts {}.", transaction.getIdentifier(), cohorts);
+ return coordinator.submit(transaction, cohorts, Optional.<DOMDataCommitErrorListener> absent());
}
}
--- /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 java.util.concurrent.atomic.AtomicLong;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+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.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * NormalizedNode implementation of {@link org.opendaylight.controller.md.sal.common.api.data.TransactionChain} which is backed
+ * by several {@link DOMStoreTransactionChain} differentiated by provided
+ * {@link org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType} type.
+ *
+ */
+public class DOMDataBrokerTransactionChainImpl extends AbstractDOMForwardedTransactionFactory<DOMStoreTransactionChain>
+ implements DOMTransactionChain, DOMDataCommitErrorListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DOMDataBrokerTransactionChainImpl.class);
+ private final DOMDataCommitExecutor coordinator;
+ private final TransactionChainListener listener;
+ private final long chainId;
+ private final AtomicLong txNum = new AtomicLong();
+ @GuardedBy("this")
+ private boolean failed = false;
+
+ /**
+ *
+ * @param chainId
+ * ID of transaction chain
+ * @param chains
+ * Backing {@link DOMStoreTransactionChain}s.
+ * @param coordinator
+ * Commit Coordinator which should be used to coordinate commits
+ * of transaction
+ * produced by this chain.
+ * @param listener
+ * Listener, which listens on transaction chain events.
+ * @throws NullPointerException
+ * If any of arguments is null.
+ */
+ public DOMDataBrokerTransactionChainImpl(final long chainId,
+ final ImmutableMap<LogicalDatastoreType, DOMStoreTransactionChain> chains,
+ final DOMDataCommitExecutor coordinator, final TransactionChainListener listener) {
+ super(chains);
+ this.chainId = chainId;
+ this.coordinator = Preconditions.checkNotNull(coordinator);
+ this.listener = Preconditions.checkNotNull(listener);
+ }
+
+ @Override
+ protected Object newTransactionIdentifier() {
+ return "DOM-CHAIN-" + chainId + "-" + txNum.getAndIncrement();
+ }
+
+ @Override
+ public synchronized ListenableFuture<RpcResult<TransactionStatus>> commit(
+ final DOMDataWriteTransaction transaction, final Iterable<DOMStoreThreePhaseCommitCohort> cohorts) {
+ return coordinator.submit(transaction, cohorts, Optional.<DOMDataCommitErrorListener> of(this));
+ }
+
+ @Override
+ public synchronized void close() {
+ super.close();
+ for (DOMStoreTransactionChain subChain : getTxFactories().values()) {
+ subChain.close();
+ }
+
+ if (!failed) {
+ LOG.debug("Transaction chain {}Â successfully finished.", this);
+ listener.onTransactionChainSuccessful(this);
+ }
+ }
+
+ @Override
+ public synchronized void onCommitFailed(final DOMDataWriteTransaction tx, final Throwable cause) {
+ failed = true;
+ LOG.debug("Transaction chain {}Â failed.", this, cause);
+ listener.onTransactionChainFailed(this, tx, cause);
+ }
+}
--- /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 java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+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.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+/**
+ *
+ * Implementation of blocking three phase commit coordinator, which which
+ * supports coordination on multiple {@link DOMStoreThreePhaseCommitCohort}.
+ *
+ * This implementation does not support cancelation of commit,
+ *
+ * In order to advance to next phase of three phase commit all subtasks of
+ * previous step must be finish.
+ *
+ * This executor does not have an upper bound on subtask timeout.
+ *
+ *
+ */
+public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DOMDataCommitCoordinatorImpl.class);
+
+ /**
+ * Runs AND binary operation between all booleans in supplied iteration of booleans.
+ *
+ * This method will stop evaluating iterables if first found is false.
+ */
+ 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) {
+ return Boolean.FALSE;
+ }
+ }
+ return Boolean.TRUE;
+ }
+ };
+
+ private final ListeningExecutorService executor;
+
+ /**
+ *
+ * Construct DOMDataCommitCoordinator which uses supplied executor to
+ * process commit coordinations.
+ *
+ * @param executor
+ */
+ public DOMDataCommitCoordinatorImpl(final ListeningExecutorService executor) {
+ this.executor = Preconditions.checkNotNull(executor, "executor must not be null.");
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> submit(final DOMDataWriteTransaction transaction,
+ final Iterable<DOMStoreThreePhaseCommitCohort> cohorts, final Optional<DOMDataCommitErrorListener> listener) {
+ Preconditions.checkArgument(transaction != null, "Transaction must not be null.");
+ Preconditions.checkArgument(cohorts != null, "Cohorts must not be null.");
+ Preconditions.checkArgument(listener != null, "Listener must not be null");
+ LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier());
+ ListenableFuture<RpcResult<TransactionStatus>> commitFuture = executor.submit(new CommitCoordinationTask(
+ transaction, cohorts, listener));
+ if (listener.isPresent()) {
+ Futures.addCallback(commitFuture, new DOMDataCommitErrorInvoker(transaction, listener.get()));
+ }
+ return commitFuture;
+ }
+
+ /**
+ *
+ * Phase of 3PC commit
+ *
+ * Represents phase of 3PC Commit
+ *
+ *
+ */
+ private static enum CommitPhase {
+ /**
+ *
+ * Commit Coordination Task is submitted for executing
+ *
+ */
+ SUBMITTED,
+ /**
+ * Commit Coordination Task is in can commit phase of 3PC
+ *
+ */
+ CAN_COMMIT,
+ /**
+ * Commit Coordination Task is in pre-commit phase of 3PC
+ *
+ */
+ PRE_COMMIT,
+ /**
+ * Commit Coordination Task is in commit phase of 3PC
+ *
+ */
+ COMMIT,
+ /**
+ * Commit Coordination Task is in abort phase of 3PC
+ *
+ */
+ ABORT
+ }
+
+ /**
+ *
+ * Implementation of blocking three-phase commit-coordination tasks without
+ * support of cancelation.
+ *
+ */
+ private static class CommitCoordinationTask implements Callable<RpcResult<TransactionStatus>> {
+
+ private final DOMDataWriteTransaction tx;
+ private final Iterable<DOMStoreThreePhaseCommitCohort> cohorts;
+
+ @GuardedBy("this")
+ private CommitPhase currentPhase;
+
+ public CommitCoordinationTask(final DOMDataWriteTransaction transaction,
+ final Iterable<DOMStoreThreePhaseCommitCohort> cohorts,
+ final Optional<DOMDataCommitErrorListener> listener) {
+ this.tx = Preconditions.checkNotNull(transaction, "transaction must not be null");
+ this.cohorts = Preconditions.checkNotNull(cohorts, "cohorts must not be null");
+ this.currentPhase = CommitPhase.SUBMITTED;
+ }
+
+ @Override
+ public RpcResult<TransactionStatus> call() throws TransactionCommitFailedException {
+
+ try {
+ canCommitBlocking();
+ preCommitBlocking();
+ return commitBlocking();
+ } catch (TransactionCommitFailedException e) {
+ LOG.warn("Tx: {} Error during phase {}, starting Abort", tx.getIdentifier(), currentPhase, e);
+ abortBlocking(e);
+ throw e;
+ }
+ }
+
+ /**
+ *
+ * Invokes canCommit on underlying cohorts and blocks till
+ * all results are returned.
+ *
+ * Valid state transition is from SUBMITTED to CAN_COMMIT,
+ * if currentPhase is not SUBMITTED throws IllegalStateException.
+ *
+ * @throws TransactionCommitFailedException
+ * If one of cohorts failed can Commit
+ *
+ */
+ private void canCommitBlocking() throws TransactionCommitFailedException {
+ final Boolean canCommitResult = canCommitAll().checkedGet();
+ if (!canCommitResult) {
+ throw new TransactionCommitFailedException("Can Commit failed, no detailed cause available.");
+ }
+ }
+
+ /**
+ *
+ * Invokes preCommit on underlying cohorts and blocks till
+ * all results are returned.
+ *
+ * Valid state transition is from CAN_COMMIT to PRE_COMMIT, if current
+ * state is not CAN_COMMIT
+ * throws IllegalStateException.
+ *
+ * @throws TransactionCommitFailedException
+ * If one of cohorts failed preCommit
+ *
+ */
+ private void preCommitBlocking() throws TransactionCommitFailedException {
+ preCommitAll().checkedGet();
+ }
+
+ /**
+ *
+ * Invokes commit on underlying cohorts and blocks till
+ * all results are returned.
+ *
+ * Valid state transition is from PRE_COMMIT to COMMIT, if not throws
+ * IllegalStateException.
+ *
+ * @throws TransactionCommitFailedException
+ * If one of cohorts failed preCommit
+ *
+ */
+ private RpcResult<TransactionStatus> commitBlocking() throws TransactionCommitFailedException {
+ commitAll().checkedGet();
+ return Rpcs.getRpcResult(true, TransactionStatus.COMMITED, Collections.<RpcError> emptySet());
+ }
+
+ /**
+ * Aborts transaction.
+ *
+ * Invokes {@link DOMStoreThreePhaseCommitCohort#abort()} on all
+ * cohorts, blocks
+ * for all results. If any of the abort failed throws
+ * IllegalStateException,
+ * which will contains originalCause as suppressed Exception.
+ *
+ * If aborts we're successful throws supplied exception
+ *
+ * @param originalCause
+ * Exception which should be used to fail transaction for
+ * consumers of transaction
+ * future and listeners of transaction failure.
+ * @throws TransactionCommitFailedException
+ * on invocation of this method.
+ * originalCa
+ * @throws IllegalStateException
+ * if abort failed.
+ */
+ private void abortBlocking(final TransactionCommitFailedException originalCause)
+ throws TransactionCommitFailedException {
+ LOG.warn("Tx: {} Error during phase {}, starting Abort", tx.getIdentifier(), currentPhase, originalCause);
+ Exception cause = originalCause;
+ try {
+ abortAsyncAll().get();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Tx: {} Error during Abort.", tx.getIdentifier(), e);
+ cause = new IllegalStateException("Abort failed.", e);
+ cause.addSuppressed(e);
+ }
+ Throwables.propagateIfPossible(cause, TransactionCommitFailedException.class);
+ }
+
+ /**
+ *
+ * Invokes preCommit on underlying cohorts and returns future
+ * which will complete once all preCommit on cohorts completed or
+ * failed.
+ *
+ *
+ * Valid state transition is from CAN_COMMIT to PRE_COMMIT, if current
+ * state is not CAN_COMMIT
+ * throws IllegalStateException.
+ *
+ * @return Future which will complete once all cohorts completed
+ * preCommit.
+ * Future throws TransactionCommitFailedException
+ * If any of cohorts failed preCommit
+ *
+ */
+ private CheckedFuture<Void, TransactionCommitFailedException> preCommitAll() {
+ changeStateFrom(CommitPhase.CAN_COMMIT, CommitPhase.PRE_COMMIT);
+ Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
+ for (DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+ ops.add(cohort.preCommit());
+ }
+ /*
+ * We are returing all futures as list, not only succeeded ones in
+ * order to fail composite future if any of them failed.
+ * See Futures.allAsList for this description.
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ ListenableFuture<Void> compositeResult = (ListenableFuture) Futures.allAsList(ops.build());
+ return Futures.makeChecked(compositeResult, TransactionCommitFailedExceptionMapper.PRE_COMMIT_MAPPER);
+ }
+
+ /**
+ *
+ * Invokes commit on underlying cohorts and returns future which
+ * completes
+ * once all commits on cohorts are completed.
+ *
+ * Valid state transition is from PRE_COMMIT to COMMIT, if not throws
+ * IllegalStateException
+ *
+ * @return Future which will complete once all cohorts completed
+ * commit.
+ * Future throws TransactionCommitFailedException
+ * If any of cohorts failed preCommit
+ *
+ */
+ private CheckedFuture<Void, TransactionCommitFailedException> commitAll() {
+ changeStateFrom(CommitPhase.PRE_COMMIT, CommitPhase.COMMIT);
+ Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
+ for (DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+ ops.add(cohort.commit());
+ }
+ /*
+ * We are returing all futures as list, not only succeeded ones in
+ * order to fail composite future if any of them failed.
+ * See Futures.allAsList for this description.
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ ListenableFuture<Void> compositeResult = (ListenableFuture) Futures.allAsList(ops.build());
+ return Futures.makeChecked(compositeResult, TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER);
+ }
+
+ /**
+ *
+ * Invokes canCommit on underlying cohorts and returns composite future
+ * which will contains {@link Boolean#TRUE} only and only if
+ * all cohorts returned true.
+ *
+ * Valid state transition is from SUBMITTED to CAN_COMMIT,
+ * if currentPhase is not SUBMITTED throws IllegalStateException.
+ *
+ * @return Future which will complete once all cohorts completed
+ * preCommit.
+ * Future throws TransactionCommitFailedException
+ * If any of cohorts failed preCommit
+ *
+ */
+ private CheckedFuture<Boolean, TransactionCommitFailedException> canCommitAll() {
+ changeStateFrom(CommitPhase.SUBMITTED, CommitPhase.CAN_COMMIT);
+ Builder<ListenableFuture<Boolean>> canCommitOperations = ImmutableList.builder();
+ for (DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+ canCommitOperations.add(cohort.canCommit());
+ }
+ ListenableFuture<List<Boolean>> allCanCommits = Futures.allAsList(canCommitOperations.build());
+ ListenableFuture<Boolean> allSuccessFuture = Futures.transform(allCanCommits, AND_FUNCTION);
+ return Futures
+ .makeChecked(allSuccessFuture, TransactionCommitFailedExceptionMapper.CAN_COMMIT_ERROR_MAPPER);
+
+ }
+
+ /**
+ *
+ * Invokes abort on underlying cohorts and returns future which
+ * completes
+ * once all abort on cohorts are completed.
+ *
+ * @return Future which will complete once all cohorts completed
+ * abort.
+ *
+ */
+ private ListenableFuture<Void> abortAsyncAll() {
+ changeStateFrom(currentPhase, CommitPhase.ABORT);
+ Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
+ for (DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+ ops.add(cohort.abort());
+ }
+ /*
+ * We are returing all futures as list, not only succeeded ones in
+ * order to fail composite future if any of them failed.
+ * See Futures.allAsList for this description.
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ ListenableFuture<Void> compositeResult = (ListenableFuture) Futures.allAsList(ops.build());
+ return compositeResult;
+ }
+
+ /**
+ * Change phase / state of transaction from expected value to new value
+ *
+ * This method checks state and updates state to new state of
+ * of this task if current state equals expected state.
+ * If expected state and current state are different raises
+ * IllegalStateException
+ * which means there is probably bug in implementation of commit
+ * coordination.
+ *
+ * If transition is successful, it logs transition on DEBUG level.
+ *
+ * @param currentExpected
+ * Required phase for change of state
+ * @param newState
+ * New Phase which will be entered by transaction.
+ * @throws IllegalStateException
+ * If currentState of task does not match expected state
+ */
+ private synchronized void changeStateFrom(final CommitPhase currentExpected, final CommitPhase newState) {
+ Preconditions.checkState(currentPhase.equals(currentExpected),
+ "Invalid state transition: Tx: %s current state: %s new state: %s", tx.getIdentifier(),
+ currentPhase, newState);
+ LOG.debug("Transaction {}: Phase {} Started ", tx.getIdentifier(), newState);
+ currentPhase = newState;
+ };
+
+ }
+
+}
--- /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 org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.FutureCallback;
+
+/**
+ *
+ * Utility implemetation of {@link FutureCallback} which is responsible
+ * for invoking {@link DOMDataCommitErrorListener} on TransactionCommit failed.
+ *
+ * When {@link #onFailure(Throwable)} is invoked, supplied {@link DOMDataCommitErrorListener}
+ * callback is invoked with associated transaction and throwable is invoked on listener.
+ *
+ */
+class DOMDataCommitErrorInvoker implements FutureCallback<RpcResult<TransactionStatus>> {
+
+ private final DOMDataWriteTransaction tx;
+ private final DOMDataCommitErrorListener listener;
+
+
+ /**
+ *
+ * Construct new DOMDataCommitErrorInvoker.
+ *
+ * @param transaction Transaction which should be passed as argument to {@link DOMDataCommitErrorListener#onCommitFailed(DOMDataWriteTransaction, Throwable)}
+ * @param listener Listener which should be invoked on error.
+ */
+ public DOMDataCommitErrorInvoker(DOMDataWriteTransaction transaction, DOMDataCommitErrorListener listener) {
+ this.tx = Preconditions.checkNotNull(transaction, "Transaction must not be null");
+ this.listener = Preconditions.checkNotNull(listener, "Listener must not be null");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ listener.onCommitFailed(tx, t);
+ }
+
+ @Override
+ public void onSuccess(RpcResult<TransactionStatus> result) {
+ // NOOP
+ }
+}
\ 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.md.sal.dom.broker.impl;
+
+import java.util.EventListener;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+
+/**
+ *
+ * Listener on transaction failure which may be passed to
+ * {@link DOMDataCommitExecutor}. This listener is notified during transaction
+ * processing, before result is delivered to other client code outside MD-SAL.
+ * This allows implementors to update their internal state before transaction
+ * failure is visible to client code.
+ *
+ * This is internal API for MD-SAL implementations, for consumer facing error
+ * listeners see {@link org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener}.
+ *
+ */
+interface DOMDataCommitErrorListener extends EventListener {
+
+ /**
+ *
+ * Callback which is invoked on transaction failure during three phase
+ * commit in {@link DOMDataCommitExecutor}.
+ *
+ *
+ * Implementation of this callback MUST NOT do any blocking calls or any
+ * calls to MD-SAL, since this callback is invoked synchronously on MD-SAL
+ * Broker coordination thread.
+ *
+ * @param tx
+ * Transaction which failed
+ * @param cause
+ * Failure reason
+ */
+ void onCommitFailed(DOMDataWriteTransaction tx, Throwable cause);
+
+}
--- /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 org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Executor of Three Phase Commit coordination for
+ * {@link DOMDataWriteTransaction} transactions.
+ *
+ * Implementations are responsible for executing implementation of three-phase
+ * commit protocol on supplied {@link DOMStoreThreePhaseCommitCohort}s.
+ *
+ *
+ */
+interface DOMDataCommitExecutor {
+
+ /**
+ * Submits supplied transaction to be executed in context of provided
+ * cohorts.
+ *
+ * Transaction is used only as a context, cohorts should be associated with
+ * this transaction.
+ *
+ * @param tx
+ * Transaction to be used as context for reporting
+ * @param cohort
+ * DOM Store cohorts representing provided transaction, its
+ * subtransactoins.
+ * @param listener
+ * Error listener which should be notified if transaction failed.
+ * @return ListenableFuture which contains RpcResult with
+ * {@link TransactionStatus#COMMITED} if commit coordination on
+ * cohorts finished successfully.
+ *
+ */
+ ListenableFuture<RpcResult<TransactionStatus>> submit(DOMDataWriteTransaction tx,
+ Iterable<DOMStoreThreePhaseCommitCohort> cohort, Optional<DOMDataCommitErrorListener> listener);
+
+}
--- /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 org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ * Implementation prototype of commit method for
+ * {@link DOMForwardedWriteTransaction}.
+ *
+ */
+public interface DOMDataCommitImplementation {
+
+ /**
+ * User-supplied implementation of {@link DOMDataWriteTransaction#commit()}
+ * for transaction.
+ *
+ * Callback invoked when {@link DOMDataWriteTransaction#commit()} is invoked
+ * on transaction created by this factory.
+ *
+ * @param transaction
+ * Transaction on which {@link DOMDataWriteTransaction#commit()}
+ * was invoked.
+ * @param cohorts
+ * Iteration of cohorts for subtransactions associated with
+ * commited transaction.
+ *
+ */
+ ListenableFuture<RpcResult<TransactionStatus>> commit(final DOMDataWriteTransaction transaction,
+ final Iterable<DOMStoreThreePhaseCommitCohort> cohorts);
+}
+
--- /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 org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+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.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ * Read Only Transaction, which is composed of several
+ * {@link DOMStoreReadTransaction} transactions. Subtransaction is selected by
+ * {@link LogicalDatastoreType} type parameter in
+ * {@link #read(LogicalDatastoreType, InstanceIdentifier)}.
+ */
+class DOMForwardedReadOnlyTransaction extends
+ AbstractDOMForwardedCompositeTransaction<LogicalDatastoreType, DOMStoreReadTransaction> implements
+ DOMDataReadTransaction {
+
+ protected DOMForwardedReadOnlyTransaction(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);
+ }
+
+}
--- /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 org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+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.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ * Read-Write Transaction, which is composed of several
+ * {@link DOMStoreReadWriteTransaction} transactions. Subtransaction is selected by
+ * {@link LogicalDatastoreType} type parameter in:
+ *
+ * <ul>
+ * <li>{@link #read(LogicalDatastoreType, InstanceIdentifier)}
+ * <li>{@link #put(LogicalDatastoreType, InstanceIdentifier, NormalizedNode)}
+ * <li>{@link #delete(LogicalDatastoreType, InstanceIdentifier)}
+ * <li>{@link #merge(LogicalDatastoreType, InstanceIdentifier, NormalizedNode)}
+ * </ul>
+ * {@link #commit()} will result in invocation of
+ * {@link DOMDataCommitImplementation#commit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)}
+ * invocation with all {@link org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort} for underlying
+ * transactions.
+ *
+ */
+
+class DOMForwardedReadWriteTransaction extends DOMForwardedWriteTransaction<DOMStoreReadWriteTransaction> implements
+ DOMDataReadWriteTransaction {
+
+ protected DOMForwardedReadWriteTransaction(final Object identifier,
+ final ImmutableMap<LogicalDatastoreType, DOMStoreReadWriteTransaction> backingTxs,
+ final DOMDataCommitImplementation commitImpl) {
+ super(identifier, backingTxs, commitImpl);
+ }
+
+ @Override
+ public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
+ final InstanceIdentifier path) {
+ return getSubtransaction(store).read(path);
+ }
+}
\ 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.md.sal.dom.broker.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import javax.annotation.concurrent.GuardedBy;
+
+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.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+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 com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ *
+ * Read-Write Transaction, which is composed of several
+ * {@link DOMStoreWriteTransaction} transactions. Subtransaction is selected by
+ * {@link LogicalDatastoreType} type parameter in:
+ *
+ * <ul>
+ * <li>{@link #put(LogicalDatastoreType, InstanceIdentifier, NormalizedNode)}
+ * <li>{@link #delete(LogicalDatastoreType, InstanceIdentifier)}
+ * <li>{@link #merge(LogicalDatastoreType, InstanceIdentifier, NormalizedNode)}
+ * </ul>
+ * <p>
+ * {@link #commit()} will result in invocation of
+ * {@link DOMDataCommitImplementation#commit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)}
+ * invocation with all {@link org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort} for underlying
+ * transactions.
+ *
+ * @param <T>
+ * Subtype of {@link DOMStoreWriteTransaction} which is used as
+ * subtransaction.
+ */
+class DOMForwardedWriteTransaction<T extends DOMStoreWriteTransaction> extends
+ AbstractDOMForwardedCompositeTransaction<LogicalDatastoreType, T> implements DOMDataWriteTransaction {
+
+ @GuardedBy("this")
+ private DOMDataCommitImplementation commitImpl;
+
+ @GuardedBy("this")
+ private boolean canceled;
+ @GuardedBy("this")
+ private ListenableFuture<RpcResult<TransactionStatus>> commitFuture;
+
+ protected DOMForwardedWriteTransaction(final Object identifier,
+ final ImmutableMap<LogicalDatastoreType, T> backingTxs, final DOMDataCommitImplementation commitImpl) {
+ super(identifier, backingTxs);
+ this.commitImpl = Preconditions.checkNotNull(commitImpl, "commitImpl must not be null.");
+ }
+
+ @Override
+ public void put(final LogicalDatastoreType store, final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ checkNotReady();
+ getSubtransaction(store).write(path, data);
+ }
+
+ @Override
+ public void delete(final LogicalDatastoreType store, final InstanceIdentifier path) {
+ checkNotReady();
+ getSubtransaction(store).delete(path);
+ }
+
+ @Override
+ public void merge(final LogicalDatastoreType store, final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ checkNotReady();
+ getSubtransaction(store).merge(path, data);
+ }
+
+ @Override
+ public synchronized void cancel() {
+ checkState(!canceled, "Transaction was canceled.");
+ if (commitFuture != null) {
+ // FIXME: Implement cancelation of commit future
+ // when Broker impl will support cancelation.
+ throw new UnsupportedOperationException("Not implemented yet.");
+ }
+ canceled = true;
+ commitImpl = null;
+
+ }
+
+ @Override
+ public synchronized ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ checkNotReady();
+
+ ImmutableList.Builder<DOMStoreThreePhaseCommitCohort> cohortsBuilder = ImmutableList.builder();
+ for (DOMStoreWriteTransaction subTx : getSubtransactions()) {
+ cohortsBuilder.add(subTx.ready());
+ }
+ ImmutableList<DOMStoreThreePhaseCommitCohort> cohorts = cohortsBuilder.build();
+ commitFuture = commitImpl.commit(this, cohorts);
+ return commitFuture;
+ }
+
+ private void checkNotReady() {
+ checkNotCanceled();
+ checkNotCommited();
+ }
+
+ private void checkNotCanceled() {
+ Preconditions.checkState(!canceled, "Transaction was canceled.");
+ }
+
+ private void checkNotCommited() {
+ checkState(commitFuture == null, "Transaction was already commited.");
+ }
+
+}
\ 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.md.sal.dom.broker.impl;
+
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+
+/**
+ *
+ * Utility exception mapper which translates {@link Exception}
+ * to {@link TransactionCommitFailedException}.
+ *
+ * This mapper is intended to be used with {@link com.google.common.util.concurrent.Futures#makeChecked(com.google.common.util.concurrent.ListenableFuture, Function)}
+ * <ul>
+ * <li>if exception is {@link TransactionCommitFailedException} or one of its subclasses returns original exception.
+ * <li>if exception is {@link ExecutionException} and cause is {@link TransactionCommitFailedException} return cause
+ * <li>otherwise returns {@link TransactionCommitFailedException} with original exception as a cause.
+ * </ul>
+ *
+ */
+
+final class TransactionCommitFailedExceptionMapper implements
+ Function<Exception, TransactionCommitFailedException> {
+
+ static final TransactionCommitFailedExceptionMapper PRE_COMMIT_MAPPER = create("canCommit");
+
+ static final TransactionCommitFailedExceptionMapper CAN_COMMIT_ERROR_MAPPER = create("preCommit");
+
+ static final TransactionCommitFailedExceptionMapper COMMIT_ERROR_MAPPER = create("commit");
+
+ private final String opName;
+
+ private TransactionCommitFailedExceptionMapper(final String opName) {
+ this.opName = Preconditions.checkNotNull(opName);
+ }
+
+ public static final TransactionCommitFailedExceptionMapper create(final String opName) {
+ return new TransactionCommitFailedExceptionMapper(opName);
+ }
+
+ @Override
+ public TransactionCommitFailedException apply(final Exception e) {
+ // If excetion is TransactionCommitFailedException
+ // we reuse it directly.
+ if (e instanceof TransactionCommitFailedException) {
+ return (TransactionCommitFailedException) e;
+ }
+ // If error is ExecutionException which was caused by cause of
+ // TransactionCommitFailedException
+ // we reuse original cause
+ if (e instanceof ExecutionException && e.getCause() instanceof TransactionCommitFailedException) {
+ return (TransactionCommitFailedException) e.getCause();
+ }
+ if (e instanceof InterruptedException) {
+ return new TransactionCommitFailedException(opName + " failed - DOMStore was interupted.", e);
+ }
+ // Otherwise we are using new exception, with original cause
+ return new TransactionCommitFailedException(opName + " failed", e);
+ }
+}
\ No newline at end of file
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.OptimisticLockFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ConflictingModificationAppliedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTree;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeCandidate;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeModification;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeSnapshot;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.InMemoryDataTreeFactory;
import org.opendaylight.controller.sal.core.spi.data.DOMStore;
public ListenableFuture<Boolean> canCommit() {
return executor.submit(new Callable<Boolean>() {
@Override
- public Boolean call() {
+ public Boolean call() throws TransactionCommitFailedException {
try {
dataTree.validate(modification);
LOG.debug("Store Transaction: {} can be committed", transaction.getIdentifier());
return true;
- } catch (DataPreconditionFailedException e) {
+ } catch (ConflictingModificationAppliedException e) {
+ LOG.warn("Store Tx: {} Conflicting modification for {}.", transaction.getIdentifier(),
+ e.getPath());
+ throw new OptimisticLockFailedException("Optimistic lock failed.",e);
+ } catch (DataValidationFailedException e) {
LOG.warn("Store Tx: {} Data Precondition failed for {}.", transaction.getIdentifier(),
e.getPath(), e);
- return false;
+ throw new TransactionCommitFailedException("Data did not pass validation.",e);
}
}
});
--- /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;
+
+/**
+ * Exception thrown when a proposed change fails validation before being
+ * applied into the Data Tree because the Data Tree has been modified
+ * in way that a conflicting
+ * node is present.
+ */
+public class ConflictingModificationAppliedException extends DataValidationFailedException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public ConflictingModificationAppliedException(final InstanceIdentifier path, final String message, final Throwable cause) {
+ super(path, message, cause);
+ }
+
+ public ConflictingModificationAppliedException(final InstanceIdentifier path, final String message) {
+ super(path, message);
+ }
+
+}
/**
* Validate whether a particular modification can be applied to the data tree.
*/
- void validate(DataTreeModification modification) throws DataPreconditionFailedException;
+ void validate(DataTreeModification modification) throws DataValidationFailedException;
/**
* Prepare a modification for commit.
* the datastore has been concurrently modified such that a conflicting
* node is present, or the modification is structurally incorrect.
*/
-public class DataPreconditionFailedException extends Exception {
+public class DataValidationFailedException extends Exception {
private static final long serialVersionUID = 1L;
private final InstanceIdentifier path;
* @param path Object path which caused this exception
* @param message Specific message describing the failure
*/
- public DataPreconditionFailedException(final InstanceIdentifier path, final String message) {
+ public DataValidationFailedException(final InstanceIdentifier path, final String message) {
this(path, message, null);
}
/**
* @param message Specific message describing the failure
* @param cause Exception which triggered this failure, may be null
*/
- public DataPreconditionFailedException(final InstanceIdentifier path, final String message, final Throwable cause) {
+ public DataValidationFailedException(final InstanceIdentifier path, final String message, final Throwable cause) {
super(message, cause);
this.path = Preconditions.checkNotNull(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;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+/**
+ * Exception thrown when a proposed change fails validation before being
+ * applied into the datastore because of incorrect structure of user supplied
+ * data.
+ *
+ */
+public class IncorrectDataStructureException extends DataValidationFailedException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public IncorrectDataStructureException(final InstanceIdentifier path, final String message, final Throwable cause) {
+ super(path, message, cause);
+ }
+
+ public IncorrectDataStructureException(final InstanceIdentifier path, final String message) {
+ super(path, message);
+ }
+
+}
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTree;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeCandidate;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeModification;
}
@Override
- public void validate(final DataTreeModification modification) throws DataPreconditionFailedException {
+ public void validate(final DataTreeModification modification) throws DataValidationFailedException {
Preconditions.checkArgument(modification instanceof InMemoryDataTreeModification, "Invalid modification class %s", modification.getClass());
final InMemoryDataTreeModification m = (InMemoryDataTreeModification)modification;
*/
package org.opendaylight.controller.md.sal.dom.store.impl.tree.data;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreTreeNode;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNode;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.Version;
* @param current Metadata Node to which modification should be applied
* @return true if modification is applicable
* false if modification is no applicable
- * @throws DataPreconditionFailedException
+ * @throws DataValidationFailedException
*/
- void checkApplicable(InstanceIdentifier path, NodeModification modification, Optional<TreeNode> current) throws DataPreconditionFailedException;
+ void checkApplicable(InstanceIdentifier path, NodeModification modification, Optional<TreeNode> current) throws DataValidationFailedException;
}
import java.util.Map;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.ListEntryModificationStrategy;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.ValueNodeModificationStrategy.LeafSetEntryModificationStrategy;
@Override
protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ final Optional<TreeNode> current) throws DataValidationFailedException {
// FIXME: Implement proper write check for replacement of node container
// prerequisite is to have transaction chain available for clients
// otherwise this will break chained writes to same node.
@Override
protected void checkSubtreeModificationApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
- checkDataPrecondition(path, current.isPresent(), "Node was deleted by other transaction.");
+ final Optional<TreeNode> current) throws DataValidationFailedException {
+ checkConflicting(path, current.isPresent(), "Node was deleted by other transaction.");
checkChildPreconditions(path, modification, current);
}
- private void checkChildPreconditions(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ private void checkChildPreconditions(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
final TreeNode currentMeta = current.get();
for (NodeModification childMod : modification.getChildren()) {
final PathArgument childId = childMod.getIdentifier();
@Override
protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ final Optional<TreeNode> current) throws DataValidationFailedException {
if(current.isPresent()) {
checkChildPreconditions(path, modification,current);
}
import java.util.List;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ConflictingModificationAppliedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.IncorrectDataStructureException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.ContainerModificationStrategy;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.UnkeyedListItemModificationStrategy;
return null;
}
- public static boolean checkDataPrecondition(final InstanceIdentifier path, final boolean condition, final String message) throws DataPreconditionFailedException {
+ public static boolean checkConflicting(final InstanceIdentifier path, final boolean condition, final String message) throws ConflictingModificationAppliedException {
if(!condition) {
- throw new DataPreconditionFailedException(path, message);
+ throw new ConflictingModificationAppliedException(path, message);
}
return condition;
}
}
}
- private static final void checkNotConflicting(final InstanceIdentifier path, final TreeNode original, final TreeNode current) throws DataPreconditionFailedException {
- checkDataPrecondition(path, original.getVersion().equals(current.getVersion()),
+ private static final void checkNotConflicting(final InstanceIdentifier path, final TreeNode original, final TreeNode current) throws ConflictingModificationAppliedException {
+ checkConflicting(path, original.getVersion().equals(current.getVersion()),
"Node was replaced by other transaction.");
- checkDataPrecondition(path, original.getSubtreeVersion().equals(current.getSubtreeVersion()),
+ checkConflicting(path, original.getSubtreeVersion().equals(current.getSubtreeVersion()),
"Node children was modified by other transaction");
}
}
@Override
- public final void checkApplicable(final InstanceIdentifier path,final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ public final void checkApplicable(final InstanceIdentifier path,final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
switch (modification.getType()) {
case DELETE:
checkDeleteApplicable(modification, current);
}
- protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
Optional<TreeNode> original = modification.getOriginal();
if (original.isPresent() && current.isPresent()) {
/*
}
}
- protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
Optional<TreeNode> original = modification.getOriginal();
if (original.isPresent() && current.isPresent()) {
checkNotConflicting(path, original.get(), current.get());
} else if(original.isPresent()) {
- throw new DataPreconditionFailedException(path,"Node was deleted by other transaction.");
+ throw new ConflictingModificationAppliedException(path,"Node was deleted by other transaction.");
}
}
protected abstract TreeNode applySubtreeChange(ModifiedNode modification,
TreeNode currentMeta, Version version);
+ /**
+ *
+ * Checks is supplied {@link NodeModification} is applicable for Subtree Modification.
+ *
+ * @param path Path to current node
+ * @param modification Node modification which should be applied.
+ * @param current Current state of data tree
+ * @throws ConflictingModificationAppliedException If subtree was changed in conflicting way
+ * @throws IncorrectDataStructureException If subtree modification is not applicable (e.g. leaf node).
+ */
protected abstract void checkSubtreeModificationApplicable(InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException;
+ final Optional<TreeNode> current) throws DataValidationFailedException;
protected abstract void verifyWrittenStructure(NormalizedNode<?, ?> writtenValue);
@Override
protected void checkSubtreeModificationApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
- throw new DataPreconditionFailedException(path, "Subtree modification is not allowed.");
+ final Optional<TreeNode> current) throws IncorrectDataStructureException {
+ throw new IncorrectDataStructureException(path, "Subtree modification is not allowed.");
}
}
}
import static com.google.common.base.Preconditions.checkArgument;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.IncorrectDataStructureException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNode;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNodeFactory;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.Version;
@Override
protected void checkSubtreeModificationApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
- throw new DataPreconditionFailedException(path, "Subtree modification is not allowed.");
+ final Optional<TreeNode> current) throws IncorrectDataStructureException {
+ throw new IncorrectDataStructureException(path, "Subtree modification is not allowed.");
}
public static class LeafSetEntryModificationStrategy extends ValueNodeModificationStrategy<LeafListSchemaNode> {
package org.opendaylight.controller.sal.dom.broker.osgi;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
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.md.sal.dom.api.DOMTransactionChain;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.osgi.framework.ServiceReference;
return getDelegate().registerDataChangeListener(store, path, listener, triggeringScope);
}
+ @Override
+ public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+ return getDelegate().createTransactionChain(listener);
+ }
+
}
--- /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 org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+
+import com.google.common.util.concurrent.SettableFuture;
+
+/**
+ * Simple implementation of {@link TransactionChainListener} for testing.
+ *
+ * This transaction chain listener does not contain any logic, only update
+ * futures ({@link #getFailFuture()} and {@link #getSuccessFuture()} when
+ * transaction chain event is retrieved.
+ *
+ */
+class BlockingTransactionChainListener implements TransactionChainListener {
+
+ private final SettableFuture<Throwable> failFuture = SettableFuture.create();
+ private final SettableFuture<Void> successFuture = SettableFuture.create();
+
+ @Override
+ public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction,
+ final Throwable cause) {
+ failFuture.set(cause);
+ }
+
+ @Override
+ public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
+ successFuture.set(null);
+ }
+
+ public SettableFuture<Throwable> getFailFuture() {
+ return failFuture;
+ }
+
+ public SettableFuture<Void> getSuccessFuture() {
+ return successFuture;
+ }
+
+}
--- /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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+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 java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.junit.Before;
+import org.junit.Test;
+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.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+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.common.RpcResult;
+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 DOMTransactionChainTest {
+
+ 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 testTransactionChainNoConflict() throws InterruptedException, ExecutionException, TimeoutException {
+ BlockingTransactionChainListener listener = new BlockingTransactionChainListener();
+ DOMTransactionChain txChain = domBroker.createTransactionChain(listener);
+ assertNotNull(txChain);
+
+ /**
+ * We alocate new read-write transaction and write /test
+ *
+ *
+ */
+ DOMDataReadWriteTransaction firstTx = allocateAndWrite(txChain);
+
+ /**
+ * First transaction is marked as ready, we are able to allocate chained
+ * transactions
+ */
+ ListenableFuture<RpcResult<TransactionStatus>> firstWriteTxFuture = firstTx.commit();
+
+ /**
+ * We alocate chained transaction - read transaction.
+ */
+ DOMDataReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
+
+ /**
+ *
+ * We test if we are able to read data from tx, read should not fail
+ * since we are using chained transaction.
+ *
+ *
+ */
+ assertTestContainerExists(secondReadTx);
+
+ /**
+ *
+ * We alocate next transaction, which is still based on first one, but
+ * is read-write.
+ *
+ */
+ DOMDataReadWriteTransaction thirdDeleteTx = allocateAndDelete(txChain);
+
+ /**
+ * third transaction is sealed.
+ */
+ ListenableFuture<RpcResult<TransactionStatus>> thirdDeleteTxFuture = thirdDeleteTx.commit();
+
+ /**
+ * We commit first transaction
+ *
+ */
+ assertCommitSuccessful(firstWriteTxFuture);
+
+ // Alocates store transaction
+ DOMDataReadTransaction storeReadTx = domBroker.newReadOnlyTransaction();
+ /**
+ * We verify transaction is commited to store, container should exists
+ * in datastore.
+ */
+ assertTestContainerExists(storeReadTx);
+ /**
+ * We commit third transaction
+ *
+ */
+ assertCommitSuccessful(thirdDeleteTxFuture);
+
+ /**
+ * We close transaction chain.
+ */
+ txChain.close();
+
+ listener.getSuccessFuture().get(1000, TimeUnit.MILLISECONDS);
+ }
+
+ @Test
+ public void testTransactionChainNotSealed() throws InterruptedException, ExecutionException, TimeoutException {
+ BlockingTransactionChainListener listener = new BlockingTransactionChainListener();
+ DOMTransactionChain txChain = domBroker.createTransactionChain(listener);
+ assertNotNull(txChain);
+
+ /**
+ * We alocate new read-write transaction and write /test
+ *
+ *
+ */
+ allocateAndWrite(txChain);
+
+ /**
+ * We alocate chained transaction - read transaction, note first one is
+ * still not commited to datastore, so this allocation should fail with
+ * IllegalStateException.
+ */
+ try {
+ txChain.newReadOnlyTransaction();
+ fail("Allocation of secondReadTx should fail with IllegalStateException");
+ } catch (Exception e) {
+ assertTrue(e instanceof IllegalStateException);
+ }
+ }
+
+ private static DOMDataReadWriteTransaction allocateAndDelete(final DOMTransactionChain txChain)
+ throws InterruptedException, ExecutionException {
+ DOMDataReadWriteTransaction tx = txChain.newReadWriteTransaction();
+
+ /**
+ * We test existence of /test in third transaction container should
+ * still be visible from first one (which is still uncommmited).
+ *
+ */
+ assertTestContainerExists(tx);
+
+ /**
+ * We delete node in third transaction
+ */
+ tx.delete(LogicalDatastoreType.OPERATIONAL, TestModel.TEST_PATH);
+ return tx;
+ }
+
+ private static DOMDataReadWriteTransaction allocateAndWrite(final DOMTransactionChain txChain)
+ throws InterruptedException, ExecutionException {
+ DOMDataReadWriteTransaction tx = txChain.newReadWriteTransaction();
+ assertTestContainerWrite(tx);
+ return tx;
+ }
+
+ private static void assertCommitSuccessful(final ListenableFuture<RpcResult<TransactionStatus>> future)
+ throws InterruptedException, ExecutionException {
+ RpcResult<TransactionStatus> rpcResult = future.get();
+ assertTrue(rpcResult.isSuccessful());
+ assertEquals(TransactionStatus.COMMITED, rpcResult.getResult());
+ }
+
+ private static void assertTestContainerExists(final DOMDataReadTransaction readTx) throws InterruptedException,
+ ExecutionException {
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> readFuture = readTx.read(OPERATIONAL, TestModel.TEST_PATH);
+ Optional<NormalizedNode<?, ?>> readedData = readFuture.get();
+ assertTrue(readedData.isPresent());
+ }
+
+ private static void assertTestContainerWrite(final DOMDataReadWriteTransaction tx) throws InterruptedException,
+ ExecutionException {
+ tx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+ assertTestContainerExists(tx);
+ }
+}
module basic-module {
- namespace "basic:module";
-
- prefix "basmod";
-
- import referenced-module {prefix refmo; revision-date 2013-12-2;}
-
- revision 2013-12-2 {
- }
-
- container cont {
- container cont1 {
- leaf lf11 {
- type identityref {
- base "refmo:iden";
- }
- }
- }
- leaf lfStr {
- type string;
- }
- leaf lfInt8 {
- type int8;
- }
-
- leaf lfInt16 {
- type int16;
- }
-
- leaf lfInt32 {
- type int32;
- }
-
- leaf lfInt64 {
- type int64;
- }
-
- leaf lfUint8 {
- type uint8;
- }
-
- leaf lfUint16 {
- type uint16;
- }
-
- leaf lfUint32 {
- type uint32;
- }
-
- leaf lfUint64 {
- type uint64;
- }
-
- leaf lfBinary {
- type binary;
- }
-
- leaf lfBits {
- type bits {
- bit one;
- bit two;
- bit three;
- }
- }
-
- leaf lfEnumeration {
- type enumeration {
- enum enum1;
- enum enum2;
- enum enum3;
- }
- }
-
- leaf lfEmpty {
- type empty;
- }
-
- leaf lfBoolean {
- type boolean;
- }
-
- leaf lfUnion {
- type union {
- type int8;
- type string;
- type bits {
- bit first;
- bit second;
- }
- type boolean;
- }
- }
-
- leaf lfLfref {
- type leafref {
- path "/cont/lfBoolean";
- }
- }
-
- leaf lfInIdentifier {
- type instance-identifier;
- }
-
- }
-
-}
\ No newline at end of file
+ namespace "basic:module";
+
+ prefix "basmod";
+
+ import referenced-module {prefix refmo; revision-date 2013-12-2;}
+
+ revision 2013-12-2 {
+ }
+
+ container cont {
+ container cont1 {
+ leaf lf11 {
+ type identityref {
+ base "refmo:iden";
+ }
+ }
+ }
+ leaf lfStr {
+ type string;
+ }
+ leaf lfInt8 {
+ type int8;
+ }
+
+ leaf lfInt16 {
+ type int16;
+ }
+
+ leaf lfInt32 {
+ type int32;
+ }
+
+ leaf lfInt64 {
+ type int64;
+ }
+
+ leaf lfUint8 {
+ type uint8;
+ }
+
+ leaf lfUint16 {
+ type uint16;
+ }
+
+ leaf lfUint32 {
+ type uint32;
+ }
+
+ leaf lfUint64 {
+ type uint64;
+ }
+
+ leaf lfBinary {
+ type binary;
+ }
+
+ leaf lfBits {
+ type bits {
+ bit one;
+ bit two;
+ bit three;
+ }
+ }
+
+ leaf lfEnumeration {
+ type enumeration {
+ enum enum1;
+ enum enum2;
+ enum enum3;
+ }
+ }
+
+ leaf lfEmpty {
+ type empty;
+ }
+
+ leaf lfBoolean {
+ type boolean;
+ }
+
+ leaf lfUnion {
+ type union {
+ type int8;
+ type string;
+ type bits {
+ bit first;
+ bit second;
+ }
+ type boolean;
+ }
+ }
+
+ leaf lfLfref {
+ type leafref {
+ path "/cont/lfBoolean";
+ }
+ }
+
+ leaf lfInIdentifier {
+ type instance-identifier;
+ }
+
+ }
+
+}
<module>opendaylight/hosttracker_new/implementation</module>
<module>opendaylight/containermanager/api</module>
<module>opendaylight/containermanager/implementation</module>
+ <module>opendaylight/containermanager/shell</module>
<module>opendaylight/appauth</module>
<module>opendaylight/switchmanager/api</module>
<module>opendaylight/switchmanager/implementation</module>
<!-- Karaf Distribution -->
<module>features/base</module>
+ <module>features/controller</module>
<module>opendaylight/dummy-console</module>
<module>opendaylight/karaf-branding</module>
<module>opendaylight/distribution/opendaylight-karaf</module>