From: Tony Tkacik Date: Wed, 25 Feb 2015 09:56:13 +0000 (+0000) Subject: Merge "Startup arch - add odlparent to root pom." X-Git-Tag: release/lithium~470 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=64db87e8fb5de25e68ba824ec2eaa17e3f217c96;hp=72a7c1289f3c6565261e1b3bc96f6a33705e64ae Merge "Startup arch - add odlparent to root pom." --- diff --git a/features/mdsal/src/main/resources/features.xml b/features/mdsal/src/main/resources/features.xml index 4b9f8c2288..bec365cda0 100644 --- a/features/mdsal/src/main/resources/features.xml +++ b/features/mdsal/src/main/resources/features.xml @@ -100,6 +100,7 @@ mvn:org.opendaylight.controller/sal-clustering-config/${project.version}/xml/akkaconf mvn:org.opendaylight.controller/sal-clustering-config/${project.version}/xml/moduleshardconf mvn:org.opendaylight.controller/sal-clustering-config/${project.version}/xml/moduleconf + mvn:org.opendaylight.controller/sal-clustering-config/${project.version}/cfg/datastore diff --git a/features/restconf/src/main/resources/features.xml b/features/restconf/src/main/resources/features.xml index 123b00767e..8060206b53 100644 --- a/features/restconf/src/main/resources/features.xml +++ b/features/restconf/src/main/resources/features.xml @@ -97,6 +97,7 @@ mvn:org.opendaylight.controller/sal-rest-connector/${project.version} mvn:com.google.code.gson/gson/${gson.version} mvn:org.opendaylight.yangtools/yang-data-codec-gson/${yangtools.version} + mvn:org.opendaylight.yangtools/yang-model-export/${yangtools.version} mvn:com.sun.jersey/jersey-core/${jersey.version} mvn:com.sun.jersey/jersey-server/${jersey.version} mvn:com.sun.jersey/jersey-servlet/${jersey.version} diff --git a/opendaylight/adsal/features/nsf/src/main/resources/features.xml b/opendaylight/adsal/features/nsf/src/main/resources/features.xml index e8f7bc1e5c..692faa746e 100644 --- a/opendaylight/adsal/features/nsf/src/main/resources/features.xml +++ b/opendaylight/adsal/features/nsf/src/main/resources/features.xml @@ -12,6 +12,12 @@ --> + + odl-adsal-all + odl-nsf-controller-managers + odl-adsal-controller-northbound + + odl-base-all odl-adsal-all @@ -51,6 +57,42 @@ mvn:org.opendaylight.controller/routing.dijkstra_implementation/${routing.dijkstra_implementation.version} + + odl-base-all + odl-adsal-all + mvn:org.opendaylight.controller/usermanager/${usermanager.version} + mvn:org.opendaylight.controller/usermanager.implementation/${usermanager.version} + + mvn:org.opendaylight.controller/appauth/${appauth.version} + + mvn:org.opendaylight.controller/connectionmanager/${connectionmanager.version} + mvn:org.opendaylight.controller/connectionmanager.implementation/${connectionmanager.version} + + mvn:org.opendaylight.controller/containermanager/${containermanager.version} + mvn:org.opendaylight.controller/containermanager.implementation/${containermanager.version} + + mvn:org.opendaylight.controller/statisticsmanager/${statisticsmanager.version} + mvn:org.opendaylight.controller/statisticsmanager.implementation/${statisticsmanager.implementation.version} + + mvn:org.opendaylight.controller/switchmanager/${switchmanager.api.version} + mvn:org.opendaylight.controller/switchmanager.implementation/${switchmanager.implementation.version} + + mvn:org.opendaylight.controller/forwardingrulesmanager/${forwardingrulesmanager.version} + mvn:org.opendaylight.controller/forwardingrulesmanager.implementation/${forwardingrulesmanager.implementation.version} + + mvn:org.opendaylight.controller/topologymanager/${topologymanager.version} + mvn:org.opendaylight.controller/topologymanager.shell/${topologymanager.shell.version} + + mvn:org.opendaylight.controller/hosttracker/${hosttracker.api.version} + mvn:org.opendaylight.controller/hosttracker.implementation/${hosttracker.implementation.version} + mvn:org.opendaylight.controller/hosttracker.shell/${hosttracker.shell.version} + + mvn:org.opendaylight.controller/forwarding.staticrouting/${forwarding.staticrouting} + + mvn:org.opendaylight.controller.thirdparty/net.sf.jung2/2.0.1 + mvn:org.opendaylight.controller/routing.dijkstra_implementation/${routing.dijkstra_implementation.version} + + odl-base-all odl-nsf-managers @@ -78,4 +120,26 @@ mvn:org.opendaylight.controller/topology.northbound/${topology.northbound.version} mvn:org.opendaylight.controller/usermanager.northbound/${usermanager.northbound.version} + + + odl-base-all + odl-nsf-managers + mvn:org.ow2.asm/asm-all/${asm.version} + mvn:org.opendaylight.controller/bundlescanner/${bundlescanner.api.version} + mvn:org.opendaylight.controller/bundlescanner.implementation/${bundlescanner.implementation.version} + mvn:org.opendaylight.controller/commons.northbound/${northbound.commons.version} + mvn:org.opendaylight.controller/connectionmanager.northbound/${connectionmanager.version} + mvn:org.opendaylight.controller/flowprogrammer.northbound/${flowprogrammer.northbound.version} + mvn:org.opendaylight.controller/hosttracker.northbound/${hosttracker.northbound.version} + mvn:org.opendaylight.controller/networkconfig.bridgedomain.northbound/${networkconfig.bridgedomain.northbound.version} + mvn:org.eclipse.persistence/org.eclipse.persistence.antlr/${eclipse.persistence.version} + mvn:org.eclipse.persistence/org.eclipse.persistence.core/${eclipse.persistence.version} + mvn:org.eclipse.persistence/org.eclipse.persistence.moxy/${eclipse.persistence.version} + mvn:org.opendaylight.controller/forwarding.staticrouting.northbound/${forwarding.staticrouting.northbound.version} + mvn:org.opendaylight.controller/statistics.northbound/${statistics.northbound.version} + mvn:org.opendaylight.controller/subnets.northbound/${subnets.northbound.version} + mvn:org.opendaylight.controller/switchmanager.northbound/${switchmanager.northbound.version} + mvn:org.opendaylight.controller/topology.northbound/${topology.northbound.version} + mvn:org.opendaylight.controller/usermanager.northbound/${usermanager.northbound.version} + diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/pom.xml b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/pom.xml index 49b43f442e..c5adb28db7 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/pom.xml +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/pom.xml @@ -26,6 +26,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL 1.2.0-SNAPSHOT 0.7.0-SNAPSHOT + etc/opendaylight/karaf diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/src/main/features/features.xml b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/src/main/features/features.xml index bbeb2de90e..1facf4c8aa 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/src/main/features/features.xml +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/features/src/main/features/features.xml @@ -16,22 +16,22 @@ and is available at http://www.eclipse.org/legal/epl-v10.html mvn:org.opendaylight.yangtools/features-yangtools/${symbol_dollar}{yangtools.version}/xml/features mvn:org.opendaylight.controller/features-mdsal/${symbol_dollar}{mdsal.version}/xml/features mvn:org.opendaylight.controller/features-restconf/${symbol_dollar}{mdsal.version}/xml/features - + odl-yangtools-models mvn:${groupId}/${artifactId}-api/${symbol_dollar}{project.version} - + odl-mdsal-broker odl-${artifactId}-api mvn:${groupId}/${artifactId}-impl/${symbol_dollar}{project.version} - mvn:${groupId}/${artifactId}-impl/${symbol_dollar}{project.version}/xml/config + mvn:${groupId}/${artifactId}-impl/${symbol_dollar}{project.version}/xml/config - - odl-${artifactId}-impl + + odl-${artifactId} odl-restconf - - odl-${artifactId}-impl-rest + + odl-${artifactId}-rest odl-mdsal-apidocs odl-mdsal-xsql diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/__packageInPathFormat__/__classPrefix__Provider.java b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/__packageInPathFormat__/impl/__classPrefix__Provider.java similarity index 97% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/__packageInPathFormat__/__classPrefix__Provider.java rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/__packageInPathFormat__/impl/__classPrefix__Provider.java index 070fb4d4e4..b913f58ed4 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/__packageInPathFormat__/__classPrefix__Provider.java +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/__packageInPathFormat__/impl/__classPrefix__Provider.java @@ -8,7 +8,7 @@ * 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 ${package}; +package ${package}.impl; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext; import org.opendaylight.controller.sal.binding.api.BindingAwareProvider; diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__Module.java b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__Module.java index 3290e85469..3cf83415d7 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__Module.java +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__Module.java @@ -10,7 +10,7 @@ */ package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.${artifactId}.impl.rev141210; -import ${package}.${classPrefix}Provider; +import ${package}.impl.${classPrefix}Provider; public class ${classPrefix}Module extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.${artifactId}.impl.rev141210.Abstract${classPrefix}Module { public ${classPrefix}Module(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/__packageInPathFormat__/__classPrefix__ProviderTest.java b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/__packageInPathFormat__/impl/__classPrefix__ProviderTest.java similarity index 97% rename from opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/__packageInPathFormat__/__classPrefix__ProviderTest.java rename to opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/__packageInPathFormat__/impl/__classPrefix__ProviderTest.java index 5422c00a40..09b3dc4622 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/__packageInPathFormat__/__classPrefix__ProviderTest.java +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/__packageInPathFormat__/impl/__classPrefix__ProviderTest.java @@ -9,7 +9,7 @@ * 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 ${package}; +package ${package}.impl; import org.junit.Test; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ModuleTest.java b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ModuleTest.java index 0f921ee371..bbef4d8487 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ModuleTest.java +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/__artifactId__/impl/rev141210/__classPrefix__ModuleTest.java @@ -16,7 +16,7 @@ import org.opendaylight.controller.config.api.DependencyResolver; import org.opendaylight.controller.config.api.JmxAttribute; import org.opendaylight.controller.config.api.ModuleIdentifier; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; -import ${package}.${classPrefix}Provider; +import ${package}.impl.${classPrefix}Provider; import javax.management.ObjectName; diff --git a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/karaf/pom.xml b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/karaf/pom.xml index 87b955c6b9..486e3d39ba 100644 --- a/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/karaf/pom.xml +++ b/opendaylight/archetypes/opendaylight-startup/src/main/resources/archetype-resources/karaf/pom.xml @@ -24,7 +24,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL 3.1.1 - odl-${artifactId}-impl-ui + odl-${artifactId}-ui @@ -54,4 +54,23 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL runtime + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-install-plugin + + true + + + + diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigRegistry.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigRegistry.java index 5c9ed8767c..3d72114daf 100644 --- a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigRegistry.java +++ b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigRegistry.java @@ -66,23 +66,4 @@ public interface ConfigRegistry extends LookupRegistry, ServiceReferenceReadable Set getAvailableModuleNames(); - - /** - * Find all runtime beans - * - * @return objectNames - */ - Set lookupRuntimeBeans(); - - /** - * Find all runtime of specified module - * - * @param moduleName - * of bean - * @param instanceName - * of bean - * @return objectNames - */ - Set lookupRuntimeBeans(String moduleName, String instanceName); - } diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/LookupRegistry.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/LookupRegistry.java index b90fc9c034..5d615c2084 100644 --- a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/LookupRegistry.java +++ b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/LookupRegistry.java @@ -71,4 +71,21 @@ public interface LookupRegistry { */ Set getAvailableModuleFactoryQNames(); + /** + * Find all runtime beans + * + * @return objectNames + */ + Set lookupRuntimeBeans(); + + /** + * Find all runtime of specified module + * + * @param moduleName + * of bean + * @param instanceName + * of bean + * @return objectNames + */ + Set lookupRuntimeBeans(String moduleName, String instanceName); } diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java index 37c2e2d777..186a7218ba 100644 --- a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java +++ b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java @@ -482,6 +482,25 @@ class ConfigTransactionControllerImpl implements public void checkConfigBeanExists(ObjectName objectName) throws InstanceNotFoundException { txLookupRegistry.checkConfigBeanExists(objectName); } + + + /** + * {@inheritDoc} + */ + @Override + public Set lookupRuntimeBeans() { + return txLookupRegistry.lookupRuntimeBeans(); + } + + /** + * {@inheritDoc} + */ + @Override + public Set lookupRuntimeBeans(String moduleName, + String instanceName) { + return txLookupRegistry.lookupRuntimeBeans(moduleName, instanceName); + } + // -- /** diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionLookupRegistry.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionLookupRegistry.java index f9a3801171..a0138b2d9d 100644 --- a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionLookupRegistry.java +++ b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionLookupRegistry.java @@ -116,6 +116,26 @@ class ConfigTransactionLookupRegistry implements LookupRegistry, Closeable { return ModuleQNameUtil.getQNames(allCurrentFactories); } + /** + * {@inheritDoc} + */ + @Override + public Set lookupRuntimeBeans() { + return lookupRuntimeBeans("*", "*"); + } + + /** + * {@inheritDoc} + */ + @Override + public Set lookupRuntimeBeans(String moduleName, + String instanceName) { + String finalModuleName = moduleName == null ? "*" : moduleName; + String finalInstanceName = instanceName == null ? "*" : instanceName; + ObjectName namePattern = ObjectNameUtil.createRuntimeBeanPattern( + finalModuleName, finalInstanceName); + return transactionJMXRegistrator.queryNames(namePattern, null); + } @Override public String toString() { diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ServiceReferenceRegistryImpl.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ServiceReferenceRegistryImpl.java index 27f0d5c1f2..dd6c2b9422 100644 --- a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ServiceReferenceRegistryImpl.java +++ b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ServiceReferenceRegistryImpl.java @@ -97,6 +97,16 @@ public class ServiceReferenceRegistryImpl implements CloseableServiceReferenceRe throw new UnsupportedOperationException(); } + @Override + public Set lookupRuntimeBeans() { + throw new UnsupportedOperationException(); + } + + @Override + public Set lookupRuntimeBeans(final String moduleName, final String instanceName) { + throw new UnsupportedOperationException(); + } + @Override public String toString() { return "initial"; diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/BeanReader.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/BeanReader.java new file mode 100644 index 0000000000..de9baa6897 --- /dev/null +++ b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/BeanReader.java @@ -0,0 +1,10 @@ +package org.opendaylight.controller.config.util; + +import javax.management.ObjectName; + +/** + * Created by mmarsale on 20.2.2015. + */ +public interface BeanReader { + Object getAttributeCurrentValue(ObjectName on, String attributeName); +} diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryClient.java index 99d46cb638..d384ae55c0 100644 --- a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryClient.java +++ b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryClient.java @@ -10,7 +10,7 @@ package org.opendaylight.controller.config.util; import javax.management.ObjectName; import org.opendaylight.controller.config.api.jmx.ConfigRegistryMXBean; -public interface ConfigRegistryClient extends ConfigRegistryMXBean { +public interface ConfigRegistryClient extends ConfigRegistryMXBean, BeanReader { ConfigTransactionClient createTransaction(); @@ -23,6 +23,4 @@ public interface ConfigRegistryClient extends ConfigRegistryMXBean { Object invokeMethod(ObjectName on, String name, Object[] params, String[] signature); - Object getAttributeCurrentValue(ObjectName on, String attributeName); - } diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryJMXClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryJMXClient.java index 099d010642..a39111afee 100644 --- a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryJMXClient.java +++ b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryJMXClient.java @@ -211,7 +211,7 @@ public class ConfigRegistryJMXClient implements ConfigRegistryClient { } catch (AttributeNotFoundException | InstanceNotFoundException | MBeanException | ReflectionException e) { throw new RuntimeException("Unable to get attribute " - + attributeName + " for " + on, e); + + attributeName + " for " + on + ". Available beans: " + lookupConfigBeans(), e); } } diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionClient.java index 359035d51d..c7c072d39d 100644 --- a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionClient.java +++ b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionClient.java @@ -16,7 +16,7 @@ import org.opendaylight.controller.config.api.jmx.CommitStatus; import org.opendaylight.controller.config.api.jmx.ConfigTransactionControllerMXBean; public interface ConfigTransactionClient extends - ConfigTransactionControllerMXBean { + ConfigTransactionControllerMXBean, BeanReader { CommitStatus commit() throws ConflictingVersionException, ValidationException; @@ -47,7 +47,7 @@ public interface ConfigTransactionClient extends * @param on - ObjectName of the Object from which the attribute should be read * @param jmxName - name of the attribute to be read * - * @return Attribute of Object on with attribute name jmxName + * @return Object of Object on with attribute name jmxName */ Attribute getAttribute(ObjectName on, String jmxName); } diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionJMXClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionJMXClient.java index a0af19796e..26ca1391ad 100644 --- a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionJMXClient.java +++ b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionJMXClient.java @@ -240,6 +240,26 @@ public class ConfigTransactionJMXClient implements ConfigTransactionClient { configTransactionControllerMXBeanProxy.checkServiceReferenceExists(objectName); } + @Override + public Attribute getAttribute(ObjectName on, String attrName) { + if (ObjectNameUtil.getTransactionName(on) == null) { + throw new IllegalArgumentException("Not in transaction instance " + + on + ", no transaction name present"); + } + + try { + return new Attribute(attrName, configMBeanServer.getAttribute(on,attrName)); + } catch (JMException e) { + throw new IllegalStateException("Unable to get attribute " + + attrName + " for " + on, e); + } + } + + @Override + public Object getAttributeCurrentValue(ObjectName on, String attrName) { + return getAttribute(on, attrName).getValue(); + } + @Override public void validateBean(ObjectName configBeanON) throws ValidationException { @@ -273,22 +293,17 @@ public class ConfigTransactionJMXClient implements ConfigTransactionClient { } @Override - public Attribute getAttribute(ObjectName on, String attrName) { - if (ObjectNameUtil.getTransactionName(on) == null) { - throw new IllegalArgumentException("Not in transaction instance " - + on + ", no transaction name present"); - } + public Set getAvailableModuleFactoryQNames() { + return configTransactionControllerMXBeanProxy.getAvailableModuleFactoryQNames(); + } - try { - return new Attribute(attrName, configMBeanServer.getAttribute(on,attrName)); - } catch (JMException e) { - throw new IllegalStateException("Unable to get attribute " - + attrName + " for " + on, e); - } + @Override + public Set lookupRuntimeBeans() { + return configTransactionControllerMXBeanProxy.lookupRuntimeBeans(); } @Override - public Set getAvailableModuleFactoryQNames() { - return configTransactionControllerMXBeanProxy.getAvailableModuleFactoryQNames(); + public Set lookupRuntimeBeans(final String moduleName, final String instanceName) { + return configTransactionControllerMXBeanProxy.lookupRuntimeBeans(moduleName, instanceName); } } diff --git a/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigTransactionController.java b/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigTransactionController.java index e1138addc7..e69019405d 100644 --- a/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigTransactionController.java +++ b/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigTransactionController.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.config.util; import com.google.common.collect.Sets; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -184,6 +185,16 @@ public class TestingConfigTransactionController implements return Sets.newHashSet("availableModuleFactoryQNames"); } + @Override + public Set lookupRuntimeBeans() { + return Collections.emptySet(); + } + + @Override + public Set lookupRuntimeBeans(final String moduleName, final String instanceName) { + return Collections.emptySet(); + } + @Override public ObjectName getServiceReference(String serviceInterfaceQName, String refName) throws InstanceNotFoundException { return conf3; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImpl.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImpl.java index e2aa16918e..1aecc89eea 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImpl.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/AbstractReplicatedLogImpl.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.cluster.raft; +import com.google.common.base.Preconditions; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -187,9 +188,14 @@ public abstract class AbstractReplicatedLogImpl implements ReplicatedLog { @Override public void snapshotPreCommit(long snapshotCapturedIndex, long snapshotCapturedTerm) { + Preconditions.checkArgument(snapshotCapturedIndex >= snapshotIndex, + "snapshotCapturedIndex must be greater than or equal to snapshotIndex"); + snapshottedJournal = new ArrayList<>(journal.size()); - snapshottedJournal.addAll(journal.subList(0, (int)(snapshotCapturedIndex - snapshotIndex))); + List snapshotJournalEntries = journal.subList(0, (int) (snapshotCapturedIndex - snapshotIndex)); + + snapshottedJournal.addAll(snapshotJournalEntries); clear(0, (int) (snapshotCapturedIndex - snapshotIndex)); previousSnapshotIndex = snapshotIndex; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java index 3ec8cc5c58..285be39c0b 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java @@ -677,12 +677,22 @@ public abstract class RaftActor extends AbstractUntypedPersistentActor { context.getReplicatedLog().snapshotPreCommit(captureSnapshot.getLastAppliedIndex(), captureSnapshot.getLastAppliedTerm()); - } else { + getCurrentBehavior().setReplicatedToAllIndex(captureSnapshot.getReplicatedToAllIndex()); + } else if(captureSnapshot.getReplicatedToAllIndex() != -1){ // clear the log based on replicatedToAllIndex context.getReplicatedLog().snapshotPreCommit(captureSnapshot.getReplicatedToAllIndex(), captureSnapshot.getReplicatedToAllTerm()); + + getCurrentBehavior().setReplicatedToAllIndex(captureSnapshot.getReplicatedToAllIndex()); + } else { + // The replicatedToAllIndex was not found in the log + // This means that replicatedToAllIndex never moved beyond -1 or that it is already in the snapshot. + // In this scenario we may need to save the snapshot to the akka persistence + // snapshot for recovery but we do not need to do the replicated log trimming. + context.getReplicatedLog().snapshotPreCommit(replicatedLog.getSnapshotIndex(), + replicatedLog.getSnapshotTerm()); } - getCurrentBehavior().setReplicatedToAllIndex(captureSnapshot.getReplicatedToAllIndex()); + LOG.info("{}: Removed in-memory snapshotted entries, adjusted snaphsotIndex:{} " + "and term:{}", persistenceId(), captureSnapshot.getLastAppliedIndex(), diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/MockRaftActorContext.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/MockRaftActorContext.java index 24581d6d2a..297d781251 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/MockRaftActorContext.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/MockRaftActorContext.java @@ -134,6 +134,16 @@ public class MockRaftActorContext implements RaftActorContext { } @Override + // FIXME : A lot of tests try to manipulate the replicated log by setting it using this method + // This is OK to do if the underlyingActor is not RafActor or a derived class. If not then you should not + // used this way to manipulate the log because the RaftActor actually has a field replicatedLog + // which it creates internally and sets on the RaftActorContext + // The only right way to manipulate the replicated log therefore is to get it from either the RaftActor + // or the RaftActorContext and modify the entries in there instead of trying to replace it by using this setter + // Simple assertion that will fail if you do so + // ReplicatedLog log = new ReplicatedLogImpl(); + // raftActor.underlyingActor().getRaftActorContext().setReplicatedLog(log); + // assertEquals(log, raftActor.underlyingActor().getReplicatedLog()) public void setReplicatedLog(ReplicatedLog replicatedLog) { this.replicatedLog = replicatedLog; } diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorTest.java index 83868b6a2a..56bfc21f23 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorTest.java @@ -1227,6 +1227,128 @@ public class RaftActorTest extends AbstractActorTest { }; } + + private static class NonPersistentProvider implements DataPersistenceProvider { + @Override + public boolean isRecoveryApplicable() { + return false; + } + + @Override + public void persist(T o, Procedure procedure) { + try { + procedure.apply(o); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void saveSnapshot(Object o) { + + } + + @Override + public void deleteSnapshots(SnapshotSelectionCriteria criteria) { + + } + + @Override + public void deleteMessages(long sequenceNumber) { + + } + } + + @Test + public void testRealSnapshotWhenReplicatedToAllIndexMinusOne() throws Exception { + new JavaTestKit(getSystem()) {{ + String persistenceId = factory.generateActorId("leader-"); + DefaultConfigParamsImpl config = new DefaultConfigParamsImpl(); + config.setHeartBeatInterval(new FiniteDuration(1, TimeUnit.DAYS)); + config.setIsolatedLeaderCheckInterval(new FiniteDuration(1, TimeUnit.DAYS)); + config.setSnapshotBatchCount(5); + + DataPersistenceProvider dataPersistenceProvider = new NonPersistentProvider(); + + Map peerAddresses = new HashMap<>(); + + TestActorRef mockActorRef = factory.createTestActor( + MockRaftActor.props(persistenceId, peerAddresses, + Optional.of(config), dataPersistenceProvider), persistenceId); + + MockRaftActor leaderActor = mockActorRef.underlyingActor(); + leaderActor.getRaftActorContext().setCommitIndex(3); + leaderActor.getRaftActorContext().setLastApplied(3); + leaderActor.getRaftActorContext().getTermInformation().update(1, persistenceId); + + leaderActor.waitForInitializeBehaviorComplete(); + for(int i=0;i< 4;i++) { + leaderActor.getReplicatedLog() + .append(new MockRaftActorContext.MockReplicatedLogEntry(1, i, + new MockRaftActorContext.MockPayload("A"))); + } + + Leader leader = new Leader(leaderActor.getRaftActorContext()); + leaderActor.setCurrentBehavior(leader); + assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state()); + + // Persist another entry (this will cause a CaptureSnapshot to be triggered + leaderActor.persistData(mockActorRef, "x", new MockRaftActorContext.MockPayload("duh")); + + // Now send a CaptureSnapshotReply + mockActorRef.tell(new CaptureSnapshotReply(fromObject("foo").toByteArray()), mockActorRef); + + // Trimming log in this scenario is a no-op + assertEquals(-1, leaderActor.getReplicatedLog().getSnapshotIndex()); + assertFalse(leaderActor.getRaftActorContext().isSnapshotCaptureInitiated()); + assertEquals(-1, leader.getReplicatedToAllIndex()); + + }}; + } + + @Test + public void testRealSnapshotWhenReplicatedToAllIndexNotInReplicatedLog() throws Exception { + new JavaTestKit(getSystem()) {{ + String persistenceId = factory.generateActorId("leader-"); + DefaultConfigParamsImpl config = new DefaultConfigParamsImpl(); + config.setHeartBeatInterval(new FiniteDuration(1, TimeUnit.DAYS)); + config.setIsolatedLeaderCheckInterval(new FiniteDuration(1, TimeUnit.DAYS)); + config.setSnapshotBatchCount(5); + + DataPersistenceProvider dataPersistenceProvider = new NonPersistentProvider(); + + Map peerAddresses = new HashMap<>(); + + TestActorRef mockActorRef = factory.createTestActor( + MockRaftActor.props(persistenceId, peerAddresses, + Optional.of(config), dataPersistenceProvider), persistenceId); + + MockRaftActor leaderActor = mockActorRef.underlyingActor(); + leaderActor.getRaftActorContext().setCommitIndex(3); + leaderActor.getRaftActorContext().setLastApplied(3); + leaderActor.getRaftActorContext().getTermInformation().update(1, persistenceId); + leaderActor.getReplicatedLog().setSnapshotIndex(3); + + leaderActor.waitForInitializeBehaviorComplete(); + Leader leader = new Leader(leaderActor.getRaftActorContext()); + leaderActor.setCurrentBehavior(leader); + leader.setReplicatedToAllIndex(3); + assertEquals(RaftState.Leader, leaderActor.getCurrentBehavior().state()); + + // Persist another entry (this will cause a CaptureSnapshot to be triggered + leaderActor.persistData(mockActorRef, "x", new MockRaftActorContext.MockPayload("duh")); + + // Now send a CaptureSnapshotReply + mockActorRef.tell(new CaptureSnapshotReply(fromObject("foo").toByteArray()), mockActorRef); + + // Trimming log in this scenario is a no-op + assertEquals(3, leaderActor.getReplicatedLog().getSnapshotIndex()); + assertFalse(leaderActor.getRaftActorContext().isSnapshotCaptureInitiated()); + assertEquals(3, leader.getReplicatedToAllIndex()); + + }}; + } + private ByteString fromObject(Object snapshot) throws Exception { ByteArrayOutputStream b = null; ObjectOutputStream o = null; diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataObjectModification.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataObjectModification.java new file mode 100644 index 0000000000..2eee0e8099 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataObjectModification.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html. + */ +package org.opendaylight.controller.md.sal.binding.api; + +import java.util.Collection; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.concepts.Identifiable; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument; + +/** + * Modified Data Object. + * + * Represents modification of Data Object. + * + */ +public interface DataObjectModification extends Identifiable { + + enum ModificationType { + /** + * + * Child node (direct or indirect) was modified. + * + */ + SUBTREE_MODIFIED, + /** + * + * Node was explicitly created / overwritten. + * + */ + WRITE, + /** + * + * Node was deleted. + * + */ + DELETE + } + + @Override + PathArgument getIdentifier(); + + /** + * Returns type of modified object. + * + * @return type of modified object. + */ + @Nonnull Class getDataType(); + + /** + * + * Returns type of modification + * + * @return type Type of performed modification. + */ + @Nonnull ModificationType getModificationType(); + + /** + * Returns after state of top level container. + * + * @param root Class representing data container + * @return State of object after modification. Null if subtree is not present. + */ + @Nullable T getDataAfter(); + + /** + * Returns unmodifiable collection of modified direct children. + * + * @return unmodifiable collection of modified direct children. + */ + @Nonnull Collection> getModifiedChildren(); + + +} diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java new file mode 100644 index 0000000000..6b1df719ac --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeListener.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.binding.api; + +import java.util.Collection; +import java.util.EventListener; +import javax.annotation.Nonnull; + +/** + * Interface implemented by classes interested in receiving notifications about + * data tree changes. This interface differs from {@link DataChangeListener} + * in that it provides a cursor-based view of the change, which has potentially + * lower overhead and allow more flexible consumption of change event. + */ +public interface DataTreeChangeListener extends EventListener { + /** + * Invoked when there was data change for the supplied path, which was used + * to register this listener. + * + *

+ * This method may be also invoked during registration of the listener if + * there is any pre-existing data in the conceptual data tree for supplied + * path. This initial event will contain all pre-existing data as created. + * + *

+ * A data change event may be triggered spuriously, e.g. such that data before + * and after compare as equal. Implementations of this interface are expected + * to recover from such events. Event producers are expected to exert reasonable + * effort to suppress such events. + * + * In other words, it is completely acceptable to observe + * a {@link DataObjectModification}, while the state observed before and + * after- data items compare as equal. + * + * @param changes Collection of change events, may not be null or empty. + */ + void onDataTreeChanged(@Nonnull Collection changes); +} diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeService.java new file mode 100644 index 0000000000..ae4e36f14a --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeChangeService.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.binding.api; + +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.ListenerRegistration; + +/** + * A {@link DOMService} which allows users to register for changes to a + * subtree. + */ +public interface DataTreeChangeService extends BindingService { + /** + * Registers a {@link DataTreeChangeListener} to receive + * notifications when data changes under a given path in the conceptual data + * tree. + *

+ * You are able to register for notifications for any node or subtree + * which can be represented using {@link DataTreeIdentifier}. + *

+ * + * You are able to register for data change notifications for a subtree or leaf + * even if it does not exist. You will receive notification once that node is + * created. + *

+ * If there is any pre-existing data in the data tree for the path for which you are + * registering, you will receive an initial data change event, which will + * contain all pre-existing data, marked as created. + * + *

+ * This method returns a {@link ListenerRegistration} object. To + * "unregister" your listener for changes call the {@link ListenerRegistration#close()} + * method on the returned object. + *

+ * You MUST explicitly unregister your listener when you no longer want to receive + * notifications. This is especially true in OSGi environments, where failure to + * do so during bundle shutdown can lead to stale listeners being still registered. + * + * @param treeId + * Data tree identifier of the subtree which should be watched for + * changes. + * @param listener + * Listener instance which is being registered + * @return Listener registration object, which may be used to unregister + * your listener using {@link ListenerRegistration#close()} to stop + * delivery of change events. + */ + @Nonnull ListenerRegistration registerDataTreeChangeListener(@Nonnull DataTreeIdentifier treeId, @Nonnull L listener); +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeIdentifier.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeIdentifier.java new file mode 100644 index 0000000000..428957e988 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeIdentifier.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.binding.api; + +import com.google.common.base.Preconditions; +import java.io.Serializable; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yangtools.concepts.Immutable; +import org.opendaylight.yangtools.concepts.Path; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * A unique identifier for a particular subtree. It is composed of the logical + * data store type and the instance identifier of the root node. + */ +public final class DataTreeIdentifier implements Immutable, Path, Serializable { + private static final long serialVersionUID = 1L; + private final InstanceIdentifier rootIdentifier; + private final LogicalDatastoreType datastoreType; + + public DataTreeIdentifier(final LogicalDatastoreType datastoreType, final InstanceIdentifier rootIdentifier) { + this.datastoreType = Preconditions.checkNotNull(datastoreType); + this.rootIdentifier = Preconditions.checkNotNull(rootIdentifier); + } + + /** + * Return the logical data store type. + * + * @return Logical data store type. Guaranteed to be non-null. + */ + public @Nonnull LogicalDatastoreType getDatastoreType() { + return datastoreType; + } + + /** + * Return the {@link YangInstanceIdentifier} of the root node. + * + * @return Instance identifier corresponding to the root node. + */ + public @Nonnull InstanceIdentifier getRootIdentifier() { + return rootIdentifier; + } + + @Override + public boolean contains(final DataTreeIdentifier other) { + return datastoreType == other.datastoreType && rootIdentifier.contains(other.rootIdentifier); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + datastoreType.hashCode(); + result = prime * result + rootIdentifier.hashCode(); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof DataTreeIdentifier)) { + return false; + } + DataTreeIdentifier other = (DataTreeIdentifier) obj; + if (datastoreType != other.datastoreType) { + return false; + } + return rootIdentifier.equals(other.rootIdentifier); + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeModification.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeModification.java new file mode 100644 index 0000000000..aac51a6a4c --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataTreeModification.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html. + */ + +package org.opendaylight.controller.md.sal.binding.api; + +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; + +/** + * Represent root of modification. + * + * @author Tony Tkacik <ttkacik@cisco.com> + * + */ +public interface DataTreeModification { + + /** + * Get the modification root path. This is the path of the root node + * relative to the root of InstanceIdentifier namespace. + * + * @return absolute path of the root node + */ + @Nonnull DataTreeIdentifier getRootPath(); + + /** + * Get the modification root node. + * + * @return modification root node + */ + @Nonnull DataObjectModification getRootNode(); + +} diff --git a/opendaylight/md-sal/sal-clustering-config/pom.xml b/opendaylight/md-sal/sal-clustering-config/pom.xml index 1c018cade1..57da717daf 100644 --- a/opendaylight/md-sal/sal-clustering-config/pom.xml +++ b/opendaylight/md-sal/sal-clustering-config/pom.xml @@ -51,6 +51,11 @@ xml moduleconf + + ${project.build.directory}/classes/initial/datastore.cfg + cfg + datastore + diff --git a/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/datastore.cfg b/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/datastore.cfg new file mode 100644 index 0000000000..8e03223a71 --- /dev/null +++ b/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/datastore.cfg @@ -0,0 +1,75 @@ +# This file specifies property settings for the clustered data store to control its behavior. A +# property may be applied to every data store type ("config" and "operational") or can be customized +# differently for each data store type by prefixing the data store type + '.'. For example, specifying +# the "shard-election-timeout-factor" property would be applied to both data stores whereas specifying +# "operational.shard-election-timeout-factor" would only apply to the "operational" data store. Similarly, +# specifying "config.shard-election-timeout-factor" would only apply to the "config" data store. + +# The multiplication factor to be used to determine shard election timeout. The shard election timeout +# is determined by multiplying shardHeartbeatIntervalInMillis with the shardElectionTimeoutFactor. +#shard-election-timeout-factor=2 + +# The interval at which a shard will send a heart beat message to its remote shard. +#shard-heartbeat-interval-in-millis=500 + +# The maximum amount of time to wait for a shard to elect a leader before failing an operation (eg transaction create). +#shard-leader-election-timeout-in-seconds=30 + +# Enable or disable data persistence. +#persistent=true + +# Disable persistence for the operational data store by default. +operational.persistent=false + +# The maximum amount of time a shard transaction can be idle without receiving any messages before it self-destructs. +#shard-transaction-idle-timeout-in-minutes=10 + +# The maximum amount of time a shard transaction three-phase commit can be idle without receiving the +# next messages before it aborts the transaction. +#shard-transaction-commit-timeout-in-seconds=30 + +# The maximum allowed capacity for each shard's transaction commit queue. +#shard-transaction-commit-queue-capacity=20000 + +# The maximum amount of time to wait for a shard to initialize from persistence on startup before +# failing an operation (eg transaction create and change listener registration). +#shard-initialization-timeout-in-seconds=300 + +# The minimum number of entries to be present in the in-memory journal log before a snapshot is to be taken. +#shard-journal-recovery-log-batch-size=5000 + +# The minimum number of entries to be present in the in-memory journal log before a snapshot is to be taken. +#shard-snapshot-batch-count=20000 + +# The percentage of Runtime.totalMemory() used by the in-memory journal log before a snapshot is to be taken. +#shard-snapshot-data-threshold-percentage=12 + +# The interval at which the leader of the shard will check if its majority followers are active and +# term itself as isolated. +#shard-isolated-leader-check-interval-in-millis=5000 + +# The number of transaction modification operations (put, merge, delete) to batch before sending to the +# shard transaction actor. Batching improves performance as less modifications messages are sent to the +# actor and thus lessens the chance that the transaction actor's mailbox queue could get full. +#shard-batched-modification-count=100 + +# The maximum amount of time for akka operations (remote or local) to complete before failing. +#operation-timeout-in-seconds=5 + +# The initial number of transactions per second that are allowed before the data store should begin +# applying back pressure. This number is only used as an initial guidance, subsequently the datastore +# measures the latency for a commit and auto-adjusts the rate limit. +#transaction-creation-initial-rate-limit=100 + +# The maximum thread pool size for each shard's data store data change notification executor. +#max-shard-data-change-executor-pool-size=20 + +# The maximum queue size for each shard's data store data change notification executor. +#max-shard-data-change-executor-queue-size=1000 + +# The maximum queue size for each shard's data store data change listener. +#max-shard-data-change-listener-queue-size=1000 + +# The maximum queue size for each shard's data store executor. +#max-shard-data-store-executor-queue-size=5000 + diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ConcurrentDOMDataBroker.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ConcurrentDOMDataBroker.java index 13334c9272..886c473067 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ConcurrentDOMDataBroker.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ConcurrentDOMDataBroker.java @@ -8,13 +8,13 @@ package org.opendaylight.controller.cluster.datastore; import com.google.common.base.Preconditions; -import com.google.common.collect.Iterables; import com.google.common.util.concurrent.AbstractFuture; import com.google.common.util.concurrent.AbstractListeningExecutorService; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; @@ -70,17 +70,16 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { @Override public CheckedFuture submit(DOMDataWriteTransaction transaction, - Iterable cohorts) { + Collection cohorts) { Preconditions.checkArgument(transaction != null, "Transaction must not be null."); Preconditions.checkArgument(cohorts != null, "Cohorts must not be null."); LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier()); - final int cohortSize = Iterables.size(cohorts); final AsyncNotifyingSettableFuture clientSubmitFuture = new AsyncNotifyingSettableFuture(clientFutureCallbackExecutor); - doCanCommit(clientSubmitFuture, transaction, cohorts, cohortSize); + doCanCommit(clientSubmitFuture, transaction, cohorts); return MappingCheckedFuture.create(clientSubmitFuture, TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER); @@ -88,31 +87,31 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { private void doCanCommit(final AsyncNotifyingSettableFuture clientSubmitFuture, final DOMDataWriteTransaction transaction, - final Iterable cohorts, final int cohortSize) { + final Collection cohorts) { final long startTime = System.nanoTime(); // Not using Futures.allAsList here to avoid its internal overhead. - final AtomicInteger remaining = new AtomicInteger(cohortSize); + final AtomicInteger remaining = new AtomicInteger(cohorts.size()); FutureCallback futureCallback = new FutureCallback() { @Override public void onSuccess(Boolean result) { if (result == null || !result) { - handleException(clientSubmitFuture, transaction, cohorts, cohortSize, + handleException(clientSubmitFuture, transaction, cohorts, CAN_COMMIT, TransactionCommitFailedExceptionMapper.CAN_COMMIT_ERROR_MAPPER, new TransactionCommitFailedException( "Can Commit failed, no detailed cause available.")); } else { if(remaining.decrementAndGet() == 0) { // All cohorts completed successfully - we can move on to the preCommit phase - doPreCommit(startTime, clientSubmitFuture, transaction, cohorts, cohortSize); + doPreCommit(startTime, clientSubmitFuture, transaction, cohorts); } } } @Override public void onFailure(Throwable t) { - handleException(clientSubmitFuture, transaction, cohorts, cohortSize, CAN_COMMIT, + handleException(clientSubmitFuture, transaction, cohorts, CAN_COMMIT, TransactionCommitFailedExceptionMapper.CAN_COMMIT_ERROR_MAPPER, t); } }; @@ -125,22 +124,22 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { private void doPreCommit(final long startTime, final AsyncNotifyingSettableFuture clientSubmitFuture, final DOMDataWriteTransaction transaction, - final Iterable cohorts, final int cohortSize) { + final Collection cohorts) { // Not using Futures.allAsList here to avoid its internal overhead. - final AtomicInteger remaining = new AtomicInteger(cohortSize); + final AtomicInteger remaining = new AtomicInteger(cohorts.size()); FutureCallback futureCallback = new FutureCallback() { @Override public void onSuccess(Void notUsed) { if(remaining.decrementAndGet() == 0) { // All cohorts completed successfully - we can move on to the commit phase - doCommit(startTime, clientSubmitFuture, transaction, cohorts, cohortSize); + doCommit(startTime, clientSubmitFuture, transaction, cohorts); } } @Override public void onFailure(Throwable t) { - handleException(clientSubmitFuture, transaction, cohorts, cohortSize, PRE_COMMIT, + handleException(clientSubmitFuture, transaction, cohorts, PRE_COMMIT, TransactionCommitFailedExceptionMapper.PRE_COMMIT_MAPPER, t); } }; @@ -153,10 +152,10 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { private void doCommit(final long startTime, final AsyncNotifyingSettableFuture clientSubmitFuture, final DOMDataWriteTransaction transaction, - final Iterable cohorts, final int cohortSize) { + final Collection cohorts) { // Not using Futures.allAsList here to avoid its internal overhead. - final AtomicInteger remaining = new AtomicInteger(cohortSize); + final AtomicInteger remaining = new AtomicInteger(cohorts.size()); FutureCallback futureCallback = new FutureCallback() { @Override public void onSuccess(Void notUsed) { @@ -170,7 +169,7 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { @Override public void onFailure(Throwable t) { - handleException(clientSubmitFuture, transaction, cohorts, cohortSize, COMMIT, + handleException(clientSubmitFuture, transaction, cohorts, COMMIT, TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER, t); } }; @@ -183,7 +182,7 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { private void handleException(final AsyncNotifyingSettableFuture clientSubmitFuture, final DOMDataWriteTransaction transaction, - final Iterable cohorts, int cohortSize, + final Collection cohorts, final String phase, final TransactionCommitFailedExceptionMapper exMapper, final Throwable t) { @@ -205,7 +204,7 @@ public class ConcurrentDOMDataBroker extends AbstractDOMDataBroker { // Transaction failed - tell all cohorts to abort. @SuppressWarnings("unchecked") - ListenableFuture[] canCommitFutures = new ListenableFuture[cohortSize]; + ListenableFuture[] canCommitFutures = new ListenableFuture[cohorts.size()]; int i = 0; for(DOMStoreThreePhaseCommitCohort cohort: cohorts) { canCommitFutures[i++] = cohort.abort(); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxy.java index 06f3afc57c..681132e660 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxy.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxy.java @@ -12,6 +12,7 @@ import akka.actor.ActorRef; import akka.actor.ActorSelection; import akka.actor.PoisonPill; import akka.dispatch.OnComplete; +import com.google.common.annotations.VisibleForTesting; import org.opendaylight.controller.cluster.datastore.exceptions.LocalShardNotFoundException; import org.opendaylight.controller.cluster.datastore.messages.CloseDataChangeListenerRegistration; import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener; @@ -25,7 +26,6 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.annotations.VisibleForTesting; import scala.concurrent.Future; /** @@ -93,7 +93,7 @@ public class DataChangeListenerRegistrationProxy implements ListenerRegistration public void init(final YangInstanceIdentifier path, final AsyncDataBroker.DataChangeScope scope) { dataChangeListenerActor = actorContext.getActorSystem().actorOf( - DataChangeListener.props(listener)); + DataChangeListener.props(listener).withDispatcher(actorContext.getNotificationDispatcherPath())); Future findFuture = actorContext.findLocalShardAsync(shardName); findFuture.onComplete(new OnComplete() { @@ -109,7 +109,7 @@ public class DataChangeListenerRegistrationProxy implements ListenerRegistration doRegistration(shard, path, scope); } } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); } private void doRegistration(ActorRef shard, final YangInstanceIdentifier path, @@ -131,7 +131,7 @@ public class DataChangeListenerRegistrationProxy implements ListenerRegistration reply.getListenerRegistrationPath())); } } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); } @Override diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContext.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContext.java index cee781fb88..7f8a4e779d 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContext.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContext.java @@ -42,6 +42,7 @@ public class DatastoreContext { public static final int DEFAULT_SHARD_ELECTION_TIMEOUT_FACTOR = 2; public static final int DEFAULT_TX_CREATION_INITIAL_RATE_LIMIT = 100; public static final String UNKNOWN_DATA_STORE_TYPE = "unknown"; + public static final int DEFAULT_SHARD_BATCHED_MODIFICATION_COUNT= 100; private InMemoryDOMDataStoreConfigProperties dataStoreProperties; private Duration shardTransactionIdleTimeout = DatastoreContext.DEFAULT_SHARD_TRANSACTION_IDLE_TIMEOUT; @@ -54,19 +55,48 @@ public class DatastoreContext { private boolean persistent = DEFAULT_PERSISTENT; private ConfigurationReader configurationReader = DEFAULT_CONFIGURATION_READER; private long transactionCreationInitialRateLimit = DEFAULT_TX_CREATION_INITIAL_RATE_LIMIT; - private DefaultConfigParamsImpl raftConfig = new DefaultConfigParamsImpl(); + private final DefaultConfigParamsImpl raftConfig = new DefaultConfigParamsImpl(); private String dataStoreType = UNKNOWN_DATA_STORE_TYPE; + private int shardBatchedModificationCount = DEFAULT_SHARD_BATCHED_MODIFICATION_COUNT; - private DatastoreContext(){ + private DatastoreContext() { setShardJournalRecoveryLogBatchSize(DEFAULT_JOURNAL_RECOVERY_BATCH_SIZE); setSnapshotBatchCount(DEFAULT_SNAPSHOT_BATCH_COUNT); setHeartbeatInterval(DEFAULT_HEARTBEAT_INTERVAL_IN_MILLIS); setIsolatedLeaderCheckInterval(DEFAULT_ISOLATED_LEADER_CHECK_INTERVAL_IN_MILLIS); setSnapshotDataThresholdPercentage(DEFAULT_SHARD_SNAPSHOT_DATA_THRESHOLD_PERCENTAGE); + setElectionTimeoutFactor(DEFAULT_SHARD_ELECTION_TIMEOUT_FACTOR); + } + + private DatastoreContext(DatastoreContext other) { + this.dataStoreProperties = other.dataStoreProperties; + this.shardTransactionIdleTimeout = other.shardTransactionIdleTimeout; + this.operationTimeoutInSeconds = other.operationTimeoutInSeconds; + this.dataStoreMXBeanType = other.dataStoreMXBeanType; + this.shardTransactionCommitTimeoutInSeconds = other.shardTransactionCommitTimeoutInSeconds; + this.shardTransactionCommitQueueCapacity = other.shardTransactionCommitQueueCapacity; + this.shardInitializationTimeout = other.shardInitializationTimeout; + this.shardLeaderElectionTimeout = other.shardLeaderElectionTimeout; + this.persistent = other.persistent; + this.configurationReader = other.configurationReader; + this.transactionCreationInitialRateLimit = other.transactionCreationInitialRateLimit; + this.dataStoreType = other.dataStoreType; + this.shardBatchedModificationCount = other.shardBatchedModificationCount; + + setShardJournalRecoveryLogBatchSize(other.raftConfig.getJournalRecoveryLogBatchSize()); + setSnapshotBatchCount(other.raftConfig.getSnapshotBatchCount()); + setHeartbeatInterval(other.raftConfig.getHeartBeatInterval().toMillis()); + setIsolatedLeaderCheckInterval(other.raftConfig.getIsolatedCheckIntervalInMillis()); + setSnapshotDataThresholdPercentage(other.raftConfig.getSnapshotDataThresholdPercentage()); + setElectionTimeoutFactor(other.raftConfig.getElectionTimeoutFactor()); } public static Builder newBuilder() { - return new Builder(); + return new Builder(new DatastoreContext()); + } + + public static Builder newBuilderFrom(DatastoreContext context) { + return new Builder(new DatastoreContext(context)); } public InMemoryDOMDataStoreConfigProperties getDataStoreProperties() { @@ -148,18 +178,60 @@ public class DatastoreContext { raftConfig.setSnapshotDataThresholdPercentage(shardSnapshotDataThresholdPercentage); } - private void setSnapshotBatchCount(int shardSnapshotBatchCount) { + private void setSnapshotBatchCount(long shardSnapshotBatchCount) { raftConfig.setSnapshotBatchCount(shardSnapshotBatchCount); } + public int getShardBatchedModificationCount() { + return shardBatchedModificationCount; + } + public static class Builder { - private DatastoreContext datastoreContext = new DatastoreContext(); + private final DatastoreContext datastoreContext; + private int maxShardDataChangeExecutorPoolSize = + InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE; + private int maxShardDataChangeExecutorQueueSize = + InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE; + private int maxShardDataChangeListenerQueueSize = + InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE; + private int maxShardDataStoreExecutorQueueSize = + InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE; + + private Builder(DatastoreContext datastoreContext) { + this.datastoreContext = datastoreContext; + + if(datastoreContext.getDataStoreProperties() != null) { + maxShardDataChangeExecutorPoolSize = + datastoreContext.getDataStoreProperties().getMaxDataChangeExecutorPoolSize(); + maxShardDataChangeExecutorQueueSize = + datastoreContext.getDataStoreProperties().getMaxDataChangeExecutorQueueSize(); + maxShardDataChangeListenerQueueSize = + datastoreContext.getDataStoreProperties().getMaxDataChangeListenerQueueSize(); + maxShardDataStoreExecutorQueueSize = + datastoreContext.getDataStoreProperties().getMaxDataStoreExecutorQueueSize(); + } + } + + public Builder boundedMailboxCapacity(int boundedMailboxCapacity) { + // TODO - this is defined in the yang DataStoreProperties but not currently used. + return this; + } - public Builder shardTransactionIdleTimeout(Duration shardTransactionIdleTimeout) { - datastoreContext.shardTransactionIdleTimeout = shardTransactionIdleTimeout; + public Builder enableMetricCapture(boolean enableMetricCapture) { + // TODO - this is defined in the yang DataStoreProperties but not currently used. return this; } + + public Builder shardTransactionIdleTimeout(long timeout, TimeUnit unit) { + datastoreContext.shardTransactionIdleTimeout = Duration.create(timeout, unit); + return this; + } + + public Builder shardTransactionIdleTimeoutInMinutes(long timeout) { + return shardTransactionIdleTimeout(timeout, TimeUnit.MINUTES); + } + public Builder operationTimeoutInSeconds(int operationTimeoutInSeconds) { datastoreContext.operationTimeoutInSeconds = operationTimeoutInSeconds; return this; @@ -170,11 +242,6 @@ public class DatastoreContext { return this; } - public Builder dataStoreProperties(InMemoryDOMDataStoreConfigProperties dataStoreProperties) { - datastoreContext.dataStoreProperties = dataStoreProperties; - return this; - } - public Builder shardTransactionCommitTimeoutInSeconds(int shardTransactionCommitTimeoutInSeconds) { datastoreContext.shardTransactionCommitTimeoutInSeconds = shardTransactionCommitTimeoutInSeconds; return this; @@ -210,11 +277,19 @@ public class DatastoreContext { return this; } + public Builder shardInitializationTimeoutInSeconds(long timeout) { + return shardInitializationTimeout(timeout, TimeUnit.SECONDS); + } + public Builder shardLeaderElectionTimeout(long timeout, TimeUnit unit) { datastoreContext.shardLeaderElectionTimeout = new Timeout(timeout, unit); return this; } + public Builder shardLeaderElectionTimeoutInSeconds(long timeout) { + return shardLeaderElectionTimeout(timeout, TimeUnit.SECONDS); + } + public Builder configurationReader(ConfigurationReader configurationReader){ datastoreContext.configurationReader = configurationReader; return this; @@ -246,7 +321,35 @@ public class DatastoreContext { return this; } + public Builder shardBatchedModificationCount(int shardBatchedModificationCount) { + datastoreContext.shardBatchedModificationCount = shardBatchedModificationCount; + return this; + } + + public Builder maxShardDataChangeExecutorPoolSize(int maxShardDataChangeExecutorPoolSize) { + this.maxShardDataChangeExecutorPoolSize = maxShardDataChangeExecutorPoolSize; + return this; + } + + public Builder maxShardDataChangeExecutorQueueSize(int maxShardDataChangeExecutorQueueSize) { + this.maxShardDataChangeExecutorQueueSize = maxShardDataChangeExecutorQueueSize; + return this; + } + + public Builder maxShardDataChangeListenerQueueSize(int maxShardDataChangeListenerQueueSize) { + this.maxShardDataChangeListenerQueueSize = maxShardDataChangeListenerQueueSize; + return this; + } + + public Builder maxShardDataStoreExecutorQueueSize(int maxShardDataStoreExecutorQueueSize) { + this.maxShardDataStoreExecutorQueueSize = maxShardDataStoreExecutorQueueSize; + return this; + } + public DatastoreContext build() { + datastoreContext.dataStoreProperties = InMemoryDOMDataStoreConfigProperties.create( + maxShardDataChangeExecutorPoolSize, maxShardDataChangeExecutorQueueSize, + maxShardDataChangeListenerQueueSize, maxShardDataStoreExecutorQueueSize); return datastoreContext; } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContextConfigAdminOverlay.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContextConfigAdminOverlay.java new file mode 100644 index 0000000000..20ee55519b --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContextConfigAdminOverlay.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore; + +import java.io.IOException; +import java.util.Dictionary; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.cm.Configuration; +import org.osgi.service.cm.ConfigurationAdmin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class that overlays DatastoreContext settings with settings obtained from an OSGi Config Admin + * service. + * + * @author Thomas Pantelis + */ +public class DatastoreContextConfigAdminOverlay implements AutoCloseable { + public static final String CONFIG_ID = "org.opendaylight.controller.cluster.datastore"; + + private static final Logger LOG = LoggerFactory.getLogger(DatastoreContextConfigAdminOverlay.class); + + private final DatastoreContextIntrospector introspector; + private final BundleContext bundleContext; + + public DatastoreContextConfigAdminOverlay(DatastoreContextIntrospector introspector, BundleContext bundleContext) { + this.introspector = introspector; + this.bundleContext = bundleContext; + + ServiceReference configAdminServiceReference = + bundleContext.getServiceReference(ConfigurationAdmin.class); + if(configAdminServiceReference == null) { + LOG.warn("No ConfigurationAdmin service found"); + } else { + overlaySettings(configAdminServiceReference); + } + } + + private void overlaySettings(ServiceReference configAdminServiceReference) { + try { + ConfigurationAdmin configAdmin = bundleContext.getService(configAdminServiceReference); + + Configuration config = configAdmin.getConfiguration(CONFIG_ID); + if(config != null) { + Dictionary properties = config.getProperties(); + + LOG.debug("Overlaying settings: {}", properties); + + introspector.update(properties); + } else { + LOG.debug("No Configuration found for {}", CONFIG_ID); + } + } catch (IOException e) { + LOG.error("Error obtaining Configuration for pid {}", CONFIG_ID, e); + } catch(IllegalStateException e) { + // Ignore - indicates the bundleContext has been closed. + } finally { + try { + bundleContext.ungetService(configAdminServiceReference); + } catch (Exception e) { + LOG.debug("Error from ungetService", e); + } + } + } + + @Override + public void close() { + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContextIntrospector.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContextIntrospector.java new file mode 100644 index 0000000000..3ca64210be --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContextIntrospector.java @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.common.primitives.Primitives; +import java.beans.BeanInfo; +import java.beans.ConstructorProperties; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.MethodDescriptor; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.text.WordUtils; +import org.opendaylight.controller.cluster.datastore.DatastoreContext.Builder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.distributed.datastore.provider.rev140612.DataStoreProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Introspects on a DatastoreContext instance to set its properties via reflection. + * i + * @author Thomas Pantelis + */ +public class DatastoreContextIntrospector { + private static final Logger LOG = LoggerFactory.getLogger(DatastoreContextIntrospector.class); + + private static final Map> dataStorePropTypes = new HashMap<>(); + + private static final Map, Constructor> constructors = new HashMap<>(); + + private static final Map, Method> yangTypeGetters = new HashMap<>(); + + private static final Map builderSetters = new HashMap<>(); + + static { + try { + introspectDatastoreContextBuilder(); + introspectDataStoreProperties(); + introspectPrimitiveTypes(); + } catch (IntrospectionException e) { + LOG.error("Error initializing DatastoreContextIntrospector", e); + } + } + + /** + * Introspects each primitive wrapper (ie Integer, Long etc) and String type to find the + * constructor that takes a single String argument. For primitive wrappers, this constructor + * converts from a String representation. + */ + private static void introspectPrimitiveTypes() { + + Set> primitives = ImmutableSet.>builder().addAll( + Primitives.allWrapperTypes()).add(String.class).build(); + for(Class primitive: primitives) { + try { + processPropertyType(primitive); + } catch (Exception e) { + // Ignore primitives that can't be constructed from a String, eg Character and Void. + } + } + } + + /** + * Introspects the DatastoreContext.Builder class to find all its setter methods that we will + * invoke via reflection. We can't use the bean Introspector here as the Builder setters don't + * follow the bean property naming convention, ie setter prefixed with "set", so look for all + * the methods that return Builder. + */ + private static void introspectDatastoreContextBuilder() { + for(Method method: Builder.class.getMethods()) { + if(Builder.class.equals(method.getReturnType())) { + builderSetters.put(method.getName(), method); + } + } + } + + /** + * Introspects the DataStoreProperties interface that is generated from the DataStoreProperties + * yang grouping. We use the bean Introspector to find the types of all the properties defined + * in the interface (this is the type returned from the getter method). For each type, we find + * the appropriate constructor that we will use. + */ + private static void introspectDataStoreProperties() throws IntrospectionException { + BeanInfo beanInfo = Introspector.getBeanInfo(DataStoreProperties.class); + for(PropertyDescriptor desc: beanInfo.getPropertyDescriptors()) { + processDataStoreProperty(desc.getName(), desc.getPropertyType()); + } + + // Getter methods that return Boolean and start with "is" instead of "get" aren't recognized as + // properties and thus aren't returned from getPropertyDescriptors. A getter starting with + // "is" is only supported if it returns primitive boolean. So we'll check for these via + // getMethodDescriptors. + for(MethodDescriptor desc: beanInfo.getMethodDescriptors()) { + String methodName = desc.getName(); + if(Boolean.class.equals(desc.getMethod().getReturnType()) && methodName.startsWith("is")) { + String propertyName = WordUtils.uncapitalize(methodName.substring(2)); + processDataStoreProperty(propertyName, Boolean.class); + } + } + } + + /** + * Processes a property defined on the DataStoreProperties interface. + */ + private static void processDataStoreProperty(String name, Class propertyType) { + Preconditions.checkArgument(builderSetters.containsKey(name), String.format( + "DataStoreProperties property \"%s\" does not have corresponding setter in DatastoreContext.Builder", name)); + try { + processPropertyType(propertyType); + dataStorePropTypes.put(name, propertyType); + } catch (Exception e) { + LOG.error("Error finding constructor for type {}", propertyType, e); + } + } + + /** + * Finds the appropriate constructor for the specified type that we will use to construct + * instances. + */ + private static void processPropertyType(Class propertyType) throws Exception { + Class wrappedType = Primitives.wrap(propertyType); + if(constructors.containsKey(wrappedType)) { + return; + } + + // If the type is a primitive (or String type), we look for the constructor that takes a + // single String argument, which, for primitives, validates and converts from a String + // representation which is the form we get on ingress. + if(propertyType.isPrimitive() || Primitives.isWrapperType(propertyType) || + propertyType.equals(String.class)) + { + constructors.put(wrappedType, propertyType.getConstructor(String.class)); + } else { + // This must be a yang-defined type. We need to find the constructor that takes a + // primitive as the only argument. This will be used to construct instances to perform + // validation (eg range checking). The yang-generated types have a couple single-argument + // constructors but the one we want has the bean ConstructorProperties annotation. + for(Constructor ctor: propertyType.getConstructors()) { + ConstructorProperties ctorPropsAnnotation = ctor.getAnnotation(ConstructorProperties.class); + if(ctor.getParameterTypes().length == 1 && ctorPropsAnnotation != null) { + findYangTypeGetter(propertyType, ctorPropsAnnotation.value()[0]); + constructors.put(propertyType, ctor); + break; + } + } + } + } + + /** + * Finds the getter method on a yang-generated type for the specified property name. + */ + private static void findYangTypeGetter(Class type, String propertyName) + throws Exception { + for(PropertyDescriptor desc: Introspector.getBeanInfo(type).getPropertyDescriptors()) { + if(desc.getName().equals(propertyName)) { + yangTypeGetters.put(type, desc.getReadMethod()); + return; + } + } + + throw new IllegalArgumentException(String.format( + "Getter method for constructor property %s not found for YANG type %s", + propertyName, type)); + } + + private DatastoreContext context; + + public DatastoreContextIntrospector(DatastoreContext context) { + this.context = context; + } + + public DatastoreContext getContext() { + return context; + } + + /** + * Applies the given properties to the cached DatastoreContext and yields a new DatastoreContext + * instance which can be obtained via {@link getContext}. + * + * @param properties the properties to apply + * @return true if the cached DatastoreContext was updated, false otherwise. + */ + public boolean update(Dictionary properties) { + if(properties == null || properties.isEmpty()) { + return false; + } + + Builder builder = DatastoreContext.newBuilderFrom(context); + + final String dataStoreTypePrefix = context.getDataStoreType() + '.'; + + // Sort the property keys by putting the names prefixed with the data store type last. This + // is done so data store specific settings are applied after global settings. + ArrayList keys = Collections.list(properties.keys()); + Collections.sort(keys, new Comparator() { + @Override + public int compare(String key1, String key2) { + return key1.startsWith(dataStoreTypePrefix) ? 1 : + key2.startsWith(dataStoreTypePrefix) ? -1 : key1.compareTo(key2); + } + }); + + boolean updated = false; + for(String key: keys) { + Object value = properties.get(key); + try { + // If the key is prefixed with the data store type, strip it off. + if(key.startsWith(dataStoreTypePrefix)) { + key = key.replaceFirst(dataStoreTypePrefix, ""); + } + + key = convertToCamelCase(key); + + // Convert the value to the right type. + value = convertValue(key, value); + if(value == null) { + continue; + } + + LOG.debug("Converted value for property {}: {} ({})", + key, value, value.getClass().getSimpleName()); + + // Call the setter method on the Builder instance. + Method setter = builderSetters.get(key); + setter.invoke(builder, constructorValueRecursively( + Primitives.wrap(setter.getParameterTypes()[0]), value.toString())); + + updated = true; + + } catch (Exception e) { + LOG.error("Error converting value ({}) for property {}", value, key, e); + } + } + + if(updated) { + context = builder.build(); + } + + return updated; + } + + private String convertToCamelCase(String inString) { + String str = inString.trim(); + if(StringUtils.contains(str, '-') || StringUtils.contains(str, ' ')) { + str = inString.replace('-', ' '); + str = WordUtils.capitalizeFully(str); + str = StringUtils.deleteWhitespace(str); + } + + return StringUtils.uncapitalize(str); + } + + private Object convertValue(String name, Object from) throws Exception { + Class propertyType = dataStorePropTypes.get(name); + if(propertyType == null) { + LOG.debug("Property not found for {}", name); + return null; + } + + LOG.debug("Type for property {}: {}, converting value {} ({})", + name, propertyType.getSimpleName(), from, from.getClass().getSimpleName()); + + // Recurse the chain of constructors depth-first to get the resulting value. Eg, if the + // property type is the yang-generated NonZeroUint32Type, it's constructor takes a Long so + // we have to first construct a Long instance from the input value. + Object converted = constructorValueRecursively(propertyType, from.toString()); + + // If the converted type is a yang-generated type, call the getter to obtain the actual value. + Method getter = yangTypeGetters.get(converted.getClass()); + if(getter != null) { + converted = getter.invoke(converted); + } + + return converted; + } + + private Object constructorValueRecursively(Class toType, Object fromValue) throws Exception { + LOG.debug("convertValueRecursively - toType: {}, fromValue {} ({})", + toType.getSimpleName(), fromValue, fromValue.getClass().getSimpleName()); + + Constructor ctor = constructors.get(toType); + + LOG.debug("Found {}", ctor); + + if(ctor == null) { + throw new IllegalArgumentException(String.format("Constructor not found for type %s", toType)); + } + + Object value = fromValue; + + // Since the original input type is a String, once we find a constructor that takes a String + // argument, we're done recursing. + if(!ctor.getParameterTypes()[0].equals(String.class)) { + value = constructorValueRecursively(ctor.getParameterTypes()[0], fromValue); + } + + return ctor.newInstance(value); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java index 107c959112..434efc9111 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java @@ -12,8 +12,10 @@ import akka.actor.ActorSystem; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import org.opendaylight.controller.cluster.datastore.identifiers.ShardManagerIdentifier; +import org.opendaylight.controller.cluster.datastore.jmx.mbeans.DatastoreConfigurationMXBeanImpl; import org.opendaylight.controller.cluster.datastore.shardstrategy.ShardStrategyFactory; import org.opendaylight.controller.cluster.datastore.utils.ActorContext; +import org.opendaylight.controller.cluster.datastore.utils.Dispatchers; 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; @@ -39,6 +41,10 @@ public class DistributedDataStore implements DOMStore, SchemaContextListener, Au private final ActorContext actorContext; + private AutoCloseable closeable; + + private DatastoreConfigurationMXBeanImpl datastoreConfigMXBean; + public DistributedDataStore(ActorSystem actorSystem, ClusterWrapper cluster, Configuration configuration, DatastoreContext datastoreContext) { Preconditions.checkNotNull(actorSystem, "actorSystem should not be null"); @@ -52,16 +58,27 @@ public class DistributedDataStore implements DOMStore, SchemaContextListener, Au LOG.info("Creating ShardManager : {}", shardManagerId); + String shardDispatcher = + new Dispatchers(actorSystem.dispatchers()).getDispatcherPath(Dispatchers.DispatcherType.Shard); + actorContext = new ActorContext(actorSystem, actorSystem.actorOf( ShardManager.props(cluster, configuration, datastoreContext) - .withMailbox(ActorContext.MAILBOX), shardManagerId ), + .withDispatcher(shardDispatcher).withMailbox(ActorContext.MAILBOX), shardManagerId ), cluster, configuration, datastoreContext); + + datastoreConfigMXBean = new DatastoreConfigurationMXBeanImpl(datastoreContext.getDataStoreMXBeanType()); + datastoreConfigMXBean.setContext(datastoreContext); + datastoreConfigMXBean.registerMBean(); } public DistributedDataStore(ActorContext actorContext) { this.actorContext = Preconditions.checkNotNull(actorContext, "actorContext should not be null"); } + public void setCloseable(AutoCloseable closeable) { + this.closeable = closeable; + } + @SuppressWarnings("unchecked") @Override public >> @@ -111,7 +128,17 @@ public class DistributedDataStore implements DOMStore, SchemaContextListener, Au } @Override - public void close() throws Exception { + public void close() { + datastoreConfigMXBean.unregisterMBean(); + + if(closeable != null) { + try { + closeable.close(); + } catch (Exception e) { + LOG.debug("Error closing insance", e); + } + } + actorContext.shutdown(); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java index a9a735ede7..f3a55c55a7 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java @@ -23,16 +23,21 @@ public class DistributedDataStoreFactory { private static volatile ActorSystem persistentActorSystem = null; public static DistributedDataStore createInstance(SchemaService schemaService, - DatastoreContext datastoreContext, BundleContext bundleContext) { + DatastoreContext datastoreContext, BundleContext bundleContext) { + + DatastoreContextIntrospector introspector = new DatastoreContextIntrospector(datastoreContext); + DatastoreContextConfigAdminOverlay overlay = new DatastoreContextConfigAdminOverlay( + introspector, bundleContext); ActorSystem actorSystem = getOrCreateInstance(bundleContext, datastoreContext.getConfigurationReader()); Configuration config = new ConfigurationImpl("module-shards.conf", "modules.conf"); - final DistributedDataStore dataStore = - new DistributedDataStore(actorSystem, new ClusterWrapperImpl(actorSystem), - config, datastoreContext); + final DistributedDataStore dataStore = new DistributedDataStore(actorSystem, + new ClusterWrapperImpl(actorSystem), config, introspector.getContext()); ShardStrategyFactory.setConfiguration(config); schemaService.registerSchemaContextListener(dataStore); + + dataStore.setCloseable(overlay); return dataStore; } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/LegacyTransactionContextImpl.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/LegacyTransactionContextImpl.java new file mode 100644 index 0000000000..65d82b73d9 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/LegacyTransactionContextImpl.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore; + +import akka.actor.ActorSelection; +import org.opendaylight.controller.cluster.datastore.identifiers.TransactionIdentifier; +import org.opendaylight.controller.cluster.datastore.messages.DeleteData; +import org.opendaylight.controller.cluster.datastore.messages.MergeData; +import org.opendaylight.controller.cluster.datastore.messages.WriteData; +import org.opendaylight.controller.cluster.datastore.utils.ActorContext; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +/** + * Implementation of TransactionContextImpl used when talking to a pre-Lithium controller that doesn't + * support the BatchedModifications message. + * + * @author Thomas Pantelis + */ +class LegacyTransactionContextImpl extends TransactionContextImpl { + + LegacyTransactionContextImpl(String transactionPath, ActorSelection actor, TransactionIdentifier identifier, + ActorContext actorContext, SchemaContext schemaContext, boolean isTxActorLocal, + short remoteTransactionVersion, OperationCompleter operationCompleter) { + super(transactionPath, actor, identifier, actorContext, schemaContext, isTxActorLocal, + remoteTransactionVersion, operationCompleter); + } + + @Override + public void deleteData(YangInstanceIdentifier path) { + recordedOperationFutures.add(executeOperationAsync( + new DeleteData(path, getRemoteTransactionVersion()))); + } + + @Override + public void mergeData(YangInstanceIdentifier path, NormalizedNode data) { + recordedOperationFutures.add(executeOperationAsync( + new MergeData(path, data, getRemoteTransactionVersion()))); + } + + @Override + public void writeData(YangInstanceIdentifier path, NormalizedNode data) { + recordedOperationFutures.add(executeOperationAsync( + new WriteData(path, data, getRemoteTransactionVersion()))); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/OperationCompleter.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/OperationCompleter.java index 09fa61b570..80aa3793c1 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/OperationCompleter.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/OperationCompleter.java @@ -10,6 +10,7 @@ package org.opendaylight.controller.cluster.datastore; import akka.dispatch.OnComplete; import com.google.common.base.Preconditions; import java.util.concurrent.Semaphore; +import org.opendaylight.controller.cluster.datastore.messages.BatchedModificationsReply; final class OperationCompleter extends OnComplete { private final Semaphore operationLimiter; @@ -19,7 +20,11 @@ final class OperationCompleter extends OnComplete { } @Override - public void onComplete(Throwable throwable, Object o){ - this.operationLimiter.release(); + public void onComplete(Throwable throwable, Object message) { + if(message instanceof BatchedModificationsReply) { + this.operationLimiter.release(((BatchedModificationsReply)message).getNumBatched()); + } else { + this.operationLimiter.release(); + } } } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java index 21d74a6e1a..0672023fcb 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java @@ -60,10 +60,13 @@ import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContex import org.opendaylight.controller.cluster.datastore.modification.Modification; import org.opendaylight.controller.cluster.datastore.modification.ModificationPayload; import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification; +import org.opendaylight.controller.cluster.datastore.utils.Dispatchers; +import org.opendaylight.controller.cluster.datastore.utils.MessageTracker; import org.opendaylight.controller.cluster.datastore.utils.SerializationUtils; import org.opendaylight.controller.cluster.notifications.RoleChangeNotifier; import org.opendaylight.controller.cluster.raft.RaftActor; import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; +import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationByteStringPayload; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; @@ -71,6 +74,7 @@ import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListene import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory; 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.DOMStoreTransactionChain; import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionFactory; import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; @@ -125,6 +129,8 @@ public class Shard extends RaftActor { private final Optional roleChangeNotifier; + private final MessageTracker appendEntriesReplyTracker; + /** * Coordinates persistence recovery on startup. */ @@ -133,6 +139,8 @@ public class Shard extends RaftActor { private final Map transactionChains = new HashMap<>(); + private final String txnDispatcherPath; + protected Shard(final ShardIdentifier name, final Map peerAddresses, final DatastoreContext datastoreContext, final SchemaContext schemaContext) { super(name.toString(), mapPeerAddresses(peerAddresses), @@ -141,7 +149,11 @@ public class Shard extends RaftActor { this.name = name; this.datastoreContext = datastoreContext; this.schemaContext = schemaContext; - this.dataPersistenceProvider = (datastoreContext.isPersistent()) ? new PersistentDataProvider() : new NonPersistentRaftDataProvider(); + this.dataPersistenceProvider = (datastoreContext.isPersistent()) + ? new PersistentDataProvider() : new NonPersistentRaftDataProvider(); + this.txnDispatcherPath = new Dispatchers(context().system().dispatchers()) + .getDispatcherPath(Dispatchers.DispatcherType.Transaction); + LOG.info("Shard created : {}, persistent : {}", name, datastoreContext.isPersistent()); @@ -168,6 +180,9 @@ public class Shard extends RaftActor { // create a notifier actor for each cluster member roleChangeNotifier = createRoleChangeNotifier(name.toString()); + + appendEntriesReplyTracker = new MessageTracker(AppendEntriesReply.class, + getRaftActorContext().getConfigParams().getIsolatedCheckIntervalInMillis()); } private static Map mapPeerAddresses( @@ -224,35 +239,50 @@ public class Shard extends RaftActor { onRecoveryComplete(); } else { super.onReceiveRecover(message); + if(LOG.isTraceEnabled()) { + appendEntriesReplyTracker.begin(); + } } } @Override public void onReceiveCommand(final Object message) throws Exception { - if (message.getClass().equals(CreateTransaction.SERIALIZABLE_CLASS)) { - handleCreateTransaction(message); - } else if(message instanceof ForwardedReadyTransaction) { - handleForwardedReadyTransaction((ForwardedReadyTransaction)message); - } else if(message.getClass().equals(CanCommitTransaction.SERIALIZABLE_CLASS)) { - handleCanCommitTransaction(CanCommitTransaction.fromSerializable(message)); - } else if(message.getClass().equals(CommitTransaction.SERIALIZABLE_CLASS)) { - handleCommitTransaction(CommitTransaction.fromSerializable(message)); - } else if(message.getClass().equals(AbortTransaction.SERIALIZABLE_CLASS)) { - handleAbortTransaction(AbortTransaction.fromSerializable(message)); - } else if (message.getClass().equals(CloseTransactionChain.SERIALIZABLE_CLASS)){ - closeTransactionChain(CloseTransactionChain.fromSerializable(message)); - } else if (message instanceof RegisterChangeListener) { - registerChangeListener((RegisterChangeListener) message); - } else if (message instanceof UpdateSchemaContext) { - updateSchemaContext((UpdateSchemaContext) message); - } else if (message instanceof PeerAddressResolved) { - PeerAddressResolved resolved = (PeerAddressResolved) message; - setPeerAddress(resolved.getPeerId().toString(), - resolved.getPeerAddress()); - } else if(message.equals(TX_COMMIT_TIMEOUT_CHECK_MESSAGE)) { - handleTransactionCommitTimeoutCheck(); - } else { - super.onReceiveCommand(message); + + MessageTracker.Context context = appendEntriesReplyTracker.received(message); + + if(context.error().isPresent()){ + LOG.trace("{} : AppendEntriesReply failed to arrive at the expected interval {}", persistenceId(), + context.error()); + } + + try { + if (message.getClass().equals(CreateTransaction.SERIALIZABLE_CLASS)) { + handleCreateTransaction(message); + } else if (message instanceof ForwardedReadyTransaction) { + handleForwardedReadyTransaction((ForwardedReadyTransaction) message); + } else if (message.getClass().equals(CanCommitTransaction.SERIALIZABLE_CLASS)) { + handleCanCommitTransaction(CanCommitTransaction.fromSerializable(message)); + } else if (message.getClass().equals(CommitTransaction.SERIALIZABLE_CLASS)) { + handleCommitTransaction(CommitTransaction.fromSerializable(message)); + } else if (message.getClass().equals(AbortTransaction.SERIALIZABLE_CLASS)) { + handleAbortTransaction(AbortTransaction.fromSerializable(message)); + } else if (message.getClass().equals(CloseTransactionChain.SERIALIZABLE_CLASS)) { + closeTransactionChain(CloseTransactionChain.fromSerializable(message)); + } else if (message instanceof RegisterChangeListener) { + registerChangeListener((RegisterChangeListener) message); + } else if (message instanceof UpdateSchemaContext) { + updateSchemaContext((UpdateSchemaContext) message); + } else if (message instanceof PeerAddressResolved) { + PeerAddressResolved resolved = (PeerAddressResolved) message; + setPeerAddress(resolved.getPeerId().toString(), + resolved.getPeerAddress()); + } else if (message.equals(TX_COMMIT_TIMEOUT_CHECK_MESSAGE)) { + handleTransactionCommitTimeoutCheck(); + } else { + super.onReceiveCommand(message); + } + } finally { + context.done(); } } @@ -493,32 +523,19 @@ public class Shard extends RaftActor { shardMBean.incrementReadOnlyTransactionCount(); - return getContext().actorOf( - ShardTransaction.props(factory.newReadOnlyTransaction(), getSelf(), - schemaContext,datastoreContext, shardMBean, - transactionId.getRemoteTransactionId(), clientVersion), - transactionId.toString()); + return createShardTransaction(factory.newReadOnlyTransaction(), transactionId, clientVersion); } else if (transactionType == TransactionProxy.TransactionType.READ_WRITE.ordinal()) { shardMBean.incrementReadWriteTransactionCount(); - return getContext().actorOf( - ShardTransaction.props(factory.newReadWriteTransaction(), getSelf(), - schemaContext, datastoreContext, shardMBean, - transactionId.getRemoteTransactionId(), clientVersion), - transactionId.toString()); - + return createShardTransaction(factory.newReadWriteTransaction(), transactionId, clientVersion); } else if (transactionType == TransactionProxy.TransactionType.WRITE_ONLY.ordinal()) { shardMBean.incrementWriteOnlyTransactionCount(); - return getContext().actorOf( - ShardTransaction.props(factory.newWriteOnlyTransaction(), getSelf(), - schemaContext, datastoreContext, shardMBean, - transactionId.getRemoteTransactionId(), clientVersion), - transactionId.toString()); + return createShardTransaction(factory.newWriteOnlyTransaction(), transactionId, clientVersion); } else { throw new IllegalArgumentException( "Shard="+name + ":CreateTransaction message has unidentified transaction type=" @@ -526,6 +543,17 @@ public class Shard extends RaftActor { } } + private ActorRef createShardTransaction(DOMStoreTransaction transaction, ShardTransactionIdentifier transactionId, + short clientVersion){ + return getContext().actorOf( + ShardTransaction.props(transaction, getSelf(), + schemaContext, datastoreContext, shardMBean, + transactionId.getRemoteTransactionId(), clientVersion) + .withDispatcher(txnDispatcherPath), + transactionId.toString()); + + } + private void createTransaction(CreateTransaction createTransaction) { try { ActorRef transactionActor = createTransaction(createTransaction.getTransactionType(), diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java index d52965e055..426a2e0934 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java @@ -50,6 +50,7 @@ import org.opendaylight.controller.cluster.datastore.messages.PeerAddressResolve import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound; import org.opendaylight.controller.cluster.datastore.messages.PrimaryNotFound; import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext; +import org.opendaylight.controller.cluster.datastore.utils.Dispatchers; import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; @@ -87,6 +88,8 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering { private final Configuration configuration; + private final String shardDispatcherPath; + private ShardManagerInfoMBean mBean; private final DatastoreContext datastoreContext; @@ -105,6 +108,8 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering { this.datastoreContext = datastoreContext; this.dataPersistenceProvider = createDataPersistenceProvider(datastoreContext.isPersistent()); this.type = datastoreContext.getDataStoreType(); + this.shardDispatcherPath = + new Dispatchers(context().system().dispatchers()).getDispatcherPath(Dispatchers.DispatcherType.Shard); // Subscribe this actor to cluster member events cluster.subscribeToMemberEvents(getSelf()); @@ -283,8 +288,8 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering { for (ShardInformation info : localShards.values()) { if (info.getActor() == null) { info.setActor(getContext().actorOf(Shard.props(info.getShardId(), - info.getPeerAddresses(), datastoreContext, schemaContext), - info.getShardId().toString())); + info.getPeerAddresses(), datastoreContext, schemaContext) + .withDispatcher(shardDispatcherPath), info.getShardId().toString())); } else { info.getActor().tell(message, getSelf()); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransaction.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransaction.java index af25df13d2..613b3749e0 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransaction.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransaction.java @@ -129,9 +129,9 @@ public abstract class ShardTransaction extends AbstractUntypedActorWithMetering try { final CheckedFuture>, ReadFailedException> future = transaction.read(path); Optional> optional = future.checkedGet(); - ReadDataReply readDataReply = new ReadDataReply(optional.orNull()); + ReadDataReply readDataReply = new ReadDataReply(optional.orNull(), clientTxVersion); - sender().tell((returnSerialized ? readDataReply.toSerializable(clientTxVersion): readDataReply), self()); + sender().tell((returnSerialized ? readDataReply.toSerializable(): readDataReply), self()); } catch (Exception e) { LOG.debug(String.format("Unexpected error reading path %s", path), e); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java index a4a2f45fdb..d5dcfde803 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java @@ -13,6 +13,8 @@ package org.opendaylight.controller.cluster.datastore; import akka.actor.ActorRef; import akka.actor.PoisonPill; import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats; +import org.opendaylight.controller.cluster.datastore.messages.BatchedModifications; +import org.opendaylight.controller.cluster.datastore.messages.BatchedModificationsReply; import org.opendaylight.controller.cluster.datastore.messages.DeleteData; import org.opendaylight.controller.cluster.datastore.messages.DeleteDataReply; import org.opendaylight.controller.cluster.datastore.messages.ForwardedReadyTransaction; @@ -24,6 +26,7 @@ import org.opendaylight.controller.cluster.datastore.messages.WriteDataReply; import org.opendaylight.controller.cluster.datastore.modification.CompositeModification; import org.opendaylight.controller.cluster.datastore.modification.DeleteModification; import org.opendaylight.controller.cluster.datastore.modification.MergeModification; +import org.opendaylight.controller.cluster.datastore.modification.Modification; import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification; import org.opendaylight.controller.cluster.datastore.modification.WriteModification; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; @@ -37,7 +40,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; */ public class ShardWriteTransaction extends ShardTransaction { - private final MutableCompositeModification modification = new MutableCompositeModification(); + private final MutableCompositeModification compositeModification = new MutableCompositeModification(); private final DOMStoreWriteTransaction transaction; public ShardWriteTransaction(DOMStoreWriteTransaction transaction, ActorRef shardActor, @@ -55,18 +58,12 @@ public class ShardWriteTransaction extends ShardTransaction { @Override public void handleReceive(Object message) throws Exception { - if (message instanceof WriteData) { - writeData(transaction, (WriteData) message, !SERIALIZED_REPLY); - - } else if (message instanceof MergeData) { - mergeData(transaction, (MergeData) message, !SERIALIZED_REPLY); - - } else if (message instanceof DeleteData) { - deleteData(transaction, (DeleteData) message, !SERIALIZED_REPLY); - + if (message instanceof BatchedModifications) { + batchedModifications((BatchedModifications)message); } else if (message instanceof ReadyTransaction) { readyTransaction(transaction, !SERIALIZED_REPLY); - + } else if(ReadyTransaction.SERIALIZABLE_CLASS.equals(message.getClass())) { + readyTransaction(transaction, SERIALIZED_REPLY); } else if(WriteData.isSerializedType(message)) { writeData(transaction, WriteData.fromSerializable(message), SERIALIZED_REPLY); @@ -76,22 +73,32 @@ public class ShardWriteTransaction extends ShardTransaction { } else if(DeleteData.isSerializedType(message)) { deleteData(transaction, DeleteData.fromSerializable(message), SERIALIZED_REPLY); - } else if(ReadyTransaction.SERIALIZABLE_CLASS.equals(message.getClass())) { - readyTransaction(transaction, SERIALIZED_REPLY); - } else if (message instanceof GetCompositedModification) { // This is here for testing only - getSender().tell(new GetCompositeModificationReply(modification), getSelf()); + getSender().tell(new GetCompositeModificationReply(compositeModification), getSelf()); } else { super.handleReceive(message); } } + private void batchedModifications(BatchedModifications batched) { + try { + for(Modification modification: batched.getModifications()) { + compositeModification.addModification(modification); + modification.apply(transaction); + } + + getSender().tell(new BatchedModificationsReply(batched.getModifications().size()), getSelf()); + } catch (Exception e) { + getSender().tell(new akka.actor.Status.Failure(e), getSelf()); + } + } + private void writeData(DOMStoreWriteTransaction transaction, WriteData message, boolean returnSerialized) { LOG.debug("writeData at path : {}", message.getPath()); - modification.addModification( + compositeModification.addModification( new WriteModification(message.getPath(), message.getData())); try { transaction.write(message.getPath(), message.getData()); @@ -107,7 +114,7 @@ public class ShardWriteTransaction extends ShardTransaction { boolean returnSerialized) { LOG.debug("mergeData at path : {}", message.getPath()); - modification.addModification( + compositeModification.addModification( new MergeModification(message.getPath(), message.getData())); try { @@ -124,7 +131,7 @@ public class ShardWriteTransaction extends ShardTransaction { boolean returnSerialized) { LOG.debug("deleteData at path : {}", message.getPath()); - modification.addModification(new DeleteModification(message.getPath())); + compositeModification.addModification(new DeleteModification(message.getPath())); try { transaction.delete(message.getPath()); DeleteDataReply deleteDataReply = DeleteDataReply.INSTANCE; @@ -143,7 +150,7 @@ public class ShardWriteTransaction extends ShardTransaction { DOMStoreThreePhaseCommitCohort cohort = transaction.ready(); getShardActor().forward(new ForwardedReadyTransaction(transactionID, getClientTxVersion(), - cohort, modification, returnSerialized), getContext()); + cohort, compositeModification, returnSerialized), getContext()); // The shard will handle the commit from here so we're no longer needed - self-destruct. getSelf().tell(PoisonPill.getInstance(), getSelf()); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java index c51ea80726..4445b14e2e 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java @@ -71,7 +71,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho private Future buildCohortList() { Future> combinedFutures = Futures.sequence(cohortFutures, - actorContext.getActorSystem().dispatcher()); + actorContext.getClientDispatcher()); return combinedFutures.transform(new AbstractFunction1, Void>() { @Override @@ -83,7 +83,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho } return null; } - }, TransactionProxy.SAME_FAILURE_TRANSFORMER, actorContext.getActorSystem().dispatcher()); + }, TransactionProxy.SAME_FAILURE_TRANSFORMER, actorContext.getClientDispatcher()); } @Override @@ -111,7 +111,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho finishCanCommit(returnFuture); } } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); return returnFuture; } @@ -158,7 +158,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho } returnFuture.set(Boolean.valueOf(result)); } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); } private Future> invokeCohorts(Object message) { @@ -170,7 +170,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho futureList.add(actorContext.executeOperationAsync(cohort, message, actorContext.getTransactionCommitOperationTimeout())); } - return Futures.sequence(futureList, actorContext.getActorSystem().dispatcher()); + return Futures.sequence(futureList, actorContext.getClientDispatcher()); } @Override @@ -239,7 +239,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho propagateException, returnFuture, callback); } } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); } return returnFuture; @@ -304,7 +304,7 @@ public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCoho callback.success(); } } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); } @VisibleForTesting diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionContextImpl.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionContextImpl.java index 530a36cff6..1e222e4c0a 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionContextImpl.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionContextImpl.java @@ -15,18 +15,19 @@ import com.google.common.collect.Lists; import com.google.common.util.concurrent.SettableFuture; import java.util.List; import org.opendaylight.controller.cluster.datastore.identifiers.TransactionIdentifier; +import org.opendaylight.controller.cluster.datastore.messages.BatchedModifications; import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction; import org.opendaylight.controller.cluster.datastore.messages.DataExists; import org.opendaylight.controller.cluster.datastore.messages.DataExistsReply; -import org.opendaylight.controller.cluster.datastore.messages.DeleteData; -import org.opendaylight.controller.cluster.datastore.messages.MergeData; import org.opendaylight.controller.cluster.datastore.messages.ReadData; import org.opendaylight.controller.cluster.datastore.messages.ReadDataReply; import org.opendaylight.controller.cluster.datastore.messages.ReadyTransaction; import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply; import org.opendaylight.controller.cluster.datastore.messages.SerializableMessage; -import org.opendaylight.controller.cluster.datastore.messages.VersionedSerializableMessage; -import org.opendaylight.controller.cluster.datastore.messages.WriteData; +import org.opendaylight.controller.cluster.datastore.modification.DeleteModification; +import org.opendaylight.controller.cluster.datastore.modification.MergeModification; +import org.opendaylight.controller.cluster.datastore.modification.Modification; +import org.opendaylight.controller.cluster.datastore.modification.WriteModification; import org.opendaylight.controller.cluster.datastore.utils.ActorContext; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -36,7 +37,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scala.concurrent.Future; -final class TransactionContextImpl extends AbstractTransactionContext { +class TransactionContextImpl extends AbstractTransactionContext { private static final Logger LOG = LoggerFactory.getLogger(TransactionContextImpl.class); private final ActorContext actorContext; @@ -44,8 +45,9 @@ final class TransactionContextImpl extends AbstractTransactionContext { private final ActorSelection actor; private final boolean isTxActorLocal; private final short remoteTransactionVersion; - private final OperationCompleter operationCompleter; + private final OperationCompleter operationCompleter; + private BatchedModifications batchedModifications; TransactionContextImpl(String transactionPath, ActorSelection actor, TransactionIdentifier identifier, ActorContext actorContext, SchemaContext schemaContext, @@ -60,7 +62,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { } private Future completeOperation(Future operationFuture){ - operationFuture.onComplete(this.operationCompleter, actorContext.getActorSystem().dispatcher()); + operationFuture.onComplete(this.operationCompleter, actorContext.getClientDispatcher()); return operationFuture; } @@ -69,13 +71,12 @@ final class TransactionContextImpl extends AbstractTransactionContext { return actor; } - private Future executeOperationAsync(SerializableMessage msg) { - return completeOperation(actorContext.executeOperationAsync(getActor(), isTxActorLocal ? msg : msg.toSerializable())); + protected short getRemoteTransactionVersion() { + return remoteTransactionVersion; } - private Future executeOperationAsync(VersionedSerializableMessage msg) { - return completeOperation(actorContext.executeOperationAsync(getActor(), isTxActorLocal ? msg : - msg.toSerializable(remoteTransactionVersion))); + protected Future executeOperationAsync(SerializableMessage msg) { + return completeOperation(actorContext.executeOperationAsync(getActor(), isTxActorLocal ? msg : msg.toSerializable())); } @Override @@ -90,6 +91,10 @@ final class TransactionContextImpl extends AbstractTransactionContext { LOG.debug("Tx {} readyTransaction called with {} previous recorded operations pending", identifier, recordedOperationFutures.size()); + // Send the remaining batched modifications if any. + + sendBatchedModifications(); + // Send the ReadyTransaction message to the Tx actor. final Future replyFuture = executeOperationAsync(ReadyTransaction.INSTANCE); @@ -105,7 +110,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { futureList.add(replyFuture); Future> combinedFutures = akka.dispatch.Futures.sequence(futureList, - actorContext.getActorSystem().dispatcher()); + actorContext.getClientDispatcher()); // Transform the combined Future into a Future that returns the cohort actor path from // the ReadyTransactionReply. That's the end result of the ready operation. @@ -152,28 +157,51 @@ final class TransactionContextImpl extends AbstractTransactionContext { serializedReadyReply.getClass())); } } - }, TransactionProxy.SAME_FAILURE_TRANSFORMER, actorContext.getActorSystem().dispatcher()); + }, TransactionProxy.SAME_FAILURE_TRANSFORMER, actorContext.getClientDispatcher()); + } + + private void batchModification(Modification modification) { + if(batchedModifications == null) { + batchedModifications = new BatchedModifications(remoteTransactionVersion); + } + + batchedModifications.addModification(modification); + + if(batchedModifications.getModifications().size() >= + actorContext.getDatastoreContext().getShardBatchedModificationCount()) { + sendBatchedModifications(); + } + } + + private void sendBatchedModifications() { + if(batchedModifications != null) { + LOG.debug("Tx {} sending {} batched modifications", identifier, + batchedModifications.getModifications().size()); + + recordedOperationFutures.add(executeOperationAsync(batchedModifications)); + batchedModifications = null; + } } @Override public void deleteData(YangInstanceIdentifier path) { LOG.debug("Tx {} deleteData called path = {}", identifier, path); - recordedOperationFutures.add(executeOperationAsync(new DeleteData(path))); + batchModification(new DeleteModification(path)); } @Override public void mergeData(YangInstanceIdentifier path, NormalizedNode data) { LOG.debug("Tx {} mergeData called path = {}", identifier, path); - recordedOperationFutures.add(executeOperationAsync(new MergeData(path, data))); + batchModification(new MergeModification(path, data)); } @Override public void writeData(YangInstanceIdentifier path, NormalizedNode data) { LOG.debug("Tx {} writeData called path = {}", identifier, path); - recordedOperationFutures.add(executeOperationAsync(new WriteData(path, data))); + batchModification(new WriteModification(path, data)); } @Override @@ -182,6 +210,10 @@ final class TransactionContextImpl extends AbstractTransactionContext { LOG.debug("Tx {} readData called path = {}", identifier, path); + // Send the remaining batched modifications if any. + + sendBatchedModifications(); + // If there were any previous recorded put/merge/delete operation reply Futures then we // must wait for them to successfully complete. This is necessary to honor the read // uncommitted semantics of the public API contract. If any one fails then fail the read. @@ -198,7 +230,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { Future> combinedFutures = akka.dispatch.Futures.sequence( Lists.newArrayList(recordedOperationFutures), - actorContext.getActorSystem().dispatcher()); + actorContext.getClientDispatcher()); OnComplete> onComplete = new OnComplete>() { @Override @@ -216,7 +248,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { } }; - combinedFutures.onComplete(onComplete, actorContext.getActorSystem().dispatcher()); + combinedFutures.onComplete(onComplete, actorContext.getClientDispatcher()); } } @@ -255,7 +287,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { Future readFuture = executeOperationAsync(new ReadData(path)); - readFuture.onComplete(onComplete, actorContext.getActorSystem().dispatcher()); + readFuture.onComplete(onComplete, actorContext.getClientDispatcher()); } @Override @@ -263,6 +295,10 @@ final class TransactionContextImpl extends AbstractTransactionContext { LOG.debug("Tx {} dataExists called path = {}", identifier, path); + // Send the remaining batched modifications if any. + + sendBatchedModifications(); + // If there were any previous recorded put/merge/delete operation reply Futures then we // must wait for them to successfully complete. This is necessary to honor the read // uncommitted semantics of the public API contract. If any one fails then fail this @@ -280,7 +316,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { Future> combinedFutures = akka.dispatch.Futures.sequence( Lists.newArrayList(recordedOperationFutures), - actorContext.getActorSystem().dispatcher()); + actorContext.getClientDispatcher()); OnComplete> onComplete = new OnComplete>() { @Override public void onComplete(Throwable failure, Iterable notUsed) @@ -297,7 +333,7 @@ final class TransactionContextImpl extends AbstractTransactionContext { } }; - combinedFutures.onComplete(onComplete, actorContext.getActorSystem().dispatcher()); + combinedFutures.onComplete(onComplete, actorContext.getClientDispatcher()); } } @@ -332,6 +368,6 @@ final class TransactionContextImpl extends AbstractTransactionContext { Future future = executeOperationAsync(new DataExists(path)); - future.onComplete(onComplete, actorContext.getActorSystem().dispatcher()); + future.onComplete(onComplete, actorContext.getClientDispatcher()); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java index 5bc53442ae..58b37be2a2 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java @@ -304,7 +304,8 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { private void throttleOperation(int acquirePermits) { try { - if(!operationLimiter.tryAcquire(acquirePermits, actorContext.getDatastoreContext().getOperationTimeoutInSeconds(), TimeUnit.SECONDS)){ + if(!operationLimiter.tryAcquire(acquirePermits, + actorContext.getDatastoreContext().getOperationTimeoutInSeconds(), TimeUnit.SECONDS)){ LOG.warn("Failed to acquire operation permit for transaction {}", getIdentifier()); } } catch (InterruptedException e) { @@ -484,7 +485,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { newTxFutureCallback.setPrimaryShard(primaryShard); } } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); } return txFutureCallback; @@ -601,7 +602,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { TransactionProxy.this.transactionType.ordinal(), getTransactionChainId()).toSerializable()); - createTxFuture.onComplete(this, actorContext.getActorSystem().dispatcher()); + createTxFuture.onComplete(this, actorContext.getClientDispatcher()); } @Override @@ -621,7 +622,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { public void run() { tryCreateTransaction(); } - }, actorContext.getActorSystem().dispatcher()); + }, actorContext.getClientDispatcher()); return; } } @@ -689,7 +690,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { private TransactionContext createValidTransactionContext(CreateTransactionReply reply) { String transactionPath = reply.getTransactionPath(); - LOG.debug("Tx {} Received transaction actor path {}", identifier, transactionPath); + LOG.debug("Tx {} Received {}", identifier, reply); ActorSelection transactionActor = actorContext.actorSelection(transactionPath); @@ -707,8 +708,13 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { // Check if TxActor is created in the same node boolean isTxActorLocal = actorContext.isPathLocal(transactionPath); - return new TransactionContextImpl(transactionPath, transactionActor, identifier, - actorContext, schemaContext, isTxActorLocal, reply.getVersion(), operationCompleter); + if(reply.getVersion() >= DataStoreVersions.LITHIUM_VERSION) { + return new TransactionContextImpl(transactionPath, transactionActor, identifier, + actorContext, schemaContext, isTxActorLocal, reply.getVersion(), operationCompleter); + } else { + return new LegacyTransactionContextImpl(transactionPath, transactionActor, identifier, + actorContext, schemaContext, isTxActorLocal, reply.getVersion(), operationCompleter); + } } } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/DatastoreConfigurationMXBean.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/DatastoreConfigurationMXBean.java new file mode 100644 index 0000000000..6b81792a57 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/DatastoreConfigurationMXBean.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore.jmx.mbeans; + + +/** + * MXBean interface for data store configuration. + * + * @author Thomas Pantelis + */ +public interface DatastoreConfigurationMXBean { + long getShardTransactionIdleTimeoutInSeconds(); + + long getOperationTimeoutInSeconds(); + + long getShardHeartbeatIntervalInMillis(); + + int getShardJournalRecoveryLogBatchSize(); + + long getShardIsolatedLeaderCheckIntervalInMillis(); + + long getShardElectionTimeoutFactor(); + + int getShardSnapshotDataThresholdPercentage(); + + long getShardSnapshotBatchCount(); + + long getShardTransactionCommitTimeoutInSeconds(); + + int getShardTransactionCommitQueueCapacity(); + + long getShardInitializationTimeoutInSeconds(); + + long getShardLeaderElectionTimeoutInSeconds(); + + boolean isPersistent(); + + long getTransactionCreationInitialRateLimit(); + + int getMaxShardDataChangeExecutorPoolSize(); + + int getMaxShardDataChangeExecutorQueueSize(); + + int getMaxShardDataChangeListenerQueueSize(); + + int getMaxShardDataStoreExecutorQueueSize(); +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/DatastoreConfigurationMXBeanImpl.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/DatastoreConfigurationMXBeanImpl.java new file mode 100644 index 0000000000..79ff2a4e54 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/DatastoreConfigurationMXBeanImpl.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore.jmx.mbeans; + +import org.opendaylight.controller.cluster.datastore.DatastoreContext; +import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean; + +/** + * Implementation of DatastoreConfigurationMXBean. + * + * @author Thomas Pantelis + */ +public class DatastoreConfigurationMXBeanImpl extends AbstractMXBean implements DatastoreConfigurationMXBean { + public static final String JMX_CATEGORY_CONFIGURATION = "Configuration"; + + private DatastoreContext context; + + public DatastoreConfigurationMXBeanImpl(String mxBeanType) { + super("Datastore", mxBeanType, JMX_CATEGORY_CONFIGURATION); + } + + public void setContext(DatastoreContext context) { + this.context = context; + } + + @Override + public long getShardTransactionIdleTimeoutInSeconds() { + return context.getShardTransactionIdleTimeout().toSeconds(); + } + + @Override + public long getOperationTimeoutInSeconds() { + return context.getOperationTimeoutInSeconds(); + } + + @Override + public long getShardHeartbeatIntervalInMillis() { + return context.getShardRaftConfig().getHeartBeatInterval().toMillis(); + } + + @Override + public int getShardJournalRecoveryLogBatchSize() { + return context.getShardRaftConfig().getJournalRecoveryLogBatchSize(); + } + + @Override + public long getShardIsolatedLeaderCheckIntervalInMillis() { + return context.getShardRaftConfig().getIsolatedCheckIntervalInMillis(); + } + + @Override + public long getShardElectionTimeoutFactor() { + return context.getShardRaftConfig().getElectionTimeoutFactor(); + } + + @Override + public int getShardSnapshotDataThresholdPercentage() { + return context.getShardRaftConfig().getSnapshotDataThresholdPercentage(); + } + + @Override + public long getShardSnapshotBatchCount() { + return context.getShardRaftConfig().getSnapshotBatchCount(); + } + + @Override + public long getShardTransactionCommitTimeoutInSeconds() { + return context.getShardTransactionCommitTimeoutInSeconds(); + } + + @Override + public int getShardTransactionCommitQueueCapacity() { + return context.getShardTransactionCommitQueueCapacity(); + } + + @Override + public long getShardInitializationTimeoutInSeconds() { + return context.getShardInitializationTimeout().duration().toSeconds(); + } + + @Override + public long getShardLeaderElectionTimeoutInSeconds() { + return context.getShardLeaderElectionTimeout().duration().toSeconds(); + } + + @Override + public boolean isPersistent() { + return context.isPersistent(); + } + + @Override + public long getTransactionCreationInitialRateLimit() { + return context.getTransactionCreationInitialRateLimit(); + } + + @Override + public int getMaxShardDataChangeExecutorPoolSize() { + return context.getDataStoreProperties().getMaxDataChangeExecutorPoolSize(); + } + + @Override + public int getMaxShardDataChangeExecutorQueueSize() { + return context.getDataStoreProperties().getMaxDataChangeExecutorQueueSize(); + } + + @Override + public int getMaxShardDataChangeListenerQueueSize() { + return context.getDataStoreProperties().getMaxDataChangeListenerQueueSize(); + } + + @Override + public int getMaxShardDataStoreExecutorQueueSize() { + return context.getDataStoreProperties().getMaxDataStoreExecutorQueueSize(); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/BatchedModifications.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/BatchedModifications.java new file mode 100644 index 0000000000..670641f6ac --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/BatchedModifications.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore.messages; + +import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification; + +/** + * Message used to batch write, merge, delete modification operations to the ShardTransaction actor. + * + * @author Thomas Pantelis + */ +public class BatchedModifications extends MutableCompositeModification implements SerializableMessage { + private static final long serialVersionUID = 1L; + + public BatchedModifications() { + } + + public BatchedModifications(short version) { + super(version); + } + + @Override + public Object toSerializable() { + return this; + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/BatchedModificationsReply.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/BatchedModificationsReply.java new file mode 100644 index 0000000000..33c5733fdb --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/BatchedModificationsReply.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore.messages; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +/** + * The reply for the BatchedModifications message. + * + * @author Thomas Pantelis + */ +public class BatchedModificationsReply extends VersionedExternalizableMessage { + private static final long serialVersionUID = 1L; + + private int numBatched; + + public BatchedModificationsReply() { + } + + public BatchedModificationsReply(int numBatched) { + this.numBatched = numBatched; + } + + + public int getNumBatched() { + return numBatched; + } + + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + super.readExternal(in); + numBatched = in.readInt(); + } + + @Override + public void writeExternal(ObjectOutput out) throws IOException { + super.writeExternal(out); + out.writeInt(numBatched); + } + + @Override + public Object toSerializable() { + return this; + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/CreateTransactionReply.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/CreateTransactionReply.java index ffd0f1ccf3..c2bf81fa8e 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/CreateTransactionReply.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/CreateTransactionReply.java @@ -57,4 +57,11 @@ public class CreateTransactionReply implements SerializableMessage { (short)o.getMessageVersion()); } + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("CreateTransactionReply [transactionPath=").append(transactionPath).append(", transactionId=") + .append(transactionId).append(", version=").append(version).append("]"); + return builder.toString(); + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DeleteData.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DeleteData.java index 04bc63c5a5..5ba787c983 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DeleteData.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DeleteData.java @@ -8,7 +8,6 @@ package org.opendaylight.controller.cluster.datastore.messages; -import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; @@ -18,18 +17,22 @@ import org.opendaylight.controller.cluster.datastore.utils.SerializationUtils; import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -public class DeleteData implements VersionedSerializableMessage, Externalizable { +/** + * @deprecated Replaced by BatchedModifications. + */ +@Deprecated +public class DeleteData extends VersionedExternalizableMessage { private static final long serialVersionUID = 1L; public static final Class SERIALIZABLE_CLASS = DeleteData.class; private YangInstanceIdentifier path; - private short version; public DeleteData() { } - public DeleteData(final YangInstanceIdentifier path) { + public DeleteData(final YangInstanceIdentifier path, short version) { + super(version); this.path = path; } @@ -37,26 +40,21 @@ public class DeleteData implements VersionedSerializableMessage, Externalizable return path; } - public short getVersion() { - return version; - } - @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - version = in.readShort(); // Read the version - don't need to do anything with it now + super.readExternal(in); path = SerializationUtils.deserializePath(in); } @Override public void writeExternal(ObjectOutput out) throws IOException { - out.writeShort(version); + super.writeExternal(out); SerializationUtils.serializePath(path, out); } @Override - public Object toSerializable(short toVersion) { - if(toVersion >= DataStoreVersions.LITHIUM_VERSION) { - version = toVersion; + public Object toSerializable() { + if(getVersion() >= DataStoreVersions.LITHIUM_VERSION) { return this; } else { // To base or R1 Helium version @@ -71,7 +69,8 @@ public class DeleteData implements VersionedSerializableMessage, Externalizable } else { // From base or R1 Helium version ShardTransactionMessages.DeleteData o = (ShardTransactionMessages.DeleteData) serializable; - return new DeleteData(InstanceIdentifierUtils.fromSerializable(o.getInstanceIdentifierPathArguments())); + return new DeleteData(InstanceIdentifierUtils.fromSerializable(o.getInstanceIdentifierPathArguments()), + DataStoreVersions.HELIUM_2_VERSION); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DeleteDataReply.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DeleteDataReply.java index 0c6ff0e68d..dd21b0e2e6 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DeleteDataReply.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DeleteDataReply.java @@ -10,7 +10,12 @@ package org.opendaylight.controller.cluster.datastore.messages; import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages; +/** + * @deprecated Replaced by BatchedModificationsReply. + */ +@Deprecated public class DeleteDataReply extends EmptyReply { + private static final long serialVersionUID = 1L; private static final Object LEGACY_SERIALIZED_INSTANCE = ShardTransactionMessages.DeleteDataReply.newBuilder().build(); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/EmptyReply.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/EmptyReply.java index 284c6eff8d..38a37f0ccf 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/EmptyReply.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/EmptyReply.java @@ -14,7 +14,7 @@ import org.opendaylight.controller.cluster.datastore.DataStoreVersions; * * @author Thomas Pantelis */ -public abstract class EmptyReply extends EmptyExternalizable implements VersionedSerializableMessage { +public abstract class EmptyReply extends EmptyExternalizable { private final Object legacySerializedInstance; @@ -23,7 +23,6 @@ public abstract class EmptyReply extends EmptyExternalizable implements Versione this.legacySerializedInstance = legacySerializedInstance; } - @Override public Object toSerializable(short toVersion) { return toVersion >= DataStoreVersions.LITHIUM_VERSION ? this : legacySerializedInstance; } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/MergeData.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/MergeData.java index ae0d630cf2..0f44733503 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/MergeData.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/MergeData.java @@ -16,7 +16,11 @@ import org.opendaylight.controller.protobuff.messages.transaction.ShardTransacti import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -public class MergeData extends ModifyData implements VersionedSerializableMessage { +/** + * @deprecated Replaced by BatchedModifications. + */ +@Deprecated +public class MergeData extends ModifyData { private static final long serialVersionUID = 1L; public static final Class SERIALIZABLE_CLASS = MergeData.class; @@ -24,14 +28,13 @@ public class MergeData extends ModifyData implements VersionedSerializableMessag public MergeData() { } - public MergeData(YangInstanceIdentifier path, NormalizedNode data) { - super(path, data); + public MergeData(YangInstanceIdentifier path, NormalizedNode data, short version) { + super(path, data, version); } @Override - public Object toSerializable(short toVersion) { - if(toVersion >= DataStoreVersions.LITHIUM_VERSION) { - setVersion(toVersion); + public Object toSerializable() { + if(getVersion() >= DataStoreVersions.LITHIUM_VERSION) { return this; } else { // To base or R1 Helium version @@ -50,7 +53,8 @@ public class MergeData extends ModifyData implements VersionedSerializableMessag ShardTransactionMessages.MergeData o = (ShardTransactionMessages.MergeData) serializable; Decoded decoded = new NormalizedNodeToNodeCodec(null).decode( o.getInstanceIdentifierPathArguments(), o.getNormalizedNode()); - return new MergeData(decoded.getDecodedPath(), decoded.getDecodedNode()); + return new MergeData(decoded.getDecodedPath(), decoded.getDecodedNode(), + DataStoreVersions.HELIUM_2_VERSION); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/MergeDataReply.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/MergeDataReply.java index a4c514bdbf..6936ef14c5 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/MergeDataReply.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/MergeDataReply.java @@ -10,6 +10,10 @@ package org.opendaylight.controller.cluster.datastore.messages; import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages; +/** + * @deprecated Replaced by BatchedModificationsReply. + */ +@Deprecated public class MergeDataReply extends EmptyReply { private static final long serialVersionUID = 1L; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ModifyData.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ModifyData.java index 69c41c2a56..bbd090f929 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ModifyData.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ModifyData.java @@ -8,7 +8,6 @@ package org.opendaylight.controller.cluster.datastore.messages; -import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; @@ -17,17 +16,21 @@ import org.opendaylight.controller.cluster.datastore.utils.SerializationUtils.Ap import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -public abstract class ModifyData implements Externalizable { +/** + * @deprecated Replaced by BatchedModifications. + */ +@Deprecated +public abstract class ModifyData extends VersionedExternalizableMessage { private static final long serialVersionUID = 1L; private YangInstanceIdentifier path; private NormalizedNode data; - private short version; protected ModifyData() { } - protected ModifyData(YangInstanceIdentifier path, NormalizedNode data) { + protected ModifyData(YangInstanceIdentifier path, NormalizedNode data, short version) { + super(version); this.path = path; this.data = data; } @@ -40,23 +43,15 @@ public abstract class ModifyData implements Externalizable { return data; } - public short getVersion() { - return version; - } - - protected void setVersion(short version) { - this.version = version; - } - @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - version = in.readShort(); + super.readExternal(in); SerializationUtils.deserializePathAndNode(in, this, APPLIER); } @Override public void writeExternal(ObjectOutput out) throws IOException { - out.writeShort(version); + super.writeExternal(out); SerializationUtils.serializePathAndNode(path, data, out); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ReadDataReply.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ReadDataReply.java index 8ac6e1b149..b0c163d87f 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ReadDataReply.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ReadDataReply.java @@ -9,7 +9,6 @@ package org.opendaylight.controller.cluster.datastore.messages; import com.google.protobuf.ByteString; -import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; @@ -19,18 +18,18 @@ import org.opendaylight.controller.cluster.datastore.utils.SerializationUtils; import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -public class ReadDataReply implements VersionedSerializableMessage, Externalizable { +public class ReadDataReply extends VersionedExternalizableMessage { private static final long serialVersionUID = 1L; public static final Class SERIALIZABLE_CLASS = ReadDataReply.class; private NormalizedNode normalizedNode; - private short version; public ReadDataReply() { } - public ReadDataReply(NormalizedNode normalizedNode) { + public ReadDataReply(NormalizedNode normalizedNode, short version) { + super(version); this.normalizedNode = normalizedNode; } @@ -40,20 +39,19 @@ public class ReadDataReply implements VersionedSerializableMessage, Externalizab @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - version = in.readShort(); + super.readExternal(in); normalizedNode = SerializationUtils.deserializeNormalizedNode(in); } @Override public void writeExternal(ObjectOutput out) throws IOException { - out.writeShort(version); + super.writeExternal(out); SerializationUtils.serializeNormalizedNode(normalizedNode, out); } @Override - public Object toSerializable(short toVersion) { - if(toVersion >= DataStoreVersions.LITHIUM_VERSION) { - version = toVersion; + public Object toSerializable() { + if(getVersion() >= DataStoreVersions.LITHIUM_VERSION) { return this; } else { return toSerializableReadDataReply(normalizedNode); @@ -78,7 +76,8 @@ public class ReadDataReply implements VersionedSerializableMessage, Externalizab } else { ShardTransactionMessages.ReadDataReply o = (ShardTransactionMessages.ReadDataReply) serializable; - return new ReadDataReply(new NormalizedNodeToNodeCodec(null).decode(o.getNormalizedNode())); + return new ReadDataReply(new NormalizedNodeToNodeCodec(null).decode(o.getNormalizedNode()), + DataStoreVersions.HELIUM_2_VERSION); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/VersionedExternalizableMessage.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/VersionedExternalizableMessage.java new file mode 100644 index 0000000000..2a660fa4b2 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/VersionedExternalizableMessage.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore.messages; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +/** + * Abstract base class for a versioned Externalizable message. + * + * @author Thomas Pantelis + */ +public abstract class VersionedExternalizableMessage implements Externalizable, SerializableMessage { + private static final long serialVersionUID = 1L; + + private short version; + + public VersionedExternalizableMessage() { + } + + public VersionedExternalizableMessage(short version) { + this.version = version; + } + + public short getVersion() { + return version; + } + + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + version = in.readShort(); + } + + @Override + public void writeExternal(ObjectOutput out) throws IOException { + out.writeShort(version); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/VersionedSerializableMessage.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/VersionedSerializableMessage.java deleted file mode 100644 index 5c30b1078e..0000000000 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/VersionedSerializableMessage.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.controller.cluster.datastore.messages; - -/** - * Interface for a Serializable message with versioning. - * - * @author Thomas Pantelis - */ -public interface VersionedSerializableMessage { - Object toSerializable(short toVersion); -} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/WriteData.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/WriteData.java index 989949c88f..a4f648b6b3 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/WriteData.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/WriteData.java @@ -16,7 +16,11 @@ import org.opendaylight.controller.protobuff.messages.transaction.ShardTransacti import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -public class WriteData extends ModifyData implements VersionedSerializableMessage { +/** + * @deprecated Replaced by BatchedModifications. + */ +@Deprecated +public class WriteData extends ModifyData { private static final long serialVersionUID = 1L; public static final Class SERIALIZABLE_CLASS = WriteData.class; @@ -24,14 +28,13 @@ public class WriteData extends ModifyData implements VersionedSerializableMessag public WriteData() { } - public WriteData(YangInstanceIdentifier path, NormalizedNode data) { - super(path, data); + public WriteData(YangInstanceIdentifier path, NormalizedNode data, short version) { + super(path, data, version); } @Override - public Object toSerializable(short toVersion) { - if(toVersion >= DataStoreVersions.LITHIUM_VERSION) { - setVersion(toVersion); + public Object toSerializable() { + if(getVersion() >= DataStoreVersions.LITHIUM_VERSION) { return this; } else { // To base or R1 Helium version @@ -50,7 +53,8 @@ public class WriteData extends ModifyData implements VersionedSerializableMessag ShardTransactionMessages.WriteData o = (ShardTransactionMessages.WriteData) serializable; Decoded decoded = new NormalizedNodeToNodeCodec(null).decode( o.getInstanceIdentifierPathArguments(), o.getNormalizedNode()); - return new WriteData(decoded.getDecodedPath(), decoded.getDecodedNode()); + return new WriteData(decoded.getDecodedPath(), decoded.getDecodedNode(), + DataStoreVersions.HELIUM_2_VERSION); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/WriteDataReply.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/WriteDataReply.java index 8255828819..3455571a51 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/WriteDataReply.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/WriteDataReply.java @@ -10,6 +10,10 @@ package org.opendaylight.controller.cluster.datastore.messages; import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages; +/** + * @deprecated Replaced by BatchedModificationsReply. + */ +@Deprecated public class WriteDataReply extends EmptyReply { private static final long serialVersionUID = 1L; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/AbstractModification.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/AbstractModification.java index f04d004404..77f0858d7b 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/AbstractModification.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/AbstractModification.java @@ -17,8 +17,10 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; public abstract class AbstractModification implements Modification { private YangInstanceIdentifier path; + private short version; - protected AbstractModification() { + protected AbstractModification(short version) { + this.version = version; } protected AbstractModification(YangInstanceIdentifier path) { @@ -32,4 +34,8 @@ public abstract class AbstractModification implements Modification { public YangInstanceIdentifier getPath() { return path; } + + public short getVersion() { + return version; + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/DeleteModification.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/DeleteModification.java index 833f86fb98..3a63f5b173 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/DeleteModification.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/DeleteModification.java @@ -25,6 +25,11 @@ public class DeleteModification extends AbstractModification { private static final long serialVersionUID = 1L; public DeleteModification() { + this(DataStoreVersions.CURRENT_VERSION); + } + + public DeleteModification(short version) { + super(version); } public DeleteModification(YangInstanceIdentifier path) { @@ -43,13 +48,11 @@ public class DeleteModification extends AbstractModification { @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - in.readShort(); setPath(SerializationUtils.deserializePath(in)); } @Override public void writeExternal(ObjectOutput out) throws IOException { - out.writeShort(DataStoreVersions.CURRENT_VERSION); SerializationUtils.serializePath(getPath(), out); } @@ -66,8 +69,9 @@ public class DeleteModification extends AbstractModification { return new DeleteModification(InstanceIdentifierUtils.fromSerializable(o.getPath())); } - public static DeleteModification fromStream(ObjectInput in) throws ClassNotFoundException, IOException { - DeleteModification mod = new DeleteModification(); + public static DeleteModification fromStream(ObjectInput in, short version) + throws ClassNotFoundException, IOException { + DeleteModification mod = new DeleteModification(version); mod.readExternal(in); return mod; } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/MergeModification.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/MergeModification.java index 571443eedd..7ba74f4e7f 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/MergeModification.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/MergeModification.java @@ -10,6 +10,7 @@ package org.opendaylight.controller.cluster.datastore.modification; import java.io.IOException; import java.io.ObjectInput; +import org.opendaylight.controller.cluster.datastore.DataStoreVersions; import org.opendaylight.controller.cluster.datastore.node.NormalizedNodeToNodeCodec; import org.opendaylight.controller.cluster.datastore.node.NormalizedNodeToNodeCodec.Decoded; import org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages; @@ -24,6 +25,11 @@ public class MergeModification extends WriteModification { private static final long serialVersionUID = 1L; public MergeModification() { + this(DataStoreVersions.CURRENT_VERSION); + } + + public MergeModification(short version) { + super(version); } public MergeModification(final YangInstanceIdentifier path, final NormalizedNode data) { @@ -47,8 +53,9 @@ public class MergeModification extends WriteModification { return new MergeModification(decoded.getDecodedPath(), decoded.getDecodedNode()); } - public static MergeModification fromStream(ObjectInput in) throws ClassNotFoundException, IOException { - MergeModification mod = new MergeModification(); + public static MergeModification fromStream(ObjectInput in, short version) + throws ClassNotFoundException, IOException { + MergeModification mod = new MergeModification(version); mod.readExternal(in); return mod; } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModification.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModification.java index 5d7947b19f..b597742319 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModification.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModification.java @@ -27,10 +27,15 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; public class MutableCompositeModification implements CompositeModification { private static final long serialVersionUID = 1L; - private final List modifications; + private final List modifications = new ArrayList<>(); + private short version; public MutableCompositeModification() { - modifications = new ArrayList<>(); + this(DataStoreVersions.CURRENT_VERSION); + } + + public MutableCompositeModification(short version) { + this.version = version; } @Override @@ -45,6 +50,14 @@ public class MutableCompositeModification implements CompositeModification { return COMPOSITE; } + public short getVersion() { + return version; + } + + public void setVersion(short version) { + this.version = version; + } + /** * Add a new Modification to the list of Modifications represented by this * composite @@ -62,7 +75,7 @@ public class MutableCompositeModification implements CompositeModification { @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - in.readShort(); + version = in.readShort(); int size = in.readInt(); @@ -75,15 +88,15 @@ public class MutableCompositeModification implements CompositeModification { byte type = in.readByte(); switch(type) { case Modification.WRITE: - modifications.add(WriteModification.fromStream(in)); + modifications.add(WriteModification.fromStream(in, version)); break; case Modification.MERGE: - modifications.add(MergeModification.fromStream(in)); + modifications.add(MergeModification.fromStream(in, version)); break; case Modification.DELETE: - modifications.add(DeleteModification.fromStream(in)); + modifications.add(DeleteModification.fromStream(in, version)); break; } } @@ -94,7 +107,7 @@ public class MutableCompositeModification implements CompositeModification { @Override public void writeExternal(ObjectOutput out) throws IOException { - out.writeShort(DataStoreVersions.CURRENT_VERSION); + out.writeShort(version); out.writeInt(modifications.size()); @@ -121,8 +134,7 @@ public class MutableCompositeModification implements CompositeModification { builder.setTimeStamp(System.nanoTime()); for (Modification m : modifications) { - builder.addModification( - (PersistentMessages.Modification) m.toSerializable()); + builder.addModification((PersistentMessages.Modification) m.toSerializable()); } return builder.build(); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/WriteModification.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/WriteModification.java index 9c122c9ade..2fdca5f379 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/WriteModification.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/modification/WriteModification.java @@ -31,6 +31,11 @@ public class WriteModification extends AbstractModification { private NormalizedNode data; public WriteModification() { + this(DataStoreVersions.CURRENT_VERSION); + } + + public WriteModification(short version) { + super(version); } public WriteModification(final YangInstanceIdentifier path, final NormalizedNode data) { @@ -54,14 +59,11 @@ public class WriteModification extends AbstractModification { @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - in.readShort(); // version - SerializationUtils.deserializePathAndNode(in, this, APPLIER); } @Override public void writeExternal(ObjectOutput out) throws IOException { - out.writeShort(DataStoreVersions.CURRENT_VERSION); SerializationUtils.serializePathAndNode(getPath(), data, out); } @@ -81,8 +83,9 @@ public class WriteModification extends AbstractModification { return new WriteModification(decoded.getDecodedPath(), decoded.getDecodedNode()); } - public static WriteModification fromStream(ObjectInput in) throws ClassNotFoundException, IOException { - WriteModification mod = new WriteModification(); + public static WriteModification fromStream(ObjectInput in, short version) + throws ClassNotFoundException, IOException { + WriteModification mod = new WriteModification(version); mod.readExternal(in); return mod; } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java index cb06c898fd..26e6318f6d 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java @@ -47,6 +47,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scala.concurrent.Await; +import scala.concurrent.ExecutionContext; import scala.concurrent.Future; import scala.concurrent.duration.Duration; import scala.concurrent.duration.FiniteDuration; @@ -93,6 +94,7 @@ public class ActorContext { private final JmxReporter jmxReporter = JmxReporter.forRegistry(metricRegistry).inDomain(DOMAIN).build(); private final int transactionOutstandingOperationLimit; private final Timeout transactionCommitOperationTimeout; + private final Dispatchers dispatchers; private volatile SchemaContext schemaContext; @@ -111,6 +113,7 @@ public class ActorContext { this.configuration = configuration; this.datastoreContext = datastoreContext; this.txRateLimiter = RateLimiter.create(datastoreContext.getTransactionCreationInitialRateLimit()); + this.dispatchers = new Dispatchers(actorSystem.dispatchers()); operationDuration = Duration.create(datastoreContext.getOperationTimeoutInSeconds(), TimeUnit.SECONDS); operationTimeout = new Timeout(operationDuration); @@ -127,6 +130,7 @@ public class ActorContext { transactionOutstandingOperationLimit = new CommonConfig(this.getActorSystem().settings().config()).getMailBoxCapacity(); jmxReporter.start(); + } public DatastoreContext getDatastoreContext() { @@ -200,7 +204,7 @@ public class ActorContext { throw new UnknownMessageException(String.format( "FindPrimary returned unkown response: %s", response)); } - }, FIND_PRIMARY_FAILURE_TRANSFORMER, getActorSystem().dispatcher()); + }, FIND_PRIMARY_FAILURE_TRANSFORMER, getClientDispatcher()); } /** @@ -251,7 +255,7 @@ public class ActorContext { throw new UnknownMessageException(String.format( "FindLocalShard returned unkown response: %s", response)); } - }, getActorSystem().dispatcher()); + }, getClientDispatcher()); } private String findPrimaryPathOrNull(String shardName) { @@ -514,5 +518,17 @@ public class ActorContext { return transactionCommitOperationTimeout; } + /** + * An akka dispatcher that is meant to be used when processing ask Futures which were triggered by client + * code on the datastore + * @return + */ + public ExecutionContext getClientDispatcher() { + return this.dispatchers.getDispatcher(Dispatchers.DispatcherType.Client); + } + + public String getNotificationDispatcherPath(){ + return this.dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Notification); + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/Dispatchers.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/Dispatchers.java new file mode 100644 index 0000000000..8de8a9d193 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/Dispatchers.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.cluster.datastore.utils; + +import com.google.common.base.Preconditions; +import scala.concurrent.ExecutionContext; + +public class Dispatchers { + public static final String DEFAULT_DISPATCHER_PATH = "akka.actor.default-dispatcher"; + public static final String CLIENT_DISPATCHER_PATH = "client-dispatcher"; + public static final String TXN_DISPATCHER_PATH = "txn-dispatcher"; + public static final String SHARD_DISPATCHER_PATH = "shard-dispatcher"; + public static final String NOTIFICATION_DISPATCHER_PATH = "notification-dispatcher"; + + private final akka.dispatch.Dispatchers dispatchers; + + public static enum DispatcherType { + Client(CLIENT_DISPATCHER_PATH), + Transaction(TXN_DISPATCHER_PATH), + Shard(SHARD_DISPATCHER_PATH), + Notification(NOTIFICATION_DISPATCHER_PATH); + + private final String path; + private DispatcherType(String path){ + this.path = path; + } + private String path(akka.dispatch.Dispatchers dispatchers){ + if(dispatchers.hasDispatcher(path)){ + return path; + } + return DEFAULT_DISPATCHER_PATH; + } + + private ExecutionContext dispatcher(akka.dispatch.Dispatchers dispatchers){ + if(dispatchers.hasDispatcher(path)){ + return dispatchers.lookup(path); + } + return dispatchers.defaultGlobalDispatcher(); + } + } + + public Dispatchers(akka.dispatch.Dispatchers dispatchers){ + Preconditions.checkNotNull(dispatchers, "dispatchers should not be null"); + this.dispatchers = dispatchers; + } + + public ExecutionContext getDispatcher(DispatcherType dispatcherType){ + return dispatcherType.dispatcher(this.dispatchers); + } + + public String getDispatcherPath(DispatcherType dispatcherType){ + return dispatcherType.path(this.dispatchers); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/MessageTracker.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/MessageTracker.java new file mode 100644 index 0000000000..2757d2f5f6 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/MessageTracker.java @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.cluster.datastore.utils; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Stopwatch; +import com.google.common.collect.ImmutableList; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * MessageTracker is a diagnostic utility class to be used for figuring out why a certain message which was + * expected to arrive in a given time interval does not arrive. It attempts to keep track of all the messages that + * received between the arrival of two instances of the same message and the amount of time it took to process each + * of those messages. + *
+ * Usage of the API is as follows, + *
+ *
+ *      // Track the Foo class, Here we expect to see a message of type Foo come in every 10 millis
+ *     MessageTracker tracker = new MessageTracker(Foo.class, 10);
+ *
+ *     // Begin the tracking process. If this is not called then calling received and done on the resultant Context
+ *     // will do nothing
+ *     tracker.begin();
+ *
+ *     .....
+ *
+ *     MessageTracker.Context context = tracker.received(message);
+ *
+ *     if(context.error().isPresent()){
+ *         LOG.error("{}", context.error().get());
+ *     }
+ *
+ *     // Some custom processing
+ *     process(message);
+ *
+ *     context.done();
+ *
+ * 
+ */ +public class MessageTracker { + + private static final Context NO_OP_CONTEXT = new NoOpContext(); + + private final Class expectedMessageClass; + + private final long expectedArrivalInterval; + + private final List messagesSinceLastExpectedMessage = new LinkedList<>(); + + private Stopwatch expectedMessageWatch; + + private boolean enabled = false; + + private Object lastExpectedMessage; + + private Object currentMessage; + + private final CurrentMessageContext currentMessageContext = new CurrentMessageContext(); + + /** + * + * @param expectedMessageClass The class of the message to track + * @param expectedArrivalIntervalInMillis The expected arrival interval between two instances of the expected + * message + */ + public MessageTracker(Class expectedMessageClass, long expectedArrivalIntervalInMillis){ + this.expectedMessageClass = expectedMessageClass; + this.expectedArrivalInterval = expectedArrivalIntervalInMillis; + } + + public void begin(){ + if(enabled) { + return; + } + enabled = true; + expectedMessageWatch = Stopwatch.createStarted(); + } + + public Context received(Object message){ + if(!enabled) { + return NO_OP_CONTEXT; + } + this.currentMessage = message; + if(expectedMessageClass.isInstance(message)){ + long actualElapsedTime = expectedMessageWatch.elapsed(TimeUnit.MILLISECONDS); + if(actualElapsedTime > expectedArrivalInterval){ + return new ErrorContext(message, Optional.of(new FailedExpectation(lastExpectedMessage, message, + ImmutableList.copyOf(messagesSinceLastExpectedMessage), expectedArrivalInterval, + actualElapsedTime))); + } + this.lastExpectedMessage = message; + this.messagesSinceLastExpectedMessage.clear(); + } + + currentMessageContext.reset(); + return currentMessageContext; + } + + private void processed(Object message, long messageElapseTimeInNanos){ + if(!enabled) { + return; + } + if(!expectedMessageClass.isInstance(message)){ + this.messagesSinceLastExpectedMessage.add(new MessageProcessingTime(message.getClass(), messageElapseTimeInNanos)); + } + } + + public List getMessagesSinceLastExpectedMessage(){ + return ImmutableList.copyOf(this.messagesSinceLastExpectedMessage); + } + + public static class MessageProcessingTime { + private final Class messageClass; + private final long elapsedTimeInNanos; + + MessageProcessingTime(Class messageClass, long elapsedTimeInNanos){ + this.messageClass = messageClass; + this.elapsedTimeInNanos = elapsedTimeInNanos; + } + + @Override + public String toString() { + return "MessageProcessingTime{" + + "messageClass=" + messageClass.getSimpleName() + + ", elapsedTimeInMillis=" + TimeUnit.NANOSECONDS.toMillis(elapsedTimeInNanos) + + '}'; + } + + public Class getMessageClass() { + return messageClass; + } + + public long getElapsedTimeInNanos() { + return elapsedTimeInNanos; + } + } + + public interface Error { + Object getLastExpectedMessage(); + Object getCurrentExpectedMessage(); + List getMessageProcessingTimesSinceLastExpectedMessage(); + } + + private class FailedExpectation implements Error { + + private final Object lastExpectedMessage; + private final Object currentExpectedMessage; + private final List messagesSinceLastExpectedMessage; + private final long expectedTimeInMillis; + private final long actualTimeInMillis; + + public FailedExpectation(Object lastExpectedMessage, Object message, List messagesSinceLastExpectedMessage, long expectedTimeInMillis, long actualTimeInMillis) { + this.lastExpectedMessage = lastExpectedMessage; + this.currentExpectedMessage = message; + this.messagesSinceLastExpectedMessage = messagesSinceLastExpectedMessage; + this.expectedTimeInMillis = expectedTimeInMillis; + this.actualTimeInMillis = actualTimeInMillis; + } + + public Object getLastExpectedMessage() { + return lastExpectedMessage; + } + + public Object getCurrentExpectedMessage() { + return currentExpectedMessage; + } + + public List getMessageProcessingTimesSinceLastExpectedMessage() { + return messagesSinceLastExpectedMessage; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("\n> Last Expected Message = " + lastExpectedMessage); + builder.append("\n> Current Expected Message = " + currentExpectedMessage); + builder.append("\n> Expected time in between messages = " + expectedTimeInMillis); + builder.append("\n> Actual time in between messages = " + actualTimeInMillis); + for (MessageProcessingTime time : messagesSinceLastExpectedMessage) { + builder.append("\n\t> ").append(time.toString()); + } + return builder.toString(); + } + + } + + public interface Context { + Context done(); + Optional error(); + } + + private static class NoOpContext implements Context { + + @Override + public Context done() { + return this; + } + + @Override + public Optional error() { + return Optional.absent(); + } + } + + private class CurrentMessageContext implements Context { + Stopwatch stopwatch = Stopwatch.createStarted(); + boolean done = true; + + public void reset(){ + Preconditions.checkState(done); + done = false; + stopwatch.reset().start(); + } + + @Override + public Context done() { + processed(currentMessage, stopwatch.elapsed(TimeUnit.NANOSECONDS)); + done = true; + return this; + } + + @Override + public Optional error() { + return Optional.absent(); + } + } + + private class ErrorContext implements Context { + Object message; + private final Optional error; + Stopwatch stopwatch; + + ErrorContext(Object message, Optional error){ + this.message = message; + this.error = error; + this.stopwatch = Stopwatch.createStarted(); + } + + @Override + public Context done(){ + processed(message, this.stopwatch.elapsed(TimeUnit.NANOSECONDS)); + this.stopwatch.stop(); + return this; + } + + @Override + public Optional error() { + return error; + } + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModule.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModule.java index 7e8307465b..f78b134d42 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModule.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModule.java @@ -1,14 +1,9 @@ package org.opendaylight.controller.config.yang.config.distributed_datastore_provider; -import java.util.concurrent.TimeUnit; - import org.opendaylight.controller.cluster.datastore.DatastoreContext; import org.opendaylight.controller.cluster.datastore.DistributedDataStoreFactory; -import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties; import org.osgi.framework.BundleContext; -import scala.concurrent.duration.Duration; - public class DistributedConfigDataStoreProviderModule extends org.opendaylight.controller.config.yang.config.distributed_datastore_provider.AbstractDistributedConfigDataStoreProviderModule { private BundleContext bundleContext; @@ -42,23 +37,19 @@ public class DistributedConfigDataStoreProviderModule extends DatastoreContext datastoreContext = DatastoreContext.newBuilder() .dataStoreType("config") - .dataStoreProperties(InMemoryDOMDataStoreConfigProperties.create( - props.getMaxShardDataChangeExecutorPoolSize().getValue().intValue(), - props.getMaxShardDataChangeExecutorQueueSize().getValue().intValue(), - props.getMaxShardDataChangeListenerQueueSize().getValue().intValue(), - props.getMaxShardDataStoreExecutorQueueSize().getValue().intValue())) - .shardTransactionIdleTimeout(Duration.create( - props.getShardTransactionIdleTimeoutInMinutes().getValue(), TimeUnit.MINUTES)) + .maxShardDataChangeExecutorPoolSize(props.getMaxShardDataChangeExecutorPoolSize().getValue().intValue()) + .maxShardDataChangeExecutorQueueSize(props.getMaxShardDataChangeExecutorQueueSize().getValue().intValue()) + .maxShardDataChangeListenerQueueSize(props.getMaxShardDataChangeListenerQueueSize().getValue().intValue()) + .maxShardDataStoreExecutorQueueSize(props.getMaxShardDataStoreExecutorQueueSize().getValue().intValue()) + .shardTransactionIdleTimeoutInMinutes(props.getShardTransactionIdleTimeoutInMinutes().getValue()) .operationTimeoutInSeconds(props.getOperationTimeoutInSeconds().getValue()) .shardJournalRecoveryLogBatchSize(props.getShardJournalRecoveryLogBatchSize(). getValue().intValue()) .shardSnapshotBatchCount(props.getShardSnapshotBatchCount().getValue().intValue()) .shardSnapshotDataThresholdPercentage(props.getShardSnapshotDataThresholdPercentage().getValue().intValue()) - .shardHeartbeatIntervalInMillis(props.getShardHearbeatIntervalInMillis().getValue()) - .shardInitializationTimeout(props.getShardInitializationTimeoutInSeconds().getValue(), - TimeUnit.SECONDS) - .shardLeaderElectionTimeout(props.getShardLeaderElectionTimeoutInSeconds().getValue(), - TimeUnit.SECONDS) + .shardHeartbeatIntervalInMillis(props.getShardHeartbeatIntervalInMillis().getValue()) + .shardInitializationTimeoutInSeconds(props.getShardInitializationTimeoutInSeconds().getValue()) + .shardLeaderElectionTimeoutInSeconds(props.getShardLeaderElectionTimeoutInSeconds().getValue()) .shardTransactionCommitTimeoutInSeconds( props.getShardTransactionCommitTimeoutInSeconds().getValue().intValue()) .shardTransactionCommitQueueCapacity( @@ -67,7 +58,8 @@ public class DistributedConfigDataStoreProviderModule extends .shardIsolatedLeaderCheckIntervalInMillis( props.getShardIsolatedLeaderCheckIntervalInMillis().getValue()) .shardElectionTimeoutFactor(props.getShardElectionTimeoutFactor().getValue()) - .transactionCreationInitialRateLimit(props.getTxCreationInitialRateLimit().getValue()) + .transactionCreationInitialRateLimit(props.getTransactionCreationInitialRateLimit().getValue()) + .shardBatchedModificationCount(props.getShardBatchedModificationCount().getValue().intValue()) .build(); return DistributedDataStoreFactory.createInstance(getConfigSchemaServiceDependency(), diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModule.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModule.java index 0655468531..6711a10074 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModule.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModule.java @@ -1,14 +1,9 @@ package org.opendaylight.controller.config.yang.config.distributed_datastore_provider; -import java.util.concurrent.TimeUnit; - import org.opendaylight.controller.cluster.datastore.DatastoreContext; import org.opendaylight.controller.cluster.datastore.DistributedDataStoreFactory; -import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties; import org.osgi.framework.BundleContext; -import scala.concurrent.duration.Duration; - public class DistributedOperationalDataStoreProviderModule extends org.opendaylight.controller.config.yang.config.distributed_datastore_provider.AbstractDistributedOperationalDataStoreProviderModule { private BundleContext bundleContext; @@ -42,23 +37,19 @@ public class DistributedOperationalDataStoreProviderModule extends DatastoreContext datastoreContext = DatastoreContext.newBuilder() .dataStoreType("operational") - .dataStoreProperties(InMemoryDOMDataStoreConfigProperties.create( - props.getMaxShardDataChangeExecutorPoolSize().getValue().intValue(), - props.getMaxShardDataChangeExecutorQueueSize().getValue().intValue(), - props.getMaxShardDataChangeListenerQueueSize().getValue().intValue(), - props.getMaxShardDataStoreExecutorQueueSize().getValue().intValue())) - .shardTransactionIdleTimeout(Duration.create( - props.getShardTransactionIdleTimeoutInMinutes().getValue(), TimeUnit.MINUTES)) + .maxShardDataChangeExecutorPoolSize(props.getMaxShardDataChangeExecutorPoolSize().getValue().intValue()) + .maxShardDataChangeExecutorQueueSize(props.getMaxShardDataChangeExecutorQueueSize().getValue().intValue()) + .maxShardDataChangeListenerQueueSize(props.getMaxShardDataChangeListenerQueueSize().getValue().intValue()) + .maxShardDataStoreExecutorQueueSize(props.getMaxShardDataStoreExecutorQueueSize().getValue().intValue()) + .shardTransactionIdleTimeoutInMinutes(props.getShardTransactionIdleTimeoutInMinutes().getValue()) .operationTimeoutInSeconds(props.getOperationTimeoutInSeconds().getValue()) .shardJournalRecoveryLogBatchSize(props.getShardJournalRecoveryLogBatchSize(). getValue().intValue()) .shardSnapshotBatchCount(props.getShardSnapshotBatchCount().getValue().intValue()) .shardSnapshotDataThresholdPercentage(props.getShardSnapshotDataThresholdPercentage().getValue().intValue()) - .shardHeartbeatIntervalInMillis(props.getShardHearbeatIntervalInMillis().getValue()) - .shardInitializationTimeout(props.getShardInitializationTimeoutInSeconds().getValue(), - TimeUnit.SECONDS) - .shardLeaderElectionTimeout(props.getShardLeaderElectionTimeoutInSeconds().getValue(), - TimeUnit.SECONDS) + .shardHeartbeatIntervalInMillis(props.getShardHeartbeatIntervalInMillis().getValue()) + .shardInitializationTimeoutInSeconds(props.getShardInitializationTimeoutInSeconds().getValue()) + .shardLeaderElectionTimeoutInSeconds(props.getShardLeaderElectionTimeoutInSeconds().getValue()) .shardTransactionCommitTimeoutInSeconds( props.getShardTransactionCommitTimeoutInSeconds().getValue().intValue()) .shardTransactionCommitQueueCapacity( @@ -67,7 +58,8 @@ public class DistributedOperationalDataStoreProviderModule extends .shardIsolatedLeaderCheckIntervalInMillis( props.getShardIsolatedLeaderCheckIntervalInMillis().getValue()) .shardElectionTimeoutFactor(props.getShardElectionTimeoutFactor().getValue()) - .transactionCreationInitialRateLimit(props.getTxCreationInitialRateLimit().getValue()) + .transactionCreationInitialRateLimit(props.getTransactionCreationInitialRateLimit().getValue()) + .shardBatchedModificationCount(props.getShardBatchedModificationCount().getValue().intValue()) .build(); return DistributedDataStoreFactory.createInstance(getOperationalSchemaServiceDependency(), diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/yang/distributed-datastore-provider.yang b/opendaylight/md-sal/sal-distributed-datastore/src/main/yang/distributed-datastore-provider.yang index e2ee7373d0..b775cf0a99 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/yang/distributed-datastore-provider.yang +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/yang/distributed-datastore-provider.yang @@ -104,7 +104,7 @@ module distributed-datastore-provider { } - leaf shard-hearbeat-interval-in-millis { + leaf shard-heartbeat-interval-in-millis { default 500; type heartbeat-interval-type; description "The interval at which a shard will send a heart beat message to its remote shard."; @@ -156,6 +156,15 @@ module distributed-datastore-provider { an operation (eg transaction create)."; } + leaf shard-batched-modification-count { + default 100; + type non-zero-uint32-type; + description "The number of transaction modification operations (put, merge, delete) to + batch before sending to the shard transaction actor. Batching improves + performance as less modifications messages are sent to the actor and thus + lessens the chance that the transaction actor's mailbox queue could get full."; + } + leaf enable-metric-capture { default false; type boolean; @@ -181,7 +190,7 @@ module distributed-datastore-provider { followers are active and term itself as isolated"; } - leaf tx-creation-initial-rate-limit { + leaf transaction-creation-initial-rate-limit { default 100; type non-zero-uint32-type; description "The initial number of transactions per second that are allowed before the data store diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxyTest.java index 58aec30a84..f6c8f07f6b 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxyTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxyTest.java @@ -36,6 +36,7 @@ import org.opendaylight.controller.cluster.datastore.messages.LocalShardNotFound import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener; import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply; import org.opendaylight.controller.cluster.datastore.utils.ActorContext; +import org.opendaylight.controller.cluster.datastore.utils.Dispatchers; import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor; import org.opendaylight.controller.md.cluster.datastore.model.TestModel; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; @@ -193,10 +194,12 @@ public class DataChangeListenerRegistrationProxyTest extends AbstractActorTest { doReturn(mockActor).when(mockActorSystem).actorOf(any(Props.class)); ExecutionContextExecutor executor = ExecutionContexts.fromExecutor( MoreExecutors.sameThreadExecutor()); - doReturn(executor).when(mockActorSystem).dispatcher(); + ActorContext actorContext = mock(ActorContext.class); + doReturn(executor).when(actorContext).getClientDispatcher(); + String shardName = "shard-1"; final DataChangeListenerRegistrationProxy proxy = new DataChangeListenerRegistrationProxy( shardName, actorContext, mockListener); @@ -227,7 +230,9 @@ public class DataChangeListenerRegistrationProxyTest extends AbstractActorTest { shardName, actorContext, mockListener); doReturn(DatastoreContext.newBuilder().build()).when(actorContext).getDatastoreContext(); + doReturn(getSystem().dispatchers().defaultGlobalDispatcher()).when(actorContext).getClientDispatcher(); doReturn(getSystem()).when(actorContext).getActorSystem(); + doReturn(Dispatchers.DEFAULT_DISPATCHER_PATH).when(actorContext).getNotificationDispatcherPath(); doReturn(getSystem().actorSelection(getRef().path())). when(actorContext).actorSelection(getRef().path()); doReturn(duration("5 seconds")).when(actorContext).getOperationDuration(); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextConfigAdminOverlayTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextConfigAdminOverlayTest.java new file mode 100644 index 0000000000..3693c01b42 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextConfigAdminOverlayTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import java.io.IOException; +import java.util.Dictionary; +import java.util.Hashtable; +import org.junit.Test; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.cm.Configuration; +import org.osgi.service.cm.ConfigurationAdmin; + +/** + * Unit tests for DatastoreContextConfigAdminOverlay. + * + * @author Thomas Pantelis + */ +public class DatastoreContextConfigAdminOverlayTest { + + @SuppressWarnings("unchecked") + @Test + public void test() throws IOException { + BundleContext mockBundleContext = mock(BundleContext.class); + ServiceReference mockServiceRef = mock(ServiceReference.class); + ConfigurationAdmin mockConfigAdmin = mock(ConfigurationAdmin.class); + Configuration mockConfig = mock(Configuration.class); + DatastoreContextIntrospector mockIntrospector = mock(DatastoreContextIntrospector.class); + + doReturn(mockServiceRef).when(mockBundleContext).getServiceReference(ConfigurationAdmin.class); + doReturn(mockConfigAdmin).when(mockBundleContext).getService(mockServiceRef); + + doReturn(mockConfig).when(mockConfigAdmin).getConfiguration(DatastoreContextConfigAdminOverlay.CONFIG_ID); + + doReturn(DatastoreContextConfigAdminOverlay.CONFIG_ID).when(mockConfig).getPid(); + + Dictionary properties = new Hashtable<>(); + properties.put("property", "value"); + doReturn(properties).when(mockConfig).getProperties(); + + try(DatastoreContextConfigAdminOverlay overlay = new DatastoreContextConfigAdminOverlay( + mockIntrospector, mockBundleContext)) { + } + + verify(mockIntrospector).update(properties); + + verify(mockBundleContext).ungetService(mockServiceRef); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextIntrospectorTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextIntrospectorTest.java new file mode 100644 index 0000000000..745c9b3c85 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextIntrospectorTest.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore; + +import static org.junit.Assert.assertEquals; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_HEARTBEAT_INTERVAL_IN_MILLIS; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_OPERATION_TIMEOUT_IN_SECONDS; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_SHARD_INITIALIZATION_TIMEOUT; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_SHARD_SNAPSHOT_DATA_THRESHOLD_PERCENTAGE; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_SHARD_TRANSACTION_IDLE_TIMEOUT; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_SHARD_TX_COMMIT_TIMEOUT_IN_SECONDS; +import java.util.Dictionary; +import java.util.Hashtable; +import org.junit.Test; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties; + +/** + * Unit tests for DatastoreContextIntrospector. + * + * @author Thomas Pantelis + */ +public class DatastoreContextIntrospectorTest { + + @Test + public void testUpdate() { + DatastoreContext context = DatastoreContext.newBuilder().dataStoreType("operational").build(); + DatastoreContextIntrospector introspector = new DatastoreContextIntrospector(context ); + + Dictionary properties = new Hashtable<>(); + properties.put("shard-transaction-idle-timeout-in-minutes", "31"); + properties.put("operation-timeout-in-seconds", "26"); + properties.put("shard-transaction-commit-timeout-in-seconds", "100"); + properties.put("shard-journal-recovery-log-batch-size", "199"); + properties.put("shard-snapshot-batch-count", "212"); + properties.put("shard-heartbeat-interval-in-millis", "101"); + properties.put("shard-transaction-commit-queue-capacity", "567"); + properties.put("shard-initialization-timeout-in-seconds", "82"); + properties.put("shard-leader-election-timeout-in-seconds", "66"); + properties.put("shard-isolated-leader-check-interval-in-millis", "123"); + properties.put("shard-snapshot-data-threshold-percentage", "100"); + properties.put("shard-election-timeout-factor", "21"); + properties.put("shard-batched-modification-count", "901"); + properties.put("transactionCreationInitialRateLimit", "200"); + properties.put("MaxShardDataChangeExecutorPoolSize", "41"); + properties.put("Max-Shard-Data-Change Executor-Queue Size", "1111"); + properties.put(" max shard data change listener queue size", "2222"); + properties.put("mAx-shaRd-data-STORE-executor-quEUe-size", "3333"); + properties.put("persistent", "false"); + + boolean updated = introspector.update(properties); + assertEquals("updated", true, updated); + context = introspector.getContext(); + + assertEquals(31, context.getShardTransactionIdleTimeout().toMinutes()); + assertEquals(26, context.getOperationTimeoutInSeconds()); + assertEquals(100, context.getShardTransactionCommitTimeoutInSeconds()); + assertEquals(199, context.getShardRaftConfig().getJournalRecoveryLogBatchSize()); + assertEquals(212, context.getShardRaftConfig().getSnapshotBatchCount()); + assertEquals(101, context.getShardRaftConfig().getHeartBeatInterval().length()); + assertEquals(567, context.getShardTransactionCommitQueueCapacity()); + assertEquals(82, context.getShardInitializationTimeout().duration().toSeconds()); + assertEquals(66, context.getShardLeaderElectionTimeout().duration().toSeconds()); + assertEquals(123, context.getShardRaftConfig().getIsolatedCheckIntervalInMillis()); + assertEquals(100, context.getShardRaftConfig().getSnapshotDataThresholdPercentage()); + assertEquals(21, context.getShardRaftConfig().getElectionTimeoutFactor()); + assertEquals(901, context.getShardBatchedModificationCount()); + assertEquals(200, context.getTransactionCreationInitialRateLimit()); + assertEquals(41, context.getDataStoreProperties().getMaxDataChangeExecutorPoolSize()); + assertEquals(1111, context.getDataStoreProperties().getMaxDataChangeExecutorQueueSize()); + assertEquals(2222, context.getDataStoreProperties().getMaxDataChangeListenerQueueSize()); + assertEquals(3333, context.getDataStoreProperties().getMaxDataStoreExecutorQueueSize()); + assertEquals(false, context.isPersistent()); + + properties.put("shard-transaction-idle-timeout-in-minutes", "32"); + properties.put("operation-timeout-in-seconds", "27"); + properties.put("shard-heartbeat-interval-in-millis", "102"); + properties.put("shard-election-timeout-factor", "22"); + properties.put("max-shard-data-change-executor-pool-size", "42"); + properties.put("max-shard-data-store-executor-queue-size", "4444"); + properties.put("persistent", "true"); + + updated = introspector.update(properties); + assertEquals("updated", true, updated); + context = introspector.getContext(); + + assertEquals(32, context.getShardTransactionIdleTimeout().toMinutes()); + assertEquals(27, context.getOperationTimeoutInSeconds()); + assertEquals(100, context.getShardTransactionCommitTimeoutInSeconds()); + assertEquals(199, context.getShardRaftConfig().getJournalRecoveryLogBatchSize()); + assertEquals(212, context.getShardRaftConfig().getSnapshotBatchCount()); + assertEquals(102, context.getShardRaftConfig().getHeartBeatInterval().length()); + assertEquals(567, context.getShardTransactionCommitQueueCapacity()); + assertEquals(82, context.getShardInitializationTimeout().duration().toSeconds()); + assertEquals(66, context.getShardLeaderElectionTimeout().duration().toSeconds()); + assertEquals(123, context.getShardRaftConfig().getIsolatedCheckIntervalInMillis()); + assertEquals(100, context.getShardRaftConfig().getSnapshotDataThresholdPercentage()); + assertEquals(22, context.getShardRaftConfig().getElectionTimeoutFactor()); + assertEquals(200, context.getTransactionCreationInitialRateLimit()); + assertEquals(42, context.getDataStoreProperties().getMaxDataChangeExecutorPoolSize()); + assertEquals(1111, context.getDataStoreProperties().getMaxDataChangeExecutorQueueSize()); + assertEquals(2222, context.getDataStoreProperties().getMaxDataChangeListenerQueueSize()); + assertEquals(4444, context.getDataStoreProperties().getMaxDataStoreExecutorQueueSize()); + assertEquals(true, context.isPersistent()); + + updated = introspector.update(null); + assertEquals("updated", false, updated); + + updated = introspector.update(new Hashtable()); + assertEquals("updated", false, updated); + } + + + @Test + public void testUpdateWithInvalidValues() { + DatastoreContext context = DatastoreContext.newBuilder().dataStoreType("operational").build(); + DatastoreContextIntrospector introspector = new DatastoreContextIntrospector(context ); + + Dictionary properties = new Hashtable<>(); + properties.put("shard-transaction-idle-timeout-in-minutes", "0"); // bad - must be > 0 + properties.put("shard-journal-recovery-log-batch-size", "199"); + properties.put("shard-transaction-commit-timeout-in-seconds", "bogus"); // bad - NaN + properties.put("shard-snapshot-batch-count", "212"); // good + properties.put("operation-timeout-in-seconds", "4"); // bad - must be >= 5 + properties.put("shard-heartbeat-interval-in-millis", "99"); // bad - must be >= 100 + properties.put("shard-transaction-commit-queue-capacity", "567"); // good + properties.put("shard-snapshot-data-threshold-percentage", "101"); // bad - must be 0-100 + properties.put("shard-initialization-timeout-in-seconds", "-1"); // bad - must be > 0 + properties.put("max-shard-data-change-executor-pool-size", "bogus"); // bad - NaN + properties.put("unknownProperty", "1"); // bad - invalid property name + + boolean updated = introspector.update(properties); + assertEquals("updated", true, updated); + context = introspector.getContext(); + + assertEquals(DEFAULT_SHARD_TRANSACTION_IDLE_TIMEOUT, context.getShardTransactionIdleTimeout()); + assertEquals(199, context.getShardRaftConfig().getJournalRecoveryLogBatchSize()); + assertEquals(DEFAULT_SHARD_TX_COMMIT_TIMEOUT_IN_SECONDS, context.getShardTransactionCommitTimeoutInSeconds()); + assertEquals(212, context.getShardRaftConfig().getSnapshotBatchCount()); + assertEquals(DEFAULT_OPERATION_TIMEOUT_IN_SECONDS, context.getOperationTimeoutInSeconds()); + assertEquals(DEFAULT_HEARTBEAT_INTERVAL_IN_MILLIS, context.getShardRaftConfig().getHeartBeatInterval().length()); + assertEquals(567, context.getShardTransactionCommitQueueCapacity()); + assertEquals(DEFAULT_SHARD_SNAPSHOT_DATA_THRESHOLD_PERCENTAGE, + context.getShardRaftConfig().getSnapshotDataThresholdPercentage()); + assertEquals(DEFAULT_SHARD_INITIALIZATION_TIMEOUT, context.getShardInitializationTimeout()); + assertEquals(InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE, + context.getDataStoreProperties().getMaxDataChangeExecutorPoolSize()); + } + + @Test + public void testUpdateWithDatastoreTypeSpecificProperties() { + Dictionary properties = new Hashtable<>(); + properties.put("shard-transaction-idle-timeout-in-minutes", "22"); // global setting + properties.put("operational.shard-transaction-idle-timeout-in-minutes", "33"); // operational override + properties.put("config.shard-transaction-idle-timeout-in-minutes", "44"); // config override + + properties.put("max-shard-data-change-executor-pool-size", "222"); // global setting + properties.put("operational.max-shard-data-change-executor-pool-size", "333"); // operational override + properties.put("config.max-shard-data-change-executor-pool-size", "444"); // config override + + properties.put("persistent", "false"); // global setting + properties.put("operational.Persistent", "true"); // operational override + + DatastoreContext operContext = DatastoreContext.newBuilder().dataStoreType("operational").build(); + DatastoreContextIntrospector operIntrospector = new DatastoreContextIntrospector(operContext); + boolean updated = operIntrospector.update(properties); + assertEquals("updated", true, updated); + operContext = operIntrospector.getContext(); + + assertEquals(33, operContext.getShardTransactionIdleTimeout().toMinutes()); + assertEquals(true, operContext.isPersistent()); + assertEquals(333, operContext.getDataStoreProperties().getMaxDataChangeExecutorPoolSize()); + + DatastoreContext configContext = DatastoreContext.newBuilder().dataStoreType("config").build(); + DatastoreContextIntrospector configIntrospector = new DatastoreContextIntrospector(configContext); + updated = configIntrospector.update(properties); + assertEquals("updated", true, updated); + configContext = configIntrospector.getContext(); + + assertEquals(44, configContext.getShardTransactionIdleTimeout().toMinutes()); + assertEquals(false, configContext.isPersistent()); + assertEquals(444, configContext.getDataStoreProperties().getMaxDataChangeExecutorPoolSize()); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextTest.java index d3a3a8fc2d..5197a7d991 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DatastoreContextTest.java @@ -1,37 +1,140 @@ package org.opendaylight.controller.cluster.datastore; import static org.junit.Assert.assertEquals; -import org.junit.Before; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_CONFIGURATION_READER; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_HEARTBEAT_INTERVAL_IN_MILLIS; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_ISOLATED_LEADER_CHECK_INTERVAL_IN_MILLIS; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_JOURNAL_RECOVERY_BATCH_SIZE; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_OPERATION_TIMEOUT_IN_SECONDS; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_PERSISTENT; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_SHARD_BATCHED_MODIFICATION_COUNT; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_SHARD_ELECTION_TIMEOUT_FACTOR; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_SHARD_INITIALIZATION_TIMEOUT; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_SHARD_LEADER_ELECTION_TIMEOUT; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_SHARD_SNAPSHOT_DATA_THRESHOLD_PERCENTAGE; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_SHARD_TRANSACTION_IDLE_TIMEOUT; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_SHARD_TX_COMMIT_QUEUE_CAPACITY; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_SHARD_TX_COMMIT_TIMEOUT_IN_SECONDS; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_SNAPSHOT_BATCH_COUNT; +import static org.opendaylight.controller.cluster.datastore.DatastoreContext.DEFAULT_TX_CREATION_INITIAL_RATE_LIMIT; +import java.util.concurrent.TimeUnit; +import org.junit.Assert; import org.junit.Test; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties; public class DatastoreContextTest { - private DatastoreContext.Builder builder; + @Test + public void testNewBuilderWithDefaultSettings() { + DatastoreContext context = DatastoreContext.newBuilder().build(); - @Before - public void setUp(){ - builder = new DatastoreContext.Builder(); + assertEquals(DEFAULT_SHARD_TRANSACTION_IDLE_TIMEOUT, context.getShardTransactionIdleTimeout()); + assertEquals(DEFAULT_OPERATION_TIMEOUT_IN_SECONDS, context.getOperationTimeoutInSeconds()); + assertEquals(DEFAULT_SHARD_TX_COMMIT_TIMEOUT_IN_SECONDS, context.getShardTransactionCommitTimeoutInSeconds()); + assertEquals(DEFAULT_JOURNAL_RECOVERY_BATCH_SIZE, context.getShardRaftConfig().getJournalRecoveryLogBatchSize()); + assertEquals(DEFAULT_SNAPSHOT_BATCH_COUNT, context.getShardRaftConfig().getSnapshotBatchCount()); + assertEquals(DEFAULT_HEARTBEAT_INTERVAL_IN_MILLIS, context.getShardRaftConfig().getHeartBeatInterval().length()); + assertEquals(DEFAULT_SHARD_TX_COMMIT_QUEUE_CAPACITY, context.getShardTransactionCommitQueueCapacity()); + assertEquals(DEFAULT_SHARD_INITIALIZATION_TIMEOUT.duration().toMillis(), + context.getShardInitializationTimeout().duration().toMillis()); + assertEquals(DEFAULT_SHARD_LEADER_ELECTION_TIMEOUT.duration().toMillis(), + context.getShardLeaderElectionTimeout().duration().toMillis()); + assertEquals(DEFAULT_PERSISTENT, context.isPersistent()); + assertEquals(DEFAULT_CONFIGURATION_READER, context.getConfigurationReader()); + assertEquals(DEFAULT_ISOLATED_LEADER_CHECK_INTERVAL_IN_MILLIS, context.getShardRaftConfig().getIsolatedCheckIntervalInMillis()); + assertEquals(DEFAULT_SHARD_SNAPSHOT_DATA_THRESHOLD_PERCENTAGE, context.getShardRaftConfig().getSnapshotDataThresholdPercentage()); + assertEquals(DEFAULT_SHARD_ELECTION_TIMEOUT_FACTOR, context.getShardRaftConfig().getElectionTimeoutFactor()); + assertEquals(DEFAULT_TX_CREATION_INITIAL_RATE_LIMIT, context.getTransactionCreationInitialRateLimit()); + assertEquals(DatastoreContext.DEFAULT_SHARD_BATCHED_MODIFICATION_COUNT, context.getShardBatchedModificationCount()); + assertEquals(InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE, + context.getDataStoreProperties().getMaxDataChangeExecutorPoolSize()); + assertEquals(InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE, + context.getDataStoreProperties().getMaxDataChangeExecutorQueueSize()); + assertEquals(InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE, + context.getDataStoreProperties().getMaxDataChangeListenerQueueSize()); + assertEquals(InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE, + context.getDataStoreProperties().getMaxDataStoreExecutorQueueSize()); } @Test - public void testDefaults(){ - DatastoreContext build = builder.build(); - - assertEquals(DatastoreContext.DEFAULT_SHARD_TRANSACTION_IDLE_TIMEOUT , build.getShardTransactionIdleTimeout()); - assertEquals(DatastoreContext.DEFAULT_OPERATION_TIMEOUT_IN_SECONDS, build.getOperationTimeoutInSeconds()); - assertEquals(DatastoreContext.DEFAULT_SHARD_TX_COMMIT_TIMEOUT_IN_SECONDS, build.getShardTransactionCommitTimeoutInSeconds()); - assertEquals(DatastoreContext.DEFAULT_JOURNAL_RECOVERY_BATCH_SIZE, build.getShardRaftConfig().getJournalRecoveryLogBatchSize()); - assertEquals(DatastoreContext.DEFAULT_SNAPSHOT_BATCH_COUNT, build.getShardRaftConfig().getSnapshotBatchCount()); - assertEquals(DatastoreContext.DEFAULT_HEARTBEAT_INTERVAL_IN_MILLIS, build.getShardRaftConfig().getHeartBeatInterval().length()); - assertEquals(DatastoreContext.DEFAULT_SHARD_TX_COMMIT_QUEUE_CAPACITY, build.getShardTransactionCommitQueueCapacity()); - assertEquals(DatastoreContext.DEFAULT_SHARD_INITIALIZATION_TIMEOUT, build.getShardInitializationTimeout()); - assertEquals(DatastoreContext.DEFAULT_SHARD_LEADER_ELECTION_TIMEOUT, build.getShardLeaderElectionTimeout()); - assertEquals(DatastoreContext.DEFAULT_PERSISTENT, build.isPersistent()); - assertEquals(DatastoreContext.DEFAULT_CONFIGURATION_READER, build.getConfigurationReader()); - assertEquals(DatastoreContext.DEFAULT_ISOLATED_LEADER_CHECK_INTERVAL_IN_MILLIS, build.getShardRaftConfig().getIsolatedCheckIntervalInMillis()); - assertEquals(DatastoreContext.DEFAULT_SHARD_SNAPSHOT_DATA_THRESHOLD_PERCENTAGE, build.getShardRaftConfig().getSnapshotDataThresholdPercentage()); - assertEquals(DatastoreContext.DEFAULT_SHARD_ELECTION_TIMEOUT_FACTOR, build.getShardRaftConfig().getElectionTimeoutFactor()); - assertEquals(DatastoreContext.DEFAULT_TX_CREATION_INITIAL_RATE_LIMIT, build.getTransactionCreationInitialRateLimit()); + public void testNewBuilderWithCustomSettings() { + DatastoreContext.Builder builder = DatastoreContext.newBuilder(); + + builder.shardTransactionIdleTimeout(DEFAULT_SHARD_TRANSACTION_IDLE_TIMEOUT.toMillis() + 1, + TimeUnit.MILLISECONDS); + builder.operationTimeoutInSeconds(DEFAULT_OPERATION_TIMEOUT_IN_SECONDS + 1); + builder.shardTransactionCommitTimeoutInSeconds(DEFAULT_SHARD_TX_COMMIT_TIMEOUT_IN_SECONDS + 1); + builder.shardJournalRecoveryLogBatchSize(DEFAULT_JOURNAL_RECOVERY_BATCH_SIZE + 1); + builder.shardSnapshotBatchCount(DEFAULT_SNAPSHOT_BATCH_COUNT + 1); + builder.shardHeartbeatIntervalInMillis(DEFAULT_HEARTBEAT_INTERVAL_IN_MILLIS + 1); + builder.shardTransactionCommitQueueCapacity(DEFAULT_SHARD_TX_COMMIT_QUEUE_CAPACITY + 1); + builder.shardInitializationTimeout(DEFAULT_SHARD_INITIALIZATION_TIMEOUT. + duration().toMillis() + 1, TimeUnit.MILLISECONDS); + builder.shardInitializationTimeout(DEFAULT_SHARD_INITIALIZATION_TIMEOUT.duration().toMillis() + 1, + TimeUnit.MILLISECONDS); + builder.shardLeaderElectionTimeout(DEFAULT_SHARD_LEADER_ELECTION_TIMEOUT.duration().toMillis() + 1, + TimeUnit.MILLISECONDS); + builder.persistent(!DEFAULT_PERSISTENT); + builder.shardIsolatedLeaderCheckIntervalInMillis(DEFAULT_ISOLATED_LEADER_CHECK_INTERVAL_IN_MILLIS + 1); + builder.shardSnapshotDataThresholdPercentage(DEFAULT_SHARD_SNAPSHOT_DATA_THRESHOLD_PERCENTAGE + 1); + builder.shardElectionTimeoutFactor(DEFAULT_SHARD_ELECTION_TIMEOUT_FACTOR + 1); + builder.transactionCreationInitialRateLimit(DEFAULT_TX_CREATION_INITIAL_RATE_LIMIT + 1); + builder.shardBatchedModificationCount(DEFAULT_SHARD_BATCHED_MODIFICATION_COUNT + 1); + builder.maxShardDataChangeExecutorPoolSize( + InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE + 1); + builder.maxShardDataChangeExecutorQueueSize( + InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE + 1); + builder.maxShardDataChangeListenerQueueSize( + InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE + 1); + builder.maxShardDataStoreExecutorQueueSize( + InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE + 1); + + DatastoreContext context = builder.build(); + + verifyCustomSettings(context); + + builder = DatastoreContext.newBuilderFrom(context); + + DatastoreContext newContext = builder.build(); + + verifyCustomSettings(newContext); + + Assert.assertNotSame(context, newContext); } -} \ No newline at end of file + private void verifyCustomSettings(DatastoreContext context) { + assertEquals(DEFAULT_SHARD_TRANSACTION_IDLE_TIMEOUT.toMillis() + 1, + context.getShardTransactionIdleTimeout().toMillis()); + assertEquals(DEFAULT_OPERATION_TIMEOUT_IN_SECONDS + 1, context.getOperationTimeoutInSeconds()); + assertEquals(DEFAULT_SHARD_TX_COMMIT_TIMEOUT_IN_SECONDS + 1, + context.getShardTransactionCommitTimeoutInSeconds()); + assertEquals(DEFAULT_JOURNAL_RECOVERY_BATCH_SIZE + 1, + context.getShardRaftConfig().getJournalRecoveryLogBatchSize()); + assertEquals(DEFAULT_SNAPSHOT_BATCH_COUNT + 1, context.getShardRaftConfig().getSnapshotBatchCount()); + assertEquals(DEFAULT_HEARTBEAT_INTERVAL_IN_MILLIS + 1, + context.getShardRaftConfig().getHeartBeatInterval().length()); + assertEquals(DEFAULT_SHARD_TX_COMMIT_QUEUE_CAPACITY + 1, context.getShardTransactionCommitQueueCapacity()); + assertEquals(DEFAULT_SHARD_INITIALIZATION_TIMEOUT.duration().toMillis() + 1, + context.getShardInitializationTimeout().duration().toMillis()); + assertEquals(DEFAULT_SHARD_LEADER_ELECTION_TIMEOUT.duration().toMillis() + 1, + context.getShardLeaderElectionTimeout().duration().toMillis()); + assertEquals(!DEFAULT_PERSISTENT, context.isPersistent()); + assertEquals(DEFAULT_CONFIGURATION_READER, context.getConfigurationReader()); + assertEquals(DEFAULT_ISOLATED_LEADER_CHECK_INTERVAL_IN_MILLIS + 1, + context.getShardRaftConfig().getIsolatedCheckIntervalInMillis()); + assertEquals(DEFAULT_SHARD_SNAPSHOT_DATA_THRESHOLD_PERCENTAGE + 1, + context.getShardRaftConfig().getSnapshotDataThresholdPercentage()); + assertEquals(DEFAULT_SHARD_ELECTION_TIMEOUT_FACTOR + 1, context.getShardRaftConfig().getElectionTimeoutFactor()); + assertEquals(DEFAULT_TX_CREATION_INITIAL_RATE_LIMIT + 1, context.getTransactionCreationInitialRateLimit()); + assertEquals(DatastoreContext.DEFAULT_SHARD_BATCHED_MODIFICATION_COUNT + 1, + context.getShardBatchedModificationCount()); + assertEquals(InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE + 1, + context.getDataStoreProperties().getMaxDataChangeExecutorPoolSize()); + assertEquals(InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE + 1, + context.getDataStoreProperties().getMaxDataChangeExecutorQueueSize()); + assertEquals(InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE + 1, + context.getDataStoreProperties().getMaxDataChangeListenerQueueSize()); + assertEquals(InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE + 1, + context.getDataStoreProperties().getMaxDataStoreExecutorQueueSize()); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java index 66fa876277..4ec035ee3b 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java @@ -25,6 +25,7 @@ public class DistributedDataStoreTest extends AbstractActorTest { schemaContext = TestModel.createTestContext(); doReturn(schemaContext).when(actorContext).getSchemaContext(); + doReturn(DatastoreContext.newBuilder().build()).when(actorContext).getDatastoreContext(); } @Test diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/OperationCompleterTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/OperationCompleterTest.java new file mode 100644 index 0000000000..e7afe262b9 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/OperationCompleterTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore; + +import static org.junit.Assert.assertEquals; +import java.util.concurrent.Semaphore; +import org.junit.Test; +import org.opendaylight.controller.cluster.datastore.messages.BatchedModificationsReply; +import org.opendaylight.controller.cluster.datastore.messages.DataExistsReply; + +/** + * Unit tests for OperationCompleter. + * + * @author Thomas Pantelis + */ +public class OperationCompleterTest { + + @Test + public void testOnComplete() throws Exception { + int permits = 10; + Semaphore operationLimiter = new Semaphore(permits); + operationLimiter.acquire(permits); + int availablePermits = 0; + + OperationCompleter completer = new OperationCompleter(operationLimiter ); + + completer.onComplete(null, new DataExistsReply(true)); + assertEquals("availablePermits", ++availablePermits, operationLimiter.availablePermits()); + + completer.onComplete(null, new DataExistsReply(true)); + assertEquals("availablePermits", ++availablePermits, operationLimiter.availablePermits()); + + completer.onComplete(null, new IllegalArgumentException()); + assertEquals("availablePermits", ++availablePermits, operationLimiter.availablePermits()); + + completer.onComplete(null, new BatchedModificationsReply(4)); + availablePermits += 4; + assertEquals("availablePermits", availablePermits, operationLimiter.availablePermits()); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionHeliumBackwardsCompatibilityTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionHeliumBackwardsCompatibilityTest.java index 58cec67a2d..1d1b08b5f8 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionHeliumBackwardsCompatibilityTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionHeliumBackwardsCompatibilityTest.java @@ -79,8 +79,8 @@ public class ShardTransactionHeliumBackwardsCompatibilityTest extends AbstractAc // Write data to the Tx txActor.tell(new WriteData(TestModel.TEST_PATH, - ImmutableNodes.containerNode(TestModel.TEST_QNAME)).toSerializable( - DataStoreVersions.BASE_HELIUM_VERSION), getRef()); + ImmutableNodes.containerNode(TestModel.TEST_QNAME), DataStoreVersions.BASE_HELIUM_VERSION). + toSerializable(), getRef()); expectMsgClass(duration, ShardTransactionMessages.WriteDataReply.class); @@ -153,9 +153,11 @@ public class ShardTransactionHeliumBackwardsCompatibilityTest extends AbstractAc // Write data to the Tx txActor.tell(new WriteData(TestModel.TEST_PATH, - ImmutableNodes.containerNode(TestModel.TEST_QNAME)), getRef()); + ImmutableNodes.containerNode(TestModel.TEST_QNAME), + DataStoreVersions.BASE_HELIUM_VERSION).toSerializable(), getRef()); - expectMsgClass(duration, WriteDataReply.class); + expectMsgClass(duration, WriteDataReply.INSTANCE.toSerializable( + DataStoreVersions.BASE_HELIUM_VERSION).getClass()); // Ready the Tx diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java index 851fb0114b..c6b5cb4402 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java @@ -14,10 +14,14 @@ import java.util.Collections; import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mockito; import org.opendaylight.controller.cluster.datastore.ShardWriteTransaction.GetCompositeModificationReply; import org.opendaylight.controller.cluster.datastore.exceptions.UnknownMessageException; import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats; +import org.opendaylight.controller.cluster.datastore.messages.BatchedModifications; +import org.opendaylight.controller.cluster.datastore.messages.BatchedModificationsReply; import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction; import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionReply; import org.opendaylight.controller.cluster.datastore.messages.CreateSnapshot; @@ -46,11 +50,12 @@ import org.opendaylight.controller.md.cluster.datastore.model.TestModel; import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages; import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import scala.concurrent.duration.Duration; public class ShardTransactionTest extends AbstractActorTest { @@ -250,8 +255,8 @@ public class ShardTransactionTest extends AbstractActorTest { "testOnReceiveWriteData"); transaction.tell(new WriteData(TestModel.TEST_PATH, - ImmutableNodes.containerNode(TestModel.TEST_QNAME)).toSerializable( - DataStoreVersions.HELIUM_2_VERSION), getRef()); + ImmutableNodes.containerNode(TestModel.TEST_QNAME), DataStoreVersions.HELIUM_2_VERSION). + toSerializable(), getRef()); expectMsgClass(duration("5 seconds"), ShardTransactionMessages.WriteDataReply.class); @@ -259,7 +264,7 @@ public class ShardTransactionTest extends AbstractActorTest { // unserialized write transaction.tell(new WriteData(TestModel.TEST_PATH, - ImmutableNodes.containerNode(TestModel.TEST_QNAME)), + ImmutableNodes.containerNode(TestModel.TEST_QNAME), DataStoreVersions.CURRENT_VERSION), getRef()); expectMsgClass(duration("5 seconds"), WriteDataReply.class); @@ -293,8 +298,8 @@ public class ShardTransactionTest extends AbstractActorTest { "testMergeData"); transaction.tell(new MergeData(TestModel.TEST_PATH, - ImmutableNodes.containerNode(TestModel.TEST_QNAME)).toSerializable( - DataStoreVersions.HELIUM_2_VERSION), getRef()); + ImmutableNodes.containerNode(TestModel.TEST_QNAME), DataStoreVersions.HELIUM_2_VERSION). + toSerializable(), getRef()); expectMsgClass(duration("5 seconds"), ShardTransactionMessages.MergeDataReply.class); @@ -302,7 +307,7 @@ public class ShardTransactionTest extends AbstractActorTest { //unserialized merge transaction.tell(new MergeData(TestModel.TEST_PATH, - ImmutableNodes.containerNode(TestModel.TEST_QNAME)), + ImmutableNodes.containerNode(TestModel.TEST_QNAME), DataStoreVersions.CURRENT_VERSION), getRef()); expectMsgClass(duration("5 seconds"), MergeDataReply.class); @@ -335,20 +340,73 @@ public class ShardTransactionTest extends AbstractActorTest { final ActorRef transaction = newTransactionActor(store.newWriteOnlyTransaction(), "testDeleteData"); - transaction.tell(new DeleteData(TestModel.TEST_PATH).toSerializable( - DataStoreVersions.HELIUM_2_VERSION), getRef()); + transaction.tell(new DeleteData(TestModel.TEST_PATH, DataStoreVersions.HELIUM_2_VERSION). + toSerializable(), getRef()); expectMsgClass(duration("5 seconds"), ShardTransactionMessages.DeleteDataReply.class); assertModification(transaction, DeleteModification.class); //unserialized - transaction.tell(new DeleteData(TestModel.TEST_PATH), getRef()); + transaction.tell(new DeleteData(TestModel.TEST_PATH, DataStoreVersions.CURRENT_VERSION), getRef()); expectMsgClass(duration("5 seconds"), DeleteDataReply.class); }}; } + @Test + public void testOnReceiveBatchedModifications() throws Exception { + new JavaTestKit(getSystem()) {{ + + DOMStoreWriteTransaction mockWriteTx = Mockito.mock(DOMStoreWriteTransaction.class); + final ActorRef transaction = newTransactionActor(mockWriteTx, "testOnReceiveBatchedModifications"); + + YangInstanceIdentifier writePath = TestModel.TEST_PATH; + NormalizedNode writeData = ImmutableContainerNodeBuilder.create().withNodeIdentifier( + new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME)). + withChild(ImmutableNodes.leafNode(TestModel.DESC_QNAME, "foo")).build(); + + YangInstanceIdentifier mergePath = TestModel.OUTER_LIST_PATH; + NormalizedNode mergeData = ImmutableContainerNodeBuilder.create().withNodeIdentifier( + new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME)).build(); + + YangInstanceIdentifier deletePath = TestModel.TEST_PATH; + + BatchedModifications batched = new BatchedModifications(DataStoreVersions.CURRENT_VERSION); + batched.addModification(new WriteModification(writePath, writeData)); + batched.addModification(new MergeModification(mergePath, mergeData)); + batched.addModification(new DeleteModification(deletePath)); + + transaction.tell(batched, getRef()); + + BatchedModificationsReply reply = expectMsgClass(duration("5 seconds"), BatchedModificationsReply.class); + assertEquals("getNumBatched", 3, reply.getNumBatched()); + + JavaTestKit verification = new JavaTestKit(getSystem()); + transaction.tell(new ShardWriteTransaction.GetCompositedModification(), verification.getRef()); + + CompositeModification compositeModification = verification.expectMsgClass(duration("5 seconds"), + GetCompositeModificationReply.class).getModification(); + + assertEquals("CompositeModification size", 3, compositeModification.getModifications().size()); + + WriteModification write = (WriteModification)compositeModification.getModifications().get(0); + assertEquals("getPath", writePath, write.getPath()); + assertEquals("getData", writeData, write.getData()); + + MergeModification merge = (MergeModification)compositeModification.getModifications().get(1); + assertEquals("getPath", mergePath, merge.getPath()); + assertEquals("getData", mergeData, merge.getData()); + + DeleteModification delete = (DeleteModification)compositeModification.getModifications().get(2); + assertEquals("getPath", deletePath, delete.getPath()); + + InOrder inOrder = Mockito.inOrder(mockWriteTx); + inOrder.verify(mockWriteTx).write(writePath, writeData); + inOrder.verify(mockWriteTx).merge(mergePath, mergeData); + inOrder.verify(mockWriteTx).delete(deletePath); + }}; + } @Test public void testOnReceiveReadyTransaction() throws Exception { @@ -463,15 +521,15 @@ public class ShardTransactionTest extends AbstractActorTest { DataStoreVersions.CURRENT_VERSION); final TestActorRef transaction = TestActorRef.apply(props,getSystem()); - transaction.receive(new DeleteData(TestModel.TEST_PATH).toSerializable( - DataStoreVersions.CURRENT_VERSION), ActorRef.noSender()); + transaction.receive(new DeleteData(TestModel.TEST_PATH, DataStoreVersions.CURRENT_VERSION). + toSerializable(), ActorRef.noSender()); } @Test public void testShardTransactionInactivity() { datastoreContext = DatastoreContext.newBuilder().shardTransactionIdleTimeout( - Duration.create(500, TimeUnit.MILLISECONDS)).build(); + 500, TimeUnit.MILLISECONDS).build(); new JavaTestKit(getSystem()) {{ final ActorRef transaction = newTransactionActor(store.newReadWriteTransaction(), diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java index b013515f25..0a2a0d1bc0 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java @@ -66,6 +66,7 @@ public class ThreePhaseCommitCohortProxyTest extends AbstractActorTest { MockitoAnnotations.initMocks(this); doReturn(getSystem()).when(actorContext).getActorSystem(); + doReturn(getSystem().dispatchers().defaultGlobalDispatcher()).when(actorContext).getClientDispatcher(); doReturn(datastoreContext).when(actorContext).getDatastoreContext(); doReturn(100).when(datastoreContext).getShardTransactionCommitTimeoutInSeconds(); doReturn(commitTimer).when(actorContext).getOperationTimer("commit"); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxyTest.java index 23c3a82a38..88ab0dd292 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxyTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxyTest.java @@ -43,6 +43,7 @@ public class TransactionChainProxyTest extends AbstractActorTest{ actorContext.setSchemaContext(schemaContext); doReturn(schemaContext).when(mockActorContext).getSchemaContext(); + doReturn(DatastoreContext.newBuilder().build()).when(mockActorContext).getDatastoreContext(); } @SuppressWarnings("resource") diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java index 7ce41a4db1..6573308c12 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java @@ -30,6 +30,7 @@ import com.google.common.util.concurrent.Uninterruptibles; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -39,13 +40,18 @@ import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.cluster.datastore.DatastoreContext.Builder; import org.opendaylight.controller.cluster.datastore.TransactionProxy.TransactionType; import org.opendaylight.controller.cluster.datastore.exceptions.PrimaryNotFoundException; import org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException; +import org.opendaylight.controller.cluster.datastore.messages.BatchedModifications; +import org.opendaylight.controller.cluster.datastore.messages.BatchedModificationsReply; import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction; import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction; import org.opendaylight.controller.cluster.datastore.messages.DataExists; @@ -60,6 +66,11 @@ import org.opendaylight.controller.cluster.datastore.messages.ReadyTransaction; import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply; import org.opendaylight.controller.cluster.datastore.messages.WriteData; import org.opendaylight.controller.cluster.datastore.messages.WriteDataReply; +import org.opendaylight.controller.cluster.datastore.modification.AbstractModification; +import org.opendaylight.controller.cluster.datastore.modification.DeleteModification; +import org.opendaylight.controller.cluster.datastore.modification.MergeModification; +import org.opendaylight.controller.cluster.datastore.modification.Modification; +import org.opendaylight.controller.cluster.datastore.modification.WriteModification; import org.opendaylight.controller.cluster.datastore.shardstrategy.DefaultShardStrategy; import org.opendaylight.controller.cluster.datastore.shardstrategy.ShardStrategyFactory; import org.opendaylight.controller.cluster.datastore.utils.ActorContext; @@ -71,6 +82,7 @@ import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages; import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages.CreateTransactionReply; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; 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; @@ -102,7 +114,10 @@ public class TransactionProxyTest { @Mock private ClusterWrapper mockClusterWrapper; - String memberName = "mock-member"; + private final String memberName = "mock-member"; + + private final Builder dataStoreContextBuilder = DatastoreContext.newBuilder().operationTimeoutInSeconds(2). + shardBatchedModificationCount(1); @BeforeClass public static void setUpClass() throws IOException { @@ -126,14 +141,13 @@ public class TransactionProxyTest { schemaContext = TestModel.createTestContext(); - DatastoreContext dataStoreContext = DatastoreContext.newBuilder().operationTimeoutInSeconds(2).build(); - doReturn(getSystem()).when(mockActorContext).getActorSystem(); + doReturn(getSystem().dispatchers().defaultGlobalDispatcher()).when(mockActorContext).getClientDispatcher(); doReturn(memberName).when(mockActorContext).getCurrentMemberName(); doReturn(schemaContext).when(mockActorContext).getSchemaContext(); doReturn(mockClusterWrapper).when(mockActorContext).getClusterWrapper(); doReturn(mockClusterWrapper).when(mockActorContext).getClusterWrapper(); - doReturn(dataStoreContext).when(mockActorContext).getDatastoreContext(); + doReturn(dataStoreContextBuilder.build()).when(mockActorContext).getDatastoreContext(); doReturn(10).when(mockActorContext).getTransactionOutstandingOperationLimit(); ShardStrategyFactory.setConfiguration(configuration); @@ -186,11 +200,15 @@ public class TransactionProxyTest { } private ReadData eqSerializedReadData() { + return eqSerializedReadData(TestModel.TEST_PATH); + } + + private ReadData eqSerializedReadData(final YangInstanceIdentifier path) { ArgumentMatcher matcher = new ArgumentMatcher() { @Override public boolean matches(Object argument) { return ReadData.SERIALIZABLE_CLASS.equals(argument.getClass()) && - ReadData.fromSerializable(argument).getPath().equals(TestModel.TEST_PATH); + ReadData.fromSerializable(argument).getPath().equals(path); } }; @@ -209,23 +227,13 @@ public class TransactionProxyTest { return argThat(matcher); } - private WriteData eqSerializedWriteData(final NormalizedNode nodeToWrite) { - return eqSerializedWriteData(nodeToWrite, DataStoreVersions.CURRENT_VERSION); - } - - private WriteData eqSerializedWriteData(final NormalizedNode nodeToWrite, - final int transactionVersion) { + private WriteData eqLegacyWriteData(final NormalizedNode nodeToWrite) { ArgumentMatcher matcher = new ArgumentMatcher() { @Override public boolean matches(Object argument) { - if((transactionVersion >= DataStoreVersions.LITHIUM_VERSION && - WriteData.SERIALIZABLE_CLASS.equals(argument.getClass())) || - (transactionVersion < DataStoreVersions.LITHIUM_VERSION && - ShardTransactionMessages.WriteData.class.equals(argument.getClass()))) { - + if(ShardTransactionMessages.WriteData.class.equals(argument.getClass())) { WriteData obj = WriteData.fromSerializable(argument); - return obj.getPath().equals(TestModel.TEST_PATH) && - obj.getData().equals(nodeToWrite); + return obj.getPath().equals(TestModel.TEST_PATH) && obj.getData().equals(nodeToWrite); } return false; @@ -235,39 +243,13 @@ public class TransactionProxyTest { return argThat(matcher); } - private WriteData eqWriteData(final NormalizedNode nodeToWrite) { - ArgumentMatcher matcher = new ArgumentMatcher() { - @Override - public boolean matches(Object argument) { - if(argument instanceof WriteData) { - WriteData obj = (WriteData) argument; - return obj.getPath().equals(TestModel.TEST_PATH) && - obj.getData().equals(nodeToWrite); - } - return false; - } - }; - - return argThat(matcher); - } - - private MergeData eqSerializedMergeData(final NormalizedNode nodeToWrite) { - return eqSerializedMergeData(nodeToWrite, DataStoreVersions.CURRENT_VERSION); - } - - private MergeData eqSerializedMergeData(final NormalizedNode nodeToWrite, - final int transactionVersion) { + private MergeData eqLegacyMergeData(final NormalizedNode nodeToWrite) { ArgumentMatcher matcher = new ArgumentMatcher() { @Override public boolean matches(Object argument) { - if((transactionVersion >= DataStoreVersions.LITHIUM_VERSION && - MergeData.SERIALIZABLE_CLASS.equals(argument.getClass())) || - (transactionVersion < DataStoreVersions.LITHIUM_VERSION && - ShardTransactionMessages.MergeData.class.equals(argument.getClass()))) { - + if(ShardTransactionMessages.MergeData.class.equals(argument.getClass())) { MergeData obj = MergeData.fromSerializable(argument); - return obj.getPath().equals(TestModel.TEST_PATH) && - obj.getData().equals(nodeToWrite); + return obj.getPath().equals(TestModel.TEST_PATH) && obj.getData().equals(nodeToWrite); } return false; @@ -277,41 +259,12 @@ public class TransactionProxyTest { return argThat(matcher); } - private MergeData eqMergeData(final NormalizedNode nodeToWrite) { - ArgumentMatcher matcher = new ArgumentMatcher() { - @Override - public boolean matches(Object argument) { - if(argument instanceof MergeData) { - MergeData obj = ((MergeData) argument); - return obj.getPath().equals(TestModel.TEST_PATH) && - obj.getData().equals(nodeToWrite); - } - - return false; - } - }; - - return argThat(matcher); - } - - private DeleteData eqSerializedDeleteData() { - ArgumentMatcher matcher = new ArgumentMatcher() { - @Override - public boolean matches(Object argument) { - return DeleteData.SERIALIZABLE_CLASS.equals(argument.getClass()) && - DeleteData.fromSerializable(argument).getPath().equals(TestModel.TEST_PATH); - } - }; - - return argThat(matcher); - } - - private DeleteData eqDeleteData() { + private DeleteData eqLegacyDeleteData(final YangInstanceIdentifier expPath) { ArgumentMatcher matcher = new ArgumentMatcher() { @Override public boolean matches(Object argument) { - return argument instanceof DeleteData && - ((DeleteData)argument).getPath().equals(TestModel.TEST_PATH); + return ShardTransactionMessages.DeleteData.class.equals(argument.getClass()) && + DeleteData.fromSerializable(argument).getPath().equals(expPath); } }; @@ -328,7 +281,7 @@ public class TransactionProxyTest { private Future readSerializedDataReply(NormalizedNode data, short transactionVersion) { - return Futures.successful(new ReadDataReply(data).toSerializable(transactionVersion)); + return Futures.successful(new ReadDataReply(data, transactionVersion).toSerializable()); } private Future readSerializedDataReply(NormalizedNode data) { @@ -336,7 +289,7 @@ public class TransactionProxyTest { } private Future readDataReply(NormalizedNode data) { - return Futures.successful(new ReadDataReply(data)); + return Futures.successful(new ReadDataReply(data, DataStoreVersions.CURRENT_VERSION)); } private Future dataExistsSerializedReply(boolean exists) { @@ -347,48 +300,41 @@ public class TransactionProxyTest { return Futures.successful(new DataExistsReply(exists)); } - private Future writeSerializedDataReply(short version) { - return Futures.successful(new WriteDataReply().toSerializable(version)); - } - - private Future writeSerializedDataReply() { - return writeSerializedDataReply(DataStoreVersions.CURRENT_VERSION); - } - - private Future writeDataReply() { - return Futures.successful(new WriteDataReply()); - } - - private Future mergeSerializedDataReply(short version) { - return Futures.successful(new MergeDataReply().toSerializable(version)); - } - - private Future mergeSerializedDataReply() { - return mergeSerializedDataReply(DataStoreVersions.CURRENT_VERSION); + private Future batchedModificationsReply(int count) { + return Futures.successful(new BatchedModificationsReply(count)); } private Future incompleteFuture(){ return mock(Future.class); } - private Future mergeDataReply() { - return Futures.successful(new MergeDataReply()); + private ActorSelection actorSelection(ActorRef actorRef) { + return getSystem().actorSelection(actorRef.path()); + } + + private void expectBatchedModifications(ActorRef actorRef, int count) { + doReturn(batchedModificationsReply(count)).when(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), isA(BatchedModifications.class)); } - private Future deleteSerializedDataReply(short version) { - return Futures.successful(new DeleteDataReply().toSerializable(version)); + private void expectBatchedModifications(int count) { + doReturn(batchedModificationsReply(count)).when(mockActorContext).executeOperationAsync( + any(ActorSelection.class), isA(BatchedModifications.class)); } - private Future deleteSerializedDataReply() { - return deleteSerializedDataReply(DataStoreVersions.CURRENT_VERSION); + private void expectIncompleteBatchedModifications() { + doReturn(incompleteFuture()).when(mockActorContext).executeOperationAsync( + any(ActorSelection.class), isA(BatchedModifications.class)); } - private Future deleteDataReply() { - return Futures.successful(new DeleteDataReply()); + private void expectReadyTransaction(ActorRef actorRef) { + doReturn(readySerializedTxReply(actorRef.path().toString())).when(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), isA(ReadyTransaction.SERIALIZABLE_CLASS)); } - private ActorSelection actorSelection(ActorRef actorRef) { - return getSystem().actorSelection(actorRef.path()); + private void expectFailedBatchedModifications(ActorRef actorRef) { + doReturn(Futures.failed(new TestException())).when(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), isA(BatchedModifications.class)); } private CreateTransactionReply createTransactionReply(ActorRef actorRef, int transactionVersion){ @@ -445,8 +391,7 @@ public class TransactionProxyTest { public void testRead() throws Exception { ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_ONLY); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - READ_ONLY); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_ONLY); doReturn(readSerializedDataReply(null)).when(mockActorContext).executeOperationAsync( eq(actorSelection(actorRef)), eqSerializedReadData()); @@ -475,8 +420,7 @@ public class TransactionProxyTest { doReturn(Futures.successful(new Object())).when(mockActorContext). executeOperationAsync(eq(actorSelection(actorRef)), eqSerializedReadData()); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - READ_ONLY); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_ONLY); transactionProxy.read(TestModel.TEST_PATH).checkedGet(5, TimeUnit.SECONDS); } @@ -488,8 +432,7 @@ public class TransactionProxyTest { doReturn(Futures.failed(new TestException())).when(mockActorContext). executeOperationAsync(eq(actorSelection(actorRef)), eqSerializedReadData()); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - READ_ONLY); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_ONLY); propagateReadFailedExceptionCause(transactionProxy.read(TestModel.TEST_PATH)); } @@ -540,21 +483,19 @@ public class TransactionProxyTest { @Test(expected = TestException.class) public void testReadWithPriorRecordingOperationFailure() throws Throwable { + doReturn(dataStoreContextBuilder.shardBatchedModificationCount(2).build()). + when(mockActorContext).getDatastoreContext(); + ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_WRITE); NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite)); - - doReturn(Futures.failed(new TestException())).when(mockActorContext). - executeOperationAsync(eq(actorSelection(actorRef)), eqSerializedDeleteData()); + expectFailedBatchedModifications(actorRef); doReturn(readSerializedDataReply(null)).when(mockActorContext).executeOperationAsync( eq(actorSelection(actorRef)), eqSerializedReadData()); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - READ_WRITE); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); @@ -574,14 +515,12 @@ public class TransactionProxyTest { NormalizedNode expectedNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedWriteData(expectedNode)); + expectBatchedModifications(actorRef, 1); doReturn(readSerializedDataReply(expectedNode)).when(mockActorContext).executeOperationAsync( eq(actorSelection(actorRef)), eqSerializedReadData()); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - READ_WRITE); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); transactionProxy.write(TestModel.TEST_PATH, expectedNode); @@ -589,16 +528,19 @@ public class TransactionProxyTest { TestModel.TEST_PATH).get(5, TimeUnit.SECONDS); assertEquals("NormalizedNode isPresent", true, readOptional.isPresent()); - assertEquals("Response NormalizedNode", expectedNode, readOptional.get()); + + InOrder inOrder = Mockito.inOrder(mockActorContext); + inOrder.verify(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), isA(BatchedModifications.class)); + + inOrder.verify(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), eqSerializedReadData()); } @Test(expected=IllegalStateException.class) public void testReadPreConditionCheck() { - - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - WRITE_ONLY); - + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); transactionProxy.read(TestModel.TEST_PATH); } @@ -624,8 +566,7 @@ public class TransactionProxyTest { public void testExists() throws Exception { ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_ONLY); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - READ_ONLY); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_ONLY); doReturn(dataExistsSerializedReply(false)).when(mockActorContext).executeOperationAsync( eq(actorSelection(actorRef)), eqSerializedDataExists()); @@ -672,23 +613,21 @@ public class TransactionProxyTest { doReturn(Futures.failed(new TestException())).when(mockActorContext). executeOperationAsync(eq(actorSelection(actorRef)), eqSerializedDataExists()); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - READ_ONLY); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_ONLY); propagateReadFailedExceptionCause(transactionProxy.exists(TestModel.TEST_PATH)); } @Test(expected = TestException.class) public void testExistsWithPriorRecordingOperationFailure() throws Throwable { + doReturn(dataStoreContextBuilder.shardBatchedModificationCount(2).build()). + when(mockActorContext).getDatastoreContext(); + ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_WRITE); NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite)); - - doReturn(Futures.failed(new TestException())).when(mockActorContext). - executeOperationAsync(eq(actorSelection(actorRef)), eqSerializedDeleteData()); + expectFailedBatchedModifications(actorRef); doReturn(dataExistsSerializedReply(false)).when(mockActorContext).executeOperationAsync( eq(actorSelection(actorRef)), eqSerializedDataExists()); @@ -714,28 +653,30 @@ public class TransactionProxyTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite)); + expectBatchedModifications(actorRef, 1); doReturn(dataExistsSerializedReply(true)).when(mockActorContext).executeOperationAsync( eq(actorSelection(actorRef)), eqSerializedDataExists()); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - READ_WRITE); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); Boolean exists = transactionProxy.exists(TestModel.TEST_PATH).checkedGet(); assertEquals("Exists response", true, exists); + + InOrder inOrder = Mockito.inOrder(mockActorContext); + inOrder.verify(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), isA(BatchedModifications.class)); + + inOrder.verify(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), eqSerializedDataExists()); } @Test(expected=IllegalStateException.class) public void testExistsPreConditionCheck() { - - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - WRITE_ONLY); - + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); transactionProxy.exists(TestModel.TEST_PATH); } @@ -756,7 +697,7 @@ public class TransactionProxyTest { // Expected } } else { - assertEquals("Recording operation Future result type", expResultType, + assertEquals(String.format("Recording operation %d Future result type", i +1 ), expResultType, Await.result(future, Duration.create(5, TimeUnit.SECONDS)).getClass()); } } @@ -768,19 +709,20 @@ public class TransactionProxyTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite)); + expectBatchedModifications(actorRef, 1); + expectReadyTransaction(actorRef); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - WRITE_ONLY); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); - verify(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite)); + // This sends the batched modification. + transactionProxy.ready(); + + verifyOneBatchedModification(actorRef, new WriteModification(TestModel.TEST_PATH, nodeToWrite)); verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), - WriteDataReply.class); + BatchedModificationsReply.class); } @Test @@ -795,10 +737,10 @@ public class TransactionProxyTest { doReturn(readSerializedDataReply(null)).when(mockActorContext).executeOperationAsync( eq(actorSelection(actorRef)), eqSerializedReadData()); - final NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); + expectBatchedModifications(actorRef, 1); + expectReadyTransaction(actorRef); - doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite)); + final NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); final TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); @@ -832,33 +774,28 @@ public class TransactionProxyTest { throw caughtEx.get(); } - verify(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite)); + // This sends the batched modification. + transactionProxy.ready(); + + verifyOneBatchedModification(actorRef, new WriteModification(TestModel.TEST_PATH, nodeToWrite)); verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), - WriteDataReply.class); + BatchedModificationsReply.class); } @Test(expected=IllegalStateException.class) public void testWritePreConditionCheck() { - - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - READ_ONLY); - - transactionProxy.write(TestModel.TEST_PATH, - ImmutableNodes.containerNode(TestModel.TEST_QNAME)); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_ONLY); + transactionProxy.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME)); } @Test(expected=IllegalStateException.class) public void testWriteAfterReadyPreConditionCheck() { - - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - WRITE_ONLY); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); transactionProxy.ready(); - transactionProxy.write(TestModel.TEST_PATH, - ImmutableNodes.containerNode(TestModel.TEST_QNAME)); + transactionProxy.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME)); } @Test @@ -867,37 +804,40 @@ public class TransactionProxyTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(mergeSerializedDataReply()).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedMergeData(nodeToWrite)); + expectBatchedModifications(actorRef, 1); + expectReadyTransaction(actorRef); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); transactionProxy.merge(TestModel.TEST_PATH, nodeToWrite); - verify(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedMergeData(nodeToWrite)); + // This sends the batched modification. + transactionProxy.ready(); + + verifyOneBatchedModification(actorRef, new MergeModification(TestModel.TEST_PATH, nodeToWrite)); verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), - MergeDataReply.class); + BatchedModificationsReply.class); } @Test public void testDelete() throws Exception { ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), WRITE_ONLY); - doReturn(deleteSerializedDataReply()).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedDeleteData()); + expectBatchedModifications(actorRef, 1); + expectReadyTransaction(actorRef); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - WRITE_ONLY); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); transactionProxy.delete(TestModel.TEST_PATH); - verify(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedDeleteData()); + // This sends the batched modification. + transactionProxy.ready(); + + verifyOneBatchedModification(actorRef, new DeleteModification(TestModel.TEST_PATH)); verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), - DeleteDataReply.class); + BatchedModificationsReply.class); } private void verifyCohortFutures(ThreePhaseCommitCohortProxy proxy, @@ -934,14 +874,10 @@ public class TransactionProxyTest { doReturn(readSerializedDataReply(null)).when(mockActorContext).executeOperationAsync( eq(actorSelection(actorRef)), eqSerializedReadData()); - doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite)); + expectBatchedModifications(actorRef, 1); + expectReadyTransaction(actorRef); - doReturn(readySerializedTxReply(actorRef.path().toString())).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), isA(ReadyTransaction.SERIALIZABLE_CLASS)); - - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - READ_WRITE); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); transactionProxy.read(TestModel.TEST_PATH); @@ -954,9 +890,12 @@ public class TransactionProxyTest { ThreePhaseCommitCohortProxy proxy = (ThreePhaseCommitCohortProxy) ready; verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), - WriteDataReply.class); + BatchedModificationsReply.class); verifyCohortFutures(proxy, getSystem().actorSelection(actorRef.path())); + + verify(mockActorContext).executeOperationAsync(eq(actorSelection(actorRef)), + isA(BatchedModifications.class)); } private ActorRef testCompatibilityWithHeliumVersion(short version) throws Exception { @@ -968,14 +907,16 @@ public class TransactionProxyTest { doReturn(readSerializedDataReply(testNode, version)).when(mockActorContext).executeOperationAsync( eq(actorSelection(actorRef)), eqSerializedReadData()); - doReturn(writeSerializedDataReply(version)).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedWriteData(testNode, version)); + doReturn(Futures.successful(new WriteDataReply().toSerializable(version))).when(mockActorContext). + executeOperationAsync(eq(actorSelection(actorRef)), eqLegacyWriteData(testNode)); - doReturn(mergeSerializedDataReply(version)).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedMergeData(testNode, version)); + doReturn(Futures.successful(new MergeDataReply().toSerializable(version))).when(mockActorContext). + executeOperationAsync(eq(actorSelection(actorRef)), eqLegacyMergeData(testNode)); - doReturn(readySerializedTxReply(actorRef.path().toString())).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), isA(ReadyTransaction.SERIALIZABLE_CLASS)); + doReturn(Futures.successful(new DeleteDataReply().toSerializable(version))).when(mockActorContext). + executeOperationAsync(eq(actorSelection(actorRef)), eqLegacyDeleteData(TestModel.TEST_PATH)); + + expectReadyTransaction(actorRef); doReturn(actorRef.path().toString()).when(mockActorContext).resolvePath(eq(actorRef.path().toString()), eq(actorRef.path().toString())); @@ -992,6 +933,8 @@ public class TransactionProxyTest { transactionProxy.merge(TestModel.TEST_PATH, testNode); + transactionProxy.delete(TestModel.TEST_PATH); + DOMStoreThreePhaseCommitCohort ready = transactionProxy.ready(); assertTrue(ready instanceof ThreePhaseCommitCohortProxy); @@ -999,7 +942,8 @@ public class TransactionProxyTest { ThreePhaseCommitCohortProxy proxy = (ThreePhaseCommitCohortProxy) ready; verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), - ShardTransactionMessages.WriteDataReply.class, ShardTransactionMessages.MergeDataReply.class); + ShardTransactionMessages.WriteDataReply.class, ShardTransactionMessages.MergeDataReply.class, + ShardTransactionMessages.DeleteDataReply.class); verifyCohortFutures(proxy, getSystem().actorSelection(actorRef.path())); @@ -1028,21 +972,13 @@ public class TransactionProxyTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(mergeSerializedDataReply()).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedMergeData(nodeToWrite)); + expectFailedBatchedModifications(actorRef); - doReturn(Futures.failed(new TestException())).when(mockActorContext). - executeOperationAsync(eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite)); - - doReturn(readySerializedTxReply(actorRef.path().toString())).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), isA(ReadyTransaction.SERIALIZABLE_CLASS)); + expectReadyTransaction(actorRef); doReturn(false).when(mockActorContext).isPathLocal(actorRef.path().toString()); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - WRITE_ONLY); - - transactionProxy.merge(TestModel.TEST_PATH, nodeToWrite); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); @@ -1054,8 +990,7 @@ public class TransactionProxyTest { verifyCohortFutures(proxy, TestException.class); - verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), - MergeDataReply.class, TestException.class); + verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), TestException.class); } @Test @@ -1064,15 +999,13 @@ public class TransactionProxyTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(mergeSerializedDataReply()).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedMergeData(nodeToWrite)); + expectBatchedModifications(actorRef, 1); doReturn(Futures.failed(new TestException())).when(mockActorContext). executeOperationAsync(eq(actorSelection(actorRef)), isA(ReadyTransaction.SERIALIZABLE_CLASS)); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - WRITE_ONLY); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); transactionProxy.merge(TestModel.TEST_PATH, nodeToWrite); @@ -1083,7 +1016,7 @@ public class TransactionProxyTest { ThreePhaseCommitCohortProxy proxy = (ThreePhaseCommitCohortProxy) ready; verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), - MergeDataReply.class); + BatchedModificationsReply.class); verifyCohortFutures(proxy, TestException.class); } @@ -1094,8 +1027,7 @@ public class TransactionProxyTest { doReturn(Futures.failed(new PrimaryNotFoundException("mock"))).when( mockActorContext).findPrimaryShardAsync(anyString()); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - WRITE_ONLY); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); @@ -1120,15 +1052,13 @@ public class TransactionProxyTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync( - eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite)); + expectBatchedModifications(actorRef, 1); doReturn(Futures.successful(new Object())).when(mockActorContext). executeOperationAsync(eq(actorSelection(actorRef)), isA(ReadyTransaction.SERIALIZABLE_CLASS)); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - WRITE_ONLY); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); @@ -1159,8 +1089,7 @@ public class TransactionProxyTest { doReturn(readSerializedDataReply(null)).when(mockActorContext).executeOperationAsync( eq(actorSelection(actorRef)), eqSerializedReadData()); - TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, - READ_WRITE); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); transactionProxy.read(TestModel.TEST_PATH); @@ -1196,9 +1125,7 @@ public class TransactionProxyTest { String actorPath = "akka.tcp://system@127.0.0.1:2550/user/tx-actor"; CreateTransactionReply createTransactionReply = CreateTransactionReply.newBuilder() - .setTransactionId("txn-1") - .setTransactionActorPath(actorPath) - .build(); + .setTransactionId("txn-1").setTransactionActorPath(actorPath).build(); doReturn(Futures.successful(createTransactionReply)).when(mockActorContext). executeOperationAsync(eq(actorSystem.actorSelection(shardActorRef.path())), @@ -1239,7 +1166,7 @@ public class TransactionProxyTest { } @Test - public void testLocalTxActorWrite() throws Exception { + public void testLocalTxActorReady() throws Exception { ActorSystem actorSystem = getSystem(); ActorRef shardActorRef = actorSystem.actorOf(Props.create(DoNothingActor.class)); @@ -1250,48 +1177,26 @@ public class TransactionProxyTest { when(mockActorContext).findPrimaryShardAsync(eq(DefaultShardStrategy.DEFAULT_SHARD)); String actorPath = "akka.tcp://system@127.0.0.1:2550/user/tx-actor"; - CreateTransactionReply createTransactionReply = CreateTransactionReply.newBuilder() - .setTransactionId("txn-1") - .setTransactionActorPath(actorPath) - .build(); + CreateTransactionReply createTransactionReply = CreateTransactionReply.newBuilder(). + setTransactionId("txn-1").setTransactionActorPath(actorPath). + setMessageVersion(DataStoreVersions.CURRENT_VERSION).build(); doReturn(Futures.successful(createTransactionReply)).when(mockActorContext). - executeOperationAsync(eq(actorSystem.actorSelection(shardActorRef.path())), + executeOperationAsync(eq(actorSystem.actorSelection(shardActorRef.path())), eqCreateTransaction(memberName, WRITE_ONLY)); doReturn(true).when(mockActorContext).isPathLocal(actorPath); - NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - - doReturn(writeDataReply()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqWriteData(nodeToWrite)); + doReturn(batchedModificationsReply(1)).when(mockActorContext).executeOperationAsync( + any(ActorSelection.class), isA(BatchedModifications.class)); TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY); - transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); - - verify(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqWriteData(nodeToWrite)); - - //testing local merge - doReturn(mergeDataReply()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqMergeData(nodeToWrite)); - - transactionProxy.merge(TestModel.TEST_PATH, nodeToWrite); - - verify(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqMergeData(nodeToWrite)); - - - //testing local delete - doReturn(deleteDataReply()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqDeleteData()); - transactionProxy.delete(TestModel.TEST_PATH); - - verify(mockActorContext).executeOperationAsync(any(ActorSelection.class), eqDeleteData()); + NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); + transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), - WriteDataReply.class, MergeDataReply.class, DeleteDataReply.class); + BatchedModificationsReply.class); // testing ready doReturn(readyTxReply(shardActorRef.path().toString())).when(mockActorContext).executeOperationAsync( @@ -1332,10 +1237,9 @@ public class TransactionProxyTest { } String actorPath = "akka.tcp://system@127.0.0.1:2550/user/tx-actor"; - CreateTransactionReply createTransactionReply = CreateTransactionReply.newBuilder() - .setTransactionId("txn-1") - .setTransactionActorPath(actorPath) - .build(); + CreateTransactionReply createTransactionReply = CreateTransactionReply.newBuilder(). + setTransactionId("txn-1").setTransactionActorPath(actorPath). + setMessageVersion(DataStoreVersions.CURRENT_VERSION).build(); doReturn(Futures.successful(createTransactionReply)).when(mockActorContext). executeOperationAsync(eq(actorSystem.actorSelection(shardActorRef.path())), @@ -1351,9 +1255,9 @@ public class TransactionProxyTest { long end = System.nanoTime(); - Assert.assertTrue(String.format("took less time than expected %s was %s", - TimeUnit.SECONDS.toNanos(mockActorContext.getDatastoreContext().getOperationTimeoutInSeconds()), - (end-start)), (end - start) > TimeUnit.SECONDS.toNanos(mockActorContext.getDatastoreContext().getOperationTimeoutInSeconds())); + long expected = TimeUnit.SECONDS.toNanos(mockActorContext.getDatastoreContext().getOperationTimeoutInSeconds()); + Assert.assertTrue(String.format("Expected elapsed time: %s. Actual: %s", + expected, (end-start)), (end - start) > expected); } @@ -1379,10 +1283,9 @@ public class TransactionProxyTest { } String actorPath = "akka.tcp://system@127.0.0.1:2550/user/tx-actor"; - CreateTransactionReply createTransactionReply = CreateTransactionReply.newBuilder() - .setTransactionId("txn-1") - .setTransactionActorPath(actorPath) - .build(); + CreateTransactionReply createTransactionReply = CreateTransactionReply.newBuilder(). + setTransactionId("txn-1").setTransactionActorPath(actorPath). + setMessageVersion(DataStoreVersions.CURRENT_VERSION).build(); doReturn(Futures.successful(createTransactionReply)).when(mockActorContext). executeOperationAsync(eq(actorSystem.actorSelection(shardActorRef.path())), @@ -1398,9 +1301,9 @@ public class TransactionProxyTest { long end = System.nanoTime(); - Assert.assertTrue(String.format("took more time than expected %s was %s", - TimeUnit.SECONDS.toNanos(mockActorContext.getDatastoreContext().getOperationTimeoutInSeconds()), - (end-start)), (end - start) <= TimeUnit.SECONDS.toNanos(mockActorContext.getDatastoreContext().getOperationTimeoutInSeconds())); + long expected = TimeUnit.SECONDS.toNanos(mockActorContext.getDatastoreContext().getOperationTimeoutInSeconds()); + Assert.assertTrue(String.format("Expected elapsed time: %s. Actual: %s", + expected, (end-start)), (end - start) <= expected); } public void testWriteThrottling(boolean shardFound){ @@ -1410,8 +1313,7 @@ public class TransactionProxyTest { public void run(TransactionProxy transactionProxy) { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(incompleteFuture()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqWriteData(nodeToWrite)); + expectBatchedModifications(2); transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); @@ -1427,15 +1329,13 @@ public class TransactionProxyTest { public void run(TransactionProxy transactionProxy) { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(incompleteFuture()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqWriteData(nodeToWrite)); + expectIncompleteBatchedModifications(); transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); } }); - } @Test @@ -1446,8 +1346,7 @@ public class TransactionProxyTest { public void run(TransactionProxy transactionProxy) { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(incompleteFuture()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqWriteData(nodeToWrite)); + expectBatchedModifications(2); transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); @@ -1465,15 +1364,13 @@ public class TransactionProxyTest { public void run(TransactionProxy transactionProxy) { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqSerializedWriteData(nodeToWrite)); + expectBatchedModifications(2); transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); } }); - } @Test @@ -1484,8 +1381,7 @@ public class TransactionProxyTest { public void run(TransactionProxy transactionProxy) { NormalizedNode nodeToMerge = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(incompleteFuture()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqMergeData(nodeToMerge)); + expectIncompleteBatchedModifications(); transactionProxy.merge(TestModel.TEST_PATH, nodeToMerge); @@ -1502,8 +1398,7 @@ public class TransactionProxyTest { public void run(TransactionProxy transactionProxy) { NormalizedNode nodeToMerge = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(incompleteFuture()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqMergeData(nodeToMerge)); + expectBatchedModifications(2); transactionProxy.merge(TestModel.TEST_PATH, nodeToMerge); @@ -1519,8 +1414,7 @@ public class TransactionProxyTest { public void run(TransactionProxy transactionProxy) { NormalizedNode nodeToMerge = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(mergeDataReply()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqMergeData(nodeToMerge)); + expectBatchedModifications(2); transactionProxy.merge(TestModel.TEST_PATH, nodeToMerge); @@ -1536,8 +1430,7 @@ public class TransactionProxyTest { throttleOperation(new TransactionProxyOperation() { @Override public void run(TransactionProxy transactionProxy) { - doReturn(incompleteFuture()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqDeleteData()); + expectIncompleteBatchedModifications(); transactionProxy.delete(TestModel.TEST_PATH); @@ -1553,8 +1446,7 @@ public class TransactionProxyTest { completeOperation(new TransactionProxyOperation() { @Override public void run(TransactionProxy transactionProxy) { - doReturn(incompleteFuture()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqDeleteData()); + expectBatchedModifications(2); transactionProxy.delete(TestModel.TEST_PATH); @@ -1568,8 +1460,7 @@ public class TransactionProxyTest { completeOperation(new TransactionProxyOperation() { @Override public void run(TransactionProxy transactionProxy) { - doReturn(deleteDataReply()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqDeleteData()); + expectBatchedModifications(2); transactionProxy.delete(TestModel.TEST_PATH); @@ -1687,8 +1578,7 @@ public class TransactionProxyTest { public void run(TransactionProxy transactionProxy) { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - doReturn(incompleteFuture()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqWriteData(nodeToWrite)); + expectBatchedModifications(1); doReturn(incompleteFuture()).when(mockActorContext).executeOperationAsync( any(ActorSelection.class), any(ReadyTransaction.class)); @@ -1709,11 +1599,7 @@ public class TransactionProxyTest { NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); NormalizedNode carsNode = ImmutableNodes.containerNode(CarsModel.BASE_QNAME); - doReturn(writeDataReply()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqWriteData(nodeToWrite)); - - doReturn(incompleteFuture()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), eqWriteData(carsNode)); + expectBatchedModifications(2); doReturn(incompleteFuture()).when(mockActorContext).executeOperationAsync( any(ActorSelection.class), any(ReadyTransaction.class)); @@ -1726,4 +1612,203 @@ public class TransactionProxyTest { } }, 2, true); } + + @Test + public void testModificationOperationBatching() throws Throwable { + int shardBatchedModificationCount = 3; + doReturn(dataStoreContextBuilder.shardBatchedModificationCount(shardBatchedModificationCount).build()). + when(mockActorContext).getDatastoreContext(); + + ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_WRITE); + + expectBatchedModifications(actorRef, shardBatchedModificationCount); + + expectReadyTransaction(actorRef); + + YangInstanceIdentifier writePath1 = TestModel.TEST_PATH; + NormalizedNode writeNode1 = ImmutableNodes.containerNode(TestModel.TEST_QNAME); + + YangInstanceIdentifier writePath2 = TestModel.OUTER_LIST_PATH; + NormalizedNode writeNode2 = ImmutableNodes.containerNode(TestModel.OUTER_LIST_QNAME); + + YangInstanceIdentifier writePath3 = TestModel.INNER_LIST_PATH; + NormalizedNode writeNode3 = ImmutableNodes.containerNode(TestModel.INNER_LIST_QNAME); + + YangInstanceIdentifier mergePath1 = TestModel.TEST_PATH; + NormalizedNode mergeNode1 = ImmutableNodes.containerNode(TestModel.TEST_QNAME); + + YangInstanceIdentifier mergePath2 = TestModel.OUTER_LIST_PATH; + NormalizedNode mergeNode2 = ImmutableNodes.containerNode(TestModel.OUTER_LIST_QNAME); + + YangInstanceIdentifier mergePath3 = TestModel.INNER_LIST_PATH; + NormalizedNode mergeNode3 = ImmutableNodes.containerNode(TestModel.INNER_LIST_QNAME); + + YangInstanceIdentifier deletePath1 = TestModel.TEST_PATH; + YangInstanceIdentifier deletePath2 = TestModel.OUTER_LIST_PATH; + + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); + + transactionProxy.write(writePath1, writeNode1); + transactionProxy.write(writePath2, writeNode2); + transactionProxy.delete(deletePath1); + transactionProxy.merge(mergePath1, mergeNode1); + transactionProxy.merge(mergePath2, mergeNode2); + transactionProxy.write(writePath3, writeNode3); + transactionProxy.merge(mergePath3, mergeNode3); + transactionProxy.delete(deletePath2); + + // This sends the last batch. + transactionProxy.ready(); + + List batchedModifications = captureBatchedModifications(actorRef); + assertEquals("Captured BatchedModifications count", 3, batchedModifications.size()); + + verifyBatchedModifications(batchedModifications.get(0), new WriteModification(writePath1, writeNode1), + new WriteModification(writePath2, writeNode2), new DeleteModification(deletePath1)); + + verifyBatchedModifications(batchedModifications.get(1), new MergeModification(mergePath1, mergeNode1), + new MergeModification(mergePath2, mergeNode2), new WriteModification(writePath3, writeNode3)); + + verifyBatchedModifications(batchedModifications.get(2), new MergeModification(mergePath3, mergeNode3), + new DeleteModification(deletePath2)); + + verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), + BatchedModificationsReply.class, BatchedModificationsReply.class, BatchedModificationsReply.class); + } + + @Test + public void testModificationOperationBatchingWithInterleavedReads() throws Throwable { + int shardBatchedModificationCount = 10; + doReturn(dataStoreContextBuilder.shardBatchedModificationCount(shardBatchedModificationCount).build()). + when(mockActorContext).getDatastoreContext(); + + ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_WRITE); + + expectBatchedModifications(actorRef, shardBatchedModificationCount); + + YangInstanceIdentifier writePath1 = TestModel.TEST_PATH; + NormalizedNode writeNode1 = ImmutableNodes.containerNode(TestModel.TEST_QNAME); + + YangInstanceIdentifier writePath2 = TestModel.OUTER_LIST_PATH; + NormalizedNode writeNode2 = ImmutableNodes.containerNode(TestModel.OUTER_LIST_QNAME); + + YangInstanceIdentifier mergePath1 = TestModel.TEST_PATH; + NormalizedNode mergeNode1 = ImmutableNodes.containerNode(TestModel.TEST_QNAME); + + YangInstanceIdentifier mergePath2 = TestModel.INNER_LIST_PATH; + NormalizedNode mergeNode2 = ImmutableNodes.containerNode(TestModel.INNER_LIST_QNAME); + + YangInstanceIdentifier deletePath = TestModel.OUTER_LIST_PATH; + + doReturn(readSerializedDataReply(writeNode2)).when(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), eqSerializedReadData(writePath2)); + + doReturn(readSerializedDataReply(mergeNode2)).when(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), eqSerializedReadData(mergePath2)); + + doReturn(dataExistsSerializedReply(true)).when(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), eqSerializedDataExists()); + + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, READ_WRITE); + + transactionProxy.write(writePath1, writeNode1); + transactionProxy.write(writePath2, writeNode2); + + Optional> readOptional = transactionProxy.read(writePath2). + get(5, TimeUnit.SECONDS); + + assertEquals("NormalizedNode isPresent", true, readOptional.isPresent()); + assertEquals("Response NormalizedNode", writeNode2, readOptional.get()); + + transactionProxy.merge(mergePath1, mergeNode1); + transactionProxy.merge(mergePath2, mergeNode2); + + readOptional = transactionProxy.read(mergePath2).get(5, TimeUnit.SECONDS); + + transactionProxy.delete(deletePath); + + Boolean exists = transactionProxy.exists(TestModel.TEST_PATH).checkedGet(); + assertEquals("Exists response", true, exists); + + assertEquals("NormalizedNode isPresent", true, readOptional.isPresent()); + assertEquals("Response NormalizedNode", mergeNode2, readOptional.get()); + + List batchedModifications = captureBatchedModifications(actorRef); + assertEquals("Captured BatchedModifications count", 3, batchedModifications.size()); + + verifyBatchedModifications(batchedModifications.get(0), new WriteModification(writePath1, writeNode1), + new WriteModification(writePath2, writeNode2)); + + verifyBatchedModifications(batchedModifications.get(1), new MergeModification(mergePath1, mergeNode1), + new MergeModification(mergePath2, mergeNode2)); + + verifyBatchedModifications(batchedModifications.get(2), new DeleteModification(deletePath)); + + InOrder inOrder = Mockito.inOrder(mockActorContext); + inOrder.verify(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), isA(BatchedModifications.class)); + + inOrder.verify(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), eqSerializedReadData(writePath2)); + + inOrder.verify(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), isA(BatchedModifications.class)); + + inOrder.verify(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), eqSerializedReadData(mergePath2)); + + inOrder.verify(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), isA(BatchedModifications.class)); + + inOrder.verify(mockActorContext).executeOperationAsync( + eq(actorSelection(actorRef)), eqSerializedDataExists()); + + verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(), + BatchedModificationsReply.class, BatchedModificationsReply.class, BatchedModificationsReply.class); + } + + private List captureBatchedModifications(ActorRef actorRef) { + ArgumentCaptor batchedModificationsCaptor = + ArgumentCaptor.forClass(BatchedModifications.class); + verify(mockActorContext, Mockito.atLeastOnce()).executeOperationAsync( + eq(actorSelection(actorRef)), batchedModificationsCaptor.capture()); + + List batchedModifications = filterCaptured( + batchedModificationsCaptor, BatchedModifications.class); + return batchedModifications; + } + + private List filterCaptured(ArgumentCaptor captor, Class type) { + List captured = new ArrayList<>(); + for(T c: captor.getAllValues()) { + if(type.isInstance(c)) { + captured.add(c); + } + } + + return captured; + } + + private void verifyOneBatchedModification(ActorRef actorRef, Modification expected) { + List batchedModifications = captureBatchedModifications(actorRef); + assertEquals("Captured BatchedModifications count", 1, batchedModifications.size()); + + verifyBatchedModifications(batchedModifications.get(0), expected); + } + + private void verifyBatchedModifications(Object message, Modification... expected) { + assertEquals("Message type", BatchedModifications.class, message.getClass()); + BatchedModifications batchedModifications = (BatchedModifications)message; + assertEquals("BatchedModifications size", expected.length, batchedModifications.getModifications().size()); + for(int i = 0; i < batchedModifications.getModifications().size(); i++) { + Modification actual = batchedModifications.getModifications().get(i); + assertEquals("Modification type", expected[i].getClass(), actual.getClass()); + assertEquals("getPath", ((AbstractModification)expected[i]).getPath(), + ((AbstractModification)actual).getPath()); + if(actual instanceof WriteModification) { + assertEquals("getData", ((WriteModification)expected[i]).getData(), + ((WriteModification)actual).getData()); + } + } + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/BatchedModificationsTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/BatchedModificationsTest.java new file mode 100644 index 0000000000..15d2eea598 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/BatchedModificationsTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore.messages; + +import static org.junit.Assert.assertEquals; +import java.io.Serializable; +import org.apache.commons.lang.SerializationUtils; +import org.junit.Test; +import org.opendaylight.controller.cluster.datastore.DataStoreVersions; +import org.opendaylight.controller.cluster.datastore.modification.DeleteModification; +import org.opendaylight.controller.cluster.datastore.modification.MergeModification; +import org.opendaylight.controller.cluster.datastore.modification.WriteModification; +import org.opendaylight.controller.md.cluster.datastore.model.TestModel; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; + +/** + * Unit tests for BatchedModifications. + * + * @author Thomas Pantelis + */ +public class BatchedModificationsTest { + + @Test + public void testSerialization() { + YangInstanceIdentifier writePath = TestModel.TEST_PATH; + NormalizedNode writeData = ImmutableContainerNodeBuilder.create().withNodeIdentifier( + new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME)). + withChild(ImmutableNodes.leafNode(TestModel.DESC_QNAME, "foo")).build(); + + YangInstanceIdentifier mergePath = TestModel.OUTER_LIST_PATH; + NormalizedNode mergeData = ImmutableContainerNodeBuilder.create().withNodeIdentifier( + new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME)).build(); + + YangInstanceIdentifier deletePath = TestModel.TEST_PATH; + + BatchedModifications batched = new BatchedModifications(DataStoreVersions.CURRENT_VERSION); + batched.addModification(new WriteModification(writePath, writeData)); + batched.addModification(new MergeModification(mergePath, mergeData)); + batched.addModification(new DeleteModification(deletePath)); + + BatchedModifications clone = (BatchedModifications) SerializationUtils.clone( + (Serializable) batched.toSerializable()); + + assertEquals("getVersion", DataStoreVersions.CURRENT_VERSION, clone.getVersion()); + + assertEquals("getModifications size", 3, clone.getModifications().size()); + + WriteModification write = (WriteModification)clone.getModifications().get(0); + assertEquals("getVersion", DataStoreVersions.CURRENT_VERSION, write.getVersion()); + assertEquals("getPath", writePath, write.getPath()); + assertEquals("getData", writeData, write.getData()); + + MergeModification merge = (MergeModification)clone.getModifications().get(1); + assertEquals("getVersion", DataStoreVersions.CURRENT_VERSION, merge.getVersion()); + assertEquals("getPath", mergePath, merge.getPath()); + assertEquals("getData", mergeData, merge.getData()); + + DeleteModification delete = (DeleteModification)clone.getModifications().get(2); + assertEquals("getVersion", DataStoreVersions.CURRENT_VERSION, delete.getVersion()); + assertEquals("getPath", deletePath, delete.getPath()); + } + + @Test + public void testBatchedModificationsReplySerialization() { + BatchedModificationsReply clone = (BatchedModificationsReply) SerializationUtils.clone( + (Serializable) new BatchedModificationsReply(100).toSerializable()); + assertEquals("getNumBatched", 100, clone.getNumBatched()); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/DeleteDataTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/DeleteDataTest.java index e950b78ab7..97bade152e 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/DeleteDataTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/DeleteDataTest.java @@ -22,21 +22,22 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; * * @author Thomas Pantelis */ +@Deprecated public class DeleteDataTest { @Test public void testSerialization() { YangInstanceIdentifier path = TestModel.TEST_PATH; - DeleteData expected = new DeleteData(path); + DeleteData expected = new DeleteData(path, DataStoreVersions.CURRENT_VERSION); - Object serialized = expected.toSerializable(DataStoreVersions.CURRENT_VERSION); + Object serialized = expected.toSerializable(); assertEquals("Serialized type", DeleteData.class, serialized.getClass()); assertEquals("Version", DataStoreVersions.CURRENT_VERSION, ((DeleteData)serialized).getVersion()); Object clone = SerializationUtils.clone((Serializable) serialized); - assertEquals("Version", DataStoreVersions.CURRENT_VERSION, ((DeleteData)clone).getVersion()); DeleteData actual = DeleteData.fromSerializable(clone); + assertEquals("getVersion", DataStoreVersions.CURRENT_VERSION, actual.getVersion()); assertEquals("getPath", expected.getPath(), actual.getPath()); } @@ -58,9 +59,9 @@ public class DeleteDataTest { public void testSerializationWithHeliumR1Version() throws Exception { YangInstanceIdentifier path = TestModel.TEST_PATH; - DeleteData expected = new DeleteData(path); + DeleteData expected = new DeleteData(path, DataStoreVersions.HELIUM_1_VERSION); - Object serialized = expected.toSerializable(DataStoreVersions.HELIUM_1_VERSION); + Object serialized = expected.toSerializable(); assertEquals("Serialized type", ShardTransactionMessages.DeleteData.class, serialized.getClass()); DeleteData actual = DeleteData.fromSerializable(SerializationUtils.clone((Serializable) serialized)); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/MergeDataTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/MergeDataTest.java index 5b40afdff8..011d22798e 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/MergeDataTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/MergeDataTest.java @@ -14,6 +14,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; +@Deprecated public class MergeDataTest { @Test @@ -23,15 +24,15 @@ public class MergeDataTest { new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME)). withChild(ImmutableNodes.leafNode(TestModel.DESC_QNAME, "foo")).build(); - MergeData expected = new MergeData(path, data); + MergeData expected = new MergeData(path, data, DataStoreVersions.CURRENT_VERSION); - Object serialized = expected.toSerializable(DataStoreVersions.CURRENT_VERSION); + Object serialized = expected.toSerializable(); assertEquals("Serialized type", MergeData.class, serialized.getClass()); assertEquals("Version", DataStoreVersions.CURRENT_VERSION, ((MergeData)serialized).getVersion()); Object clone = SerializationUtils.clone((Serializable) serialized); - assertEquals("Version", DataStoreVersions.CURRENT_VERSION, ((MergeData)clone).getVersion()); MergeData actual = MergeData.fromSerializable(clone); + assertEquals("Version", DataStoreVersions.CURRENT_VERSION, actual.getVersion()); assertEquals("getPath", expected.getPath(), actual.getPath()); assertEquals("getData", expected.getData(), actual.getData()); } @@ -58,9 +59,9 @@ public class MergeDataTest { new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME)). withChild(ImmutableNodes.leafNode(TestModel.DESC_QNAME, "foo")).build(); - MergeData expected = new MergeData(path, data); + MergeData expected = new MergeData(path, data, DataStoreVersions.HELIUM_1_VERSION); - Object serialized = expected.toSerializable(DataStoreVersions.HELIUM_1_VERSION); + Object serialized = expected.toSerializable(); assertEquals("Serialized type", ShardTransactionMessages.MergeData.class, serialized.getClass()); MergeData actual = MergeData.fromSerializable(SerializationUtils.clone((Serializable) serialized)); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/ReadDataReplyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/ReadDataReplyTest.java index 8ce73296c1..7ad45a6170 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/ReadDataReplyTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/ReadDataReplyTest.java @@ -32,13 +32,14 @@ public class ReadDataReplyTest { new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME)). withChild(ImmutableNodes.leafNode(TestModel.DESC_QNAME, "foo")).build(); - ReadDataReply expected = new ReadDataReply(data); + ReadDataReply expected = new ReadDataReply(data, DataStoreVersions.CURRENT_VERSION); - Object serialized = expected.toSerializable(DataStoreVersions.CURRENT_VERSION); + Object serialized = expected.toSerializable(); assertEquals("Serialized type", ReadDataReply.class, serialized.getClass()); ReadDataReply actual = ReadDataReply.fromSerializable(SerializationUtils.clone( (Serializable) serialized)); + assertEquals("getVersion", DataStoreVersions.CURRENT_VERSION, actual.getVersion()); assertEquals("getNormalizedNode", expected.getNormalizedNode(), actual.getNormalizedNode()); } @@ -60,9 +61,9 @@ public class ReadDataReplyTest { new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME)). withChild(ImmutableNodes.leafNode(TestModel.DESC_QNAME, "foo")).build(); - ReadDataReply expected = new ReadDataReply(data); + ReadDataReply expected = new ReadDataReply(data, DataStoreVersions.HELIUM_1_VERSION); - Object serialized = expected.toSerializable(DataStoreVersions.HELIUM_1_VERSION); + Object serialized = expected.toSerializable(); assertEquals("Serialized type", ShardTransactionMessages.ReadDataReply.class, serialized.getClass()); ReadDataReply actual = ReadDataReply.fromSerializable(SerializationUtils.clone( diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/WriteDataTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/WriteDataTest.java index 90a76f229e..8148c9ca95 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/WriteDataTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/messages/WriteDataTest.java @@ -26,6 +26,7 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableCo * * @author Thomas Pantelis */ +@Deprecated public class WriteDataTest { @Test @@ -35,15 +36,15 @@ public class WriteDataTest { new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME)). withChild(ImmutableNodes.leafNode(TestModel.DESC_QNAME, "foo")).build(); - WriteData expected = new WriteData(path, data); + WriteData expected = new WriteData(path, data, DataStoreVersions.CURRENT_VERSION); - Object serialized = expected.toSerializable(DataStoreVersions.CURRENT_VERSION); + Object serialized = expected.toSerializable(); assertEquals("Serialized type", WriteData.class, serialized.getClass()); assertEquals("Version", DataStoreVersions.CURRENT_VERSION, ((WriteData)serialized).getVersion()); Object clone = SerializationUtils.clone((Serializable) serialized); - assertEquals("Version", DataStoreVersions.CURRENT_VERSION, ((WriteData)clone).getVersion()); WriteData actual = WriteData.fromSerializable(clone); + assertEquals("Version", DataStoreVersions.CURRENT_VERSION, actual.getVersion()); assertEquals("getPath", expected.getPath(), actual.getPath()); assertEquals("getData", expected.getData(), actual.getData()); } @@ -69,9 +70,9 @@ public class WriteDataTest { new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME)). withChild(ImmutableNodes.leafNode(TestModel.DESC_QNAME, "foo")).build(); - WriteData expected = new WriteData(path, data); + WriteData expected = new WriteData(path, data, DataStoreVersions.HELIUM_1_VERSION); - Object serialized = expected.toSerializable(DataStoreVersions.HELIUM_1_VERSION); + Object serialized = expected.toSerializable(); assertEquals("Serialized type", ShardTransactionMessages.WriteData.class, serialized.getClass()); WriteData actual = WriteData.fromSerializable(SerializationUtils.clone((Serializable) serialized)); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModificationTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModificationTest.java index b9d44b2586..e2acaa8d10 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModificationTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/modification/MutableCompositeModificationTest.java @@ -7,6 +7,7 @@ import com.google.common.base.Stopwatch; import org.apache.commons.lang.SerializationUtils; import org.junit.Ignore; import org.junit.Test; +import org.opendaylight.controller.cluster.datastore.DataStoreVersions; import org.opendaylight.controller.md.cluster.datastore.model.TestModel; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -53,17 +54,22 @@ public class MutableCompositeModificationTest extends AbstractModificationTest { MutableCompositeModification clone = (MutableCompositeModification) SerializationUtils.clone(compositeModification); + assertEquals("getVersion", DataStoreVersions.CURRENT_VERSION, clone.getVersion()); + assertEquals("getModifications size", 3, clone.getModifications().size()); WriteModification write = (WriteModification)clone.getModifications().get(0); + assertEquals("getVersion", DataStoreVersions.CURRENT_VERSION, write.getVersion()); assertEquals("getPath", writePath, write.getPath()); assertEquals("getData", writeData, write.getData()); MergeModification merge = (MergeModification)clone.getModifications().get(1); + assertEquals("getVersion", DataStoreVersions.CURRENT_VERSION, merge.getVersion()); assertEquals("getPath", mergePath, merge.getPath()); assertEquals("getData", mergeData, merge.getData()); DeleteModification delete = (DeleteModification)clone.getModifications().get(2); + assertEquals("getVersion", DataStoreVersions.CURRENT_VERSION, delete.getVersion()); assertEquals("getPath", deletePath, delete.getPath()); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java index eae46da2ee..3c6a0cef5c 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java @@ -1,17 +1,20 @@ package org.opendaylight.controller.cluster.datastore.utils; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import akka.actor.ActorRef; import akka.actor.ActorSelection; +import akka.actor.ActorSystem; import akka.actor.Address; import akka.actor.Props; import akka.actor.UntypedActor; import akka.japi.Creator; import akka.testkit.JavaTestKit; import com.google.common.base.Optional; +import com.typesafe.config.ConfigFactory; import java.util.concurrent.TimeUnit; import org.apache.commons.lang.time.StopWatch; import org.junit.Test; @@ -299,4 +302,41 @@ public class ActorContextTest extends AbstractActorTest{ assertTrue("did not take as much time as expected", watch.getTime() > 1000); } + + @Test + public void testClientDispatcherIsGlobalDispatcher(){ + + DatastoreContext mockDataStoreContext = mock(DatastoreContext.class); + + doReturn(155L).when(mockDataStoreContext).getTransactionCreationInitialRateLimit(); + doReturn("config").when(mockDataStoreContext).getDataStoreType(); + + ActorContext actorContext = + new ActorContext(getSystem(), mock(ActorRef.class), mock(ClusterWrapper.class), + mock(Configuration.class), mockDataStoreContext); + + assertEquals(getSystem().dispatchers().defaultGlobalDispatcher(), actorContext.getClientDispatcher()); + + } + + @Test + public void testClientDispatcherIsNotGlobalDispatcher(){ + + DatastoreContext mockDataStoreContext = mock(DatastoreContext.class); + + doReturn(155L).when(mockDataStoreContext).getTransactionCreationInitialRateLimit(); + doReturn("config").when(mockDataStoreContext).getDataStoreType(); + + ActorSystem actorSystem = ActorSystem.create("with-custom-dispatchers", ConfigFactory.load("application-with-custom-dispatchers.conf")); + + ActorContext actorContext = + new ActorContext(actorSystem, mock(ActorRef.class), mock(ClusterWrapper.class), + mock(Configuration.class), mockDataStoreContext); + + assertNotEquals(actorSystem.dispatchers().defaultGlobalDispatcher(), actorContext.getClientDispatcher()); + + actorSystem.shutdown(); + + } + } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/DispatchersTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/DispatchersTest.java new file mode 100644 index 0000000000..85a0cac3da --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/DispatchersTest.java @@ -0,0 +1,81 @@ +package org.opendaylight.controller.cluster.datastore.utils; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import akka.dispatch.MessageDispatcher; +import org.junit.Test; + +public class DispatchersTest { + + @Test + public void testGetDefaultDispatcherPath(){ + akka.dispatch.Dispatchers mockDispatchers = mock(akka.dispatch.Dispatchers.class); + doReturn(false).when(mockDispatchers).hasDispatcher(anyString()); + Dispatchers dispatchers = new Dispatchers(mockDispatchers); + + for(Dispatchers.DispatcherType type : Dispatchers.DispatcherType.values()) { + assertEquals(Dispatchers.DEFAULT_DISPATCHER_PATH, + dispatchers.getDispatcherPath(type)); + } + + } + + @Test + public void testGetDefaultDispatcher(){ + akka.dispatch.Dispatchers mockDispatchers = mock(akka.dispatch.Dispatchers.class); + MessageDispatcher mockGlobalDispatcher = mock(MessageDispatcher.class); + doReturn(false).when(mockDispatchers).hasDispatcher(anyString()); + doReturn(mockGlobalDispatcher).when(mockDispatchers).defaultGlobalDispatcher(); + Dispatchers dispatchers = new Dispatchers(mockDispatchers); + + for(Dispatchers.DispatcherType type : Dispatchers.DispatcherType.values()) { + assertEquals(mockGlobalDispatcher, + dispatchers.getDispatcher(type)); + } + + } + + @Test + public void testGetDispatcherPath(){ + akka.dispatch.Dispatchers mockDispatchers = mock(akka.dispatch.Dispatchers.class); + doReturn(true).when(mockDispatchers).hasDispatcher(anyString()); + Dispatchers dispatchers = new Dispatchers(mockDispatchers); + + assertEquals(Dispatchers.CLIENT_DISPATCHER_PATH, + dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Client)); + + assertEquals(Dispatchers.TXN_DISPATCHER_PATH, + dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Transaction)); + + assertEquals(Dispatchers.SHARD_DISPATCHER_PATH, + dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Shard)); + + assertEquals(Dispatchers.NOTIFICATION_DISPATCHER_PATH, + dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Notification)); + + } + + @Test + public void testGetDispatcher(){ + akka.dispatch.Dispatchers mockDispatchers = mock(akka.dispatch.Dispatchers.class); + MessageDispatcher mockDispatcher = mock(MessageDispatcher.class); + doReturn(true).when(mockDispatchers).hasDispatcher(anyString()); + doReturn(mockDispatcher).when(mockDispatchers).lookup(anyString()); + Dispatchers dispatchers = new Dispatchers(mockDispatchers); + + assertEquals(Dispatchers.CLIENT_DISPATCHER_PATH, + dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Client)); + + assertEquals(Dispatchers.TXN_DISPATCHER_PATH, + dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Transaction)); + + assertEquals(Dispatchers.SHARD_DISPATCHER_PATH, + dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Shard)); + + assertEquals(Dispatchers.NOTIFICATION_DISPATCHER_PATH, + dispatchers.getDispatcherPath(Dispatchers.DispatcherType.Notification)); + + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MessageTrackerTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MessageTrackerTest.java new file mode 100644 index 0000000000..a125b49a5a --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MessageTrackerTest.java @@ -0,0 +1,188 @@ +package org.opendaylight.controller.cluster.datastore.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import com.google.common.util.concurrent.Uninterruptibles; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MessageTrackerTest { + + private final Logger LOG = LoggerFactory.getLogger(getClass()); + + private class Foo {} + + @Test + public void testNoTracking(){ + MessageTracker messageTracker = new MessageTracker(Foo.class, 10); + + MessageTracker.Context context1 = messageTracker.received(new Foo()); + context1.done(); + + Uninterruptibles.sleepUninterruptibly(20, TimeUnit.MILLISECONDS); + + MessageTracker.Context context2 = messageTracker.received(new Foo()); + context2.done(); + + } + + @Test + public void testFailedExpectationOnTracking(){ + MessageTracker messageTracker = new MessageTracker(Foo.class, 10); + messageTracker.begin(); + + MessageTracker.Context context1 = messageTracker.received(new Foo()); + context1.done(); + + Uninterruptibles.sleepUninterruptibly(20, TimeUnit.MILLISECONDS); + + MessageTracker.Context context2 = messageTracker.received(new Foo()); + Assert.assertEquals(true, context2.error().isPresent()); + Assert.assertEquals(0, context2.error().get().getMessageProcessingTimesSinceLastExpectedMessage().size()); + + } + + @Test + public void testFailedExpectationOnTrackingWithMessagesInBetween(){ + MessageTracker messageTracker = new MessageTracker(Foo.class, 10); + messageTracker.begin(); + + MessageTracker.Context context1 = messageTracker.received(new Foo()); + context1.done(); + + messageTracker.received("A").done(); + messageTracker.received(Long.valueOf(10)).done(); + MessageTracker.Context c = messageTracker.received(Integer.valueOf(100)); + + Uninterruptibles.sleepUninterruptibly(20, TimeUnit.MILLISECONDS); + + c.done(); + + MessageTracker.Context context2 = messageTracker.received(new Foo()); + + Assert.assertEquals(true, context2.error().isPresent()); + + MessageTracker.Error error = context2.error().get(); + + List messageProcessingTimes = + error.getMessageProcessingTimesSinceLastExpectedMessage(); + + Assert.assertEquals(3, messageProcessingTimes.size()); + + Assert.assertEquals(String.class, messageProcessingTimes.get(0).getMessageClass()); + Assert.assertEquals(Long.class, messageProcessingTimes.get(1).getMessageClass()); + Assert.assertEquals(Integer.class, messageProcessingTimes.get(2).getMessageClass()); + Assert.assertTrue(messageProcessingTimes.get(2).getElapsedTimeInNanos() > TimeUnit.MILLISECONDS.toNanos(10)); + Assert.assertEquals(Foo.class, error.getLastExpectedMessage().getClass()); + Assert.assertEquals(Foo.class, error.getCurrentExpectedMessage().getClass()); + + LOG.error("An error occurred : {}" , error); + + } + + + @Test + public void testMetExpectationOnTracking(){ + MessageTracker messageTracker = new MessageTracker(Foo.class, 10); + messageTracker.begin(); + + MessageTracker.Context context1 = messageTracker.received(new Foo()); + context1.done(); + + Uninterruptibles.sleepUninterruptibly(1, TimeUnit.MILLISECONDS); + + MessageTracker.Context context2 = messageTracker.received(new Foo()); + Assert.assertEquals(false, context2.error().isPresent()); + + } + + @Test + public void testIllegalStateExceptionWhenDoneIsNotCalledWhileTracking(){ + MessageTracker messageTracker = new MessageTracker(Foo.class, 10); + messageTracker.begin(); + + messageTracker.received(new Foo()); + + try { + messageTracker.received(new Foo()); + fail("Expected an IllegalStateException"); + } catch (IllegalStateException e){ + + } + } + + @Test + public void testNoIllegalStateExceptionWhenDoneIsNotCalledWhileNotTracking(){ + MessageTracker messageTracker = new MessageTracker(Foo.class, 10); + + messageTracker.received(new Foo()); + messageTracker.received(new Foo()); + } + + @Test + public void testDelayInFirstExpectedMessageArrival(){ + + MessageTracker messageTracker = new MessageTracker(Foo.class, 10); + messageTracker.begin(); + + Uninterruptibles.sleepUninterruptibly(20, TimeUnit.MILLISECONDS); + + MessageTracker.Context context = messageTracker.received(new Foo()); + + Assert.assertEquals(true, context.error().isPresent()); + + MessageTracker.Error error = context.error().get(); + + Assert.assertEquals(null, error.getLastExpectedMessage()); + Assert.assertEquals(Foo.class, error.getCurrentExpectedMessage().getClass()); + + String errorString = error.toString(); + Assert.assertTrue(errorString.contains("Last Expected Message = null")); + + LOG.error("An error occurred : {}", error); + } + + @Test + public void testCallingBeginDoesNotResetWatch(){ + MessageTracker messageTracker = new MessageTracker(Foo.class, 10); + messageTracker.begin(); + + Uninterruptibles.sleepUninterruptibly(20, TimeUnit.MILLISECONDS); + + messageTracker.begin(); + + MessageTracker.Context context = messageTracker.received(new Foo()); + + Assert.assertEquals(true, context.error().isPresent()); + + } + + @Test + public void testMessagesSinceLastExpectedMessage(){ + + MessageTracker messageTracker = new MessageTracker(Foo.class, 10); + messageTracker.begin(); + + MessageTracker.Context context1 = messageTracker.received(Integer.valueOf(45)).done(); + + Assert.assertEquals(false, context1.error().isPresent()); + + MessageTracker.Context context2 = messageTracker.received(Long.valueOf(45)).done(); + + Assert.assertEquals(false, context2.error().isPresent()); + + List processingTimeList = + messageTracker.getMessagesSinceLastExpectedMessage(); + + Assert.assertEquals(2, processingTimeList.size()); + + assertEquals(Integer.class, processingTimeList.get(0).getMessageClass()); + assertEquals(Long.class, processingTimeList.get(1).getMessageClass()); + + } + +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/TestModel.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/TestModel.java index 67fa0960cb..9761ed8615 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/TestModel.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/TestModel.java @@ -31,7 +31,10 @@ public class TestModel { private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang"; public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.of(TEST_QNAME); - public static final YangInstanceIdentifier OUTER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH).node(OUTER_LIST_QNAME).build(); + public static final YangInstanceIdentifier OUTER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH). + node(OUTER_LIST_QNAME).build(); + public static final YangInstanceIdentifier INNER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH). + node(OUTER_LIST_QNAME).node(INNER_LIST_QNAME).build(); public static final QName TWO_QNAME = QName.create(TEST_QNAME,"two"); public static final QName THREE_QNAME = QName.create(TEST_QNAME,"three"); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application-with-custom-dispatchers.conf b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application-with-custom-dispatchers.conf new file mode 100644 index 0000000000..32c55a65f6 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application-with-custom-dispatchers.conf @@ -0,0 +1,116 @@ +akka { + persistence.snapshot-store.plugin = "in-memory-snapshot-store" + persistence.journal.plugin = "in-memory-journal" + + loggers = ["akka.testkit.TestEventListener", "akka.event.slf4j.Slf4jLogger"] + + actor { + serializers { + java = "akka.serialization.JavaSerializer" + proto = "akka.remote.serialization.ProtobufSerializer" + } + + serialization-bindings { + "org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification" = java + "com.google.protobuf.Message" = proto + + } + } +} + +in-memory-journal { + class = "org.opendaylight.controller.cluster.datastore.utils.InMemoryJournal" +} + +in-memory-snapshot-store { + # Class name of the plugin. + class = "org.opendaylight.controller.cluster.datastore.utils.InMemorySnapshotStore" + # Dispatcher for the plugin actor. + plugin-dispatcher = "akka.persistence.dispatchers.default-plugin-dispatcher" +} + +bounded-mailbox { + mailbox-type = "org.opendaylight.controller.cluster.common.actor.MeteredBoundedMailbox" + mailbox-capacity = 1000 + mailbox-push-timeout-time = 100ms +} + +client-dispatcher { + # Dispatcher is the name of the event-based dispatcher + type = Dispatcher + # What kind of ExecutionService to use + executor = "fork-join-executor" + # Configuration for the fork join pool + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 2 + # Parallelism (threads) ... ceil(available processors * factor) + parallelism-factor = 2.0 + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 10 + } + # Throughput defines the maximum number of messages to be + # processed per actor before the thread jumps to the next actor. + # Set to 1 for as fair as possible. + throughput = 100 +} + +transaction-dispatcher { + # Dispatcher is the name of the event-based dispatcher + type = Dispatcher + # What kind of ExecutionService to use + executor = "fork-join-executor" + # Configuration for the fork join pool + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 2 + # Parallelism (threads) ... ceil(available processors * factor) + parallelism-factor = 2.0 + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 10 + } + # Throughput defines the maximum number of messages to be + # processed per actor before the thread jumps to the next actor. + # Set to 1 for as fair as possible. + throughput = 100 +} + +shard-dispatcher { + # Dispatcher is the name of the event-based dispatcher + type = Dispatcher + # What kind of ExecutionService to use + executor = "fork-join-executor" + # Configuration for the fork join pool + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 2 + # Parallelism (threads) ... ceil(available processors * factor) + parallelism-factor = 2.0 + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 10 + } + # Throughput defines the maximum number of messages to be + # processed per actor before the thread jumps to the next actor. + # Set to 1 for as fair as possible. + throughput = 100 +} + +notification-dispatcher { + # Dispatcher is the name of the event-based dispatcher + type = Dispatcher + # What kind of ExecutionService to use + executor = "fork-join-executor" + # Configuration for the fork join pool + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 2 + # Parallelism (threads) ... ceil(available processors * factor) + parallelism-factor = 2.0 + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 10 + } + # Throughput defines the maximum number of messages to be + # processed per actor before the thread jumps to the next actor. + # Set to 1 for as fair as possible. + throughput = 100 +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeIdentifier.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeIdentifier.java index 7370ebee7f..f404c0637f 100644 --- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeIdentifier.java +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataTreeIdentifier.java @@ -8,17 +8,19 @@ package org.opendaylight.controller.md.sal.dom.api; import com.google.common.base.Preconditions; import java.io.Serializable; +import java.util.Iterator; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.yangtools.concepts.Immutable; import org.opendaylight.yangtools.concepts.Path; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; /** * A unique identifier for a particular subtree. It is composed of the logical * data store type and the instance identifier of the root node. */ -public final class DOMDataTreeIdentifier implements Immutable, Path, Serializable { +public final class DOMDataTreeIdentifier implements Immutable, Path, Serializable, Comparable { private static final long serialVersionUID = 1L; private final YangInstanceIdentifier rootIdentifier; private final LogicalDatastoreType datastoreType; @@ -74,4 +76,30 @@ public final class DOMDataTreeIdentifier implements Immutable, Path mi = rootIdentifier.getPathArguments().iterator(); + final Iterator oi = o.rootIdentifier.getPathArguments().iterator(); + + while (mi.hasNext()) { + if (!oi.hasNext()) { + return 1; + } + + final PathArgument ma = mi.next(); + final PathArgument oa = oi.next(); + i = ma.compareTo(oa); + if (i != 0) { + return i; + } + } + + return oi.hasNext() ? -1 : 0; + } } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java index 08888c13cf..8ee928e878 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java @@ -9,6 +9,7 @@ package org.opendaylight.controller.md.sal.dom.broker.impl; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.CheckedFuture; +import java.util.Collection; import java.util.EnumMap; import java.util.Map; import java.util.Map.Entry; @@ -77,7 +78,7 @@ abstract class AbstractDOMForwardedTransactionFactory submit(final DOMDataWriteTransaction transaction, - final Iterable cohorts); + final Collection cohorts); /** * Creates a new composite read-only transaction diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/CommitCoordinationTask.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/CommitCoordinationTask.java index e0ac702dad..b85350f95d 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/CommitCoordinationTask.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/CommitCoordinationTask.java @@ -8,9 +8,9 @@ package org.opendaylight.controller.md.sal.dom.broker.impl; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; -import com.google.common.collect.Iterables; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import java.util.Collection; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; @@ -32,18 +32,16 @@ final class CommitCoordinationTask implements Callable { }; private static final Logger LOG = LoggerFactory.getLogger(CommitCoordinationTask.class); - private final Iterable cohorts; + private final Collection cohorts; private final DurationStatisticsTracker commitStatTracker; private final DOMDataWriteTransaction tx; - private final int cohortSize; public CommitCoordinationTask(final DOMDataWriteTransaction transaction, - final Iterable cohorts, + final Collection cohorts, final DurationStatisticsTracker commitStatTracker) { this.tx = Preconditions.checkNotNull(transaction, "transaction must not be null"); this.cohorts = Preconditions.checkNotNull(cohorts, "cohorts must not be null"); this.commitStatTracker = commitStatTracker; - this.cohortSize = Iterables.size(cohorts); } @Override @@ -115,7 +113,7 @@ final class CommitCoordinationTask implements Callable { * */ private ListenableFuture[] canCommitAll() { - final ListenableFuture[] ops = new ListenableFuture[cohortSize]; + final ListenableFuture[] ops = new ListenableFuture[cohorts.size()]; int i = 0; for (DOMStoreThreePhaseCommitCohort cohort : cohorts) { ops[i++] = cohort.canCommit(); @@ -162,7 +160,7 @@ final class CommitCoordinationTask implements Callable { * */ private ListenableFuture[] preCommitAll() { - final ListenableFuture[] ops = new ListenableFuture[cohortSize]; + final ListenableFuture[] ops = new ListenableFuture[cohorts.size()]; int i = 0; for (DOMStoreThreePhaseCommitCohort cohort : cohorts) { ops[i++] = cohort.preCommit(); @@ -205,7 +203,7 @@ final class CommitCoordinationTask implements Callable { * @return List of all cohorts futures from can commit phase. */ private ListenableFuture[] commitAll() { - final ListenableFuture[] ops = new ListenableFuture[cohortSize]; + final ListenableFuture[] ops = new ListenableFuture[cohorts.size()]; int i = 0; for (DOMStoreThreePhaseCommitCohort cohort : cohorts) { ops[i++] = cohort.commit(); @@ -256,7 +254,7 @@ final class CommitCoordinationTask implements Callable { */ private ListenableFuture abortAsyncAll() { - final ListenableFuture[] ops = new ListenableFuture[cohortSize]; + final ListenableFuture[] ops = new ListenableFuture[cohorts.size()]; int i = 0; for (DOMStoreThreePhaseCommitCohort cohort : cohorts) { ops[i++] = cohort.abort(); diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java index 77387c761c..201eb81a94 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java @@ -10,6 +10,7 @@ import com.google.common.base.Preconditions; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; +import java.util.Collection; import java.util.Map; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicLong; @@ -87,7 +88,7 @@ final class DOMDataBrokerTransactionChainImpl extends AbstractDOMForwardedTransa @Override public CheckedFuture submit( - final DOMDataWriteTransaction transaction, final Iterable cohorts) { + final DOMDataWriteTransaction transaction, final Collection cohorts) { checkNotFailed(); checkNotClosed(); diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java index 961b6c7b93..9895ff9ad5 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/PingPongTransactionChain.java @@ -158,13 +158,16 @@ public final class PingPongTransactionChain implements DOMTransactionChain { /* * This forces allocateTransaction() on a slow path, which has to happen after - * this method has completed executing. + * this method has completed executing. Also inflightTx may be updated outside + * the lock, hence we need to re-check. */ @GuardedBy("this") private void processIfReady() { - final PingPongTransaction tx = READY_UPDATER.getAndSet(this, null); - if (tx != null) { - processTransaction(tx); + if (inflightTx == null) { + final PingPongTransaction tx = READY_UPDATER.getAndSet(this, null); + if (tx != null) { + processTransaction(tx); + } } } @@ -251,14 +254,27 @@ public final class PingPongTransactionChain implements DOMTransactionChain { } @Override - public void close() { + public synchronized void close() { final PingPongTransaction notLocked = lockedTx; Preconditions.checkState(notLocked == null, "Attempted to close chain with outstanding transaction %s", notLocked); - synchronized (this) { - processIfReady(); - delegate.close(); + // Force allocations on slow path. We will complete the rest + final PingPongTransaction tx = READY_UPDATER.getAndSet(this, null); + + // Make sure no transaction is outstanding. Otherwise sleep a bit and retry + while (inflightTx != null) { + LOG.debug("Busy-waiting for in-flight transaction {} to complete", inflightTx); + Thread.yield(); + continue; } + + // If we have an outstanding transaction, send it down + if (tx != null) { + processTransaction(tx); + } + + // All done, close the delegate. All new allocations should fail. + delegate.close(); } @Override diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/SerializedDOMDataBroker.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/SerializedDOMDataBroker.java index 268b1b8584..ad23e3a72b 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/SerializedDOMDataBroker.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/SerializedDOMDataBroker.java @@ -11,6 +11,7 @@ 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; +import java.util.Collection; import java.util.Map; import java.util.concurrent.RejectedExecutionException; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -57,7 +58,7 @@ public class SerializedDOMDataBroker extends AbstractDOMDataBroker { @Override protected CheckedFuture submit(final DOMDataWriteTransaction transaction, - final Iterable cohorts) { + final Collection cohorts) { Preconditions.checkArgument(transaction != null, "Transaction must not be null."); Preconditions.checkArgument(cohorts != null, "Cohorts must not be null."); LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier()); diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardRegistration.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardRegistration.java new file mode 100644 index 0000000000..9a71089946 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardRegistration.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.broker.impl; + +import com.google.common.base.Preconditions; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShard; +import org.opendaylight.yangtools.concepts.AbstractListenerRegistration; + +final class ShardRegistration extends AbstractListenerRegistration { + private final DOMDataTreeIdentifier prefix; + private final ShardedDOMDataTree tree; + + protected ShardRegistration(final ShardedDOMDataTree tree, final DOMDataTreeIdentifier prefix, final T shard) { + super(shard); + this.tree = Preconditions.checkNotNull(tree); + this.prefix = Preconditions.checkNotNull(prefix); + } + + DOMDataTreeIdentifier getPrefix() { + return prefix; + } + + @Override + protected void removeRegistration() { + tree.removeShard(this); + } +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTree.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTree.java new file mode 100644 index 0000000000..11eae5d833 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTree.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.broker.impl; + +import com.google.common.base.Preconditions; +import java.util.Collection; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; +import javax.annotation.concurrent.GuardedBy; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducer; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeService; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShard; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShardingConflictException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShardingService; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class ShardedDOMDataTree implements DOMDataTreeService, DOMDataTreeShardingService { + private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTree.class); + private final Map shardingTables = new EnumMap<>(LogicalDatastoreType.class); + @GuardedBy("this") + private final Map idToProducer = new TreeMap<>(); + + @GuardedBy("this") + private ShardingTableEntry lookupShard(final DOMDataTreeIdentifier prefix) { + final ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType()); + if (t == null) { + return null; + } + + return t.lookup(prefix.getRootIdentifier()); + } + + @GuardedBy("this") + private void storeShard(final DOMDataTreeIdentifier prefix, final ShardRegistration reg) { + ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType()); + if (t == null) { + t = new ShardingTableEntry(); + shardingTables.put(prefix.getDatastoreType(), t); + } + + t.store(prefix.getRootIdentifier(), reg); + } + + void removeShard(final ShardRegistration reg) { + final DOMDataTreeIdentifier prefix = reg.getPrefix(); + final ShardRegistration parentReg; + + synchronized (this) { + final ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType()); + if (t == null) { + LOG.warn("Shard registration {} points to non-existent table", reg); + return; + } + + t.remove(prefix.getRootIdentifier()); + parentReg = lookupShard(prefix).getRegistration(); + + /* + * FIXME: adjust all producers. This is tricky, as we need different locking strategy, + * simply because we risk AB/BA deadlock with a producer being split off from + * a producer. + * + */ + } + + if (parentReg != null) { + parentReg.getInstance().onChildDetached(prefix, reg.getInstance()); + } + } + + @Override + public ListenerRegistration registerDataTreeShard(final DOMDataTreeIdentifier prefix, final T shard) throws DOMDataTreeShardingConflictException { + final ShardRegistration reg; + final ShardRegistration parentReg; + + synchronized (this) { + /* + * Lookup the parent shard (e.g. the one which currently matches the prefix), + * and if it exists, check if its registration prefix does not collide with + * this registration. + */ + final ShardingTableEntry parent = lookupShard(prefix); + parentReg = parent.getRegistration(); + if (parentReg != null && prefix.equals(parentReg.getPrefix())) { + throw new DOMDataTreeShardingConflictException(String.format("Prefix %s is already occupied by shard {}", prefix, parentReg.getInstance())); + } + + // FIXME: wrap the shard in a proper adaptor based on implemented interface + + reg = new ShardRegistration(this, prefix, shard); + + storeShard(prefix, reg); + + // FIXME: update any producers/registrations + } + + // Notify the parent shard + if (parentReg != null) { + parentReg.getInstance().onChildAttached(prefix, shard); + } + + return reg; + } + + @GuardedBy("this") + private DOMDataTreeProducer findProducer(final DOMDataTreeIdentifier subtree) { + for (Entry e : idToProducer.entrySet()) { + if (e.getKey().contains(subtree)) { + return e.getValue(); + } + } + + return null; + } + + synchronized void destroyProducer(final ShardedDOMDataTreeProducer producer) { + for (DOMDataTreeIdentifier s : producer.getSubtrees()) { + DOMDataTreeProducer r = idToProducer.remove(s); + if (!producer.equals(r)) { + LOG.error("Removed producer %s on subtree %s while removing %s", r, s, producer); + } + } + } + + @GuardedBy("this") + private DOMDataTreeProducer createProducer(final Map shardMap) { + // Record the producer's attachment points + final DOMDataTreeProducer ret = ShardedDOMDataTreeProducer.create(this, shardMap); + for (DOMDataTreeIdentifier s : shardMap.keySet()) { + idToProducer.put(s, ret); + } + + return ret; + } + + @Override + public synchronized DOMDataTreeProducer createProducer(final Collection subtrees) { + Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees may not be empty"); + + final Map shardMap = new HashMap<>(); + for (DOMDataTreeIdentifier s : subtrees) { + // Attempting to create a disconnected producer -- all subtrees have to be unclaimed + final DOMDataTreeProducer producer = findProducer(s); + Preconditions.checkArgument(producer == null, "Subtree %s is attached to producer %s", s, producer); + + shardMap.put(s, lookupShard(s).getRegistration().getInstance()); + } + + return createProducer(shardMap); + } + + synchronized DOMDataTreeProducer createProducer(final ShardedDOMDataTreeProducer parent, final Collection subtrees) { + Preconditions.checkNotNull(parent); + + final Map shardMap = new HashMap<>(); + for (DOMDataTreeIdentifier s : subtrees) { + shardMap.put(s, lookupShard(s).getRegistration().getInstance()); + } + + return createProducer(shardMap); + } + + @Override + public synchronized ListenerRegistration registerListener(final T listener, final Collection subtrees, final boolean allowRxMerges, final Collection producers) { + // TODO Auto-generated method stub + return null; + } +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTreeProducer.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTreeProducer.java new file mode 100644 index 0000000000..9712b25ac9 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataTreeProducer.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.broker.impl; + +import com.google.common.base.Preconditions; +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.ImmutableBiMap.Builder; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Queue; +import java.util.Set; +import javax.annotation.concurrent.GuardedBy; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducer; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducerBusyException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducerException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShard; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.controller.sal.core.spi.data.DOMStore; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class ShardedDOMDataTreeProducer implements DOMDataTreeProducer { + private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTreeProducer.class); + private final BiMap shardToChain; + private final Map idToShard; + private final ShardedDOMDataTree dataTree; + + @GuardedBy("this") + private Map children = Collections.emptyMap(); + @GuardedBy("this") + private DOMDataWriteTransaction openTx; + @GuardedBy("this") + private boolean closed; + + ShardedDOMDataTreeProducer(final ShardedDOMDataTree dataTree, final Map shardMap, final Set shards) { + this.dataTree = Preconditions.checkNotNull(dataTree); + + // Create shard -> chain map + final Builder cb = ImmutableBiMap.builder(); + final Queue es = new LinkedList<>(); + + for (DOMDataTreeShard s : shards) { + if (s instanceof DOMStore) { + try { + final DOMStoreTransactionChain c = ((DOMStore)s).createTransactionChain(); + LOG.trace("Using DOMStore chain {} to access shard {}", c, s); + cb.put(s, c); + } catch (Exception e) { + LOG.error("Failed to instantiate chain for shard {}", s, e); + es.add(e); + } + } else { + LOG.error("Unhandled shard instance type {}", s.getClass()); + } + } + this.shardToChain = cb.build(); + + // An error was encountered, close chains and report the error + if (shardToChain.size() != shards.size()) { + for (DOMStoreTransactionChain c : shardToChain.values()) { + try { + c.close(); + } catch (Exception e) { + LOG.warn("Exception raised while closing chain %s", c, e); + } + } + + final IllegalStateException e = new IllegalStateException("Failed to completely allocate contexts", es.poll()); + while (!es.isEmpty()) { + e.addSuppressed(es.poll()); + } + + throw e; + } + + idToShard = ImmutableMap.copyOf(shardMap); + } + + @Override + public synchronized DOMDataWriteTransaction createTransaction(final boolean isolated) { + Preconditions.checkState(!closed, "Producer is already closed"); + Preconditions.checkState(openTx == null, "Transaction %s is still open", openTx); + + // Allocate backing transactions + final Map shardToTx = new HashMap<>(); + for (Entry e : shardToChain.entrySet()) { + shardToTx.put(e.getKey(), e.getValue().newWriteOnlyTransaction()); + } + + // Create the ID->transaction map + final ImmutableMap.Builder b = ImmutableMap.builder(); + for (Entry e : idToShard.entrySet()) { + b.put(e.getKey(), shardToTx.get(e.getValue())); + } + + final ShardedDOMDataWriteTransaction ret = new ShardedDOMDataWriteTransaction(this, b.build()); + openTx = ret; + return ret; + } + + @GuardedBy("this") + private boolean haveSubtree(final DOMDataTreeIdentifier subtree) { + for (DOMDataTreeIdentifier i : idToShard.keySet()) { + if (i.contains(subtree)) { + return true; + } + } + + return false; + } + + @GuardedBy("this") + private DOMDataTreeProducer lookupChild(final DOMDataTreeIdentifier s) { + for (Entry e : children.entrySet()) { + if (e.getKey().contains(s)) { + return e.getValue(); + } + } + + return null; + } + + @Override + public synchronized DOMDataTreeProducer createProducer(final Collection subtrees) { + Preconditions.checkState(!closed, "Producer is already closed"); + Preconditions.checkState(openTx == null, "Transaction %s is still open", openTx); + + for (DOMDataTreeIdentifier s : subtrees) { + // Check if the subtree was visible at any time + if (!haveSubtree(s)) { + throw new IllegalArgumentException(String.format("Subtree %s was never available in producer %s", s, this)); + } + + // Check if the subtree has not been delegated to a child + final DOMDataTreeProducer child = lookupChild(s); + Preconditions.checkArgument(child == null, "Subtree %s is delegated to child producer %s", s, child); + + // Check if part of the requested subtree is not delegated to a child. + for (DOMDataTreeIdentifier c : children.keySet()) { + if (s.contains(c)) { + throw new IllegalArgumentException(String.format("Subtree %s cannot be delegated as it is superset of already-delegated %s", s, c)); + } + } + } + + final DOMDataTreeProducer ret = dataTree.createProducer(this, subtrees); + final ImmutableMap.Builder cb = ImmutableMap.builder(); + cb.putAll(children); + for (DOMDataTreeIdentifier s : subtrees) { + cb.put(s, ret); + } + + children = cb.build(); + return ret; + } + + @Override + public synchronized void close() throws DOMDataTreeProducerException { + if (!closed) { + if (openTx != null) { + throw new DOMDataTreeProducerBusyException(String.format("Transaction %s is still open", openTx)); + } + + closed = true; + dataTree.destroyProducer(this); + } + } + + static DOMDataTreeProducer create(final ShardedDOMDataTree dataTree, final Map shardMap) { + /* + * FIXME: we do not allow multiple multiple shards in a producer because we do not implement the + * synchronization primitives yet + */ + final Set shards = ImmutableSet.copyOf(shardMap.values()); + if (shards.size() > 1) { + throw new UnsupportedOperationException("Cross-shard producers are not supported yet"); + } + + return new ShardedDOMDataTreeProducer(dataTree, shardMap, shards); + } + + Set getSubtrees() { + return idToShard.keySet(); + } + + synchronized void cancelTransaction(final ShardedDOMDataWriteTransaction transaction) { + if (!openTx.equals(transaction)) { + LOG.warn("Transaction {} is not open in producer {}", transaction, this); + return; + } + + LOG.debug("Transaction {} cancelled", transaction); + openTx = null; + } +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataWriteTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataWriteTransaction.java new file mode 100644 index 0000000000..33f15e3f67 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardedDOMDataWriteTransaction.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.broker.impl; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.controller.md.sal.common.api.TransactionStatus; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier; +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.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@NotThreadSafe +final class ShardedDOMDataWriteTransaction implements DOMDataWriteTransaction { + private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataWriteTransaction.class); + private static final AtomicLong COUNTER = new AtomicLong(); + private final Map idToTransaction; + private final ShardedDOMDataTreeProducer producer; + private final String identifier; + @GuardedBy("this") + private boolean closed = false; + + ShardedDOMDataWriteTransaction(final ShardedDOMDataTreeProducer producer, final Map idToTransaction) { + this.producer = Preconditions.checkNotNull(producer); + this.idToTransaction = Preconditions.checkNotNull(idToTransaction); + this.identifier = "SHARDED-DOM-" + COUNTER.getAndIncrement(); + } + + // FIXME: use atomic operations + @GuardedBy("this") + private DOMStoreWriteTransaction lookup(final LogicalDatastoreType store, final YangInstanceIdentifier path) { + final DOMDataTreeIdentifier id = new DOMDataTreeIdentifier(store, path); + + for (Entry e : idToTransaction.entrySet()) { + if (e.getKey().contains(id)) { + return e.getValue(); + } + } + + throw new IllegalArgumentException(String.format("Path %s is not acessible from transaction %s", id, this)); + } + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public synchronized boolean cancel() { + if (closed) { + return false; + } + + LOG.debug("Cancelling transaction {}", identifier); + for (DOMStoreWriteTransaction tx : ImmutableSet.copyOf(idToTransaction.values())) { + tx.close(); + } + + closed = true; + producer.cancelTransaction(this); + return true; + } + + @Override + public synchronized CheckedFuture submit() { + Preconditions.checkState(!closed, "Transaction %s is already closed", identifier); + + final Set txns = ImmutableSet.copyOf(idToTransaction.values()); + final List cohorts = new ArrayList<>(txns.size()); + for (DOMStoreWriteTransaction tx : txns) { + cohorts.add(tx.ready()); + } + + try { + return Futures.immediateCheckedFuture(new CommitCoordinationTask(this, cohorts, null).call()); + } catch (TransactionCommitFailedException e) { + return Futures.immediateFailedCheckedFuture(e); + } + } + + @Override + @Deprecated + public ListenableFuture> commit() { + return AbstractDataTransaction.convertToLegacyCommitFuture(submit()); + } + + @Override + public synchronized void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) { + lookup(store, path).delete(path); + } + + @Override + public synchronized void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode data) { + lookup(store, path).write(path, data); + } + + @Override + public synchronized void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode data) { + lookup(store, path).merge(path, data); + } +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardingTableEntry.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardingTableEntry.java new file mode 100644 index 0000000000..fcd0ebdca0 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/ShardingTableEntry.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.broker.impl; + +import com.google.common.base.Preconditions; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import org.opendaylight.yangtools.concepts.Identifiable; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class ShardingTableEntry implements Identifiable { + private static final Logger LOG = LoggerFactory.getLogger(ShardingTableEntry.class); + private final Map children = Collections.emptyMap(); + private final PathArgument identifier; + private ShardRegistration registration; + + ShardingTableEntry() { + identifier = null; + } + + ShardingTableEntry(final PathArgument identifier) { + this.identifier = Preconditions.checkNotNull(identifier); + } + + @Override + public PathArgument getIdentifier() { + return identifier; + } + + public ShardRegistration getRegistration() { + return registration; + } + + ShardingTableEntry lookup(final YangInstanceIdentifier id) { + final Iterator it = id.getPathArguments().iterator(); + ShardingTableEntry entry = this; + + while (it.hasNext()) { + final PathArgument a = it.next(); + final ShardingTableEntry child = entry.children.get(a); + if (child == null) { + LOG.debug("Lookup of {} stopped at {}", id, a); + break; + } + + entry = child; + } + + return entry; + } + + void store(final YangInstanceIdentifier id, final ShardRegistration reg) { + final Iterator it = id.getPathArguments().iterator(); + ShardingTableEntry entry = this; + + while (it.hasNext()) { + final PathArgument a = it.next(); + ShardingTableEntry child = entry.children.get(a); + if (child == null) { + child = new ShardingTableEntry(a); + entry.children.put(a, child); + } + } + + Preconditions.checkState(entry.registration == null); + entry.registration = reg; + } + + private boolean remove(final Iterator it) { + if (it.hasNext()) { + final PathArgument arg = it.next(); + final ShardingTableEntry child = children.get(arg); + if (child != null) { + if (child.remove(it)) { + children.remove(arg); + } + } else { + LOG.warn("Cannot remove non-existent child {}", arg); + } + } + + return registration == null && children.isEmpty(); + } + + void remove(final YangInstanceIdentifier id) { + this.remove(id.getPathArguments().iterator()); + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java index 8553820b40..c62f56ac1e 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java @@ -248,6 +248,10 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, request.future.set( RpcResultBuilder.failed() .withRpcError( NetconfMessageTransformUtil.toRpcError( e ) ).build() ); + + //recursively processing message to eventually find matching request + processMessage(message); + return; } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java index 68fe87fb60..0ff5e2d3d5 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java @@ -296,6 +296,27 @@ public class NetconfDeviceCommunicatorTest { return new NetconfMessage( doc ); } + //Test scenario verifying whether missing message is handled + @Test + public void testOnMissingResponseMessage() throws Exception { + + setupSession(); + + String messageID1 = UUID.randomUUID().toString(); + ListenableFuture> resultFuture1 = sendRequest( messageID1 ); + + String messageID2 = UUID.randomUUID().toString(); + ListenableFuture> resultFuture2 = sendRequest( messageID2 ); + + String messageID3 = UUID.randomUUID().toString(); + ListenableFuture> resultFuture3 = sendRequest( messageID3 ); + + //response messages 1,2 are omitted + communicator.onMessage( mockSession, createSuccessResponseMessage( messageID3 ) ); + + verifyResponseMessage( resultFuture3.get(), messageID3 ); + } + @Test public void testOnSuccessfulResponseMessage() throws Exception { setupSession(); diff --git a/opendaylight/md-sal/sal-rest-connector/pom.xml b/opendaylight/md-sal/sal-rest-connector/pom.xml index 4a96847175..7daad1631a 100644 --- a/opendaylight/md-sal/sal-rest-connector/pom.xml +++ b/opendaylight/md-sal/sal-rest-connector/pom.xml @@ -99,6 +99,12 @@ org.opendaylight.yangtools yang-data-codec-gson + + org.opendaylight.yangtools + yang-model-export + + 0.7.0-SNAPSHOT + ch.qos.logback @@ -136,6 +142,7 @@ org.opendaylight.controller.sal.rest.*, org.opendaylight.controller.sal.restconf.rpc.*, org.opendaylight.controller.sal.restconf.impl, + org.opendaylight.controller.md.sal.rest.common.*, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.rest.connector.rev140724.*, *, diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/common/RestconfValidationUtils.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/common/RestconfValidationUtils.java new file mode 100644 index 0000000000..0d53c9c486 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/common/RestconfValidationUtils.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.md.sal.rest.common; + +import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; +import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; +import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType; + +/** + * sal-rest-connector + * org.opendaylight.controller.md.sal.rest.common + * + * Utility class is centralizing all needed validation functionality for a Restconf osgi module. + * All methods have to throw {@link RestconfDocumentedException} only, which is a representation + * for all error situation followed by restconf-netconf specification. + * @see {@link https://tools.ietf.org/html/draft-bierman-netconf-restconf-02} + * + * @author Vaclav Demcak + * + * Created: Feb 24, 2015 + */ +public class RestconfValidationUtils { + + private RestconfValidationUtils () { + throw new UnsupportedOperationException("Utility class"); + } + + /** + * Method returns {@link RestconfDocumentedException} for a false condition. + * + * @param condition - condition for rise {@link RestconfDocumentedException} + * @param type - input {@link ErrorType} for create {@link RestconfDocumentedException} + * @param tag - input {@link ErrorTag} for create {@link RestconfDocumentedException} + * @param message - input error message for create {@link RestconfDocumentedException} + */ + public static void checkDocumentedError(final boolean condition, final ErrorType type, + final ErrorTag tag, final String message) { + if(!condition) { + throw new RestconfDocumentedException(message, type, tag); + } + } + + /** + * Method returns {@link RestconfDocumentedException} if value is NULL or same input value. + * {@link ErrorType} is relevant for server application layer + * {@link ErrorTag} is 404 data-missing + * @see {@link https://tools.ietf.org/html/draft-bierman-netconf-restconf-02} + * + * @param value - some value from {@link org.opendaylight.yangtools.yang.model.api.Module} + * @param moduleName - name of {@link org.opendaylight.yangtools.yang.model.api.Module} + * @return - T value (same input value) + */ + public static T checkNotNullDocumented(final T value, final String moduleName) { + if(value == null) { + final String errMsg = "Module " + moduleName + "was not found."; + throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.DATA_MISSING); + } + return value; + } +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaExportContentYangBodyWriter.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaExportContentYangBodyWriter.java new file mode 100644 index 0000000000..20300e24f6 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaExportContentYangBodyWriter.java @@ -0,0 +1,47 @@ +/* + * 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.rest.schema; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; + +@Provider +@Produces(SchemaRetrievalService.YANG_MEDIA_TYPE) +public class SchemaExportContentYangBodyWriter implements MessageBodyWriter { + + @Override + public boolean isWriteable(final Class type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType) { + return type.equals(SchemaExportContext.class); + } + + @Override + public long getSize(final SchemaExportContext t, final Class type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(final SchemaExportContext t, final Class type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap httpHeaders, final OutputStream entityStream) throws IOException, + WebApplicationException { + final PrintWriter writer = new PrintWriter(entityStream); + writer.write(t.getModule().getSource()); + + } +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaExportContentYinBodyWriter.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaExportContentYinBodyWriter.java new file mode 100644 index 0000000000..9773c0afc0 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaExportContentYinBodyWriter.java @@ -0,0 +1,51 @@ +/* + * 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.rest.schema; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import javax.xml.stream.XMLStreamException; +import org.opendaylight.yangtools.yang.model.export.YinExportUtils; + +@Provider +@Produces(SchemaRetrievalService.YIN_MEDIA_TYPE) +public class SchemaExportContentYinBodyWriter implements MessageBodyWriter { + + @Override + public boolean isWriteable(final Class type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType) { + return type.equals(SchemaExportContext.class); + } + + @Override + public long getSize(final SchemaExportContext t, final Class type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(final SchemaExportContext t, final Class type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap httpHeaders, final OutputStream entityStream) throws IOException, + WebApplicationException { + try { + YinExportUtils.writeModuleToOutputStream(t.getSchemaContext(), t.getModule(), entityStream); + } catch (final XMLStreamException e) { + throw new IllegalStateException(e); + } + + } +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaExportContext.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaExportContext.java new file mode 100644 index 0000000000..4d3c95faa9 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaExportContext.java @@ -0,0 +1,31 @@ +/* + * 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.rest.schema; + +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +public class SchemaExportContext { + + private final SchemaContext schemaContext; + private final Module module; + + public SchemaExportContext(final SchemaContext ctx, final Module module) { + schemaContext = ctx; + this.module = module; + } + + public SchemaContext getSchemaContext() { + return schemaContext; + } + + public Module getModule() { + return module; + } + +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaRetrievalService.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaRetrievalService.java new file mode 100644 index 0000000000..b268247d51 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaRetrievalService.java @@ -0,0 +1,26 @@ +/* + * 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.rest.schema; + +import com.google.common.annotations.Beta; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; + +@Beta +public interface SchemaRetrievalService { + + public static final String YANG_MEDIA_TYPE = "application/yang"; + public static final String YIN_MEDIA_TYPE = "application/yin+xml"; + + @GET + @Produces({YIN_MEDIA_TYPE,YANG_MEDIA_TYPE}) + @Path("/modules/module/{identifier:.+}/schema") + SchemaExportContext getSchema(@PathParam("identifier") String mountAndModuleId); +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaRetrievalServiceImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaRetrievalServiceImpl.java new file mode 100644 index 0000000000..b7308c1fba --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/md/sal/rest/schema/SchemaRetrievalServiceImpl.java @@ -0,0 +1,92 @@ +/* + * 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.rest.schema; + +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; +import java.text.ParseException; +import java.util.Date; +import java.util.Iterator; +import org.opendaylight.controller.md.sal.rest.common.RestconfValidationUtils; +import org.opendaylight.controller.sal.restconf.impl.ControllerContext; +import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext; +import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; +import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; +import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType; +import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +public class SchemaRetrievalServiceImpl implements SchemaRetrievalService { + + private final ControllerContext salContext; + + private static final Splitter SLASH_SPLITTER = Splitter.on("/"); + private static final Splitter AT_SPLITTER = Splitter.on("@"); + private static final String MOUNT_ARG = ControllerContext.MOUNT; + + public SchemaRetrievalServiceImpl(final ControllerContext controllerContext) { + salContext = controllerContext; + } + + + @Override + public SchemaExportContext getSchema(final String mountAndModule) { + final SchemaContext schemaContext; + final Iterable pathComponents = SLASH_SPLITTER.split(mountAndModule); + final Iterator componentIter = pathComponents.iterator(); + if(!Iterables.contains(pathComponents, MOUNT_ARG)) { + schemaContext = salContext.getGlobalSchema(); + } else { + final StringBuilder pathBuilder = new StringBuilder(); + while(componentIter.hasNext()) { + final String current = componentIter.next(); + // It is argument, not last element. + if(pathBuilder.length() != 0) { + pathBuilder.append("/"); + } + pathBuilder.append(current); + if(MOUNT_ARG.equals(current)) { + // We stop right at mountpoint, last two arguments should + // be module name and revision + break; + } + } + schemaContext = getMountSchemaContext(pathBuilder.toString()); + + } + + RestconfValidationUtils.checkDocumentedError(componentIter.hasNext(), + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Module name must be supplied."); + final String moduleName = componentIter.next(); + RestconfValidationUtils.checkDocumentedError(componentIter.hasNext(), + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Revision date must be supplied."); + final String revisionString = componentIter.next(); + return getExportUsingNameAndRevision(schemaContext, moduleName, revisionString); + } + + private SchemaExportContext getExportUsingNameAndRevision(final SchemaContext schemaContext, final String moduleName, + final String revisionStr) { + try { + final Date revision = SimpleDateFormatUtil.getRevisionFormat().parse(revisionStr); + final Module module = schemaContext.findModuleByName(moduleName, revision); + return new SchemaExportContext(schemaContext, RestconfValidationUtils.checkNotNullDocumented(module, moduleName)); + } catch (final ParseException e) { + throw new RestconfDocumentedException("Supplied revision is not in expected date format YYYY-mm-dd", e); + } + } + + private SchemaContext getMountSchemaContext(final String identifier) { + final InstanceIdentifierContext mountContext = salContext.toMountPointIdentifier(identifier); + return mountContext.getSchemaContext(); + } + + + +} + diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java index 7f8f0a1d0e..7bb90c83aa 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java @@ -62,19 +62,19 @@ public interface RestconfService { @Path("/modules") @Produces({ Draft02.MediaTypes.API + JSON, Draft02.MediaTypes.API + XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML }) - public StructuredData getModules(@Context UriInfo uriInfo); + public NormalizedNodeContext getModules(@Context UriInfo uriInfo); @GET @Path("/modules/{identifier:.+}") @Produces({ Draft02.MediaTypes.API + JSON, Draft02.MediaTypes.API + XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML }) - public StructuredData getModules(@PathParam("identifier") String identifier, @Context UriInfo uriInfo); + public NormalizedNodeContext getModules(@PathParam("identifier") String identifier, @Context UriInfo uriInfo); @GET @Path("/modules/module/{identifier:.+}") @Produces({ Draft02.MediaTypes.API + JSON, Draft02.MediaTypes.API + XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML }) - public StructuredData getModule(@PathParam("identifier") String identifier, @Context UriInfo uriInfo); + public NormalizedNodeContext getModule(@PathParam("identifier") String identifier, @Context UriInfo uriInfo); @GET @Path("/operations") diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/gson/JsonParser.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/gson/JsonParser.java index a784be2adc..699aed66d7 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/gson/JsonParser.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/gson/JsonParser.java @@ -22,31 +22,34 @@ import java.io.EOFException; import java.io.IOException; /** + * @deprecated class will be removed in Lithium release + * * This class parses JSON elements from a gson JsonReader. It disallows multiple elements of the same name unlike the * default gson JsonParser." */ +@Deprecated public class JsonParser { - public JsonElement parse(JsonReader reader) throws JsonIOException, JsonSyntaxException { + public JsonElement parse(final JsonReader reader) throws JsonIOException, JsonSyntaxException { // code copied from gson's JsonParser and Stream classes - boolean lenient = reader.isLenient(); + final boolean lenient = reader.isLenient(); reader.setLenient(true); boolean isEmpty = true; try { reader.peek(); isEmpty = false; return read(reader); - } catch (EOFException e) { + } catch (final EOFException e) { if (isEmpty) { return JsonNull.INSTANCE; } // The stream ended prematurely so it is likely a syntax error. throw new JsonSyntaxException(e); - } catch (MalformedJsonException e) { + } catch (final MalformedJsonException e) { throw new JsonSyntaxException(e); - } catch (IOException e) { + } catch (final IOException e) { throw new JsonIOException(e); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { throw new JsonSyntaxException(e); } catch (StackOverflowError | OutOfMemoryError e) { throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e); @@ -55,12 +58,12 @@ public class JsonParser { } } - public JsonElement read(JsonReader in) throws IOException { + public JsonElement read(final JsonReader in) throws IOException { switch (in.peek()) { case STRING: return new JsonPrimitive(in.nextString()); case NUMBER: - String number = in.nextString(); + final String number = in.nextString(); return new JsonPrimitive(new LazilyParsedNumber(number)); case BOOLEAN: return new JsonPrimitive(in.nextBoolean()); @@ -68,7 +71,7 @@ public class JsonParser { in.nextNull(); return JsonNull.INSTANCE; case BEGIN_ARRAY: - JsonArray array = new JsonArray(); + final JsonArray array = new JsonArray(); in.beginArray(); while (in.hasNext()) { array.add(read(in)); @@ -76,7 +79,7 @@ public class JsonParser { in.endArray(); return array; case BEGIN_OBJECT: - JsonObject object = new JsonObject(); + final JsonObject object = new JsonObject(); in.beginObject(); while (in.hasNext()) { final String childName = in.nextName(); diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeProvider.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeProvider.java index caff848180..6863b964d4 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeProvider.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeProvider.java @@ -26,6 +26,9 @@ import org.opendaylight.yangtools.yang.data.api.Node; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * @deprecated class will be removed in Lithium release + */ @Provider @Consumes({ Draft02.MediaTypes.DATA + RestconfService.JSON, Draft02.MediaTypes.OPERATION + RestconfService.JSON, MediaType.APPLICATION_JSON }) @@ -47,7 +50,7 @@ public enum JsonToCompositeNodeProvider implements MessageBodyReader> { WebApplicationException { try { return JsonToCompositeNodeReader.read(entityStream); - } catch (Exception e) { + } catch (final Exception e) { LOG.debug("Error parsing json input", e); throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL, diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeReader.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeReader.java index 552e2bbd19..2834fa15c1 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeReader.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeReader.java @@ -28,6 +28,10 @@ import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * @deprecated class will be removed in Lithium release + */ +@Deprecated class JsonToCompositeNodeReader { private static final Logger LOG = LoggerFactory.getLogger(JsonToCompositeNodeReader.class); private static final Splitter COLON_SPLITTER = Splitter.on(':'); @@ -37,9 +41,9 @@ class JsonToCompositeNodeReader { } public static CompositeNodeWrapper read(final InputStream entityStream) throws UnsupportedFormatException { - JsonParser parser = new JsonParser(); + final JsonParser parser = new JsonParser(); - JsonElement rootElement = parser.parse(new JsonReader(new InputStreamReader(entityStream))); + final JsonElement rootElement = parser.parse(new JsonReader(new InputStreamReader(entityStream))); if (rootElement.isJsonNull()) { // no content, so return null to indicate no input return null; @@ -49,14 +53,14 @@ class JsonToCompositeNodeReader { throw new UnsupportedFormatException("Root element of Json has to be Object"); } - Set> entrySetsOfRootJsonObject = rootElement.getAsJsonObject().entrySet(); + final Set> entrySetsOfRootJsonObject = rootElement.getAsJsonObject().entrySet(); if (entrySetsOfRootJsonObject.size() != 1) { throw new UnsupportedFormatException("Json Object should contain one element"); } - Entry childEntry = entrySetsOfRootJsonObject.iterator().next(); - String firstElementName = childEntry.getKey(); - JsonElement firstElementType = childEntry.getValue(); + final Entry childEntry = entrySetsOfRootJsonObject.iterator().next(); + final String firstElementName = childEntry.getKey(); + final JsonElement firstElementType = childEntry.getValue(); if (firstElementType.isJsonObject()) { // container in yang return createStructureWithRoot(firstElementName, firstElementType.getAsJsonObject()); @@ -64,7 +68,7 @@ class JsonToCompositeNodeReader { if (firstElementType.isJsonArray()) { // list in yang if (firstElementType.getAsJsonArray().size() == 1) { - JsonElement firstElementInArray = firstElementType.getAsJsonArray().get(0); + final JsonElement firstElementInArray = firstElementType.getAsJsonArray().get(0); if (firstElementInArray.isJsonObject()) { return createStructureWithRoot(firstElementName, firstElementInArray.getAsJsonObject()); } @@ -77,9 +81,9 @@ class JsonToCompositeNodeReader { } private static CompositeNodeWrapper createStructureWithRoot(final String rootObjectName, final JsonObject rootObject) { - CompositeNodeWrapper firstNode = new CompositeNodeWrapper(getNamespaceFor(rootObjectName), + final CompositeNodeWrapper firstNode = new CompositeNodeWrapper(getNamespaceFor(rootObjectName), getLocalNameFor(rootObjectName)); - for (Entry childOfFirstNode : rootObject.entrySet()) { + for (final Entry childOfFirstNode : rootObject.entrySet()) { addChildToParent(childOfFirstNode.getKey(), childOfFirstNode.getValue(), firstNode); } return firstNode; @@ -88,10 +92,10 @@ class JsonToCompositeNodeReader { private static void addChildToParent(final String childName, final JsonElement childType, final CompositeNodeWrapper parent) { if (childType.isJsonObject()) { - CompositeNodeWrapper child = new CompositeNodeWrapper(getNamespaceFor(childName), + final CompositeNodeWrapper child = new CompositeNodeWrapper(getNamespaceFor(childName), getLocalNameFor(childName)); parent.addValue(child); - for (Entry childOfChild : childType.getAsJsonObject().entrySet()) { + for (final Entry childOfChild : childType.getAsJsonObject().entrySet()) { addChildToParent(childOfChild.getKey(), childOfChild.getValue(), child); } } else if (childType.isJsonArray()) { @@ -99,13 +103,13 @@ class JsonToCompositeNodeReader { parent.addValue(new EmptyNodeWrapper(getNamespaceFor(childName), getLocalNameFor(childName))); } else { - for (JsonElement childOfChildType : childType.getAsJsonArray()) { + for (final JsonElement childOfChildType : childType.getAsJsonArray()) { addChildToParent(childName, childOfChildType, parent); } } } else if (childType.isJsonPrimitive()) { - JsonPrimitive childPrimitive = childType.getAsJsonPrimitive(); - String value = childPrimitive.getAsString().trim(); + final JsonPrimitive childPrimitive = childType.getAsJsonPrimitive(); + final String value = childPrimitive.getAsString().trim(); parent.addValue(new SimpleNodeWrapper(getNamespaceFor(childName), getLocalNameFor(childName), resolveValueOfElement(value))); } else { @@ -133,7 +137,7 @@ class JsonToCompositeNodeReader { if (Iterators.size(it) == 1) { try { return URI.create(maybeURI); - } catch (IllegalArgumentException e) { + } catch (final IllegalArgumentException e) { LOG.debug("Value {} couldn't be interpreted as URI.", maybeURI); } } @@ -153,14 +157,14 @@ class JsonToCompositeNodeReader { private static Object resolveValueOfElement(final String value) { // it could be instance-identifier Built-In Type if (!value.isEmpty() && value.charAt(0) == '/') { - IdentityValuesDTO resolvedValue = RestUtil.asInstanceIdentifier(value, new PrefixMapingFromJson()); + final IdentityValuesDTO resolvedValue = RestUtil.asInstanceIdentifier(value, new PrefixMapingFromJson()); if (resolvedValue != null) { return resolvedValue; } } // it could be identityref Built-In Type therefore it is necessary to look at value as module_name:local_name - URI namespace = getNamespaceFor(value); + final URI namespace = getNamespaceFor(value); if (namespace != null) { return new IdentityValuesDTO(namespace.toString(), getLocalNameFor(value), null, value); } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/NormalizedNodeJsonBodyWriter.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/NormalizedNodeJsonBodyWriter.java index 7a879f3377..03aa31680b 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/NormalizedNodeJsonBodyWriter.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/NormalizedNodeJsonBodyWriter.java @@ -60,17 +60,16 @@ public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter httpHeaders, final OutputStream entityStream) throws IOException, WebApplicationException { NormalizedNode data = t.getData(); - InstanceIdentifierContext context = t.getInstanceIdentifierContext(); - DataSchemaNode schema = context.getSchemaNode(); + final InstanceIdentifierContext context = t.getInstanceIdentifierContext(); + final DataSchemaNode schema = context.getSchemaNode(); SchemaPath path = context.getSchemaNode().getPath(); - OutputStreamWriter outputWriter = new OutputStreamWriter(entityStream, Charsets.UTF_8); + final OutputStreamWriter outputWriter = new OutputStreamWriter(entityStream, Charsets.UTF_8); if (data == null) { throw new RestconfDocumentedException(Response.Status.NOT_FOUND); } boolean isDataRoot = false; URI initialNs = null; - outputWriter.write('{'); if (SchemaPath.ROOT.equals(path)) { isDataRoot = true; } else { @@ -80,8 +79,8 @@ public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter> iterator = data.getValue().iterator(); + private void writeDataRoot(final OutputStreamWriter outputWriter, final NormalizedNodeWriter nnWriter, final ContainerNode data) throws IOException { + final Iterator> iterator = data.getValue().iterator(); while(iterator.hasNext()) { - DataContainerChild child = iterator.next(); + final DataContainerChild child = iterator.next(); nnWriter.write(child); nnWriter.flush(); } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfApplication.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfApplication.java index c9496af4c8..9ab8fa8401 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfApplication.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfApplication.java @@ -11,6 +11,9 @@ import com.google.common.collect.ImmutableSet; import java.util.HashSet; import java.util.Set; import javax.ws.rs.core.Application; +import org.opendaylight.controller.md.sal.rest.schema.SchemaExportContentYangBodyWriter; +import org.opendaylight.controller.md.sal.rest.schema.SchemaExportContentYinBodyWriter; +import org.opendaylight.controller.md.sal.rest.schema.SchemaRetrievalServiceImpl; import org.opendaylight.controller.sal.restconf.impl.BrokerFacade; import org.opendaylight.controller.sal.restconf.impl.ControllerContext; import org.opendaylight.controller.sal.restconf.impl.RestconfImpl; @@ -26,20 +29,24 @@ public class RestconfApplication extends Application { .add(JsonNormalizedNodeBodyReader.class) .add(NormalizedNodeJsonBodyWriter.class) .add(NormalizedNodeXmlBodyWriter.class) + .add(SchemaExportContentYinBodyWriter.class) + .add(SchemaExportContentYangBodyWriter.class) .build(); } @Override public Set getSingletons() { - Set singletons = new HashSet<>(); - ControllerContext controllerContext = ControllerContext.getInstance(); - BrokerFacade brokerFacade = BrokerFacade.getInstance(); - RestconfImpl restconfImpl = RestconfImpl.getInstance(); + final Set singletons = new HashSet<>(); + final ControllerContext controllerContext = ControllerContext.getInstance(); + final BrokerFacade brokerFacade = BrokerFacade.getInstance(); + final RestconfImpl restconfImpl = RestconfImpl.getInstance(); + final SchemaRetrievalServiceImpl schemaRetrieval = new SchemaRetrievalServiceImpl(controllerContext); restconfImpl.setBroker(brokerFacade); restconfImpl.setControllerContext(controllerContext); singletons.add(controllerContext); singletons.add(brokerFacade); - singletons.add(StatisticsRestconfServiceWrapper.getInstance()); + singletons.add(schemaRetrieval); + singletons.add(new RestconfCompositeWrapper(StatisticsRestconfServiceWrapper.getInstance(), schemaRetrieval)); singletons.add(StructuredDataToXmlProvider.INSTANCE); singletons.add(StructuredDataToJsonProvider.INSTANCE); singletons.add(JsonToCompositeNodeProvider.INSTANCE); diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfCompositeWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfCompositeWrapper.java new file mode 100644 index 0000000000..bac967cfaf --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfCompositeWrapper.java @@ -0,0 +1,108 @@ +package org.opendaylight.controller.sal.rest.impl; + +import com.google.common.base.Preconditions; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import org.opendaylight.controller.md.sal.rest.schema.SchemaExportContext; +import org.opendaylight.controller.md.sal.rest.schema.SchemaRetrievalService; +import org.opendaylight.controller.sal.rest.api.RestconfService; +import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext; +import org.opendaylight.controller.sal.restconf.impl.StructuredData; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; + +public class RestconfCompositeWrapper implements RestconfService, SchemaRetrievalService { + + private final RestconfService restconf; + private final SchemaRetrievalService schema; + + public RestconfCompositeWrapper(final RestconfService restconf, final SchemaRetrievalService schema) { + this.restconf = Preconditions.checkNotNull(restconf); + this.schema = Preconditions.checkNotNull(schema); + } + + @Override + public Object getRoot() { + return restconf.getRoot(); + } + + @Override + public NormalizedNodeContext getModules(final UriInfo uriInfo) { + return restconf.getModules(uriInfo); + } + + @Override + public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) { + return restconf.getModules(identifier, uriInfo); + } + + @Override + public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) { + return restconf.getModule(identifier, uriInfo); + } + + @Override + public StructuredData getOperations(final UriInfo uriInfo) { + return restconf.getOperations(uriInfo); + } + + @Override + public StructuredData getOperations(final String identifier, final UriInfo uriInfo) { + return restconf.getOperations(identifier, uriInfo); + } + + @Override + public StructuredData invokeRpc(final String identifier, final CompositeNode payload, final UriInfo uriInfo) { + return restconf.invokeRpc(identifier, payload, uriInfo); + } + + @Override + public StructuredData invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) { + return restconf.invokeRpc(identifier, noPayload, uriInfo); + } + + @Override + public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) { + return restconf.readConfigurationData(identifier, uriInfo); + } + + @Override + public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo uriInfo) { + return restconf.readOperationalData(identifier, uriInfo); + } + + @Override + public Response updateConfigurationData(final String identifier, final Node payload) { + return restconf.updateConfigurationData(identifier, payload); + } + + @Override + public Response createConfigurationData(final String identifier, final Node payload) { + return restconf.createConfigurationData(identifier, payload); + } + + @Override + public Response createConfigurationData(final Node payload) { + return restconf.createConfigurationData(payload); + } + + @Override + public Response deleteConfigurationData(final String identifier) { + return restconf.deleteConfigurationData(identifier); + } + + @Override + public Response subscribeToStream(final String identifier, final UriInfo uriInfo) { + return restconf.subscribeToStream(identifier, uriInfo); + } + + @Override + public StructuredData getAvailableStreams(final UriInfo uriInfo) { + return restconf.getAvailableStreams(uriInfo); + } + + @Override + public SchemaExportContext getSchema(final String mountId) { + return schema.getSchema(mountId); + } +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java index 063d2f51af..13dbf26689 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java @@ -28,6 +28,9 @@ import org.opendaylight.controller.sal.restconf.impl.StructuredData; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +/** + * @deprecated class will be removed in Lithium release + */ @Provider @Produces({ Draft02.MediaTypes.API + RestconfService.JSON, Draft02.MediaTypes.DATA + RestconfService.JSON, Draft02.MediaTypes.OPERATION + RestconfService.JSON, MediaType.APPLICATION_JSON }) @@ -48,19 +51,19 @@ public enum StructuredDataToJsonProvider implements MessageBodyWriter type, final Type genericType, final Annotation[] annotations, final MediaType mediaType, final MultivaluedMap httpHeaders, final OutputStream entityStream) throws IOException, WebApplicationException { - CompositeNode data = t.getData(); + final CompositeNode data = t.getData(); if (data == null) { throw new RestconfDocumentedException(Response.Status.NOT_FOUND); } - JsonWriter writer = new JsonWriter(new OutputStreamWriter(entityStream, Charsets.UTF_8)); + final JsonWriter writer = new JsonWriter(new OutputStreamWriter(entityStream, Charsets.UTF_8)); if (t.isPrettyPrintMode()) { writer.setIndent(" "); } else { writer.setIndent(""); } - JsonMapper jsonMapper = new JsonMapper(t.getMountPoint()); + final JsonMapper jsonMapper = new JsonMapper(t.getMountPoint()); jsonMapper.write(writer, data, (DataNodeContainer) t.getSchema()); writer.flush(); } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java index 703a2a4634..fcb7b1de91 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java @@ -37,6 +37,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; +/** + * @deprecated class will be removed in Lithium release + */ @Provider @Produces({ Draft02.MediaTypes.API + RestconfService.XML, Draft02.MediaTypes.DATA + RestconfService.XML, Draft02.MediaTypes.OPERATION + RestconfService.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @@ -51,7 +54,7 @@ public enum StructuredDataToXmlProvider implements MessageBodyWriter httpHeaders, final OutputStream entityStream) throws IOException, WebApplicationException { - CompositeNode data = t.getData(); + final CompositeNode data = t.getData(); if (data == null) { throw new RestconfDocumentedException(Response.Status.NOT_FOUND); } @@ -96,7 +99,7 @@ public enum StructuredDataToXmlProvider implements MessageBodyWriter> { @Override public Node readFrom(final Class> type, final Type genericType, final Annotation[] annotations, - MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) + final MediaType mediaType, final MultivaluedMap httpHeaders, final InputStream entityStream) throws IOException, WebApplicationException { - XmlToCompositeNodeReader xmlReader = new XmlToCompositeNodeReader(); + final XmlToCompositeNodeReader xmlReader = new XmlToCompositeNodeReader(); try { return xmlReader.read(entityStream); } catch (XMLStreamException | UnsupportedFormatException e) { diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToCompositeNodeReader.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToCompositeNodeReader.java index d71a12ff74..c9a09552ad 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToCompositeNodeReader.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToCompositeNodeReader.java @@ -8,7 +8,6 @@ package org.opendaylight.controller.sal.rest.impl; import static com.google.common.base.Preconditions.checkArgument; - import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; @@ -28,6 +27,9 @@ import org.opendaylight.controller.sal.restconf.impl.NodeWrapper; import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper; import org.opendaylight.yangtools.yang.data.api.Node; +/** + * @deprecated class will be removed in Lithium release + */ @Deprecated public class XmlToCompositeNodeReader { @@ -50,7 +52,7 @@ public class XmlToCompositeNodeReader { eventReader = xmlInputFactory.createXMLEventReader(entityStream); if (eventReader.hasNext()) { - XMLEvent element = eventReader.peek(); + final XMLEvent element = eventReader.peek(); if (element.isStartDocument()) { eventReader.nextEvent(); } @@ -126,7 +128,7 @@ public class XmlToCompositeNodeReader { private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException { checkArgument(event != null, "XML Event cannot be NULL!"); if (event.isStartElement()) { - XMLEvent innerEvent = skipCommentsAndWhitespace(); + final XMLEvent innerEvent = skipCommentsAndWhitespace(); if (innerEvent != null && (innerEvent.isCharacters() || innerEvent.isEndElement())) { return true; } @@ -137,7 +139,7 @@ public class XmlToCompositeNodeReader { private boolean isCompositeNodeEvent(final XMLEvent event) throws XMLStreamException { checkArgument(event != null, "XML Event cannot be NULL!"); if (event.isStartElement()) { - XMLEvent innerEvent = skipCommentsAndWhitespace(); + final XMLEvent innerEvent = skipCommentsAndWhitespace(); if (innerEvent != null) { if (innerEvent.isStartElement()) { return true; @@ -149,14 +151,14 @@ public class XmlToCompositeNodeReader { private XMLEvent skipCommentsAndWhitespace() throws XMLStreamException { while (eventReader.hasNext()) { - XMLEvent event = eventReader.peek(); + final XMLEvent event = eventReader.peek(); if (event.getEventType() == XMLStreamConstants.COMMENT) { eventReader.nextEvent(); continue; } if (event.isCharacters()) { - Characters chars = event.asCharacters(); + final Characters chars = event.asCharacters(); if (chars.isWhiteSpace()) { eventReader.nextEvent(); continue; @@ -175,7 +177,7 @@ public class XmlToCompositeNodeReader { private NodeWrapper> resolveSimpleNodeFromStartElement(final StartElement startElement) throws XMLStreamException { checkArgument(startElement != null, "Start Element cannot be NULL!"); - String data = getValueOf(startElement); + final String data = getValueOf(startElement); if (data == null) { return new EmptyNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement)); } @@ -224,23 +226,23 @@ public class XmlToCompositeNodeReader { } private URI getNamespaceFor(final StartElement startElement) { - String namespaceURI = startElement.getName().getNamespaceURI(); + final String namespaceURI = startElement.getName().getNamespaceURI(); return namespaceURI.isEmpty() ? null : URI.create(namespaceURI); } private Object resolveValueOfElement(final String value, final StartElement startElement) { // it could be instance-identifier Built-In Type if (value.startsWith("/")) { - IdentityValuesDTO iiValue = RestUtil.asInstanceIdentifier(value, new RestUtil.PrefixMapingFromXml( + final IdentityValuesDTO iiValue = RestUtil.asInstanceIdentifier(value, new RestUtil.PrefixMapingFromXml( startElement)); if (iiValue != null) { return iiValue; } } // it could be identityref Built-In Type - String[] namespaceAndValue = value.split(":"); + final String[] namespaceAndValue = value.split(":"); if (namespaceAndValue.length == 2) { - String namespace = startElement.getNamespaceContext().getNamespaceURI(namespaceAndValue[0]); + final String namespace = startElement.getNamespaceContext().getNamespaceURI(namespaceAndValue[0]); if (namespace != null && !namespace.isEmpty()) { return new IdentityValuesDTO(namespace, namespaceAndValue[1], namespaceAndValue[0], value); } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/CompositeNodeWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/CompositeNodeWrapper.java index 206dbdeab6..c3edfccb98 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/CompositeNodeWrapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/CompositeNodeWrapper.java @@ -23,6 +23,10 @@ import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.api.SimpleNode; import org.opendaylight.yangtools.yang.data.impl.NodeFactory; +/** + * @deprecated class will be removed in Lithium release + */ +@Deprecated public final class CompositeNodeWrapper implements NodeWrapper, CompositeNode { private MutableCompositeNode compositeNode; @@ -102,8 +106,8 @@ public final class CompositeNodeWrapper implements NodeWrapper, C name = new QName(namespace, localName); } - List> nodeValues = new ArrayList<>(values.size()); - for (NodeWrapper nodeWrapper : values) { + final List> nodeValues = new ArrayList<>(values.size()); + for (final NodeWrapper nodeWrapper : values) { nodeValues.add(nodeWrapper.unwrap()); } compositeNode = NodeFactory.createMutableCompositeNode(name, null, nodeValues, null, null); diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/EmptyNodeWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/EmptyNodeWrapper.java index a3d44d3572..5a310e8af2 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/EmptyNodeWrapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/EmptyNodeWrapper.java @@ -15,6 +15,10 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.impl.NodeFactory; +/** + * @deprecated class will be removed in Lithium release + */ +@Deprecated public final class EmptyNodeWrapper implements NodeWrapper>, Node { private Node unwrapped; diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NodeWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NodeWrapper.java index 9637a36268..09c7835ef6 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NodeWrapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NodeWrapper.java @@ -11,6 +11,10 @@ import java.net.URI; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.Node; +/** + * @deprecated class will be removed in Lithium release + */ +@Deprecated public interface NodeWrapper> { void setQname(QName name); diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java index dd51246493..f44ce95155 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java @@ -63,13 +63,22 @@ import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.api.SimpleNode; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException; import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.data.impl.NodeFactory; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder; import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; @@ -163,11 +172,11 @@ public class RestconfImpl implements RestconfService { try { EVENT_SUBSCRIPTION_AUGMENT_REVISION = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08"); NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME ); - } catch (ParseException e) { + } catch (final ParseException e) { throw new RestconfDocumentedException( "It wasn't possible to convert revision date of sal-remote-augment to date", ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED); - } catch (URISyntaxException e) { + } catch (final URISyntaxException e) { throw new RestconfDocumentedException( "It wasn't possible to create instance of URI class with "+NETCONF_BASE+" URI", ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED); @@ -190,105 +199,114 @@ public class RestconfImpl implements RestconfService { } @Override - public StructuredData getModules(final UriInfo uriInfo) { - final Module restconfModule = this.getRestconfModule(); + public NormalizedNodeContext getModules(final UriInfo uriInfo) { + final Set allModules = controllerContext.getAllModules(); + final MapNode allModuleMap = makeModuleMapNode(allModules); - final List> modulesAsData = new ArrayList>(); - final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule, - Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); + final SchemaContext schemaContext = controllerContext.getGlobalSchema(); - Set allModules = this.controllerContext.getAllModules(); - for (final Module module : allModules) { - CompositeNode moduleCompositeNode = this.toModuleCompositeNode(module, moduleSchemaNode); - modulesAsData.add(moduleCompositeNode); - } + final Module restconfModule = getRestconfModule(); + final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( + restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); + Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode); + + final DataContainerNodeAttrBuilder moduleContainerBuilder = + Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode); + moduleContainerBuilder.withChild(allModuleMap); - final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule, - Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); - QName qName = modulesSchemaNode.getQName(); - final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData); - return new StructuredData(modulesNode, modulesSchemaNode, null, parsePrettyPrintParameter(uriInfo)); + return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode, + null, schemaContext), moduleContainerBuilder.build()); } + /** + * Valid only for mount point + */ @Override - public StructuredData getAvailableStreams(final UriInfo uriInfo) { - Set availableStreams = Notificator.getStreamNames(); - - final List> streamsAsData = new ArrayList>(); - Module restconfModule = this.getRestconfModule(); - final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule, - Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE); - for (final String streamName : availableStreams) { - streamsAsData.add(this.toStreamCompositeNode(streamName, streamSchemaNode)); + public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) { + Preconditions.checkNotNull(identifier); + if ( ! identifier.contains(ControllerContext.MOUNT)) { + final String errMsg = "URI has bad format. If modules behind mount point should be showed," + + " URI has to end with " + ControllerContext.MOUNT; + throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } - final DataSchemaNode streamsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule, - Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE); - QName qName = streamsSchemaNode.getQName(); - final CompositeNode streamsNode = NodeFactory.createImmutableCompositeNode(qName, null, streamsAsData); - return new StructuredData(streamsNode, streamsSchemaNode, null, parsePrettyPrintParameter(uriInfo)); + final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier); + final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint(); + final Set modules = controllerContext.getAllModules(mountPoint); + final SchemaContext schemaContext = mountPoint.getSchemaContext(); + final MapNode mountPointModulesMap = makeModuleMapNode(modules); + + final Module restconfModule = getRestconfModule(); + final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( + restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); + Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode); + + final DataContainerNodeAttrBuilder moduleContainerBuilder = + Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode); + moduleContainerBuilder.withChild(mountPointModulesMap); + + return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode, + mountPoint, schemaContext), moduleContainerBuilder.build()); } @Override - public StructuredData getModules(final String identifier, final UriInfo uriInfo) { - Set modules = null; + public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) { + Preconditions.checkNotNull(identifier); + final QName moduleNameAndRevision = getModuleNameAndRevision(identifier); + Module module = null; DOMMountPoint mountPoint = null; + final SchemaContext schemaContext; if (identifier.contains(ControllerContext.MOUNT)) { - InstanceIdentifierContext mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); + final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); - modules = this.controllerContext.getAllModules(mountPoint); + module = controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision); + schemaContext = mountPoint.getSchemaContext(); } else { - throw new RestconfDocumentedException( - "URI has bad format. If modules behind mount point should be showed, URI has to end with " - + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + module = controllerContext.findModuleByNameAndRevision(moduleNameAndRevision); + schemaContext = controllerContext.getGlobalSchema(); } - final List> modulesAsData = new ArrayList>(); - Module restconfModule = this.getRestconfModule(); - final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule, - Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); - - for (final Module module : modules) { - modulesAsData.add(this.toModuleCompositeNode(module, moduleSchemaNode)); + if (module == null) { + final String errMsg = "Module with name '" + moduleNameAndRevision.getLocalName() + + "' and revision '" + moduleNameAndRevision.getRevision() + "' was not found."; + throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); } - final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule, - Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); - QName qName = modulesSchemaNode.getQName(); - final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData); - return new StructuredData(modulesNode, modulesSchemaNode, mountPoint, parsePrettyPrintParameter(uriInfo)); + final Module restconfModule = getRestconfModule(); + final Set modules = Collections.singleton(module); + final MapNode moduleMap = makeModuleMapNode(modules); + + final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( + restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); + Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode); + + return new NormalizedNodeContext(new InstanceIdentifierContext(null, moduleSchemaNode, mountPoint, + schemaContext), moduleMap); } @Override - public StructuredData getModule(final String identifier, final UriInfo uriInfo) { - final QName moduleNameAndRevision = this.getModuleNameAndRevision(identifier); - Module module = null; - DOMMountPoint mountPoint = null; - if (identifier.contains(ControllerContext.MOUNT)) { - InstanceIdentifierContext mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); - mountPoint = mountPointIdentifier.getMountPoint(); - module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision); - } else { - module = this.controllerContext.findModuleByNameAndRevision(moduleNameAndRevision); - } + public StructuredData getAvailableStreams(final UriInfo uriInfo) { + final Set availableStreams = Notificator.getStreamNames(); - if (module == null) { - throw new RestconfDocumentedException("Module with name '" + moduleNameAndRevision.getLocalName() - + "' and revision '" + moduleNameAndRevision.getRevision() + "' was not found.", - ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); + final List> streamsAsData = new ArrayList>(); + final Module restconfModule = getRestconfModule(); + final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule, + Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE); + for (final String streamName : availableStreams) { + streamsAsData.add(toStreamCompositeNode(streamName, streamSchemaNode)); } - Module restconfModule = this.getRestconfModule(); - final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule, - Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); - final CompositeNode moduleNode = this.toModuleCompositeNode(module, moduleSchemaNode); - return new StructuredData(moduleNode, moduleSchemaNode, mountPoint, parsePrettyPrintParameter(uriInfo)); + final DataSchemaNode streamsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule, + Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE); + final QName qName = streamsSchemaNode.getQName(); + final CompositeNode streamsNode = NodeFactory.createImmutableCompositeNode(qName, null, streamsAsData); + return new StructuredData(streamsNode, streamsSchemaNode, null, parsePrettyPrintParameter(uriInfo)); } @Override public StructuredData getOperations(final UriInfo uriInfo) { - Set allModules = this.controllerContext.getAllModules(); - return this.operationsFromModulesToStructuredData(allModules, null, parsePrettyPrintParameter(uriInfo)); + final Set allModules = controllerContext.getAllModules(); + return operationsFromModulesToStructuredData(allModules, null, parsePrettyPrintParameter(uriInfo)); } @Override @@ -296,56 +314,56 @@ public class RestconfImpl implements RestconfService { Set modules = null; DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { - InstanceIdentifierContext mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); + final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); - modules = this.controllerContext.getAllModules(mountPoint); + modules = controllerContext.getAllModules(mountPoint); } else { throw new RestconfDocumentedException( "URI has bad format. If operations behind mount point should be showed, URI has to end with " + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } - return this.operationsFromModulesToStructuredData(modules, mountPoint, parsePrettyPrintParameter(uriInfo)); + return operationsFromModulesToStructuredData(modules, mountPoint, parsePrettyPrintParameter(uriInfo)); } private StructuredData operationsFromModulesToStructuredData(final Set modules, final DOMMountPoint mountPoint, final boolean prettyPrint) { final List> operationsAsData = new ArrayList>(); - Module restconfModule = this.getRestconfModule(); + final Module restconfModule = getRestconfModule(); final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE); - QName qName = operationsSchemaNode.getQName(); - SchemaPath path = operationsSchemaNode.getPath(); - ContainerSchemaNodeBuilder containerSchemaNodeBuilder = new ContainerSchemaNodeBuilder( + final QName qName = operationsSchemaNode.getQName(); + final SchemaPath path = operationsSchemaNode.getPath(); + final ContainerSchemaNodeBuilder containerSchemaNodeBuilder = new ContainerSchemaNodeBuilder( Draft02.RestConfModule.NAME, 0, qName, path); final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder; for (final Module module : modules) { - Set rpcs = module.getRpcs(); + final Set rpcs = module.getRpcs(); for (final RpcDefinition rpc : rpcs) { - QName rpcQName = rpc.getQName(); - SimpleNode immutableSimpleNode = NodeFactory. createImmutableSimpleNode(rpcQName, null, + final QName rpcQName = rpc.getQName(); + final SimpleNode immutableSimpleNode = NodeFactory. createImmutableSimpleNode(rpcQName, null, null); operationsAsData.add(immutableSimpleNode); - String name = module.getName(); - LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName, + final String name = module.getName(); + final LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName, SchemaPath.create(true, QName.create("dummy"))); final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder; fakeRpcSchemaNode.setAugmenting(true); - EmptyType instance = EmptyType.getInstance(); + final EmptyType instance = EmptyType.getInstance(); fakeRpcSchemaNode.setType(instance); fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build()); } } final CompositeNode operationsNode = NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData); - ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build(); + final ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build(); return new StructuredData(operationsNode, schemaNode, mountPoint, prettyPrint); } private Module getRestconfModule() { - Module restconfModule = controllerContext.getRestconfModule(); + final Module restconfModule = controllerContext.getRestconfModule(); if (restconfModule == null) { throw new RestconfDocumentedException("ietf-restconf module was not found.", ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED); @@ -363,8 +381,8 @@ public class RestconfImpl implements RestconfService { moduleNameAndRevision = identifier; } - Splitter splitter = Splitter.on("/").omitEmptyStrings(); - Iterable split = splitter.split(moduleNameAndRevision); + final Splitter splitter = Splitter.on("/").omitEmptyStrings(); + final Iterable split = splitter.split(moduleNameAndRevision); final List pathArgs = Lists. newArrayList(split); if (pathArgs.size() < 2) { throw new RestconfDocumentedException( @@ -374,10 +392,10 @@ public class RestconfImpl implements RestconfService { try { final String moduleName = pathArgs.get(0); - String revision = pathArgs.get(1); + final String revision = pathArgs.get(1); final Date moduleRevision = REVISION_FORMAT.parse(revision); return QName.create(null, moduleRevision, moduleName); - } catch (ParseException e) { + } catch (final ParseException e) { throw new RestconfDocumentedException("URI has bad format. It should be \'moduleName/yyyy-MM-dd\'", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } @@ -428,7 +446,7 @@ public class RestconfImpl implements RestconfService { instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) moduleSchemaNode), "revision"); final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); - Date _revision = module.getRevision(); + final Date _revision = module.getRevision(); moduleNodeValues.add(NodeFactory. createImmutableSimpleNode(revisionSchemaNode.getQName(), null, REVISION_FORMAT.format(_revision))); @@ -456,9 +474,9 @@ public class RestconfImpl implements RestconfService { @Override public StructuredData invokeRpc(final String identifier, final CompositeNode payload, final UriInfo uriInfo) { - final RpcExecutor rpc = this.resolveIdentifierInInvokeRpc(identifier); - QName rpcName = rpc.getRpcDefinition().getQName(); - URI rpcNamespace = rpcName.getNamespace(); + final RpcExecutor rpc = resolveIdentifierInInvokeRpc(identifier); + final QName rpcName = rpc.getRpcDefinition().getQName(); + final URI rpcNamespace = rpcName.getNamespace(); if (Objects.equal(rpcNamespace.toString(), SAL_REMOTE_NAMESPACE) && Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) { return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition(), parsePrettyPrintParameter(uriInfo)); @@ -500,7 +518,7 @@ public class RestconfImpl implements RestconfService { final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue); String streamName = null; if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) { - String fullRestconfIdentifier = this.controllerContext.toFullRestconfIdentifier(pathIdentifier); + final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier); LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class, DATASTORE_PARAM_NAME); @@ -547,12 +565,12 @@ public class RestconfImpl implements RestconfService { DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { // mounted RPC call - look up mount instance. - InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier); + final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointId.getMountPoint(); - int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT) + final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT) + ControllerContext.MOUNT.length() + 1; - String remoteRpcName = identifier.substring(startOfRemoteRpcName); + final String remoteRpcName = identifier.substring(startOfRemoteRpcName); identifierEncoded = remoteRpcName; } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) { @@ -591,9 +609,9 @@ public class RestconfImpl implements RestconfService { + " couldn't be splitted to 2 parts (module:rpc name)", ErrorType.APPLICATION, ErrorTag.INVALID_VALUE); } - for (Module module : schemaContext.getModules()) { + for (final Module module : schemaContext.getModules()) { if (module.getName().equals(splittedIdentifier[0])) { - for (RpcDefinition rpcDefinition : module.getRpcs()) { + for (final RpcDefinition rpcDefinition : module.getRpcs()) { if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) { return rpcDefinition; } @@ -609,18 +627,18 @@ public class RestconfImpl implements RestconfService { } CompositeNode rpcRequest = null; - RpcDefinition rpc = rpcExecutor.getRpcDefinition(); - QName rpcName = rpc.getQName(); + final RpcDefinition rpc = rpcExecutor.getRpcDefinition(); + final QName rpcName = rpc.getQName(); if (payload == null) { rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null); } else { final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null); - List> input = Collections.> singletonList(value); + final List> input = Collections.> singletonList(value); rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null); } - RpcResult rpcResult = rpcExecutor.invokeRpc(rpcRequest); + final RpcResult rpcResult = rpcExecutor.invokeRpc(rpcRequest); checkRpcSuccessAndThrowException(rpcResult); @@ -646,7 +664,7 @@ public class RestconfImpl implements RestconfService { @Override public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) { final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); - DOMMountPoint mountPoint = iiWithData.getMountPoint(); + final DOMMountPoint mountPoint = iiWithData.getMountPoint(); NormalizedNode data = null; YangInstanceIdentifier normalizedII; if (mountPoint != null) { @@ -667,9 +685,9 @@ public class RestconfImpl implements RestconfService { } if (node instanceof CompositeNode) { - ImmutableList.Builder> newChildNodes = ImmutableList.> builder(); + final ImmutableList.Builder> newChildNodes = ImmutableList.> builder(); if (depth > 1) { - for (Node childNode : ((CompositeNode) node).getValue()) { + for (final Node childNode : ((CompositeNode) node).getValue()) { newChildNodes.add(pruneDataAtDepth(childNode, depth - 1)); } } @@ -681,13 +699,13 @@ public class RestconfImpl implements RestconfService { } private Integer parseDepthParameter(final UriInfo info) { - String param = info.getQueryParameters(false).getFirst(UriParameters.DEPTH.toString()); + final String param = info.getQueryParameters(false).getFirst(UriParameters.DEPTH.toString()); if (Strings.isNullOrEmpty(param) || "unbounded".equals(param)) { return null; } try { - Integer depth = Integer.valueOf(param); + final Integer depth = Integer.valueOf(param); if (depth < 1) { throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depth, null, @@ -695,7 +713,7 @@ public class RestconfImpl implements RestconfService { } return depth; - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + e.getMessage(), null, "The depth parameter must be an integer > 1 or \"unbounded\"")); @@ -705,7 +723,7 @@ public class RestconfImpl implements RestconfService { @Override public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo info) { final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); - DOMMountPoint mountPoint = iiWithData.getMountPoint(); + final DOMMountPoint mountPoint = iiWithData.getMountPoint(); NormalizedNode data = null; YangInstanceIdentifier normalizedII; if (mountPoint != null) { @@ -721,17 +739,17 @@ public class RestconfImpl implements RestconfService { } private boolean parsePrettyPrintParameter(final UriInfo info) { - String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString()); + final String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString()); return Boolean.parseBoolean(param); } @Override public Response updateConfigurationData(final String identifier, final Node payload) { - final InstanceIdentifierContext iiWithData = this.controllerContext.toInstanceIdentifier(identifier); + final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); validateInput(iiWithData.getSchemaNode(), payload); - DOMMountPoint mountPoint = iiWithData.getMountPoint(); + final DOMMountPoint mountPoint = iiWithData.getMountPoint(); validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier()); final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint); validateListKeysEqualityInPayloadAndUri(iiWithData, value); @@ -772,7 +790,7 @@ public class RestconfImpl implements RestconfService { } break; - } catch (TransactionCommitFailedException e) { + } catch (final TransactionCommitFailedException e) { if(e instanceof OptimisticLockFailedException) { if(--tries <= 0) { LOG.debug("Got OptimisticLockFailedException on last try - failing"); @@ -834,7 +852,7 @@ public class RestconfImpl implements RestconfService { private void isEqualUriAndPayloadKeyValues(final Map uriKeyValues, final CompositeNode payload, final List keyDefinitions) { - for (QName keyDefinition : keyDefinitions) { + for (final QName keyDefinition : keyDefinitions) { final Object uriKeyValue = uriKeyValues.get(keyDefinition); // should be caught during parsing URI to InstanceIdentifier if (uriKeyValue == null) { @@ -847,7 +865,7 @@ public class RestconfImpl implements RestconfService { + " in the message body.", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING); } - Object payloadKeyValue = payloadKeyValues.iterator().next().getValue(); + final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue(); if (!uriKeyValue.equals(payloadKeyValue)) { throw new RestconfDocumentedException("The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() + "' specified in the URI doesn't match the value '" @@ -863,7 +881,7 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } - URI payloadNS = this.namespace(payload); + final URI payloadNS = namespace(payload); if (payloadNS == null) { throw new RestconfDocumentedException( "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)", @@ -872,27 +890,27 @@ public class RestconfImpl implements RestconfService { InstanceIdentifierContext iiWithData = null; CompositeNode value = null; - if (this.representsMountPointRootData(payload)) { + if (representsMountPointRootData(payload)) { // payload represents mount point data and URI represents path to the mount point - if (this.endsWithMountPoint(identifier)) { + if (endsWithMountPoint(identifier)) { throw new RestconfDocumentedException("URI has bad format. URI should be without \"" + ControllerContext.MOUNT + "\" for POST operation.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } - final String completeIdentifier = this.addMountPointIdentifier(identifier); - iiWithData = this.controllerContext.toInstanceIdentifier(completeIdentifier); + final String completeIdentifier = addMountPointIdentifier(identifier); + iiWithData = controllerContext.toInstanceIdentifier(completeIdentifier); value = this.normalizeNode(payload, iiWithData.getSchemaNode(), iiWithData.getMountPoint()); } else { - final InstanceIdentifierContext incompleteInstIdWithData = this.controllerContext + final InstanceIdentifierContext incompleteInstIdWithData = controllerContext .toInstanceIdentifier(identifier); final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode(); - DOMMountPoint mountPoint = incompleteInstIdWithData.getMountPoint(); + final DOMMountPoint mountPoint = incompleteInstIdWithData.getMountPoint(); final Module module = findModule(mountPoint, payload); - String payloadName = this.getName(payload); + final String payloadName = getName(payload); final DataSchemaNode schemaNode = ControllerContext.findInstanceDataChildByNameAndNamespace( parentSchema, payloadName, module.getNamespace()); value = this.normalizeNode(payload, schemaNode, mountPoint); @@ -902,7 +920,7 @@ public class RestconfImpl implements RestconfService { final NormalizedNode datastoreNormalizedData = compositeNodeToDatastoreNormalizedNode(value, iiWithData.getSchemaNode()); - DOMMountPoint mountPoint = iiWithData.getMountPoint(); + final DOMMountPoint mountPoint = iiWithData.getMountPoint(); YangInstanceIdentifier normalizedII; try { @@ -914,9 +932,9 @@ public class RestconfImpl implements RestconfService { normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); broker.commitConfigurationDataPost(normalizedII, datastoreNormalizedData); } - } catch(RestconfDocumentedException e) { + } catch(final RestconfDocumentedException e) { throw e; - } catch (Exception e) { + } catch (final Exception e) { throw new RestconfDocumentedException("Error creating data", e); } @@ -929,7 +947,7 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } - URI payloadNS = this.namespace(payload); + final URI payloadNS = namespace(payload); if (payloadNS == null) { throw new RestconfDocumentedException( "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)", @@ -938,13 +956,13 @@ public class RestconfImpl implements RestconfService { final Module module = this.findModule(null, payload); - String payloadName = this.getName(payload); + final String payloadName = getName(payload); final DataSchemaNode schemaNode = ControllerContext.findInstanceDataChildByNameAndNamespace(module, payloadName, module.getNamespace()); final CompositeNode value = this.normalizeNode(payload, schemaNode, null); - final InstanceIdentifierContext iiWithData = this.addLastIdentifierFromData(null, value, schemaNode,ControllerContext.getInstance().getGlobalSchema()); + final InstanceIdentifierContext iiWithData = addLastIdentifierFromData(null, value, schemaNode,ControllerContext.getInstance().getGlobalSchema()); final NormalizedNode datastoreNormalizedData = compositeNodeToDatastoreNormalizedNode(value, schemaNode); - DOMMountPoint mountPoint = iiWithData.getMountPoint(); + final DOMMountPoint mountPoint = iiWithData.getMountPoint(); YangInstanceIdentifier normalizedII; try { @@ -957,9 +975,9 @@ public class RestconfImpl implements RestconfService { normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); broker.commitConfigurationDataPost(normalizedII, datastoreNormalizedData); } - } catch(RestconfDocumentedException e) { + } catch(final RestconfDocumentedException e) { throw e; - } catch (Exception e) { + } catch (final Exception e) { throw new RestconfDocumentedException("Error creating data", e); } @@ -969,7 +987,7 @@ public class RestconfImpl implements RestconfService { @Override public Response deleteConfigurationData(final String identifier) { final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); - DOMMountPoint mountPoint = iiWithData.getMountPoint(); + final DOMMountPoint mountPoint = iiWithData.getMountPoint(); YangInstanceIdentifier normalizedII; try { @@ -981,7 +999,7 @@ public class RestconfImpl implements RestconfService { normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); broker.commitConfigurationDataDelete(normalizedII).get(); } - } catch (Exception e) { + } catch (final Exception e) { final Optional searchedException = Iterables.tryFind(Throwables.getCausalChain(e), Predicates.instanceOf(ModifiedNodeDoesNotExistException.class)); if (searchedException.isPresent()) { @@ -1013,14 +1031,14 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); } - Map paramToValues = resolveValuesFromUri(identifier); - LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class, + final Map paramToValues = resolveValuesFromUri(identifier); + final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class, paramToValues.get(DATASTORE_PARAM_NAME)); if (datastore == null) { throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)", ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE); } - DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME)); + final DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME)); if (scope == null) { throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)", ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE); @@ -1031,12 +1049,12 @@ public class RestconfImpl implements RestconfService { final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); int notificationPort = NOTIFICATION_PORT; try { - WebSocketServer webSocketServerInstance = WebSocketServer.getInstance(); + final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance(); notificationPort = webSocketServerInstance.getPort(); - } catch (NullPointerException e) { + } catch (final NullPointerException e) { WebSocketServer.createInstance(NOTIFICATION_PORT); } - UriBuilder port = uriBuilder.port(notificationPort); + final UriBuilder port = uriBuilder.port(notificationPort); final URI uriToWebsocketServer = port.replacePath(streamName).build(); return Response.status(Status.OK).location(uriToWebsocketServer).build(); @@ -1051,13 +1069,13 @@ public class RestconfImpl implements RestconfService { */ private T parseEnumTypeParameter(final CompositeNode compNode, final Class classDescriptor, final String paramName) { - QNameModule salRemoteAugment = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, + final QNameModule salRemoteAugment = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, EVENT_SUBSCRIPTION_AUGMENT_REVISION); - SimpleNode simpleNode = compNode.getFirstSimpleByName(QName.create(salRemoteAugment, paramName)); + final SimpleNode simpleNode = compNode.getFirstSimpleByName(QName.create(salRemoteAugment, paramName)); if (simpleNode == null) { return null; } - Object rawValue = simpleNode.getValue(); + final Object rawValue = simpleNode.getValue(); if (!(rawValue instanceof String)) { return null; } @@ -1079,9 +1097,9 @@ public class RestconfImpl implements RestconfService { } private T resolveAsEnum(final Class classDescriptor, final String value) { - T[] enumConstants = classDescriptor.getEnumConstants(); + final T[] enumConstants = classDescriptor.getEnumConstants(); if (enumConstants != null) { - for (T enm : classDescriptor.getEnumConstants()) { + for (final T enm : classDescriptor.getEnumConstants()) { if (((Enum) enm).name().equals(value)) { return enm; } @@ -1091,10 +1109,10 @@ public class RestconfImpl implements RestconfService { } private Map resolveValuesFromUri(final String uri) { - Map result = new HashMap<>(); - String[] tokens = uri.split("/"); + final Map result = new HashMap<>(); + final String[] tokens = uri.split("/"); for (int i = 1; i < tokens.length; i++) { - String[] parameterTokens = tokens[i].split("="); + final String[] parameterTokens = tokens[i].split("="); if (parameterTokens.length == 2) { result.put(parameterTokens[0], parameterTokens[1]); } @@ -1107,11 +1125,11 @@ public class RestconfImpl implements RestconfService { if (data instanceof NodeWrapper) { module = findModule(mountPoint, (NodeWrapper) data); } else if (data != null) { - URI namespace = data.getNodeType().getNamespace(); + final URI namespace = data.getNodeType().getNamespace(); if (mountPoint != null) { - module = this.controllerContext.findModuleByNamespace(mountPoint, namespace); + module = controllerContext.findModuleByNamespace(mountPoint, namespace); } else { - module = this.controllerContext.findModuleByNamespace(namespace); + module = controllerContext.findModuleByNamespace(namespace); } } if (module != null) { @@ -1123,19 +1141,19 @@ public class RestconfImpl implements RestconfService { } private Module findModule(final DOMMountPoint mountPoint, final NodeWrapper data) { - URI namespace = data.getNamespace(); + final URI namespace = data.getNamespace(); Preconditions. checkNotNull(namespace); Module module = null; if (mountPoint != null) { - module = this.controllerContext.findModuleByNamespace(mountPoint, namespace); + module = controllerContext.findModuleByNamespace(mountPoint, namespace); if (module == null) { - module = this.controllerContext.findModuleByName(mountPoint, namespace.toString()); + module = controllerContext.findModuleByName(mountPoint, namespace.toString()); } } else { - module = this.controllerContext.findModuleByNamespace(namespace); + module = controllerContext.findModuleByNamespace(namespace); if (module == null) { - module = this.controllerContext.findModuleByName(namespace.toString()); + module = controllerContext.findModuleByName(namespace.toString()); } } @@ -1143,7 +1161,7 @@ public class RestconfImpl implements RestconfService { } private InstanceIdentifierContext addLastIdentifierFromData(final InstanceIdentifierContext identifierWithSchemaNode, - final CompositeNode data, final DataSchemaNode schemaOfData, SchemaContext schemaContext) { + final CompositeNode data, final DataSchemaNode schemaOfData, final SchemaContext schemaContext) { YangInstanceIdentifier instanceIdentifier = null; if (identifierWithSchemaNode != null) { instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier(); @@ -1158,15 +1176,15 @@ public class RestconfImpl implements RestconfService { } if ((schemaOfData instanceof ListSchemaNode)) { - HashMap keys = this.resolveKeysFromData(((ListSchemaNode) schemaOfData), data); + final HashMap keys = resolveKeysFromData(((ListSchemaNode) schemaOfData), data); iiBuilder.nodeWithKey(schemaOfData.getQName(), keys); } else { iiBuilder.node(schemaOfData.getQName()); } - YangInstanceIdentifier instance = iiBuilder.toInstance(); + final YangInstanceIdentifier instance = iiBuilder.toInstance(); DOMMountPoint mountPoint = null; - SchemaContext schemaCtx = null; + final SchemaContext schemaCtx = null; if (identifierWithSchemaNode != null) { mountPoint = identifierWithSchemaNode.getMountPoint(); } @@ -1176,11 +1194,11 @@ public class RestconfImpl implements RestconfService { private HashMap resolveKeysFromData(final ListSchemaNode listNode, final CompositeNode dataNode) { final HashMap keyValues = new HashMap(); - List _keyDefinition = listNode.getKeyDefinition(); + final List _keyDefinition = listNode.getKeyDefinition(); for (final QName key : _keyDefinition) { SimpleNode head = null; - String localName = key.getLocalName(); - List> simpleNodesByName = dataNode.getSimpleNodesByName(localName); + final String localName = key.getLocalName(); + final List> simpleNodesByName = dataNode.getSimpleNodesByName(localName); if (simpleNodesByName != null) { head = Iterables.getFirst(simpleNodesByName, null); } @@ -1207,16 +1225,16 @@ public class RestconfImpl implements RestconfService { } private boolean representsMountPointRootData(final Node data) { - URI namespace = this.namespace(data); + final URI namespace = namespace(data); return (SchemaContext.NAME.getNamespace().equals(namespace) /* * || MOUNT_POINT_MODULE_NAME .equals( namespace . * toString( ) ) */) - && SchemaContext.NAME.getLocalName().equals(this.localName(data)); + && SchemaContext.NAME.getLocalName().equals(localName(data)); } private String addMountPointIdentifier(final String identifier) { - boolean endsWith = identifier.endsWith("/"); + final boolean endsWith = identifier.endsWith("/"); if (endsWith) { return (identifier + ControllerContext.MOUNT); } @@ -1226,7 +1244,7 @@ public class RestconfImpl implements RestconfService { private CompositeNode normalizeNode(final Node node, final DataSchemaNode schema, final DOMMountPoint mountPoint) { if (schema == null) { - String localName = node == null ? null : + final String localName = node == null ? null : node instanceof NodeWrapper ? ((NodeWrapper)node).getLocalName() : node.getNodeType().getLocalName(); @@ -1241,13 +1259,13 @@ public class RestconfImpl implements RestconfService { if ((node instanceof NodeWrapper)) { NodeWrapper nodeWrap = (NodeWrapper) node; - boolean isChangeAllowed = ((NodeWrapper) node).isChangeAllowed(); + final boolean isChangeAllowed = ((NodeWrapper) node).isChangeAllowed(); if (isChangeAllowed) { nodeWrap = topLevelElementAsCompositeNodeWrapper((NodeWrapper) node, schema); try { this.normalizeNode(nodeWrap, schema, null, mountPoint); - } catch (IllegalArgumentException e) { - RestconfDocumentedException restconfDocumentedException = new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } catch (final IllegalArgumentException e) { + final RestconfDocumentedException restconfDocumentedException = new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); restconfDocumentedException.addSuppressed(e); throw restconfDocumentedException; } @@ -1276,7 +1294,7 @@ public class RestconfImpl implements RestconfService { if (nodeBuilder.getQname() != null) { currentAugment = previousAugment; } else { - currentAugment = this.normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint); + currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint); if (nodeBuilder.getQname() == null) { throw new RestconfDocumentedException( "Data has bad format.\nIf data is in XML format then namespace for \"" @@ -1302,8 +1320,8 @@ public class RestconfImpl implements RestconfService { } private void normalizeAnyXmlNode(final CompositeNodeWrapper compositeNode, final AnyXmlSchemaNode schema) { - List> children = compositeNode.getValues(); - for (NodeWrapper child : children) { + final List> children = compositeNode.getValues(); + for (final NodeWrapper child : children) { child.setNamespace(schema.getQName().getNamespace()); if (child instanceof CompositeNodeWrapper) { normalizeAnyXmlNode((CompositeNodeWrapper) child, schema); @@ -1326,15 +1344,15 @@ public class RestconfImpl implements RestconfService { final DOMMountPoint mountPoint) { final Object value = simpleNode.getValue(); Object inputValue = value; - TypeDef typeDef = this.typeDefinition(schema); + final TypeDef typeDef = this.typeDefinition(schema); TypeDefinition typeDefinition = typeDef != null ? typeDef.typedef : null; // For leafrefs, extract the type it is pointing to if(typeDefinition instanceof LeafrefTypeDefinition) { if (schema.getQName().equals(typeDef.qName)) { - typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? this.controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), schema); + typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), schema); } else { - typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? this.controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), typeDef.qName); + typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), typeDef.qName); } } @@ -1345,7 +1363,7 @@ public class RestconfImpl implements RestconfService { Object outputValue = inputValue; if (typeDefinition != null) { - Codec codec = RestCodec.from(typeDefinition, mountPoint); + final Codec codec = RestCodec.from(typeDefinition, mountPoint); outputValue = codec == null ? null : codec.deserialize(inputValue); } @@ -1369,7 +1387,7 @@ public class RestconfImpl implements RestconfService { schema, child.getLocalName()); if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) { - StringBuilder builder = new StringBuilder(); + final StringBuilder builder = new StringBuilder(); for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) { builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString()).append("\n"); } @@ -1384,7 +1402,7 @@ public class RestconfImpl implements RestconfService { boolean rightNodeSchemaFound = false; for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) { if (!rightNodeSchemaFound) { - final QName potentialCurrentAugment = this.normalizeNodeName(child, potentialSchemaNode, + final QName potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode, currentAugment, mountPoint); if (child.getQname() != null) { this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint); @@ -1400,7 +1418,7 @@ public class RestconfImpl implements RestconfService { } if ((schema instanceof ListSchemaNode)) { - ListSchemaNode listSchemaNode = (ListSchemaNode) schema; + final ListSchemaNode listSchemaNode = (ListSchemaNode) schema; final List listKeys = listSchemaNode.getKeyDefinition(); for (final QName listKey : listKeys) { boolean foundKey = false; @@ -1421,16 +1439,16 @@ public class RestconfImpl implements RestconfService { private void checkNodeMultiplicityAccordingToSchema(final DataNodeContainer dataNodeContainer, final List> nodes) { - Map equalNodeNamesToCounts = new HashMap(); - for (NodeWrapper child : nodes) { + final Map equalNodeNamesToCounts = new HashMap(); + for (final NodeWrapper child : nodes) { Integer count = equalNodeNamesToCounts.get(child.getLocalName()); equalNodeNamesToCounts.put(child.getLocalName(), count == null ? 1 : ++count); } - for (DataSchemaNode childSchemaNode : dataNodeContainer.getChildNodes()) { + for (final DataSchemaNode childSchemaNode : dataNodeContainer.getChildNodes()) { if (childSchemaNode instanceof ContainerSchemaNode || childSchemaNode instanceof LeafSchemaNode) { - String localName = childSchemaNode.getQName().getLocalName(); - Integer count = equalNodeNamesToCounts.get(localName); + final String localName = childSchemaNode.getQName().getLocalName(); + final Integer count = equalNodeNamesToCounts.get(localName); if (count != null && count > 1) { throw new RestconfDocumentedException("Multiple input data elements were specified for '" + childSchemaNode.getQName().getLocalName() @@ -1557,7 +1575,7 @@ public class RestconfImpl implements RestconfService { private NormalizedNode compositeNodeToDatastoreNormalizedNode(final CompositeNode compNode, final DataSchemaNode schema) { - List> lst = new ArrayList>(); + final List> lst = new ArrayList>(); lst.add(compNode); if (schema instanceof ContainerSchemaNode) { return CnSnToNormalizedNodeParserFactory.getInstance().getContainerNodeParser() @@ -1624,4 +1642,25 @@ public class RestconfImpl implements RestconfService { // TODO Auto-generated method stub return null; } + + private MapNode makeModuleMapNode(final Set modules) { + Preconditions.checkNotNull(modules); + final Module restconfModule = getRestconfModule(); + final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( + restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); + Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode); + + final CollectionNodeBuilder listModuleBuilder = Builders + .mapBuilder((ListSchemaNode) moduleSchemaNode); + + for (final Module module : modules) { + listModuleBuilder.withChild(toModuleEntryNode(module, moduleSchemaNode)); + } + return listModuleBuilder.build(); + } + + protected MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) { + Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode, + "moduleSchemaNode has to be of type ListSchemaNode"); final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode; final DataContainerNodeAttrBuilder moduleNodeValues = Builders .mapEntryBuilder(listModuleSchemaNode); List instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( (listModuleSchemaNode), "name"); final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode); moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName()) .build()); instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( (listModuleSchemaNode), "revision"); final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode); final String revision = REVISION_FORMAT.format(module.getRevision()); moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision) .build()); instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( (listModuleSchemaNode), "namespace"); final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(namespaceSchemaNode instanceof LeafSchemaNode); moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) namespaceSchemaNode) .withValue(module.getNamespace().toString()).build()); instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( (listModuleSchemaNode), "feature"); final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Preconditions.checkState(featureSchemaNode instanceof LeafListSchemaNode); final ListNodeBuilder> featuresBuilder = Builders .leafSetBuilder((LeafListSchemaNode) featureSchemaNode); for (final FeatureDefinition feature : module.getFeatures()) { featuresBuilder.withChild(Builders.leafSetEntryBuilder(((LeafListSchemaNode) featureSchemaNode)) .withValue(feature.getQName().getLocalName()).build()); } moduleNodeValues.withChild(featuresBuilder.build()); + return moduleNodeValues.build(); } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java index 854e9635cf..2f50fd2ef5 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java @@ -16,6 +16,10 @@ import org.opendaylight.yangtools.yang.data.api.MutableSimpleNode; import org.opendaylight.yangtools.yang.data.api.SimpleNode; import org.opendaylight.yangtools.yang.data.impl.NodeFactory; +/** + * @deprecated class will be removed in Lithium release + */ +@Deprecated public final class SimpleNodeWrapper implements NodeWrapper>, SimpleNode { private SimpleNode simpleNode; diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/StatisticsRestconfServiceWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/StatisticsRestconfServiceWrapper.java index eafbb81c48..53dda7dbfb 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/StatisticsRestconfServiceWrapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/StatisticsRestconfServiceWrapper.java @@ -28,7 +28,7 @@ public class StatisticsRestconfServiceWrapper implements RestconfService { final RestconfService delegate; - private StatisticsRestconfServiceWrapper(RestconfService delegate) { + private StatisticsRestconfServiceWrapper(final RestconfService delegate) { this.delegate = delegate; } @@ -42,84 +42,84 @@ public class StatisticsRestconfServiceWrapper implements RestconfService { } @Override - public StructuredData getModules(UriInfo uriInfo) { + public NormalizedNodeContext getModules(final UriInfo uriInfo) { return delegate.getModules(uriInfo); } @Override - public StructuredData getModules(String identifier, UriInfo uriInfo) { + public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) { return delegate.getModules(identifier, uriInfo); } @Override - public StructuredData getModule(String identifier, UriInfo uriInfo) { + public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) { return delegate.getModule(identifier, uriInfo); } @Override - public StructuredData getOperations(UriInfo uriInfo) { + public StructuredData getOperations(final UriInfo uriInfo) { return delegate.getOperations(uriInfo); } @Override - public StructuredData getOperations(String identifier, UriInfo uriInfo) { + public StructuredData getOperations(final String identifier, final UriInfo uriInfo) { return delegate.getOperations(identifier, uriInfo); } @Override - public StructuredData invokeRpc(String identifier, CompositeNode payload, UriInfo uriInfo) { + public StructuredData invokeRpc(final String identifier, final CompositeNode payload, final UriInfo uriInfo) { rpc.incrementAndGet(); return delegate.invokeRpc(identifier, payload, uriInfo); } @Override - public StructuredData invokeRpc(String identifier, String noPayload, UriInfo uriInfo) { + public StructuredData invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) { rpc.incrementAndGet(); return delegate.invokeRpc(identifier, noPayload, uriInfo); } @Override - public NormalizedNodeContext readConfigurationData(String identifier, UriInfo uriInfo) { + public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) { configGet.incrementAndGet(); return delegate.readConfigurationData(identifier, uriInfo); } @Override - public NormalizedNodeContext readOperationalData(String identifier, UriInfo uriInfo) { + public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo uriInfo) { operationalGet.incrementAndGet(); return delegate.readOperationalData(identifier, uriInfo); } @Override - public Response updateConfigurationData(String identifier, Node payload) { + public Response updateConfigurationData(final String identifier, final Node payload) { configPut.incrementAndGet(); return delegate.updateConfigurationData(identifier, payload); } @Override - public Response createConfigurationData(String identifier, Node payload) { + public Response createConfigurationData(final String identifier, final Node payload) { configPost.incrementAndGet(); return delegate.createConfigurationData(identifier, payload); } @Override - public Response createConfigurationData(Node payload) { + public Response createConfigurationData(final Node payload) { configPost.incrementAndGet(); return delegate.createConfigurationData(payload); } @Override - public Response deleteConfigurationData(String identifier) { + public Response deleteConfigurationData(final String identifier) { return delegate.deleteConfigurationData(identifier); } @Override - public Response subscribeToStream(String identifier, UriInfo uriInfo) { + public Response subscribeToStream(final String identifier, final UriInfo uriInfo) { return delegate.subscribeToStream(identifier, uriInfo); } @Override - public StructuredData getAvailableStreams(UriInfo uriInfo) { + public StructuredData getAvailableStreams(final UriInfo uriInfo) { return delegate.getAvailableStreams(uriInfo); } @@ -146,5 +146,4 @@ public class StatisticsRestconfServiceWrapper implements RestconfService { public BigInteger getRpc() { return BigInteger.valueOf(rpc.get()); } - } diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/StructuredData.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/StructuredData.java index 2935434967..be4ac992da 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/StructuredData.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/StructuredData.java @@ -11,6 +11,10 @@ import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +/** + * @deprecated class will be removed in Lithium release + */ +@Deprecated public class StructuredData { private final CompositeNode data; @@ -27,7 +31,7 @@ public class StructuredData { this.data = data; this.schema = schema; this.mountPoint = mountPoint; - this.prettyPrintMode = preattyPrintMode; + prettyPrintMode = preattyPrintMode; } public CompositeNode getData() { diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/iana-if-type.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/iana-if-type.yang new file mode 100644 index 0000000000..7bd0003b5d --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/iana-if-type.yang @@ -0,0 +1,1517 @@ +module iana-if-type { + namespace "urn:ietf:params:xml:ns:yang:iana-if-type"; + prefix ianaift; + + organization "IANA"; + contact + " Internet Assigned Numbers Authority + + Postal: ICANN + 4676 Admiralty Way, Suite 330 + Marina del Rey, CA 90292 + + Tel: +1 310 823 9358 + E-Mail: iana&iana.org"; + description + "This YANG module defines the iana-if-type typedef, which + contains YANG definitions for IANA-registered interface types. + + This YANG module is maintained by IANA, and reflects the + 'ifType definitions' registry. + + The latest revision of this YANG module can be obtained from + the IANA web site. + + Copyright (c) 2011 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-07-04 { + description + "Initial revision."; + reference + "RFC XXXX: IANA Interface Type YANG Module"; + } + + typedef iana-if-type { + type enumeration { + enum "other" { + value 1; + description + "None of the following"; + } + enum "regular1822" { + value 2; + } + enum "hdh1822" { + value 3; + } + enum "ddnX25" { + value 4; + } + enum "rfc877x25" { + value 5; + reference + "RFC 1382 - SNMP MIB Extension for the X.25 Packet Layer"; + } + enum "ethernetCsmacd" { + value 6; + description + "For all ethernet-like interfaces, regardless of speed, + as per RFC3635."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "iso88023Csmacd" { + value 7; + status deprecated; + description + "Deprecated via RFC3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "iso88024TokenBus" { + value 8; + } + enum "iso88025TokenRing" { + value 9; + } + enum "iso88026Man" { + value 10; + } + enum "starLan" { + value 11; + status deprecated; + description + "Deprecated via RFC3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "proteon10Mbit" { + value 12; + } + enum "proteon80Mbit" { + value 13; + } + enum "hyperchannel" { + value 14; + } + enum "fddi" { + value 15; + reference + "RFC 1512 - FDDI Management Information Base"; + } + enum "lapb" { + value 16; + reference + "RFC 1381 - SNMP MIB Extension for X.25 LAPB"; + } + enum "sdlc" { + value 17; + } + enum "ds1" { + value 18; + description + "DS1-MIB"; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + enum "e1" { + value 19; + status obsolete; + description + "Obsolete see DS1-MIB"; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + enum "basicISDN" { + value 20; + description + "see also RFC2127"; + } + enum "primaryISDN" { + value 21; + } + enum "propPointToPointSerial" { + value 22; + description + "proprietary serial"; + } + enum "ppp" { + value 23; + } + enum "softwareLoopback" { + value 24; + } + enum "eon" { + value 25; + description + "CLNP over IP"; + } + enum "ethernet3Mbit" { + value 26; + } + enum "nsip" { + value 27; + description + "XNS over IP"; + } + enum "slip" { + value 28; + description + "generic SLIP"; + } + enum "ultra" { + value 29; + description + "ULTRA technologies"; + } + enum "ds3" { + value 30; + description + "DS3-MIB"; + reference + "RFC 3896 - Definitions of Managed Objects for the + DS3/E3 Interface Type"; + } + enum "sip" { + value 31; + description + "SMDS, coffee"; + reference + "RFC 1694 - Definitions of Managed Objects for SMDS + Interfaces using SMIv2"; + } + enum "frameRelay" { + value 32; + description + "DTE only."; + reference + "RFC 2115 - Management Information Base for Frame Relay + DTEs Using SMIv2"; + } + enum "rs232" { + value 33; + reference + "RFC 1659 - Definitions of Managed Objects for RS-232-like + Hardware Devices using SMIv2"; + } + enum "para" { + value 34; + description + "parallel-port"; + reference + "RFC 1660 - Definitions of Managed Objects for + Parallel-printer-like Hardware Devices using + SMIv2"; + } + enum "arcnet" { + value 35; + description + "arcnet"; + } + enum "arcnetPlus" { + value 36; + description + "arcnet plus"; + } + enum "atm" { + value 37; + description + "ATM cells"; + } + enum "miox25" { + value 38; + reference + "RFC 1461 - SNMP MIB extension for Multiprotocol + Interconnect over X.25"; + } + enum "sonet" { + value 39; + description + "SONET or SDH"; + } + enum "x25ple" { + value 40; + reference + "RFC 2127 - ISDN Management Information Base using SMIv2"; + } + enum "iso88022llc" { + value 41; + } + enum "localTalk" { + value 42; + } + enum "smdsDxi" { + value 43; + } + enum "frameRelayService" { + value 44; + description + "FRNETSERV-MIB"; + reference + "RFC 2954 - Definitions of Managed Objects for Frame + Relay Service"; + } + enum "v35" { + value 45; + } + enum "hssi" { + value 46; + } + enum "hippi" { + value 47; + } + enum "modem" { + value 48; + description + "Generic modem"; + } + enum "aal5" { + value 49; + description + "AAL5 over ATM"; + } + enum "sonetPath" { + value 50; + } + enum "sonetVT" { + value 51; + } + enum "smdsIcip" { + value 52; + description + "SMDS InterCarrier Interface"; + } + enum "propVirtual" { + value 53; + description + "proprietary virtual/internal"; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + enum "propMultiplexor" { + value 54; + description + "proprietary multiplexing"; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + enum "ieee80212" { + value 55; + description + "100BaseVG"; + } + enum "fibreChannel" { + value 56; + description + "Fibre Channel"; + } + enum "hippiInterface" { + value 57; + description + "HIPPI interfaces"; + } + enum "frameRelayInterconnect" { + value 58; + status obsolete; + description + "Obsolete use either + frameRelay(32) or frameRelayService(44)."; + } + enum "aflane8023" { + value 59; + description + "ATM Emulated LAN for 802.3"; + } + enum "aflane8025" { + value 60; + description + "ATM Emulated LAN for 802.5"; + } + enum "cctEmul" { + value 61; + description + "ATM Emulated circuit"; + } + enum "fastEther" { + value 62; + status deprecated; + description + "Obsoleted via RFC3635. + ethernetCsmacd(6) should be used instead"; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "isdn" { + value 63; + description + "ISDN and X.25"; + reference + "RFC 1356 - Multiprotocol Interconnect on X.25 and ISDN + in the Packet Mode"; + } + enum "v11" { + value 64; + description + "CCITT V.11/X.21"; + } + enum "v36" { + value 65; + description + "CCITT V.36"; + } + enum "g703at64k" { + value 66; + description + "CCITT G703 at 64Kbps"; + } + enum "g703at2mb" { + value 67; + status obsolete; + description + "Obsolete see DS1-MIB"; + } + enum "qllc" { + value 68; + description + "SNA QLLC"; + } + enum "fastEtherFX" { + value 69; + status deprecated; + description + "Obsoleted via RFC3635 + ethernetCsmacd(6) should be used instead"; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "channel" { + value 70; + description + "channel"; + } + enum "ieee80211" { + value 71; + description + "radio spread spectrum"; + } + enum "ibm370parChan" { + value 72; + description + "IBM System 360/370 OEMI Channel"; + } + enum "escon" { + value 73; + description + "IBM Enterprise Systems Connection"; + } + enum "dlsw" { + value 74; + description + "Data Link Switching"; + } + enum "isdns" { + value 75; + description + "ISDN S/T interface"; + } + enum "isdnu" { + value 76; + description + "ISDN U interface"; + } + enum "lapd" { + value 77; + description + "Link Access Protocol D"; + } + enum "ipSwitch" { + value 78; + description + "IP Switching Objects"; + } + enum "rsrb" { + value 79; + description + "Remote Source Route Bridging"; + } + enum "atmLogical" { + value 80; + description + "ATM Logical Port"; + reference + "RFC 3606 - Definitions of Supplemental Managed Objects + for ATM Interface"; + } + enum "ds0" { + value 81; + description + "Digital Signal Level 0"; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + enum "ds0Bundle" { + value 82; + description + "group of ds0s on the same ds1"; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + enum "bsc" { + value 83; + description + "Bisynchronous Protocol"; + } + enum "async" { + value 84; + description + "Asynchronous Protocol"; + } + enum "cnr" { + value 85; + description + "Combat Net Radio"; + } + enum "iso88025Dtr" { + value 86; + description + "ISO 802.5r DTR"; + } + enum "eplrs" { + value 87; + description + "Ext Pos Loc Report Sys"; + } + enum "arap" { + value 88; + description + "Appletalk Remote Access Protocol"; + } + enum "propCnls" { + value 89; + description + "Proprietary Connectionless Protocol"; + } + enum "hostPad" { + value 90; + description + "CCITT-ITU X.29 PAD Protocol"; + } + enum "termPad" { + value 91; + description + "CCITT-ITU X.3 PAD Facility"; + } + enum "frameRelayMPI" { + value 92; + description + "Multiproto Interconnect over FR"; + } + enum "x213" { + value 93; + description + "CCITT-ITU X213"; + } + enum "adsl" { + value 94; + description + "Asymmetric Digital Subscriber Loop"; + } + enum "radsl" { + value 95; + description + "Rate-Adapt. Digital Subscriber Loop"; + } + enum "sdsl" { + value 96; + description + "Symmetric Digital Subscriber Loop"; + } + enum "vdsl" { + value 97; + description + "Very H-Speed Digital Subscrib. Loop"; + } + enum "iso88025CRFPInt" { + value 98; + description + "ISO 802.5 CRFP"; + } + enum "myrinet" { + value 99; + description + "Myricom Myrinet"; + } + enum "voiceEM" { + value 100; + description + "voice recEive and transMit"; + } + enum "voiceFXO" { + value 101; + description + "voice Foreign Exchange Office"; + } + enum "voiceFXS" { + value 102; + description + "voice Foreign Exchange Station"; + } + enum "voiceEncap" { + value 103; + description + "voice encapsulation"; + } + enum "voiceOverIp" { + value 104; + description + "voice over IP encapsulation"; + } + enum "atmDxi" { + value 105; + description + "ATM DXI"; + } + enum "atmFuni" { + value 106; + description + "ATM FUNI"; + } + enum "atmIma" { + value 107; + description + "ATM IMA"; + } + enum "pppMultilinkBundle" { + value 108; + description + "PPP Multilink Bundle"; + } + enum "ipOverCdlc" { + value 109; + description + "IBM ipOverCdlc"; + } + enum "ipOverClaw" { + value 110; + description + "IBM Common Link Access to Workstn"; + } + enum "stackToStack" { + value 111; + description + "IBM stackToStack"; + } + enum "virtualIpAddress" { + value 112; + description + "IBM VIPA"; + } + enum "mpc" { + value 113; + description + "IBM multi-protocol channel support"; + } + enum "ipOverAtm" { + value 114; + description + "IBM ipOverAtm"; + reference + "RFC 2320 - Definitions of Managed Objects for Classical IP + and ARP Over ATM Using SMIv2 (IPOA-MIB)"; + } + enum "iso88025Fiber" { + value 115; + description + "ISO 802.5j Fiber Token Ring"; + } + enum "tdlc" { + value 116; + description + "IBM twinaxial data link control"; + } + enum "gigabitEthernet" { + value 117; + status deprecated; + description + "Obsoleted via RFC3635 + ethernetCsmacd(6) should be used instead"; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "hdlc" { + value 118; + description + "HDLC"; + } + enum "lapf" { + value 119; + description + "LAP F"; + } + enum "v37" { + value 120; + description + "V.37"; + } + enum "x25mlp" { + value 121; + description + "Multi-Link Protocol"; + } + enum "x25huntGroup" { + value 122; + description + "X25 Hunt Group"; + } + enum "transpHdlc" { + value 123; + description + "Transp HDLC"; + } + enum "interleave" { + value 124; + description + "Interleave channel"; + } + enum "fast" { + value 125; + description + "Fast channel"; + } + enum "ip" { + value 126; + description + "IP (for APPN HPR in IP networks)"; + } + enum "docsCableMaclayer" { + value 127; + description + "CATV Mac Layer"; + } + enum "docsCableDownstream" { + value 128; + description + "CATV Downstream interface"; + } + enum "docsCableUpstream" { + value 129; + description + "CATV Upstream interface"; + } + enum "a12MppSwitch" { + value 130; + description + "Avalon Parallel Processor"; + } + enum "tunnel" { + value 131; + description + "Encapsulation interface"; + } + enum "coffee" { + value 132; + description + "coffee pot"; + reference + "RFC 2325 - Coffee MIB"; + } + enum "ces" { + value 133; + description + "Circuit Emulation Service"; + } + enum "atmSubInterface" { + value 134; + description + "ATM Sub Interface"; + } + enum "l2vlan" { + value 135; + description + "Layer 2 Virtual LAN using 802.1Q"; + } + enum "l3ipvlan" { + value 136; + description + "Layer 3 Virtual LAN using IP"; + } + enum "l3ipxvlan" { + value 137; + description + "Layer 3 Virtual LAN using IPX"; + } + enum "digitalPowerline" { + value 138; + description + "IP over Power Lines"; + } + enum "mediaMailOverIp" { + value 139; + description + "Multimedia Mail over IP"; + } + enum "dtm" { + value 140; + description + "Dynamic syncronous Transfer Mode"; + } + enum "dcn" { + value 141; + description + "Data Communications Network"; + } + enum "ipForward" { + value 142; + description + "IP Forwarding Interface"; + } + enum "msdsl" { + value 143; + description + "Multi-rate Symmetric DSL"; + } + enum "ieee1394" { + value 144; + description + "IEEE1394 High Performance Serial Bus"; + } + enum "if-gsn" { + value 145; + description + "HIPPI-6400"; + } + enum "dvbRccMacLayer" { + value 146; + description + "DVB-RCC MAC Layer"; + } + enum "dvbRccDownstream" { + value 147; + description + "DVB-RCC Downstream Channel"; + } + enum "dvbRccUpstream" { + value 148; + description + "DVB-RCC Upstream Channel"; + } + enum "atmVirtual" { + value 149; + description + "ATM Virtual Interface"; + } + enum "mplsTunnel" { + value 150; + description + "MPLS Tunnel Virtual Interface"; + } + enum "srp" { + value 151; + description + "Spatial Reuse Protocol"; + } + enum "voiceOverAtm" { + value 152; + description + "Voice Over ATM"; + } + enum "voiceOverFrameRelay" { + value 153; + description + "Voice Over Frame Relay"; + } + enum "idsl" { + value 154; + description + "Digital Subscriber Loop over ISDN"; + } + enum "compositeLink" { + value 155; + description + "Avici Composite Link Interface"; + } + enum "ss7SigLink" { + value 156; + description + "SS7 Signaling Link"; + } + enum "propWirelessP2P" { + value 157; + description + "Prop. P2P wireless interface"; + } + enum "frForward" { + value 158; + description + "Frame Forward Interface"; + } + enum "rfc1483" { + value 159; + description + "Multiprotocol over ATM AAL5"; + reference + "RFC 1483 - Multiprotocol Encapsulation over ATM + Adaptation Layer 5"; + } + enum "usb" { + value 160; + description + "USB Interface"; + } + enum "ieee8023adLag" { + value 161; + description + "IEEE 802.3ad Link Aggregate"; + } + enum "bgppolicyaccounting" { + value 162; + description + "BGP Policy Accounting"; + } + enum "frf16MfrBundle" { + value 163; + description + "FRF .16 Multilink Frame Relay"; + } + enum "h323Gatekeeper" { + value 164; + description + "H323 Gatekeeper"; + } + enum "h323Proxy" { + value 165; + description + "H323 Voice and Video Proxy"; + } + enum "mpls" { + value 166; + description + "MPLS"; + } + enum "mfSigLink" { + value 167; + description + "Multi-frequency signaling link"; + } + enum "hdsl2" { + value 168; + description + "High Bit-Rate DSL - 2nd generation"; + } + enum "shdsl" { + value 169; + description + "Multirate HDSL2"; + } + enum "ds1FDL" { + value 170; + description + "Facility Data Link 4Kbps on a DS1"; + } + enum "pos" { + value 171; + description + "Packet over SONET/SDH Interface"; + } + enum "dvbAsiIn" { + value 172; + description + "DVB-ASI Input"; + } + enum "dvbAsiOut" { + value 173; + description + "DVB-ASI Output"; + } + enum "plc" { + value 174; + description + "Power Line Communtications"; + } + enum "nfas" { + value 175; + description + "Non Facility Associated Signaling"; + } + enum "tr008" { + value 176; + description + "TR008"; + } + enum "gr303RDT" { + value 177; + description + "Remote Digital Terminal"; + } + enum "gr303IDT" { + value 178; + description + "Integrated Digital Terminal"; + } + enum "isup" { + value 179; + description + "ISUP"; + } + enum "propDocsWirelessMaclayer" { + value 180; + description + "Cisco proprietary Maclayer"; + } + enum "propDocsWirelessDownstream" { + value 181; + description + "Cisco proprietary Downstream"; + } + enum "propDocsWirelessUpstream" { + value 182; + description + "Cisco proprietary Upstream"; + } + enum "hiperlan2" { + value 183; + description + "HIPERLAN Type 2 Radio Interface"; + } + enum "propBWAp2Mp" { + value 184; + description + "PropBroadbandWirelessAccesspt2multipt use of this value + for IEEE 802.16 WMAN interfaces as per IEEE Std 802.16f + is deprecated and ieee80216WMAN(237) should be used + instead."; + } + enum "sonetOverheadChannel" { + value 185; + description + "SONET Overhead Channel"; + } + enum "digitalWrapperOverheadChannel" { + value 186; + description + "Digital Wrapper"; + } + enum "aal2" { + value 187; + description + "ATM adaptation layer 2"; + } + enum "radioMAC" { + value 188; + description + "MAC layer over radio links"; + } + enum "atmRadio" { + value 189; + description + "ATM over radio links"; + } + enum "imt" { + value 190; + description + "Inter Machine Trunks"; + } + enum "mvl" { + value 191; + description + "Multiple Virtual Lines DSL"; + } + enum "reachDSL" { + value 192; + description + "Long Reach DSL"; + } + enum "frDlciEndPt" { + value 193; + description + "Frame Relay DLCI End Point"; + } + enum "atmVciEndPt" { + value 194; + description + "ATM VCI End Point"; + } + enum "opticalChannel" { + value 195; + description + "Optical Channel"; + } + enum "opticalTransport" { + value 196; + description + "Optical Transport"; + } + enum "propAtm" { + value 197; + description + "Proprietary ATM"; + } + enum "voiceOverCable" { + value 198; + description + "Voice Over Cable Interface"; + } + enum "infiniband" { + value 199; + description + "Infiniband"; + } + enum "teLink" { + value 200; + description + "TE Link"; + } + enum "q2931" { + value 201; + description + "Q.2931"; + } + enum "virtualTg" { + value 202; + description + "Virtual Trunk Group"; + } + enum "sipTg" { + value 203; + description + "SIP Trunk Group"; + } + enum "sipSig" { + value 204; + description + "SIP Signaling"; + } + enum "docsCableUpstreamChannel" { + value 205; + description + "CATV Upstream Channel"; + } + enum "econet" { + value 206; + description + "Acorn Econet"; + } + enum "pon155" { + value 207; + description + "FSAN 155Mb Symetrical PON interface"; + } + enum "pon622" { + value 208; + description + "FSAN622Mb Symetrical PON interface"; + } + enum "bridge" { + value 209; + description + "Transparent bridge interface"; + } + enum "linegroup" { + value 210; + description + "Interface common to multiple lines"; + } + enum "voiceEMFGD" { + value 211; + description + "voice E&M Feature Group D"; + } + enum "voiceFGDEANA" { + value 212; + description + "voice FGD Exchange Access North American"; + } + enum "voiceDID" { + value 213; + description + "voice Direct Inward Dialing"; + } + enum "mpegTransport" { + value 214; + description + "MPEG transport interface"; + } + enum "sixToFour" { + value 215; + status deprecated; + description + "6to4 interface (DEPRECATED)"; + reference + "RFC 4087 - IP Tunnel MIB"; + } + enum "gtp" { + value 216; + description + "GTP (GPRS Tunneling Protocol)"; + } + enum "pdnEtherLoop1" { + value 217; + description + "Paradyne EtherLoop 1"; + } + enum "pdnEtherLoop2" { + value 218; + description + "Paradyne EtherLoop 2"; + } + enum "opticalChannelGroup" { + value 219; + description + "Optical Channel Group"; + } + enum "homepna" { + value 220; + description + "HomePNA ITU-T G.989"; + } + enum "gfp" { + value 221; + description + "Generic Framing Procedure (GFP)"; + } + enum "ciscoISLvlan" { + value 222; + description + "Layer 2 Virtual LAN using Cisco ISL"; + } + enum "actelisMetaLOOP" { + value 223; + description + "Acteleis proprietary MetaLOOP High Speed Link"; + } + enum "fcipLink" { + value 224; + description + "FCIP Link"; + } + enum "rpr" { + value 225; + description + "Resilient Packet Ring Interface Type"; + } + enum "qam" { + value 226; + description + "RF Qam Interface"; + } + enum "lmp" { + value 227; + description + "Link Management Protocol"; + reference + "RFC 4327 - Link Management Protocol (LMP) Management + Information Base (MIB)"; + } + enum "cblVectaStar" { + value 228; + description + "Cambridge Broadband Networks Limited VectaStar"; + } + enum "docsCableMCmtsDownstream" { + value 229; + description + "CATV Modular CMTS Downstream Interface"; + } + enum "adsl2" { + value 230; + status deprecated; + description + "Asymmetric Digital Subscriber Loop Version 2 + (DEPRECATED/OBSOLETED - please use adsl2plus(238) + instead)"; + reference + "RFC 4706 - Definitions of Managed Objects for Asymmetric + Digital Subscriber Line 2 (ADSL2)"; + } + enum "macSecControlledIF" { + value 231; + description + "MACSecControlled"; + } + enum "macSecUncontrolledIF" { + value 232; + description + "MACSecUncontrolled"; + } + enum "aviciOpticalEther" { + value 233; + description + "Avici Optical Ethernet Aggregate"; + } + enum "atmbond" { + value 234; + description + "atmbond"; + } + enum "voiceFGDOS" { + value 235; + description + "voice FGD Operator Services"; + } + enum "mocaVersion1" { + value 236; + description + "MultiMedia over Coax Alliance (MoCA) Interface + as documented in information provided privately to IANA"; + } + enum "ieee80216WMAN" { + value 237; + description + "IEEE 802.16 WMAN interface"; + } + enum "adsl2plus" { + value 238; + description + "Asymmetric Digital Subscriber Loop Version 2, + Version 2 Plus and all variants"; + } + enum "dvbRcsMacLayer" { + value 239; + description + "DVB-RCS MAC Layer"; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + enum "dvbTdm" { + value 240; + description + "DVB Satellite TDM"; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + enum "dvbRcsTdma" { + value 241; + description + "DVB-RCS TDMA"; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + enum "x86Laps" { + value 242; + description + "LAPS based on ITU-T X.86/Y.1323"; + } + enum "wwanPP" { + value 243; + description + "3GPP WWAN"; + } + enum "wwanPP2" { + value 244; + description + "3GPP2 WWAN"; + } + enum "voiceEBS" { + value 245; + description + "voice P-phone EBS physical interface"; + } + enum "ifPwType" { + value 246; + description + "Pseudowire interface type"; + reference + "RFC 5601 - Pseudowire (PW) Management Information Base"; + } + enum "ilan" { + value 247; + description + "Internal LAN on a bridge per IEEE 802.1ap"; + } + enum "pip" { + value 248; + description + "Provider Instance Port on a bridge per IEEE 802.1ah PBB"; + } + enum "aluELP" { + value 249; + description + "Alcatel-Lucent Ethernet Link Protection"; + } + enum "gpon" { + value 250; + description + "Gigabit-capable passive optical networks (G-PON) as per + ITU-T G.948"; + } + enum "vdsl2" { + value 251; + description + "Very high speed digital subscriber line Version 2 + (as per ITU-T Recommendation G.993.2)"; + reference + "RFC 5650 - Definitions of Managed Objects for Very High + Speed Digital Subscriber Line 2 (VDSL2)"; + } + enum "capwapDot11Profile" { + value 252; + description + "WLAN Profile Interface"; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + enum "capwapDot11Bss" { + value 253; + description + "WLAN BSS Interface"; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + enum "capwapWtpVirtualRadio" { + value 254; + description + "WTP Virtual Radio Interface"; + reference + "RFC 5833 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Base MIB"; + } + enum "bits" { + value 255; + description + "bitsport"; + } + enum "docsCableUpstreamRfPort" { + value 256; + description + "DOCSIS CATV Upstream RF Port"; + } + enum "cableDownstreamRfPort" { + value 257; + description + "CATV downstream RF port"; + } + enum "vmwareVirtualNic" { + value 258; + description + "VMware Virtual Network Interface"; + } + enum "ieee802154" { + value 259; + description + "IEEE 802.15.4 WPAN interface"; + reference + "IEEE 802.15.4-2006"; + } + enum "otnOdu" { + value 260; + description + "OTN Optical Data Unit"; + } + enum "otnOtu" { + value 261; + description + "OTN Optical channel Transport Unit"; + } + enum "ifVfiType" { + value 262; + description + "VPLS Forwarding Instance Interface Type"; + } + enum "g9981" { + value 263; + description + "G.998.1 bonded interface"; + } + enum "g9982" { + value 264; + description + "G.998.2 bonded interface"; + } + enum "g9983" { + value 265; + description + "G.998.3 bonded interface"; + } + enum "aluEpon" { + value 266; + description + "Ethernet Passive Optical Networks (E-PON)"; + } + enum "aluEponOnu" { + value 267; + description + "EPON Optical Network Unit"; + } + enum "aluEponPhysicalUni" { + value 268; + description + "EPON physical User to Network interface"; + } + enum "aluEponLogicalLink" { + value 269; + description + "The emulation of a point-to-point link over the EPON + layer"; + } + enum "aluGponOnu" { + value 270; + description + "GPON Optical Network Unit"; + reference + "ITU-T G.984.2"; + } + enum "aluGponPhysicalUni" { + value 271; + description + "GPON physical User to Network interface"; + reference + "ITU-T G.984.2"; + } + enum "vmwareNicTeam" { + value 272; + description + "VMware NIC Team"; + } + // value 273 reserved by IANA + } + description + "This data type is used as the syntax of the 'type' + leaf in the 'interface' list in the YANG module + ietf-interface. + + The definition of this typedef with the + addition of newly assigned values is published + periodically by the IANA, in either the Assigned + Numbers RFC, or some derivative of it specific to + Internet Network Management number assignments. (The + latest arrangements can be obtained by contacting the + IANA.) + + Requests for new values should be made to IANA via + email (iana&iana.org)."; + reference + "IANA ifType definitions registry. + "; + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/ietf-inet-types.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/ietf-inet-types.yang new file mode 100644 index 0000000000..de20febbb7 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/ietf-inet-types.yang @@ -0,0 +1,418 @@ + module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: David Partain + + + WG Chair: David Kessens + + + Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of protocol field related types ***/ + + typedef ip-version { + type enumeration { + enum unknown { + value "0"; + description + "An unknown or unspecified version of the Internet protocol."; + } + enum ipv4 { + value "1"; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum ipv6 { + value "2"; + description + "The IPv6 protocol as defined in RFC 2460."; + } + } + description + "This value represents the version of the IP protocol. + + In the value set and its semantics, this type is equivalent + to the InetVersion textual convention of the SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "The dscp type represents a Differentiated Services Code-Point + that may be used for marking packets in a traffic stream. + + In the value set and its semantics, this type is equivalent + to the Dscp textual convention of the SMIv2."; + reference + "RFC 3289: Management Information Base for the Differentiated + Services Architecture + RFC 2474: Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers + RFC 2780: IANA Allocation Guidelines For Values In + the Internet Protocol and Related Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The flow-label type represents flow identifier or Flow Label + in an IPv6 packet header that may be used to discriminate + traffic flows. + + In the value set and its semantics, this type is equivalent + to the IPv6FlowLabel textual convention of the SMIv2."; + reference + "RFC 3595: Textual Conventions for IPv6 Flow Label + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16 { + range "0..65535"; + } + description + "The port-number type represents a 16-bit port number of an + Internet transport layer protocol such as UDP, TCP, DCCP, or + SCTP. Port numbers are assigned by IANA. A current list of + all assignments is available from . + + Note that the port number value zero is reserved by IANA. In + situations where the value zero does not make sense, it can + be excluded by subtyping the port-number type. + + In the value set and its semantics, this type is equivalent + to the InetPortNumber textual convention of the SMIv2."; + reference + "RFC 768: User Datagram Protocol + RFC 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of autonomous system related types ***/ + + typedef as-number { + type uint32; + description + "The as-number type represents autonomous system numbers + which identify an Autonomous System (AS). An AS is a set + of routers under a single technical administration, using + an interior gateway protocol and common metrics to route + packets within the AS, and using an exterior gateway + protocol to route packets to other ASs'. IANA maintains + the AS number space and has delegated large parts to the + regional registries. + + Autonomous system numbers were originally limited to 16 + bits. BGP extensions have enlarged the autonomous system + number space to 32 bits. This type therefore uses an uint32 + base type without a range restriction in order to support + a larger autonomous system number space. + + In the value set and its semantics, this type is equivalent + to the InetAutonomousSystemNumber textual convention of + the SMIv2."; + reference + "RFC 1930: Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271: A Border Gateway Protocol 4 (BGP-4) + RFC 4893: BGP Support for Four-octet AS Number Space + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of IP address and hostname related types ***/ + + typedef ip-address { + type union { + type inet:ipv4-address; + type inet:ipv6-address; + } + description + "The ip-address type represents an IP address and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-address { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '(%[\p{N}\p{L}]+)?'; + } + description + "The ipv4-address type represents an IPv4 address in + dotted-quad notation. The IPv4 address may include a zone + index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format for the zone index is the numerical + format"; + } + + typedef ipv6-address { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(%[\p{N}\p{L}]+)?'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(%.+)?'; + } + description + "The ipv6-address type represents an IPv6 address in full, + mixed, shortened, and shortened-mixed notation. The IPv6 + address may include a zone index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format of IPv6 addresses uses the compressed + format described in RFC 4291, Section 2.2, item 2 with the + following additional rules: the :: substitution must be + applied to the longest sequence of all-zero 16-bit chunks + in an IPv6 address. If there is a tie, the first sequence + of all-zero 16-bit chunks is replaced by ::. Single + all-zero 16-bit chunks are not compressed. The canonical + format uses lowercase characters and leading zeros are + not allowed. The canonical format for the zone index is + the numerical format as described in RFC 4007, Section + 11.2."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text Representation"; + } + + typedef ip-prefix { + type union { + type inet:ipv4-prefix; + type inet:ipv6-prefix; + } + description + "The ip-prefix type represents an IP prefix and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-prefix type represents an IPv4 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv4 prefix has all bits of + the IPv4 address set to zero that are not part of the + IPv4 prefix."; + } + + typedef ipv6-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + description + "The ipv6-prefix type represents an IPv6 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The IPv6 address should have all bits that do not belong + to the prefix set to zero. + + The canonical format of an IPv6 prefix has all bits of + the IPv6 address set to zero that are not part of the + IPv6 prefix. Furthermore, IPv6 address is represented + in the compressed format described in RFC 4291, Section + 2.2, item 2 with the following additional rules: the :: + substitution must be applied to the longest sequence of + all-zero 16-bit chunks in an IPv6 address. If there is + a tie, the first sequence of all-zero 16-bit chunks is + replaced by ::. Single all-zero 16-bit chunks are not + compressed. The canonical format uses lowercase + characters and leading zeros are not allowed."; + reference + "RFC 4291: IP Version 6 Addressing Architecture"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + Internet domain names are only loosely specified. Section + 3.5 of RFC 1034 recommends a syntax (modified in Section + 2.1 of RFC 1123). The pattern above is intended to allow + for current practice in domain name use, and some possible + future expansion. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + The encoding of DNS names in the DNS protocol is limited + to 255 characters. Since the encoding consists of labels + prefixed by a length bytes and there is a trailing NULL + byte, only 253 characters can appear in the textual dotted + notation. + + The description clause of schema nodes using the domain-name + type MUST describe when and how these names are resolved to + IP addresses. Note that the resolution of a domain-name value + may require to query multiple DNS records (e.g., A for IPv4 + and AAAA for IPv6). The order of the resolution process and + which DNS record takes precedence can either be defined + explicitely or it may depend on the configuration of the + resolver. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be encoded in punycode as described in RFC + 3492"; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1034: Domain Names - Concepts and Facilities + RFC 1123: Requirements for Internet Hosts -- Application + and Support + RFC 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 3492: Punycode: A Bootstring encoding of Unicode for + Internationalized Domain Names in Applications + (IDNA) + RFC 5891: Internationalizing Domain Names in Applications + (IDNA): Protocol"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + Objects using the uri type MUST be in US-ASCII encoding, + and MUST be normalized as described by RFC 3986 Sections + 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary + percent-encoding is removed, and all case-insensitive + characters are set to lowercase except for hexadecimal + digits, which are normalized to uppercase as described in + Section 6.2.2.1. + + The purpose of this normalization is to help provide + unique URIs. Note that this normalization is not + sufficient to provide uniqueness. Two URIs that are + textually distinct after this normalization may still be + equivalent. + + Objects using the uri type may restrict the schemes that + they permit. For example, 'data:' and 'urn:' schemes + might not be appropriate. + + A zero-length URI is not a valid URI. This can be used to + express 'URI absent' where required. + + In the value set and its semantics, this type is equivalent + to the Uri SMIv2 textual convention defined in RFC 5017."; + reference + "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax + RFC 3305: Report from the Joint W3C/IETF URI Planning Interest + Group: Uniform Resource Identifiers (URIs), URLs, + and Uniform Resource Names (URNs): Clarifications + and Recommendations + RFC 5017: MIB Textual Conventions for Uniform Resource + Identifiers (URIs)"; + } + + } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/ietf-interfaces@2013-07-04.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/ietf-interfaces@2013-07-04.yang new file mode 100644 index 0000000000..9db753c440 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/ietf-interfaces@2013-07-04.yang @@ -0,0 +1,673 @@ +module ietf-interfaces { + + namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces"; + prefix if; + + import ietf-yang-types { + prefix yang; + } + import iana-if-type { + prefix ianaift; + } + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: David Kessens + + + WG Chair: Juergen Schoenwaelder + + + Editor: Martin Bjorklund + "; + + description + "This module contains a collection of YANG definitions for + managing network interfaces. + + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-07-04 { + description + "Initial revision."; + reference + "RFC XXXX: A YANG Data Model for Interface Management"; + } + + /* Typedefs */ + + typedef interface-ref { + type leafref { + path "/if:interfaces/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + configured interfaces."; + } + + typedef interface-state-ref { + type leafref { + path "/if:interfaces-state/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + the operationally present interfaces."; + } + + /* Features */ + + feature arbitrary-names { + description + "This feature indicates that the device allows user-controlled + interfaces to be named arbitrarily."; + } + + feature pre-provisioning { + description + "This feature indicates that the device supports + pre-provisioning of interface configuration, i.e., it is + possible to configure an interface whose physical interface + hardware is not present on the device."; + } + + feature if-mib { + description + "This feature indicates that the device implements IF-MIB."; + reference + "RFC 2863: The Interfaces Group MIB"; + } + + /* Data nodes */ + + container interfaces { + description + "Interface configuration parameters."; + + list interface { + key "name"; + + description + "The list of configured interfaces on the device. + + The operational state of an interface is available in the + /interfaces-state/interface list. If the configuration of a + system-controlled interface cannot be used by the system + (e.g., the interface hardware present does not match the + interface type), then the configuration is not applied to + the system-controlled interface shown in the + /interfaces-state/interface list. If the the configuration + of a user-controlled interface cannot be used by the system, + the configured interface is not instantiated in the + /interfaces-state/interface list."; + + leaf name { + type string; + description + "The name of the interface. + + A device MAY restrict the allowed values for this leaf, + possibly depending on the type of the interface. + + For system-controlled interfaces, this leaf is the + device-specific name of the interface. The 'config false' + list /interfaces-state/interface contains the currently + existing interfaces on the device. + + If a client tries to create configuration for a + system-controlled interface that is not present in the + /interfaces-state/interface list, the server MAY reject + the request, if the implementation does not support + pre-provisioning of interfaces, or if the name refers to + an interface that can never exist in the system. A + NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case. + + If the device supports pre-provisioning of interface + configuration, the feature 'pre-provisioning' is + advertised. + + If the device allows arbitrarily named user-controlled + interfaces, the feature 'arbitrary-names' is advertised. + + When a configured user-controlled interface is created by + the system, it is instantiated with the same name in the + /interface-state/interface list. Since the name in that + list MAY be mapped to ifName by an implementation, such an + implementation MUST restrict the allowed values for this + leaf so that it matches the restrictions of ifName. + + If a NETCONF server that implements this restriction is + sent a value that doesn't match the restriction, it MUST + reply with an rpc-error with the error-tag + 'invalid-value'."; + } + + leaf description { + type string; + description + "A textual description of the interface. + + This leaf MAY be mapped to ifAlias by an implementation. + Such an implementation MUST restrict the allowed values + for this leaf so that it matches the restrictions of + ifAlias. + + If a NETCONF server that implements this restriction is + sent a value that doesn't match the restriction, it MUST + reply with an rpc-error with the error-tag + 'invalid-value'. + + Since ifAlias is defined to be stored in non-volatile + storage, the MIB implementation MUST map ifAlias to the + value of 'description' in the persistently stored + datastore. + + Specifically, if the device supports ':startup', when + ifAlias is read the device MUST return the value of + 'description' in the 'startup' datastore, and when it is + written, it MUST be written to the 'running' and 'startup' + datastores. Note that it is up to the implementation if + it modifies this single leaf in 'startup', or if it + performs an implicit copy-config from 'running' to + 'startup'. + + If the device does not support ':startup', ifAlias MUST + be mapped to the 'description' leaf in the 'running' + datastore."; + reference + "RFC 2863: The Interfaces Group MIB - ifAlias"; + } + + leaf type { + type ianaift:iana-if-type; + mandatory true; + description + "The type of the interface. + + When an interface entry is created, a server MAY + initialize the type leaf with a valid value, e.g., if it + is possible to derive the type from the name of the + interface. + + If a client tries to set the type of an interface to a + value that can never be used by the system, e.g., if the + type is not supported or if the type does not match the + name of the interface, the server MUST reject the request. + A NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf enabled { + type boolean; + default "true"; + description + "This leaf contains the configured, desired state of the + interface. + + Systems that implement the IF-MIB use the value of this + leaf in the 'running' datastore to set + IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry + has been initialized, as described in RFC 2863. + + Changes in this leaf in the 'running' datastore are + reflected in ifAdminStatus, but if ifAdminStatus is + changed over SNMP, this leaf is not affected."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf link-up-down-trap-enable { + if-feature if-mib; + type enumeration { + enum enabled { + value 1; + } + enum disabled { + value 2; + } + } + description + "Controls whether linkUp/linkDown SNMP notifications + should be generated for this interface. + + If this node is not configured, the value 'enabled' is + operationally used by the server for interfaces which do + not operate on top of any other interface (i.e., there are + no 'lower-layer-if' entries), and 'disabled' otherwise."; + reference + "RFC 2863: The Interfaces Group MIB - + ifLinkUpDownTrapEnable"; + } + } + } + + container interfaces-state { + config false; + description + "Data nodes for the operational state of interfaces."; + + list interface { + key "name"; + + description + "The list of interfaces on the device. + + System-controlled interfaces created by the system are + always present in this list, whether they are configured or + not."; + + leaf name { + type string; + description + "The name of the interface. + + This leaf MAY be mapped to ifName by an implementation."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf type { + type ianaift:iana-if-type; + mandatory true; + description + "The type of the interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + enum testing { + value 3; + description + "In some test mode."; + } + } + mandatory true; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + mandatory true; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + mandatory true; + description + "The ifIndex value for the ifEntry represented by this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a MAC address. The interface's media-specific + modules must define the bit and byte ordering and the + format of the value of this object. For interfaces that do + not have such an address (e.g., a serial line), this node + is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-state-ref; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-state-ref; + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits / second"; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + container statistics { + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + leaf in-unicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were not addressed to a + multicast or broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + leaf in-broadcast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + leaf in-multicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were addressed to a multicast + address at this sub-layer. For a MAC layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + leaf in-discards { + type yang:counter32; + description + "The number of inbound packets which were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + leaf in-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + leaf in-unknown-protos { + type yang:counter32; + description + "For packet-oriented interfaces, the number of packets + received via the interface which were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing the number of transmission + units received via the interface which were discarded + because of an unknown or unsupported protocol. For any + interface that does not support protocol multiplexing, + this counter is not present. + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf out-octets { + type yang:counter64; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + leaf out-unicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and which were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + leaf out-broadcast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and which were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + leaf out-multicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and which were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + leaf out-discards { + type yang:counter32; + description + "The number of outbound packets which were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + leaf out-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/ietf-restconf@2013-10-19.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/ietf-restconf@2013-10-19.yang new file mode 100644 index 0000000000..16766b0979 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/ietf-restconf@2013-10-19.yang @@ -0,0 +1,684 @@ +module ietf-restconf { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; + prefix "restconf"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "Editor: Andy Bierman + + + Editor: Martin Bjorklund + + + Editor: Kent Watsen + + + Editor: Rex Fernando + "; + + description + "This module contains conceptual YANG specifications + for the YANG Patch and error content that is used in + RESTCONF protocol messages. A conceptual container + representing the RESTCONF API nodes (media type + application/yang.api). + + Note that the YANG definitions within this module do not + represent configuration data of any kind. + The YANG grouping statements provide a normative syntax + for XML and JSON message encoding purposes. + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: remove this note + // Note: extracted from draft-bierman-netconf-restconf-02.txt + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-10-19 { + description + "Initial revision."; + reference + "RFC XXXX: RESTCONF Protocol."; + } + + typedef data-resource-identifier { + type string { + length "1 .. max"; + } + description + "Contains a Data Resource Identifier formatted string + to identify a specific data node. The data node that + uses this data type SHOULD define the document root + for data resource identifiers. The default document + root is the target datastore conceptual root node. + Data resource identifiers are defined relative to + this document root."; + reference + "RFC XXXX: [sec. 5.3.1.1 ABNF For Data Resource Identifiers]"; + } + + // this typedef is TBD; not currently used + typedef datastore-identifier { + type union { + type enumeration { + enum candidate { + description + "Identifies the NETCONF shared candidate datastore."; + reference + "RFC 6241, section 8.3"; + } + enum running { + description + "Identifies the NETCONF running datastore."; + reference + "RFC 6241, section 5.1"; + } + enum startup { + description + "Identifies the NETCONF startup datastore."; + reference + "RFC 6241, section 8.7"; + } + } + type string; + } + description + "Contains a string to identify a specific datastore. + The enumerated datastore identifier values are + reserved for standard datastore names."; + } + + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format. + TBD: make pattern more precise to exclude leading zeros."; + } + + grouping yang-patch { + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch edit request message."; + + container yang-patch { + description + "Represents a conceptual sequence of datastore edits, + called a patch. Each patch is given a client-assigned + patch identifier. Each edit MUST be applied + in ascending order, and all edits MUST be applied. + If any errors occur, then the target datastore MUST NOT + be changed by the patch operation. + + A patch MUST be validated by the server to be a + well-formed message before any of the patch edits + are validated or attempted. + + YANG datastore validation (defined in RFC 6020, section + 8.3.3) is performed after all edits have been + individually validated. + + It is possible for a datastore constraint violation to occur + due to any node in the datastore, including nodes not + included in the edit list. Any validation errors MUST + be reported in the reply message."; + + reference + "RFC 6020, section 8.3."; + + leaf patch-id { + type string; + description + "An arbitrary string provided by the client to identify + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch. Error messages returned by the server pertaining + to this patch will be identified by this patch-id value."; + } + + leaf comment { + type string { + length "0 .. 1024"; + } + description + "An arbitrary string provided by the client to describe + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch."; + } + + list edit { + key edit-id; + ordered-by user; + + description + "Represents one edit within the YANG Patch + request message."; + leaf edit-id { + type string; + description + "Arbitrary string index for the edit. + Error messages returned by the server pertaining + to a specific edit will be identified by this + value."; + } + + leaf operation { + type enumeration { + enum create { + description + "The target data node is created using the + supplied value, only if it does not already + exist."; + } + enum delete { + description + "Delete the target node, only if the data resource + currently exists, otherwise return an error."; + } + enum insert { + description + "Insert the supplied value into a user-ordered + list or leaf-list entry. The target node must + represent a new data resource."; + } + enum merge { + description + "The supplied value is merged with the target data + node."; + } + enum move { + description + "Move the target node. Reorder a user-ordered + list or leaf-list. The target node must represent + an existing data resource."; + } + enum replace { + description + "The supplied value is used to replace the target + data node."; + } + enum remove { + description + "Delete the target node if it currently exists."; + } + } + mandatory true; + description + "The datastore operation requested for the associated + edit entry"; + } + + leaf target { + type data-resource-identifier; + mandatory true; + description + "Identifies the target data resource for the edit + operation."; + } + + leaf point { + when "(../operation = 'insert' or " + + "../operation = 'move') and " + + "(../where = 'before' or ../where = 'after')" { + description + "Point leaf only applies for insert or move + operations, before or after an existing entry."; + } + type data-resource-identifier; + description + "The absolute URL path for the data node that is being + used as the insertion point or move point for the + target of this edit entry."; + } + + leaf where { + when "../operation = 'insert' or ../operation = 'move'" { + description + "Where leaf only applies for insert or move + operations."; + } + type enumeration { + enum before { + description + "Insert or move a data node before the data resource + identified by the 'point' parameter."; + } + enum after { + description + "Insert or move a data node after the data resource + identified by the 'point' parameter."; + } + enum first { + description + "Insert or move a data node so it becomes ordered + as the first entry."; + } + enum last { + description + "Insert or move a data node so it becomes ordered + as the last entry."; + } + + } + default last; + description + "Identifies where a data resource will be inserted or + moved. YANG only allows these operations for + list and leaf-list data nodes that are ordered-by + user."; + } + + anyxml value { + when "(../operation = 'create' or " + + "../operation = 'merge' " + + "or ../operation = 'replace' or " + + "../operation = 'insert')" { + description + "Value node only used for create, merge, + replace, and insert operations"; + } + description + "Value used for this edit operation."; + } + } + } + + } // grouping yang-patch + + + grouping yang-patch-status { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + YANG Patch status response message."; + + container yang-patch-status { + description + "A container representing the response message + sent by the server after a YANG Patch edit + request message has been processed."; + + leaf patch-id { + type string; + description + "The patch-id value used in the request"; + } + + choice global-status { + description + "Report global errors or complete success. + If there is no case selected then errors + are reported in the edit-status container."; + + case global-errors { + uses errors; + description + "This container will be present if global + errors unrelated to a specific edit occurred."; + } + leaf ok { + type empty; + description + "This leaf will be present if the request succeeded + and there are no errors reported in the edit-status + container."; + } + } + + container edit-status { + description + "This container will be present if there are + edit-specific status responses to report."; + + list edit { + key edit-id; + + description + "Represents a list of status responses, + corresponding to edits in the YANG Patch + request message. If an edit entry was + skipped or not reached by the server, + then this list will not contain a corresponding + entry for that edit."; + + leaf edit-id { + type string; + description + "Response status is for the edit list entry + with this edit-id value."; + } + choice edit-status-choice { + description + "A choice between different types of status + responses for each edit entry."; + leaf ok { + type empty; + description + "This edit entry was invoked without any + errors detected by the server associated + with this edit."; + } + leaf location { + type inet:uri; + description + "Contains the Location header value that would be + returned if this edit causes a new resource to be + created. If the edit identified by the same edit-id + value was successfully invoked and a new resource + was created, then this field will be returned + instead of 'ok'."; + } + case errors { + uses errors; + description + "The server detected errors associated with the + edit identified by the same edit-id value."; + } + } + } + } + } + } // grouping yang-patch-status + + + grouping errors { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch errors report within a response message."; + + container errors { + config false; // needed so list error does not need a key + description + "Represents an error report returned by the server if + a request results in an error."; + + list error { + description + "An entry containing information about one + specific error that occurred while processing + a RESTCONF request."; + reference "RFC 6241, Section 4.3"; + + leaf error-type { + type enumeration { + enum transport { + description "The transport layer"; + } + enum rpc { + description "The rpc or notification layer"; + } + enum protocol { + description "The protocol operation layer"; + } + enum application { + description "The server application layer"; + } + } + mandatory true; + description + "The protocol layer where the error occurred."; + } + + leaf error-tag { + type string; + mandatory true; + description + "The enumerated error tag."; + } + + leaf error-app-tag { + type string; + description + "The application-specific error tag."; + } + + leaf error-path { + type data-resource-identifier; + description + "The target data resource identifier associated + with the error, if any."; + } + leaf error-message { + type string; + description + "A message describing the error."; + } + + container error-info { + description + "A container allowing additional information + to be included in the error report."; + // arbitrary anyxml content here + } + } + } + } // grouping errors + + + grouping restconf { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + the RESTCONF API resource."; + + container restconf { + description + "Conceptual container representing the + application/yang.api resource type."; + + container config { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + unified configuration datastore containing YANG data + nodes. The child nodes of this container are + configuration data resources (application/yang.data) + defined as top-level YANG data nodes from the modules + advertised by the server in /restconf/modules."; + } + + container operational { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + operational data supported by the server. The child + nodes of this container are operational data resources + (application/yang.data) defined as top-level + YANG data nodes from the modules advertised by + the server in /restconf/modules."; + } + + container modules { + description + "Contains a list of module description entries. + These modules are currently loaded into the server."; + + list module { + key "name revision"; + description + "Each entry represents one module currently + supported by the server."; + + leaf name { + type yang:yang-identifier; + description "The YANG module name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module revision date. An empty string is + used if no revision statement is present in the + YANG module."; + } + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server."; + } + leaf-list deviation { + type yang:yang-identifier; + description + "List of YANG deviation module names used by this + server to modify the conformance of the module + associated with this entry."; + } + } + } + + container operations { + description + "Container for all operation resources + (application/yang.operation), + + Each resource is represented as an empty leaf with the + name of the RPC operation from the YANG rpc statement. + + E.g.; + + POST /restconf/operations/show-log-errors + + leaf show-log-errors { + type empty; + } + "; + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description "The stream name"; + reference "RFC 5277, Section 3.4, element."; + } + + leaf description { + type string; + description "Description of stream content"; + reference + "RFC 5277, Section 3.4, element."; + } + + leaf replay-support { + type boolean; + description + "Indicates if replay buffer supported for this stream"; + reference + "RFC 5277, Section 3.4, element."; + } + + leaf replay-log-creation-time { + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, + element."; + } + + leaf events { + type empty; + description + "Represents the entry point for establishing + notification delivery via server sent events."; + } + } + } + + leaf version { + type enumeration { + enum "1.0" { + description + "Version 1.0 of the RESTCONF protocol."; + } + } + config false; + description + "Contains the RESTCONF protocol version."; + } + } + } // grouping restconf + + + grouping notification { + description + "Contains the notification message wrapper definition."; + + container notification { + description + "RESTCONF notification message wrapper."; + leaf event-time { + type yang:date-and-time; + mandatory true; + description + "The time the event was generated by the + event source."; + reference + "RFC 5277, section 4, element."; + } + + /* The YANG-specific notification container is encoded + * after the 'event-time' element. The format + * corresponds to the notificationContent element + * in RFC 5277, section 4. For example: + * + * module example-one { + * ... + * notification event1 { ... } + * + * } + * + * Encoded as element 'event1' in the namespace + * for module 'example-one'. + */ + } + } // grouping notification + + } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/ietf-yang-types.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/ietf-yang-types.yang new file mode 100644 index 0000000000..07e50b3913 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/modules-behind-mount-point/ietf-yang-types.yang @@ -0,0 +1,417 @@ + module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: David Partain + + + WG Chair: David Kessens + + + Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifier. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type, the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + + + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined by the 'identifier' + rule in Section 12 of RFC 6020. An identifier must + start with an alphabetic character or an underscore + followed by an arbitrary sequence of alphabetic or + numeric characters, underscores, hyphens, or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lowercase or uppercase character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of date and time related types ***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z all + represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using the + time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually referring + to the notion of local time) uses the time-offset -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence happened. + The specific occurrence must be defined in the description + of any schema node defined using this type. When the specific + occurrence occurred prior to the last time the associated + timeticks attribute was zero, then the timestamp value is + zero. Note that this requires all timestamp values to be + reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java index 4ae4de18f3..ba7b2f20e4 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java @@ -17,7 +17,7 @@ import java.util.Map; import java.util.Map.Entry; import javax.management.ObjectName; import javax.management.openmbean.OpenType; -import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.config.util.BeanReader; import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry; import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; @@ -46,9 +46,9 @@ public final class InstanceConfig { private final Map yangToAttrConfig; private final String nullableDummyContainerName; private final Map jmxToAttrConfig; - private final ConfigRegistryClient configRegistryClient; + private final BeanReader configRegistryClient; - public InstanceConfig(ConfigRegistryClient configRegistryClient, Map yangNamesToAttributes, + public InstanceConfig(BeanReader configRegistryClient, Map yangNamesToAttributes, String nullableDummyContainerName) { this.yangToAttrConfig = yangNamesToAttributes; diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java index d736595719..9c55953bbc 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java @@ -26,7 +26,7 @@ public enum Datastore { TransactionProvider transactionProvider) { switch (source) { case running: - return new RunningDatastoreQueryStrategy(); + return new RunningDatastoreQueryStrategy(transactionProvider); case candidate: return new CandidateDatastoreQueryStrategy(transactionProvider); default: diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/DiscardChanges.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/DiscardChanges.java index dd2ceaec19..1f70a1e52d 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/DiscardChanges.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/DiscardChanges.java @@ -54,13 +54,15 @@ public class DiscardChanges extends AbstractConfigNetconfOperation { protected Element handleWithNoSubsequentOperations(Document document, XmlElement xml) throws NetconfDocumentedException { fromXml(xml); try { - this.transactionProvider.abortTransaction(); - } catch (final IllegalStateException e) { + if (transactionProvider.getTransaction().isPresent()) { + this.transactionProvider.abortTransaction(); + } + } catch (final RuntimeException e) { LOG.warn("Abort failed: ", e); final Map errorInfo = new HashMap<>(); errorInfo .put(ErrorTag.operation_failed.name(), - "Operation failed. Use 'get-config' or 'edit-config' before triggering 'discard-changes' operation"); + "Abort failed."); throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed, ErrorSeverity.error, errorInfo); } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java index ca6a8c46b9..bc84734190 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java @@ -20,6 +20,7 @@ import java.util.Set; import javax.management.InstanceNotFoundException; import javax.management.ObjectName; import org.opendaylight.controller.config.api.ValidationException; +import org.opendaylight.controller.config.util.BeanReader; import org.opendaylight.controller.config.util.ConfigRegistryClient; import org.opendaylight.controller.config.util.ConfigTransactionClient; import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; @@ -262,7 +263,7 @@ public class EditConfig extends AbstractConfigNetconfOperation { public static Map> transformMbeToModuleConfigs - (final ConfigRegistryClient configRegistryClient, Map> mBeanEntries) { Map> namespaceToModuleNameToModuleConfig = Maps.newHashMap(); @@ -295,7 +296,6 @@ public class EditConfig extends AbstractConfigNetconfOperation { @Override protected Element handleWithNoSubsequentOperations(Document document, XmlElement xml) throws NetconfDocumentedException { - EditConfigXmlParser.EditConfigExecution editConfigExecution; Config cfg = getConfigMapping(getConfigRegistryClient(), yangStoreSnapshot); editConfigExecution = editConfigXmlParser.fromXml(xml, cfg); diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java index 27d53cdc32..fe7f2773cd 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java @@ -14,6 +14,7 @@ import java.util.Map; import java.util.Set; import javax.management.ObjectName; import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.config.util.ConfigTransactionClient; import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; @@ -27,6 +28,7 @@ import org.opendaylight.controller.netconf.confignetconfconnector.operations.Abs import org.opendaylight.controller.netconf.confignetconfconnector.operations.Datastore; import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig; import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreContext; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException; import org.opendaylight.controller.netconf.util.exception.UnexpectedElementException; import org.opendaylight.controller.netconf.util.exception.UnexpectedNamespaceException; @@ -38,12 +40,14 @@ import org.w3c.dom.Element; public class Get extends AbstractConfigNetconfOperation { + private final TransactionProvider transactionProvider; private final YangStoreContext yangStoreSnapshot; private static final Logger LOG = LoggerFactory.getLogger(Get.class); - public Get(YangStoreContext yangStoreSnapshot, ConfigRegistryClient configRegistryClient, + public Get(final TransactionProvider transactionProvider, YangStoreContext yangStoreSnapshot, ConfigRegistryClient configRegistryClient, String netconfSessionIdForReporting) { super(configRegistryClient, netconfSessionIdForReporting); + this.transactionProvider = transactionProvider; this.yangStoreSnapshot = yangStoreSnapshot; } @@ -115,23 +119,30 @@ public class Get extends AbstractConfigNetconfOperation { protected Element handleWithNoSubsequentOperations(Document document, XmlElement xml) throws NetconfDocumentedException { checkXml(xml); - final Set runtimeBeans = getConfigRegistryClient().lookupRuntimeBeans(); + final ObjectName testTransaction = transactionProvider.getOrCreateReadTransaction(); + final ConfigTransactionClient registryClient = getConfigRegistryClient().getConfigTransactionClient(testTransaction); - //Transaction provider required only for candidate datastore - final Set configBeans = Datastore.getInstanceQueryStrategy(Datastore.running, null) - .queryInstances(getConfigRegistryClient()); + try { + // Runtime beans are not parts of transactions and have to be queried against the central registry + final Set runtimeBeans = getConfigRegistryClient().lookupRuntimeBeans(); - final Map> moduleRuntimes = createModuleRuntimes(getConfigRegistryClient(), - yangStoreSnapshot.getModuleMXBeanEntryMap()); - final Map> moduleConfigs = EditConfig.transformMbeToModuleConfigs( - getConfigRegistryClient(), yangStoreSnapshot.getModuleMXBeanEntryMap()); + final Set configBeans = Datastore.getInstanceQueryStrategy(Datastore.running, transactionProvider) + .queryInstances(getConfigRegistryClient()); - final Runtime runtime = new Runtime(moduleRuntimes, moduleConfigs); + final Map> moduleRuntimes = createModuleRuntimes(getConfigRegistryClient(), + yangStoreSnapshot.getModuleMXBeanEntryMap()); + final Map> moduleConfigs = EditConfig.transformMbeToModuleConfigs( + registryClient, yangStoreSnapshot.getModuleMXBeanEntryMap()); - final Element element = runtime.toXml(runtimeBeans, configBeans, document); + final Runtime runtime = new Runtime(moduleRuntimes, moduleConfigs); - LOG.trace("{} operation successful", XmlNetconfConstants.GET); + final Element element = runtime.toXml(runtimeBeans, configBeans, document); - return element; + LOG.trace("{} operation successful", XmlNetconfConstants.GET); + + return element; + } finally { + transactionProvider.closeReadTransaction(); + } } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java index 350ace5eb1..2c4bde0ee8 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java @@ -72,23 +72,36 @@ public class GetConfig extends AbstractConfigNetconfOperation { private Element getResponseInternal(final Document document, final ConfigRegistryClient configRegistryClient, final Datastore source) { - Element dataElement = XmlUtil.createElement(document, XmlNetconfConstants.DATA_KEY, Optional.absent()); - final Set instances = Datastore.getInstanceQueryStrategy(source, this.transactionProvider) - .queryInstances(configRegistryClient); - final Config configMapping = new Config(EditConfig.transformMbeToModuleConfigs(configRegistryClient, - yangStoreSnapshot.getModuleMXBeanEntryMap())); - - - ObjectName on = transactionProvider.getOrCreateTransaction(); - ConfigTransactionClient ta = configRegistryClient.getConfigTransactionClient(on); - - ServiceRegistryWrapper serviceTracker = new ServiceRegistryWrapper(ta); - dataElement = configMapping.toXml(instances, this.maybeNamespace, document, dataElement, serviceTracker); - - LOG.trace("{} operation successful", GET_CONFIG); - - return dataElement; + final ConfigTransactionClient registryClient; + // Read current state from a transaction, if running is source, then start new transaction just for reading + // in case of candidate, get current transaction representing candidate + if(source == Datastore.running) { + final ObjectName readTx = transactionProvider.getOrCreateReadTransaction(); + registryClient = getConfigRegistryClient().getConfigTransactionClient(readTx); + } else { + registryClient = getConfigRegistryClient().getConfigTransactionClient(transactionProvider.getOrCreateTransaction()); + } + + try { + Element dataElement = XmlUtil.createElement(document, XmlNetconfConstants.DATA_KEY, Optional.absent()); + final Set instances = Datastore.getInstanceQueryStrategy(source, this.transactionProvider) + .queryInstances(configRegistryClient); + + final Config configMapping = new Config(EditConfig.transformMbeToModuleConfigs(registryClient, + yangStoreSnapshot.getModuleMXBeanEntryMap())); + + ServiceRegistryWrapper serviceTracker = new ServiceRegistryWrapper(registryClient); + dataElement = configMapping.toXml(instances, this.maybeNamespace, document, dataElement, serviceTracker); + + LOG.trace("{} operation successful", GET_CONFIG); + + return dataElement; + } finally { + if(source == Datastore.running) { + transactionProvider.closeReadTransaction(); + } + } } @Override diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java index ae9cb2eb37..74b5f60a10 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java @@ -11,12 +11,22 @@ package org.opendaylight.controller.netconf.confignetconfconnector.operations.ge import java.util.Set; import javax.management.ObjectName; import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; public class RunningDatastoreQueryStrategy implements DatastoreQueryStrategy { + private final TransactionProvider transactionProvider; + + public RunningDatastoreQueryStrategy(TransactionProvider transactionProvider) { + this.transactionProvider = transactionProvider; + } + @Override public Set queryInstances(ConfigRegistryClient configRegistryClient) { - return configRegistryClient.lookupConfigBeans(); + ObjectName on = transactionProvider.getOrCreateReadTransaction(); + ConfigTransactionClient proxy = configRegistryClient.getConfigTransactionClient(on); + return proxy.lookupConfigBeans(); } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java index 612bd85998..e3fdc056d9 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java @@ -52,7 +52,7 @@ final class NetconfOperationProvider { ops.add(new Commit(transactionProvider, configRegistryClient, netconfSessionIdForReporting)); ops.add(new Lock(netconfSessionIdForReporting)); ops.add(new UnLock(netconfSessionIdForReporting)); - ops.add(new Get(yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting)); + ops.add(new Get(transactionProvider, yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting)); ops.add(new DiscardChanges(transactionProvider, configRegistryClient, netconfSessionIdForReporting)); ops.add(new Validate(transactionProvider, configRegistryClient, netconfSessionIdForReporting)); ops.add(new RuntimeRpc(yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting)); diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java index b2ee63a987..7655cb300d 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java @@ -31,7 +31,8 @@ public class TransactionProvider implements AutoCloseable { private final ConfigRegistryClient configRegistryClient; private final String netconfSessionIdForReporting; - private ObjectName transaction; + private ObjectName candidateTx; + private ObjectName readTx; private final List allOpenedTransactions = new ArrayList<>(); private static final String NO_TRANSACTION_FOUND_FOR_SESSION = "No transaction found for session "; @@ -56,18 +57,34 @@ public class TransactionProvider implements AutoCloseable { public synchronized Optional getTransaction() { - if (transaction == null){ + if (candidateTx == null){ return Optional.absent(); } // Transaction was already closed somehow - if (!isStillOpenTransaction(transaction)) { - LOG.warn("Fixing illegal state: transaction {} was closed in {}", transaction, + if (!isStillOpenTransaction(candidateTx)) { + LOG.warn("Fixing illegal state: transaction {} was closed in {}", candidateTx, netconfSessionIdForReporting); - transaction = null; + candidateTx = null; return Optional.absent(); } - return Optional.of(transaction); + return Optional.of(candidateTx); + } + + public synchronized Optional getReadTransaction() { + + if (readTx == null){ + return Optional.absent(); + } + + // Transaction was already closed somehow + if (!isStillOpenTransaction(readTx)) { + LOG.warn("Fixing illegal state: transaction {} was closed in {}", readTx, + netconfSessionIdForReporting); + readTx = null; + return Optional.absent(); + } + return Optional.of(readTx); } private boolean isStillOpenTransaction(ObjectName transaction) { @@ -80,9 +97,20 @@ public class TransactionProvider implements AutoCloseable { if (ta.isPresent()) { return ta.get(); } - transaction = configRegistryClient.beginConfig(); - allOpenedTransactions.add(transaction); - return transaction; + candidateTx = configRegistryClient.beginConfig(); + allOpenedTransactions.add(candidateTx); + return candidateTx; + } + + public synchronized ObjectName getOrCreateReadTransaction() { + Optional ta = getReadTransaction(); + + if (ta.isPresent()) { + return ta.get(); + } + readTx = configRegistryClient.beginConfig(); + allOpenedTransactions.add(readTx); + return readTx; } /** @@ -109,8 +137,8 @@ public class TransactionProvider implements AutoCloseable { try { CommitStatus status = configRegistryClient.commitConfig(taON); // clean up - allOpenedTransactions.remove(transaction); - transaction = null; + allOpenedTransactions.remove(candidateTx); + candidateTx = null; return status; } catch (ValidationException validationException) { // no clean up: user can reconfigure and recover this transaction @@ -131,8 +159,19 @@ public class TransactionProvider implements AutoCloseable { ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get()); transactionClient.abortConfig(); - allOpenedTransactions.remove(transaction); - transaction = null; + allOpenedTransactions.remove(candidateTx); + candidateTx = null; + } + + public synchronized void closeReadTransaction() { + LOG.debug("Closing read transaction"); + Optional taON = getReadTransaction(); + Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting); + + ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get()); + transactionClient.abortConfig(); + allOpenedTransactions.remove(readTx); + readTx = null; } public synchronized void abortTestTransaction(ObjectName testTx) { diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java index b1222997fb..d6b5e62b27 100644 --- a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java @@ -569,14 +569,14 @@ public class NetconfMappingTest extends AbstractConfigTest { commit(); } - @Test(expected = NetconfDocumentedException.class) + @Test public void testEx2() throws Exception { - discard(); + assertContainsElement(discard(), readXmlToElement("")); } - private void discard() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException { + private Document discard() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException { DiscardChanges discardOp = new DiscardChanges(transactionProvider, configRegistryClient, NETCONF_SESSION_ID); - executeOp(discardOp, "netconfMessages/discardChanges.xml"); + return executeOp(discardOp, "netconfMessages/discardChanges.xml"); } private void checkBinaryLeafEdited(final Document response) throws NodeTestException, SAXException, IOException { @@ -716,7 +716,7 @@ public class NetconfMappingTest extends AbstractConfigTest { } private Document get() throws NetconfDocumentedException, ParserConfigurationException, SAXException, IOException { - Get getOp = new Get(yangStoreSnapshot, configRegistryClient, NETCONF_SESSION_ID); + Get getOp = new Get(transactionProvider, yangStoreSnapshot, configRegistryClient, NETCONF_SESSION_ID); return executeOp(getOp, "netconfMessages/get.xml"); } diff --git a/opendaylight/networkconfiguration/neutron/northbound/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/INeutronRequest.java b/opendaylight/networkconfiguration/neutron/northbound/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/INeutronRequest.java index 8e0ff5c859..8325d84e35 100644 --- a/opendaylight/networkconfiguration/neutron/northbound/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/INeutronRequest.java +++ b/opendaylight/networkconfiguration/neutron/northbound/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/INeutronRequest.java @@ -14,6 +14,12 @@ import org.opendaylight.controller.networkconfig.neutron.INeutronObject; import java.util.List; +/** + * This interface defines the methods for Neutron Requests + * + * @deprecated Replaced by {@link org.opendaylight.neutron.northbound.api.INeutronRequest} + */ +@Deprecated public interface INeutronRequest { public T getSingleton(); public boolean isSingleton(); diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallAware.java index 9b4f579025..e0a59091f1 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallAware.java @@ -11,8 +11,10 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods a service that wishes to be aware of Firewall Rules needs to implement * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronFirewallAware} */ +@Deprecated public interface INeutronFirewallAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallCRUD.java index c986bffd6b..5e7fbb2090 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallCRUD.java @@ -13,8 +13,10 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB OpenStack Firewall objects * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronFirewallCRUD} */ +@Deprecated public interface INeutronFirewallCRUD { /** * Applications call this interface method to determine if a particular diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallPolicyAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallPolicyAware.java index 203d513923..ba467834d4 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallPolicyAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallPolicyAware.java @@ -11,8 +11,10 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods a service that wishes to be aware of Firewall Policys needs to implement * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronFirewallPolicyAware} */ +@Deprecated public interface INeutronFirewallPolicyAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallPolicyCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallPolicyCRUD.java index 6049656a6e..f280ff2266 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallPolicyCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallPolicyCRUD.java @@ -13,8 +13,10 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB OpenStack Firewall Policy objects * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronFirewallPolicyCRUD} */ +@Deprecated public interface INeutronFirewallPolicyCRUD { /** * Applications call this interface method to determine if a particular diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallRuleAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallRuleAware.java index a663058328..5863cdd0dc 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallRuleAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallRuleAware.java @@ -11,8 +11,10 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods a service that wishes to be aware of Firewall Rules needs to implement * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronFirewallRuleAware} */ +@Deprecated public interface INeutronFirewallRuleAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallRuleCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallRuleCRUD.java index 990896bdb7..b0c8c613c1 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallRuleCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFirewallRuleCRUD.java @@ -13,8 +13,10 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB OpenStack Firewall Rule objects * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronFirewallRuleCRUD} */ +@Deprecated public interface INeutronFirewallRuleCRUD { /** * Applications call this interface method to determine if a particular diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFloatingIPAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFloatingIPAware.java index 43175d3236..0a6c76ccb8 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFloatingIPAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFloatingIPAware.java @@ -11,8 +11,10 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods a service that wishes to be aware of Neutron FloatingIPs needs to implement * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronFloatingIPAware} */ +@Deprecated public interface INeutronFloatingIPAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFloatingIPCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFloatingIPCRUD.java index e416ee7d2f..561e1dae73 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFloatingIPCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronFloatingIPCRUD.java @@ -13,8 +13,10 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB FloatingIP objects * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronFloatingIPCRUD} */ +@Deprecated public interface INeutronFloatingIPCRUD { /** * Applications call this interface method to determine if a particular diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerAware.java index e4aa5f382b..7202b01617 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerAware.java @@ -11,8 +11,10 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods a service that wishes to be aware of LoadBalancer Rules needs to implement * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronLoadBalancerAware} */ +@Deprecated public interface INeutronLoadBalancerAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerCRUD.java index a2ce41eab2..394622fe6c 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerCRUD.java @@ -13,8 +13,10 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB OpenStack LoadBalancer objects * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronLoadBalancerCRUD} */ +@Deprecated public interface INeutronLoadBalancerCRUD { /** * Applications call this interface method to determine if a particular diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerHealthMonitorAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerHealthMonitorAware.java index 7194da32b4..2259435c9e 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerHealthMonitorAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerHealthMonitorAware.java @@ -11,8 +11,10 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods a service that wishes to be aware of LoadBalancerHealthMonitor Rules needs to implement * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronLoadBalancerHealthMonitorAware} */ +@Deprecated public interface INeutronLoadBalancerHealthMonitorAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerHealthMonitorCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerHealthMonitorCRUD.java index 78380001df..f7b11a53aa 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerHealthMonitorCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerHealthMonitorCRUD.java @@ -13,8 +13,10 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB OpenStack LoadBalancerHealthMonitor objects * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronLoadBalancerHealthMonitorCRUD} */ +@Deprecated public interface INeutronLoadBalancerHealthMonitorCRUD { /** * Applications call this interface method to determine if a particular diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerListenerAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerListenerAware.java index 417419f936..2043878905 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerListenerAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerListenerAware.java @@ -11,8 +11,10 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods a service that wishes to be aware of LoadBalancerListener Rules needs to implement * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronLoadBalancerListenerAware} */ +@Deprecated public interface INeutronLoadBalancerListenerAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerListenerCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerListenerCRUD.java index c160f8ed8b..7336c76cd3 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerListenerCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerListenerCRUD.java @@ -13,8 +13,10 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB OpenStack LoadBalancerListener objects * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronLoadBalancerListenerCRUD} */ +@Deprecated public interface INeutronLoadBalancerListenerCRUD { /** * Applications call this interface method to determine if a particular diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolAware.java index 16c7d37169..851df355bb 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolAware.java @@ -11,8 +11,10 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods a service that wishes to be aware of LoadBalancerPool Rules needs to implement * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronLoadBalancerPoolAware} */ +@Deprecated public interface INeutronLoadBalancerPoolAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolCRUD.java index 9614448d06..7fabcd9242 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolCRUD.java @@ -13,8 +13,10 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB OpenStack LoadBalancerPool objects * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronLoadBalancerPoolCRUD} */ +@Deprecated public interface INeutronLoadBalancerPoolCRUD { /** * Applications call this interface method to determine if a particular diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolMemberAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolMemberAware.java index 69a57748dd..88a16cae4d 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolMemberAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolMemberAware.java @@ -7,6 +7,13 @@ */ package org.opendaylight.controller.networkconfig.neutron; +/** + * This interface defines the methods for CRUD of NB OpenStack INeutronLoadBalancerPoolMemberAware objects + * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronLoadBalancerPoolMemberAware} + */ + +@Deprecated public interface INeutronLoadBalancerPoolMemberAware { diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolMemberCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolMemberCRUD.java index c1f5c7003c..99b32169e1 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolMemberCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronLoadBalancerPoolMemberCRUD.java @@ -10,6 +10,13 @@ package org.opendaylight.controller.networkconfig.neutron; import java.util.List; +/** + * This interface defines the methods for CRUD of NB OpenStack INeutronLoadBalancerPoolMemberCRUD objects + * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronLoadBalancerPoolMemberCRUD} + */ + +@Deprecated public interface INeutronLoadBalancerPoolMemberCRUD { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronNetworkAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronNetworkAware.java index 88d3c1dc6e..f5936a63a5 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronNetworkAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronNetworkAware.java @@ -11,8 +11,10 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods a service that wishes to be aware of Neutron Networks needs to implement * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronNetworkAware} */ +@Deprecated public interface INeutronNetworkAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronNetworkCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronNetworkCRUD.java index bf900a618f..7f86536fd9 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronNetworkCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronNetworkCRUD.java @@ -13,8 +13,10 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB network objects * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronNetworkCRUD} */ +@Deprecated public interface INeutronNetworkCRUD { /** * Applications call this interface method to determine if a particular diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronObject.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronObject.java index bebac37c41..e39c07eadc 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronObject.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronObject.java @@ -12,7 +12,10 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This class contains behaviour common to Neutron configuration objects + * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronObject} */ +@Deprecated public interface INeutronObject { public String getID(); public void setID(String id); diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronPortAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronPortAware.java index 3f40ba3879..3a14c308a2 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronPortAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronPortAware.java @@ -11,8 +11,10 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods a service that wishes to be aware of Neutron Ports needs to implement * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronPortAware} */ +@Deprecated public interface INeutronPortAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronPortCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronPortCRUD.java index ce30c6806e..58b007b65d 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronPortCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronPortCRUD.java @@ -13,8 +13,10 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB Port objects * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronPortCRUD} */ +@Deprecated public interface INeutronPortCRUD { /** * Applications call this interface method to determine if a particular diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronRouterAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronRouterAware.java index 3c9e83d490..f4c4434362 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronRouterAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronRouterAware.java @@ -11,8 +11,10 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods a service that wishes to be aware of Neutron Routers needs to implement * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronRouterAware} */ +@Deprecated public interface INeutronRouterAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronRouterCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronRouterCRUD.java index b1a943fce1..37b2c940e0 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronRouterCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronRouterCRUD.java @@ -13,8 +13,10 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB Router objects * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronRouterCRUD} */ +@Deprecated public interface INeutronRouterCRUD { /** * Applications call this interface method to determine if a particular diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupAware.java index 0fdf77f968..b518924e19 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupAware.java @@ -11,8 +11,11 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods a service that wishes to be aware of Neutron Security Groups needs to implement + * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronSecurityGroupAware} */ +@Deprecated public interface INeutronSecurityGroupAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupCRUD.java index a408ef92a7..6b20182eae 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupCRUD.java @@ -13,8 +13,11 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB OpenStack Security Group objects + * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronSecurityGroupCRUD} */ +@Deprecated public interface INeutronSecurityGroupCRUD { /** * Applications call this interface method to determine if a particular diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleAware.java index ff2a1c4978..3be9cc92b6 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleAware.java @@ -11,8 +11,11 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods required to be aware of Neutron Security Rules + * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronSecurityRuleAware} */ +@Deprecated public interface INeutronSecurityRuleAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleCRUD.java index 73b41c71a4..a5223b7e8d 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleCRUD.java @@ -13,8 +13,11 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB OpenStack Security Rule objects + * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronSecurityRuleCRUD} */ +@Deprecated public interface INeutronSecurityRuleCRUD { /** * Applications call this interface method to determine if a particular diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSubnetAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSubnetAware.java index fa0707698d..57d382b127 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSubnetAware.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSubnetAware.java @@ -11,8 +11,10 @@ package org.opendaylight.controller.networkconfig.neutron; /** * This interface defines the methods a service that wishes to be aware of Neutron Subnets needs to implement * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronSubnetAware} */ +@Deprecated public interface INeutronSubnetAware { /** diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSubnetCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSubnetCRUD.java index 6f9a6ffb7b..8420682848 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSubnetCRUD.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSubnetCRUD.java @@ -13,8 +13,10 @@ import java.util.List; /** * This interface defines the methods for CRUD of NB Subnet objects * + * @deprecated Replaced by {@link org.opendaylight.neutron.neutron.spi.INeutronSubnetCRUD} */ +@Deprecated public interface INeutronSubnetCRUD { /** * Applications call this interface method to determine if a particular