From: Moiz Raja Date: Mon, 15 Sep 2014 23:25:21 +0000 (+0000) Subject: Merge "Bug#1854 - Exit command in console causing OOM." X-Git-Tag: release/helium~69 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=510f3bd2d25c0e667a60ff5ef18cfbd40534cec0;hp=35eb97d26856884cbc6a5b2d0c4102c1b2bacb0a Merge "Bug#1854 - Exit command in console causing OOM." --- diff --git a/features/mdsal/pom.xml b/features/mdsal/pom.xml index 9b81f81ae4..960dfb37a1 100644 --- a/features/mdsal/pom.xml +++ b/features/mdsal/pom.xml @@ -242,6 +242,23 @@ xml config + + org.opendaylight.controller.samples + clustering-it-model + ${mdsal.version} + + + org.opendaylight.controller.samples + clustering-it-provider + ${mdsal.version} + + + org.opendaylight.controller.samples + clustering-it-config + ${mdsal.version} + xml + config + org.opendaylight.controller sal-rest-docgen diff --git a/features/mdsal/src/main/resources/features.xml b/features/mdsal/src/main/resources/features.xml index c336635df4..da246b63e3 100644 --- a/features/mdsal/src/main/resources/features.xml +++ b/features/mdsal/src/main/resources/features.xml @@ -108,4 +108,15 @@ mvn:org.opendaylight.controller/sal-clustering-config/${project.version}/xml/moduleshardconf mvn:org.opendaylight.controller/sal-clustering-config/${project.version}/xml/moduleconf + + + odl-mdsal-clustering + odl-restconf + odl-yangtools-models + mvn:org.opendaylight.controller.samples/clustering-it-model/${project.version} + mvn:org.opendaylight.controller.samples/clustering-it-provider/${project.version} + mvn:org.opendaylight.controller.samples/clustering-it-config/${project.version}/xml/config + mvn:org.opendaylight.controller.samples/clustering-it-config/${project.version}/xml/testmoduleshardconf + mvn:org.opendaylight.controller.samples/clustering-it-config/${project.version}/xml/testmoduleconf + diff --git a/features/nsf/pom.xml b/features/nsf/pom.xml index 875ca2ca61..e677d491bc 100644 --- a/features/nsf/pom.xml +++ b/features/nsf/pom.xml @@ -264,6 +264,18 @@ org.opendaylight.controller.thirdparty net.sf.jung2 + + org.eclipse.persistence + org.eclipse.persistence.antlr + + + org.eclipse.persistence + org.eclipse.persistence.core + + + org.eclipse.persistence + org.eclipse.persistence.moxy + diff --git a/features/nsf/src/main/resources/features.xml b/features/nsf/src/main/resources/features.xml index 8dc51f1644..e8f7bc1e5c 100644 --- a/features/nsf/src/main/resources/features.xml +++ b/features/nsf/src/main/resources/features.xml @@ -67,6 +67,9 @@ 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/networkconfig.neutron.northbound/${networkconfig.neutron.northbound.version} mvn:org.opendaylight.controller/forwarding.staticrouting.northbound/${forwarding.staticrouting.northbound.version} mvn:org.opendaylight.controller/statistics.northbound/${statistics.northbound.version} diff --git a/opendaylight/archetypes/opendaylight-karaf-distro-archetype/src/main/resources/archetype-resources/pom.xml b/opendaylight/archetypes/opendaylight-karaf-distro-archetype/src/main/resources/archetype-resources/pom.xml index 965c619695..fdc60625c8 100644 --- a/opendaylight/archetypes/opendaylight-karaf-distro-archetype/src/main/resources/archetype-resources/pom.xml +++ b/opendaylight/archetypes/opendaylight-karaf-distro-archetype/src/main/resources/archetype-resources/pom.xml @@ -245,6 +245,22 @@ false + + copy-dependencies + prepare-package + + copy-dependencies + + + ${project.build.directory}/assembly/system + false + true + true + true + true + true + + diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 2e817b97f3..4240db939a 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -180,7 +180,8 @@ java target/code-coverage/jacoco.exec target/code-coverage/jacoco-it.exec - org.openflow.openflowj,net.sf.jung2,org.opendaylight.controller.protobuff.messages + org.openflow.openflowj,net.sf.jung2,org.opendaylight.controller.protobuff.messages,ch.ethz.ssh2 + Sonar way with Findbugs 1.0.0 1.2.1 3.1.4.RELEASE @@ -211,7 +212,8 @@ 2013.09.07.4-SNAPSHOT 1.0.0-SNAPSHOT 0.6.2-SNAPSHOT - 0.12.0 + 0.12.0 + 0.9.7 @@ -2021,6 +2023,17 @@ xml runtime + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + diff --git a/opendaylight/distribution/opendaylight-karaf-resources/src/main/resources/bin/setenv b/opendaylight/distribution/opendaylight-karaf-resources/src/main/resources/bin/setenv new file mode 100755 index 0000000000..4f240447b4 --- /dev/null +++ b/opendaylight/distribution/opendaylight-karaf-resources/src/main/resources/bin/setenv @@ -0,0 +1,55 @@ +#!/bin/sh +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# handle specific scripts; the SCRIPT_NAME is exactly the name of the Karaf +# script; for example karaf, start, stop, admin, client, ... +# +# if [ "$KARAF_SCRIPT" == "SCRIPT_NAME" ]; then +# Actions go here... +# fi + +# +# general settings which should be applied for all scripts go here; please keep +# in mind that it is possible that scripts might be executed more than once, e.g. +# in example of the start script where the start script is executed first and the +# karaf script afterwards. +# + +# +# The following section shows the possible configuration options for the default +# karaf scripts +# +# export JAVA_HOME # Location of Java installation +# export JAVA_MIN_MEM # Minimum memory for the JVM +# export JAVA_MAX_MEM # Maximum memory for the JVM +# export JAVA_PERM_MEM # Minimum perm memory for the JVM +# export JAVA_MAX_PERM_MEM # Maximum perm memory for the JVM +# export KARAF_HOME # Karaf home folder +# export KARAF_DATA # Karaf data folder +# export KARAF_BASE # Karaf base folder +# export KARAF_ETC # Karaf etc folder +# export KARAF_OPTS # Additional available Karaf options +# export KARAF_DEBUG # Enable debug mode +if [ "x$JAVA_MAX_PERM_MEM" == "x" ]; then + export JAVA_MAX_PERM_MEM="512m" +fi +if [ "x$JAVA_MAX_MEM" == "x" ]; then + export JAVA_MAX_MEM="2048m" +fi + diff --git a/opendaylight/md-sal/benchmark-data-store/pom.xml b/opendaylight/md-sal/benchmark-data-store/pom.xml new file mode 100644 index 0000000000..1af2287a10 --- /dev/null +++ b/opendaylight/md-sal/benchmark-data-store/pom.xml @@ -0,0 +1,72 @@ + + + + + sal-parent + org.opendaylight.controller + 1.1-SNAPSHOT + + 4.0.0 + + org.opendaylight.controller + benchmark-data-store + + + + org.opendaylight.yangtools + yang-data-impl + + + org.opendaylight.yangtools + yang-parser-impl + + + org.openjdk.jmh + jmh-core + + + org.openjdk.jmh + jmh-generator-annprocess + + + org.opendaylight.controller + sal-inmemory-datastore + + + + + + + org.codehaus.mojo + exec-maven-plugin + + test + java + + -classpath + + org.openjdk.jmh.Main + .* + + + + + run-benchmarks + integration-test + + exec + + + + + + + \ No newline at end of file diff --git a/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/AbstractInMemoryDatastoreWriteTransactionBenchmark.java b/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/AbstractInMemoryDatastoreWriteTransactionBenchmark.java new file mode 100644 index 0000000000..aa5ef61ce4 --- /dev/null +++ b/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/AbstractInMemoryDatastoreWriteTransactionBenchmark.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.store.benchmark; + +import java.util.concurrent.TimeUnit; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Warmup; + +/** + * @author Lukas Sedlak + */ +public abstract class AbstractInMemoryDatastoreWriteTransactionBenchmark { + + private static final int WARMUP_ITERATIONS = 20; + private static final int MEASUREMENT_ITERATIONS = 20; + + private static final int OUTER_LIST_100K = 100000; + private static final int OUTER_LIST_50K = 50000; + private static final int OUTER_LIST_10K = 10000; + + private static final YangInstanceIdentifier[] OUTER_LIST_100K_PATHS = initOuterListPaths(OUTER_LIST_100K); + private static final YangInstanceIdentifier[] OUTER_LIST_50K_PATHS = initOuterListPaths(OUTER_LIST_50K); + private static final YangInstanceIdentifier[] OUTER_LIST_10K_PATHS = initOuterListPaths(OUTER_LIST_10K); + + private static YangInstanceIdentifier[] initOuterListPaths(final int outerListPathsCount) { + final YangInstanceIdentifier[] paths = new YangInstanceIdentifier[outerListPathsCount]; + + for (int outerListKey = 0; outerListKey < outerListPathsCount; ++outerListKey) { + paths[outerListKey] = YangInstanceIdentifier.builder(BenchmarkModel.OUTER_LIST_PATH) + .nodeWithKey(BenchmarkModel.OUTER_LIST_QNAME, BenchmarkModel.ID_QNAME, outerListKey) + .build(); + } + return paths; + } + + private static final MapNode ONE_ITEM_INNER_LIST = initInnerListItems(1); + private static final MapNode TWO_ITEM_INNER_LIST = initInnerListItems(2); + private static final MapNode TEN_ITEM_INNER_LIST = initInnerListItems(10); + + private static MapNode initInnerListItems(final int count) { + final CollectionNodeBuilder mapEntryBuilder = ImmutableNodes + .mapNodeBuilder(BenchmarkModel.INNER_LIST_QNAME); + + for (int i = 1; i <= count; ++i) { + mapEntryBuilder + .withChild(ImmutableNodes.mapEntry(BenchmarkModel.INNER_LIST_QNAME, BenchmarkModel.NAME_QNAME, i)); + } + return mapEntryBuilder.build(); + } + + private static final NormalizedNode[] OUTER_LIST_ONE_ITEM_INNER_LIST = initOuterListItems(OUTER_LIST_100K, ONE_ITEM_INNER_LIST); + private static final NormalizedNode[] OUTER_LIST_TWO_ITEM_INNER_LIST = initOuterListItems(OUTER_LIST_50K, TWO_ITEM_INNER_LIST); + private static final NormalizedNode[] OUTER_LIST_TEN_ITEM_INNER_LIST = initOuterListItems(OUTER_LIST_10K, TEN_ITEM_INNER_LIST); + + private static NormalizedNode[] initOuterListItems(int outerListItemsCount, MapNode innerList) { + final NormalizedNode[] outerListItems = new NormalizedNode[outerListItemsCount]; + + for (int i = 0; i < outerListItemsCount; ++i) { + int outerListKey = i; + outerListItems[i] = ImmutableNodes.mapEntryBuilder(BenchmarkModel.OUTER_LIST_QNAME, BenchmarkModel.ID_QNAME, outerListKey) + .withChild(innerList).build(); + } + return outerListItems; + } + + protected SchemaContext schemaContext; + protected InMemoryDOMDataStore domStore; + + abstract public void setUp() throws Exception; + + abstract public void tearDown(); + + protected void initTestNode() throws Exception { + final YangInstanceIdentifier testPath = YangInstanceIdentifier.builder(BenchmarkModel.TEST_PATH) + .build(); + DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction(); + writeTx.write(testPath, provideOuterListNode()); + + DOMStoreThreePhaseCommitCohort cohort = writeTx.ready(); + cohort.canCommit().get(); + cohort.preCommit().get(); + cohort.commit().get(); + } + + private DataContainerChild provideOuterListNode() { + return ImmutableContainerNodeBuilder + .create() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(BenchmarkModel.TEST_QNAME)) + .withChild( + ImmutableNodes.mapNodeBuilder(BenchmarkModel.OUTER_LIST_QNAME) + .build()).build(); + } + + @Benchmark + @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS) + public void write100KSingleNodeWithOneInnerItemInOneCommitBenchmark() throws Exception { + DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction(); + for (int outerListKey = 0; outerListKey < OUTER_LIST_100K; ++outerListKey) { + writeTx.write(OUTER_LIST_100K_PATHS[outerListKey], OUTER_LIST_ONE_ITEM_INNER_LIST[outerListKey]); + } + DOMStoreThreePhaseCommitCohort cohort = writeTx.ready(); + cohort.canCommit().get(); + cohort.preCommit().get(); + cohort.commit().get(); + } + + @Benchmark + @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS) + public void write100KSingleNodeWithOneInnerItemInCommitPerWriteBenchmark() throws Exception { + for (int outerListKey = 0; outerListKey < OUTER_LIST_100K; ++outerListKey) { + DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction(); + writeTx.write(OUTER_LIST_100K_PATHS[outerListKey], OUTER_LIST_ONE_ITEM_INNER_LIST[outerListKey]); + + DOMStoreThreePhaseCommitCohort cohort = writeTx.ready(); + cohort.canCommit().get(); + cohort.preCommit().get(); + cohort.commit().get(); + } + } + + @Benchmark + @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS) + public void write50KSingleNodeWithTwoInnerItemsInOneCommitBenchmark() throws Exception { + DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction(); + for (int outerListKey = 0; outerListKey < OUTER_LIST_50K; ++outerListKey) { + writeTx.write(OUTER_LIST_50K_PATHS[outerListKey], OUTER_LIST_TWO_ITEM_INNER_LIST[outerListKey]); + } + DOMStoreThreePhaseCommitCohort cohort = writeTx.ready(); + cohort.canCommit().get(); + cohort.preCommit().get(); + cohort.commit().get(); + } + + @Benchmark + @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS) + public void write50KSingleNodeWithTwoInnerItemsInCommitPerWriteBenchmark() throws Exception { + for (int outerListKey = 0; outerListKey < OUTER_LIST_50K; ++outerListKey) { + DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction(); + writeTx.write(OUTER_LIST_50K_PATHS[outerListKey], OUTER_LIST_TWO_ITEM_INNER_LIST[outerListKey]); + DOMStoreThreePhaseCommitCohort cohort = writeTx.ready(); + cohort.canCommit().get(); + cohort.preCommit().get(); + cohort.commit().get(); + } + } + + @Benchmark + @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS) + public void write10KSingleNodeWithTenInnerItemsInOneCommitBenchmark() throws Exception { + DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction(); + for (int outerListKey = 0; outerListKey < OUTER_LIST_10K; ++outerListKey) { + writeTx.write(OUTER_LIST_10K_PATHS[outerListKey], OUTER_LIST_TEN_ITEM_INNER_LIST[outerListKey]); + } + DOMStoreThreePhaseCommitCohort cohort = writeTx.ready(); + cohort.canCommit().get(); + cohort.preCommit().get(); + cohort.commit().get(); + } + + @Benchmark + @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS) + public void write10KSingleNodeWithTenInnerItemsInCommitPerWriteBenchmark() throws Exception { + for (int outerListKey = 0; outerListKey < OUTER_LIST_10K; ++outerListKey) { + DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction(); + writeTx.write(OUTER_LIST_10K_PATHS[outerListKey], OUTER_LIST_TEN_ITEM_INNER_LIST[outerListKey]); + DOMStoreThreePhaseCommitCohort cohort = writeTx.ready(); + cohort.canCommit().get(); + cohort.preCommit().get(); + cohort.commit().get(); + } + } +} diff --git a/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/BenchmarkModel.java b/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/BenchmarkModel.java new file mode 100644 index 0000000000..024385b2a9 --- /dev/null +++ b/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/BenchmarkModel.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.store.benchmark; + +import java.io.InputStream; +import java.util.Collections; +import java.util.Set; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; + +/** + * Benchmark Model class loads the odl-datastore-test.yang model from resources. + *
+ * This class serves as facilitator class which holds several references to initialized yang model as static final + * members. + * + * @author Lukas Sedlak + */ +public final class BenchmarkModel { + + public static final QName TEST_QNAME = QName + .create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13","test"); + public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list"); + public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list"); + public static final QName ID_QNAME = QName.create(TEST_QNAME, "id"); + public static final QName NAME_QNAME = QName.create(TEST_QNAME, "name"); + 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 InputStream getDatastoreBenchmarkInputStream() { + return getInputStream(DATASTORE_TEST_YANG); + } + + private static InputStream getInputStream(final String resourceName) { + return BenchmarkModel.class.getResourceAsStream(resourceName); + } + + public static SchemaContext createTestContext() { + YangParserImpl parser = new YangParserImpl(); + Set modules = parser.parseYangModelsFromStreams(Collections.singletonList( + getDatastoreBenchmarkInputStream())); + return parser.resolveSchemaContext(modules); + } +} diff --git a/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/InMemoryDataStoreWithExecutorServiceBenchmark.java b/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/InMemoryDataStoreWithExecutorServiceBenchmark.java new file mode 100644 index 0000000000..4b9d66f4f2 --- /dev/null +++ b/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/InMemoryDataStoreWithExecutorServiceBenchmark.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.store.benchmark; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; +import org.opendaylight.yangtools.util.concurrent.SpecialExecutors; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; + +/** + * Benchmark for testing of performance of write operations for InMemoryDataStore. The instance + * of benchmark creates InMemoryDataStore with Data Change Listener Executor Service as BlockingBoundedFastThreadPool + * and DOM Store Executor Service as Blocking Bounded Fast Thread Pool. + * + * @see org.opendaylight.yangtools.util.concurrent.SpecialExecutors + * @see org.opendaylight.controller.md.sal.dom.store.benchmark.AbstractInMemoryDatastoreWriteTransactionBenchmark + * + * @author Lukas Sedlak + */ +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(1) +public class InMemoryDataStoreWithExecutorServiceBenchmark extends AbstractInMemoryDatastoreWriteTransactionBenchmark { + + private static final int MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE = 20; + private static final int MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE = 1000; + private static final int MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE = 5000; + + @Setup(Level.Trial) + public void setUp() throws Exception { + final String name = "DS_BENCHMARK"; + final ExecutorService dataChangeListenerExecutor = SpecialExecutors.newBlockingBoundedFastThreadPool( + MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE, MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE, name + "-DCL"); + + final ExecutorService domStoreExecutor = SpecialExecutors.newBoundedSingleThreadExecutor( + MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE, "DOMStore-" + name ); + + domStore = new InMemoryDOMDataStore(name, domStoreExecutor, + dataChangeListenerExecutor); + schemaContext = BenchmarkModel.createTestContext(); + domStore.onGlobalContextUpdated(schemaContext); + initTestNode(); + } + + @TearDown + public void tearDown() { + schemaContext = null; + domStore = null; + } +} diff --git a/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/InMemoryDataStoreWithSameThreadedExecutorBenchmark.java b/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/InMemoryDataStoreWithSameThreadedExecutorBenchmark.java new file mode 100644 index 0000000000..6a0ceccd53 --- /dev/null +++ b/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/InMemoryDataStoreWithSameThreadedExecutorBenchmark.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.store.benchmark; + +import com.google.common.util.concurrent.MoreExecutors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; +import org.opendaylight.yangtools.util.concurrent.SpecialExecutors; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; + +/** + * Benchmark for testing of performance of write operations for InMemoryDataStore. The instance + * of benchmark creates InMemoryDataStore with Data Change Listener Executor Service as Blocking Bounded Fast Thread Pool + * and DOM Store Executor Service as Same Thread Executor. + * + * @author Lukas Sedlak + */ +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(1) +public class InMemoryDataStoreWithSameThreadedExecutorBenchmark extends AbstractInMemoryDatastoreWriteTransactionBenchmark { + + private static final int MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE = 20; + private static final int MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE = 1000; + + @Setup(Level.Trial) + public void setUp() throws Exception { + final String name = "DS_BENCHMARK"; + final ExecutorService dataChangeListenerExecutor = SpecialExecutors.newBlockingBoundedFastThreadPool( + MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE, MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE, name + "-DCL"); + + domStore = new InMemoryDOMDataStore("SINGLE_THREADED_DS_BENCHMARK", MoreExecutors.sameThreadExecutor(), + dataChangeListenerExecutor); + schemaContext = BenchmarkModel.createTestContext(); + domStore.onGlobalContextUpdated(schemaContext); + initTestNode(); + } + + @TearDown + public void tearDown() { + schemaContext = null; + domStore = null; + } +} diff --git a/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/InMemoryDataStoreWriteTransactionBenchmark.java b/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/InMemoryDataStoreWriteTransactionBenchmark.java new file mode 100644 index 0000000000..d3dda96af5 --- /dev/null +++ b/opendaylight/md-sal/benchmark-data-store/src/main/java/org/opendaylight/controller/md/sal/dom/store/benchmark/InMemoryDataStoreWriteTransactionBenchmark.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.store.benchmark; + +import com.google.common.util.concurrent.MoreExecutors; +import java.util.concurrent.TimeUnit; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.TearDown; + +/** + * Benchmark for testing of performance of write operations for InMemoryDataStore. The instance + * of benchmark creates InMemoryDataStore with Data Change Listener Executor Service as Same Thread Executor + * and DOM Store Executor Service as Same Thread Executor. + * + * @author Lukas Sedlak + */ +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(1) +public class InMemoryDataStoreWriteTransactionBenchmark extends AbstractInMemoryDatastoreWriteTransactionBenchmark { + + @Setup(Level.Trial) + public void setUp() throws Exception { + domStore = new InMemoryDOMDataStore("SINGLE_THREADED_DS_BENCHMARK", MoreExecutors.sameThreadExecutor(), + MoreExecutors.sameThreadExecutor()); + schemaContext = BenchmarkModel.createTestContext(); + domStore.onGlobalContextUpdated(schemaContext); + initTestNode(); + } + + @TearDown + public void tearDown() { + schemaContext = null; + domStore = null; + } +} diff --git a/opendaylight/md-sal/benchmark-data-store/src/main/resources/odl-datastore-test.yang b/opendaylight/md-sal/benchmark-data-store/src/main/resources/odl-datastore-test.yang new file mode 100644 index 0000000000..730ca17173 --- /dev/null +++ b/opendaylight/md-sal/benchmark-data-store/src/main/resources/odl-datastore-test.yang @@ -0,0 +1,42 @@ +module odl-datastore-test { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test"; + prefix "store-test"; + + revision "2014-03-13" { + description "Initial revision."; + } + + container test { + list outer-list { + key id; + leaf id { + type int32; + } + choice outer-choice { + case one { + leaf one { + type string; + } + } + case two-three { + leaf two { + type string; + } + leaf three { + type string; + } + } + } + list inner-list { + key name; + leaf name { + type int32; + } + leaf value { + type string; + } + } + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/impl/FlowForwarder.java b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/impl/FlowForwarder.java index e0c16a0806..9951bf7448 100644 --- a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/impl/FlowForwarder.java +++ b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/impl/FlowForwarder.java @@ -77,7 +77,7 @@ public class FlowForwarder extends AbstractListeningCommiter { if (tableIdValidationPrecondition(tableKey, removeDataObj)) { final RemoveFlowInputBuilder builder = new RemoveFlowInputBuilder(removeDataObj); builder.setFlowRef(new FlowRef(identifier)); - builder.setNode(new NodeRef(nodeIdent)); + builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class))); builder.setFlowTable(new FlowTableRef(nodeIdent.child(Table.class, tableKey))); builder.setTransactionUri(new Uri(provider.getNewTransactionId())); this.provider.getSalFlowService().removeFlow(builder.build()); @@ -93,7 +93,7 @@ public class FlowForwarder extends AbstractListeningCommiter { if (tableIdValidationPrecondition(tableKey, update)) { final UpdateFlowInputBuilder builder = new UpdateFlowInputBuilder(); - builder.setNode(new NodeRef(nodeIdent)); + builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class))); builder.setFlowRef(new FlowRef(identifier)); builder.setTransactionUri(new Uri(provider.getNewTransactionId())); builder.setUpdatedFlow((new UpdatedFlowBuilder(update)).build()); @@ -112,7 +112,7 @@ public class FlowForwarder extends AbstractListeningCommiter { if (tableIdValidationPrecondition(tableKey, addDataObj)) { final AddFlowInputBuilder builder = new AddFlowInputBuilder(addDataObj); - builder.setNode(new NodeRef(nodeIdent)); + builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class))); builder.setFlowRef(new FlowRef(identifier)); builder.setFlowTable(new FlowTableRef(nodeIdent.child(Table.class, tableKey))); builder.setTransactionUri(new Uri(provider.getNewTransactionId())); diff --git a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/impl/GroupForwarder.java b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/impl/GroupForwarder.java index 72e35ce8db..1b2c532323 100644 --- a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/impl/GroupForwarder.java +++ b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/impl/GroupForwarder.java @@ -78,7 +78,7 @@ public class GroupForwarder extends AbstractListeningCommiter { final Group group = (removeDataObj); final RemoveGroupInputBuilder builder = new RemoveGroupInputBuilder(group); - builder.setNode(new NodeRef(nodeIdent)); + builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class))); builder.setGroupRef(new GroupRef(identifier)); builder.setTransactionUri(new Uri(provider.getNewTransactionId())); this.provider.getSalGroupService().removeGroup(builder.build()); @@ -93,7 +93,7 @@ public class GroupForwarder extends AbstractListeningCommiter { final Group updatedGroup = (update); final UpdateGroupInputBuilder builder = new UpdateGroupInputBuilder(); - builder.setNode(new NodeRef(nodeIdent)); + builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class))); builder.setGroupRef(new GroupRef(identifier)); builder.setTransactionUri(new Uri(provider.getNewTransactionId())); builder.setUpdatedGroup((new UpdatedGroupBuilder(updatedGroup)).build()); @@ -109,7 +109,7 @@ public class GroupForwarder extends AbstractListeningCommiter { final Group group = (addDataObj); final AddGroupInputBuilder builder = new AddGroupInputBuilder(group); - builder.setNode(new NodeRef(nodeIdent)); + builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class))); builder.setGroupRef(new GroupRef(identifier)); builder.setTransactionUri(new Uri(provider.getNewTransactionId())); this.provider.getSalGroupService().addGroup(builder.build()); diff --git a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/impl/MeterForwarder.java b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/impl/MeterForwarder.java index 8a805b0297..2f3de2a171 100644 --- a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/impl/MeterForwarder.java +++ b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/impl/MeterForwarder.java @@ -77,7 +77,7 @@ public class MeterForwarder extends AbstractListeningCommiter { final RemoveMeterInputBuilder builder = new RemoveMeterInputBuilder(removeDataObj); - builder.setNode(new NodeRef(nodeIdent)); + builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class))); builder.setMeterRef(new MeterRef(identifier)); builder.setTransactionUri(new Uri(provider.getNewTransactionId())); this.provider.getSalMeterService().removeMeter(builder.build()); @@ -90,7 +90,7 @@ public class MeterForwarder extends AbstractListeningCommiter { final UpdateMeterInputBuilder builder = new UpdateMeterInputBuilder(); - builder.setNode(new NodeRef(nodeIdent)); + builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class))); builder.setMeterRef(new MeterRef(identifier)); builder.setTransactionUri(new Uri(provider.getNewTransactionId())); builder.setUpdatedMeter((new UpdatedMeterBuilder(update)).build()); @@ -105,7 +105,7 @@ public class MeterForwarder extends AbstractListeningCommiter { final AddMeterInputBuilder builder = new AddMeterInputBuilder(addDataObj); - builder.setNode(new NodeRef(nodeIdent)); + builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class))); builder.setMeterRef(new MeterRef(identifier)); builder.setTransactionUri(new Uri(provider.getNewTransactionId())); this.provider.getSalMeterService().addMeter(builder.build()); diff --git a/opendaylight/md-sal/pom.xml b/opendaylight/md-sal/pom.xml index ce830eaa62..71a0de9939 100644 --- a/opendaylight/md-sal/pom.xml +++ b/opendaylight/md-sal/pom.xml @@ -216,5 +216,14 @@ sal-binding-dom-it + + benchmarks + + false + + + benchmark-data-store + + \ No newline at end of file diff --git a/opendaylight/md-sal/sal-akka-raft/pom.xml b/opendaylight/md-sal/sal-akka-raft/pom.xml index 98c81c267f..e68e781525 100644 --- a/opendaylight/md-sal/sal-akka-raft/pom.xml +++ b/opendaylight/md-sal/sal-akka-raft/pom.xml @@ -99,6 +99,7 @@ ${project.groupId}.${project.artifactId} org.opendaylight.cluster.raft * + *
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/DefaultConfigParamsImpl.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/DefaultConfigParamsImpl.java index 75c237f503..9d06f63604 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/DefaultConfigParamsImpl.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/DefaultConfigParamsImpl.java @@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit; */ public class DefaultConfigParamsImpl implements ConfigParams { - private static final int SNAPSHOT_BATCH_COUNT = 100000; + private static final int SNAPSHOT_BATCH_COUNT = 20000; /** * The maximum election time variance diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java index c8cbcca6e8..190f1bd409 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 @@ -123,7 +123,7 @@ public abstract class RaftActor extends UntypedPersistentActor { @Override public void onReceiveRecover(Object message) { if (message instanceof SnapshotOffer) { - LOG.debug("SnapshotOffer called.."); + LOG.info("SnapshotOffer called.."); SnapshotOffer offer = (SnapshotOffer) message; Snapshot snapshot = (Snapshot) offer.snapshot(); @@ -135,10 +135,11 @@ public abstract class RaftActor extends UntypedPersistentActor { context.setReplicatedLog(replicatedLog); context.setLastApplied(snapshot.getLastAppliedIndex()); - LOG.debug("Applied snapshot to replicatedLog. " + - "snapshotIndex={}, snapshotTerm={}, journal-size={}", + LOG.info("Applied snapshot to replicatedLog. " + + "snapshotIndex={}, snapshotTerm={}, journal-size={}", replicatedLog.snapshotIndex, replicatedLog.snapshotTerm, - replicatedLog.size()); + replicatedLog.size() + ); // Apply the snapshot to the actors state applySnapshot(ByteString.copyFrom(snapshot.getState())); @@ -236,17 +237,17 @@ public abstract class RaftActor extends UntypedPersistentActor { context.removePeer(rrp.getName()); } else if (message instanceof CaptureSnapshot) { - LOG.debug("CaptureSnapshot received by actor"); + LOG.info("CaptureSnapshot received by actor"); CaptureSnapshot cs = (CaptureSnapshot)message; captureSnapshot = cs; createSnapshot(); } else if (message instanceof CaptureSnapshotReply){ - LOG.debug("CaptureSnapshotReply received by actor"); + LOG.info("CaptureSnapshotReply received by actor"); CaptureSnapshotReply csr = (CaptureSnapshotReply) message; ByteString stateInBytes = csr.getSnapshot(); - LOG.debug("CaptureSnapshotReply stateInBytes size:{}", stateInBytes.size()); + LOG.info("CaptureSnapshotReply stateInBytes size:{}", stateInBytes.size()); handleCaptureSnapshotReply(stateInBytes); } else { @@ -262,6 +263,8 @@ public abstract class RaftActor extends UntypedPersistentActor { if(oldBehavior != currentBehavior){ onStateChanged(); } + + onLeaderChanged(oldBehavior.getLeaderId(), currentBehavior.getLeaderId()); } } @@ -426,6 +429,8 @@ public abstract class RaftActor extends UntypedPersistentActor { */ protected abstract void onStateChanged(); + protected void onLeaderChanged(String oldLeader, String newLeader){}; + private RaftActorBehavior switchBehavior(RaftState state) { if (currentBehavior != null) { if (currentBehavior.state() == state) { diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehavior.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehavior.java index 7e896fed29..86447509d7 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehavior.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehavior.java @@ -321,19 +321,19 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior { context.getReplicatedLog().get(i); if (replicatedLogEntry != null) { + // Send a local message to the local RaftActor (it's derived class to be + // specific to apply the log to it's index) actor().tell(new ApplyState(clientActor, identifier, replicatedLogEntry), actor()); newLastApplied = i; } else { //if one index is not present in the log, no point in looping // around as the rest wont be present either - context.getLogger().error( + context.getLogger().warning( "Missing index {} from log. Cannot apply state. Ignoring {} to {}", i, i, index ); break; } } - // Send a local message to the local RaftActor (it's derived class to be - // specific to apply the log to it's index) context.getLogger().debug("Setting last applied to {}", newLastApplied); context.setLastApplied(newLastApplied); } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntries.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntries.java index 6665d7549b..5149cf9f34 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntries.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntries.java @@ -132,7 +132,7 @@ public class AppendEntries extends AbstractRaftRPC { try { if(leProtoBuff.getData() != null && leProtoBuff.getData().getClientPayloadClassName() != null) { String clientPayloadClassName = leProtoBuff.getData().getClientPayloadClassName(); - payload = (Payload)Class.forName(clientPayloadClassName).newInstance(); + payload = (Payload) Class.forName(clientPayloadClassName).newInstance(); payload = payload.decode(leProtoBuff.getData()); payload.setClientPayloadClassName(clientPayloadClassName); } else { diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java index 9d40fa3d9e..c084cba822 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java @@ -9,7 +9,7 @@ package org.opendaylight.controller.cluster.raft.messages; import com.google.protobuf.ByteString; -import org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages; +import org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages; public class InstallSnapshot extends AbstractRaftRPC { diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java index 73c9f96b82..c4ef51d968 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java @@ -22,8 +22,8 @@ import org.opendaylight.controller.cluster.raft.base.messages.SendInstallSnapsho import org.opendaylight.controller.cluster.raft.messages.AppendEntries; import org.opendaylight.controller.cluster.raft.messages.InstallSnapshot; import org.opendaylight.controller.cluster.raft.messages.InstallSnapshotReply; -import org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages; import org.opendaylight.controller.cluster.raft.utils.DoNothingActor; +import org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages; import java.io.ByteArrayOutputStream; import java.io.IOException; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/CompositeModificationPayload.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/client/messages/CompositeModificationPayload.java similarity index 95% rename from opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/CompositeModificationPayload.java rename to opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/client/messages/CompositeModificationPayload.java index 2e822f1d43..8822ac83a6 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/CompositeModificationPayload.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/client/messages/CompositeModificationPayload.java @@ -6,13 +6,12 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.cluster.datastore; +package org.opendaylight.controller.cluster.raft.protobuff.client.messages; import com.google.common.base.Preconditions; import com.google.protobuf.GeneratedMessage; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.UnknownFieldSet; -import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; import org.opendaylight.controller.protobuff.messages.cluster.raft.AppendEntriesMessages; import org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages; diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/client/messages/Payload.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/client/messages/Payload.java similarity index 100% rename from opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/client/messages/Payload.java rename to opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/client/messages/Payload.java diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/messages/InstallSnapshotMessages.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/protobuff/messages/cluster/raft/InstallSnapshotMessages.java similarity index 87% rename from opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/messages/InstallSnapshotMessages.java rename to opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/protobuff/messages/cluster/raft/InstallSnapshotMessages.java index e801ae1c10..b93be3e009 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/messages/InstallSnapshotMessages.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/protobuff/messages/cluster/raft/InstallSnapshotMessages.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // source: InstallSnapshot.proto -package org.opendaylight.controller.cluster.raft.protobuff.messages; +package org.opendaylight.controller.protobuff.messages.cluster.raft; public final class InstallSnapshotMessages { private InstallSnapshotMessages() {} @@ -186,14 +186,14 @@ public final class InstallSnapshotMessages { } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_descriptor; + return org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_descriptor; } protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_fieldAccessorTable + return org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_fieldAccessorTable .ensureFieldAccessorsInitialized( - org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.class, org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.Builder.class); + org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot.class, org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot.Builder.class); } public static com.google.protobuf.Parser PARSER = @@ -245,7 +245,7 @@ public final class InstallSnapshotMessages { if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { - com.google.protobuf.ByteString bs = + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); if (bs.isValidUtf8()) { @@ -261,7 +261,7 @@ public final class InstallSnapshotMessages { getLeaderIdBytes() { java.lang.Object ref = leaderId_; if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); leaderId_ = b; @@ -442,53 +442,53 @@ public final class InstallSnapshotMessages { return super.writeReplace(); } - public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom( + public static org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom( + public static org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom(byte[] data) + public static org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom( + public static org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom(java.io.InputStream input) + public static org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot parseFrom(java.io.InputStream input) throws java.io.IOException { return PARSER.parseFrom(input); } - public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom( + public static org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return PARSER.parseFrom(input, extensionRegistry); } - public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseDelimitedFrom(java.io.InputStream input) + public static org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return PARSER.parseDelimitedFrom(input); } - public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseDelimitedFrom( + public static org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return PARSER.parseDelimitedFrom(input, extensionRegistry); } - public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom( + public static org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return PARSER.parseFrom(input); } - public static org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parseFrom( + public static org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -497,7 +497,7 @@ public final class InstallSnapshotMessages { public static Builder newBuilder() { return Builder.create(); } public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot prototype) { + public static Builder newBuilder(org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot prototype) { return newBuilder().mergeFrom(prototype); } public Builder toBuilder() { return newBuilder(this); } @@ -513,20 +513,20 @@ public final class InstallSnapshotMessages { */ public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder - implements org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshotOrBuilder { + implements org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshotOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_descriptor; + return org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_descriptor; } protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_fieldAccessorTable + return org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_fieldAccessorTable .ensureFieldAccessorsInitialized( - org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.class, org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.Builder.class); + org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot.class, org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot.Builder.class); } - // Construct using org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.newBuilder() + // Construct using org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot.newBuilder() private Builder() { maybeForceBuilderInitialization(); } @@ -569,23 +569,23 @@ public final class InstallSnapshotMessages { public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { - return org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_descriptor; + return org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.internal_static_org_opendaylight_controller_cluster_raft_InstallSnapshot_descriptor; } - public org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot getDefaultInstanceForType() { - return org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.getDefaultInstance(); + public org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot getDefaultInstanceForType() { + return org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot.getDefaultInstance(); } - public org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot build() { - org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot result = buildPartial(); + public org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot build() { + org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } - public org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot buildPartial() { - org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot result = new org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot(this); + public org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot buildPartial() { + org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot result = new org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot(this); int from_bitField0_ = bitField0_; int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000001) == 0x00000001)) { @@ -622,16 +622,16 @@ public final class InstallSnapshotMessages { } public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot) { - return mergeFrom((org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot)other); + if (other instanceof org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot) { + return mergeFrom((org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot)other); } else { super.mergeFrom(other); return this; } } - public Builder mergeFrom(org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot other) { - if (other == org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot.getDefaultInstance()) return this; + public Builder mergeFrom(org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot other) { + if (other == org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot.getDefaultInstance()) return this; if (other.hasTerm()) { setTerm(other.getTerm()); } @@ -667,11 +667,11 @@ public final class InstallSnapshotMessages { com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot parsedMessage = null; + org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot parsedMessage = null; try { parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (org.opendaylight.controller.cluster.raft.protobuff.messages.InstallSnapshotMessages.InstallSnapshot) e.getUnfinishedMessage(); + parsedMessage = (org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages.InstallSnapshot) e.getUnfinishedMessage(); throw e; } finally { if (parsedMessage != null) { @@ -744,7 +744,7 @@ public final class InstallSnapshotMessages { getLeaderIdBytes() { java.lang.Object ref = leaderId_; if (ref instanceof String) { - com.google.protobuf.ByteString b = + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); leaderId_ = b; @@ -988,8 +988,8 @@ public final class InstallSnapshotMessages { "\021lastIncludedIndex\030\003 \001(\003\022\030\n\020lastIncluded" + "Term\030\004 \001(\003\022\014\n\004data\030\005 \001(\014\022\022\n\nchunkIndex\030\006" + " \001(\005\022\023\n\013totalChunks\030\007 \001(\005BX\n;org.openday" + - "light.controller.cluster.raft.protobuff." + - "messagesB\027InstallSnapshotMessagesH\001" + "light.controller.protobuff.messages.clus" + + "ter.raftB\027InstallSnapshotMessagesH\001" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/resources/InstallSnapshot.proto b/opendaylight/md-sal/sal-clustering-commons/src/main/resources/InstallSnapshot.proto similarity index 82% rename from opendaylight/md-sal/sal-akka-raft/src/main/resources/InstallSnapshot.proto rename to opendaylight/md-sal/sal-clustering-commons/src/main/resources/InstallSnapshot.proto index 14f821b5e2..4198644b13 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/resources/InstallSnapshot.proto +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/resources/InstallSnapshot.proto @@ -1,6 +1,6 @@ package org.opendaylight.controller.cluster.raft; -option java_package = "org.opendaylight.controller.cluster.raft.protobuff.messages"; +option java_package = "org.opendaylight.controller.protobuff.messages.cluster.raft"; option java_outer_classname = "InstallSnapshotMessages"; option optimize_for = SPEED; diff --git a/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/akka.conf b/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/akka.conf index f632b9cc83..f196ad1644 100644 --- a/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/akka.conf +++ b/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/akka.conf @@ -9,7 +9,11 @@ odl-cluster-data { metric-capture-enabled = true akka { + loglevel = "INFO" + loggers = ["akka.event.slf4j.Slf4jLogger"] + actor { + provider = "akka.cluster.ClusterActorRefProvider" serializers { java = "akka.serialization.JavaSerializer" @@ -55,6 +59,9 @@ odl-cluster-rpc { metric-capture-enabled = true akka { + loglevel = "INFO" + loggers = ["akka.event.slf4j.Slf4jLogger"] + actor { provider = "akka.cluster.ClusterActorRefProvider" diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitDeadlockException.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitDeadlockException.java index 60313bf109..50952eaaf1 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitDeadlockException.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitDeadlockException.java @@ -8,11 +8,10 @@ package org.opendaylight.controller.md.sal.common.api.data; +import com.google.common.base.Supplier; import org.opendaylight.yangtools.yang.common.RpcError; -import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; - -import com.google.common.base.Function; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; /** * A type of TransactionCommitFailedException that indicates a situation that would result in a @@ -24,23 +23,21 @@ import com.google.common.base.Function; * @author Thomas Pantelis */ public class TransactionCommitDeadlockException extends TransactionCommitFailedException { - private static final long serialVersionUID = 1L; - private static final String DEADLOCK_MESSAGE = "An attempt to block on a ListenableFuture via a get method from a write " + "transaction submit was detected that would result in deadlock. The commit " + "result must be obtained asynchronously, e.g. via Futures#addCallback, to avoid deadlock."; + private static final RpcError DEADLOCK_RPCERROR = RpcResultBuilder.newError(ErrorType.APPLICATION, "lock-denied", DEADLOCK_MESSAGE); - public static Function DEADLOCK_EXECUTOR_FUNCTION = new Function() { + public static final Supplier DEADLOCK_EXCEPTION_SUPPLIER = new Supplier() { @Override - public Exception apply(Void notUsed) { - return new TransactionCommitDeadlockException( DEADLOCK_MESSAGE, - RpcResultBuilder.newError(ErrorType.APPLICATION, "lock-denied", DEADLOCK_MESSAGE)); + public Exception get() { + return new TransactionCommitDeadlockException(DEADLOCK_MESSAGE, DEADLOCK_RPCERROR); } }; - public TransactionCommitDeadlockException(String message, final RpcError... errors) { + public TransactionCommitDeadlockException(final String message, final RpcError... errors) { super(message, errors); } } diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/AbstractDataTransaction.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/AbstractDataTransaction.java index d544c4b371..b2a03c2987 100644 --- a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/AbstractDataTransaction.java +++ b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/AbstractDataTransaction.java @@ -7,9 +7,13 @@ */ package org.opendaylight.controller.md.sal.common.impl.service; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; - import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.common.impl.AbstractDataModification; @@ -19,15 +23,11 @@ import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.AsyncFunction; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; - public abstract class AbstractDataTransaction

, D extends Object> extends AbstractDataModification { - private final static Logger LOG = LoggerFactory.getLogger(AbstractDataTransaction.class); + private static final Logger LOG = LoggerFactory.getLogger(AbstractDataTransaction.class); + private static final ListenableFuture> SUCCESS_FUTURE = + Futures.immediateFuture(RpcResultBuilder.success(TransactionStatus.COMMITED).build()); private final Object identifier; private final long allocationTime; @@ -55,9 +55,10 @@ public abstract class AbstractDataTransaction

, D extends Objec @Override public Future> commit() { readyTime = System.nanoTime(); - LOG.debug("Transaction {} Ready after {}ms.", identifier, TimeUnit.NANOSECONDS.toMillis(readyTime - allocationTime)); + if (LOG.isDebugEnabled()) { + LOG.debug("Transaction {} Ready after {}ms.", identifier, TimeUnit.NANOSECONDS.toMillis(readyTime - allocationTime)); + } changeStatus(TransactionStatus.SUBMITED); - return this.broker.commit(this); } @@ -88,7 +89,7 @@ public abstract class AbstractDataTransaction

, D extends Objec } @Override - public boolean equals(Object obj) { + public boolean equals(final Object obj) { if (this == obj) { return true; } @@ -118,13 +119,18 @@ public abstract class AbstractDataTransaction

, D extends Objec public void succeeded() { this.completeTime = System.nanoTime(); - LOG.debug("Transaction {} Committed after {}ms.", identifier, TimeUnit.NANOSECONDS.toMillis(completeTime - readyTime)); + if (LOG.isDebugEnabled()) { + LOG.debug("Transaction {} Committed after {}ms.", identifier, TimeUnit.NANOSECONDS.toMillis(completeTime - readyTime)); + } changeStatus(TransactionStatus.COMMITED); } public void failed() { this.completeTime = System.nanoTime(); - LOG.debug("Transaction {} Failed after {}ms.", identifier, TimeUnit.NANOSECONDS.toMillis(completeTime - readyTime)); + + if (LOG.isDebugEnabled()) { + LOG.debug("Transaction {} Failed after {}ms.", identifier, TimeUnit.NANOSECONDS.toMillis(completeTime - readyTime)); + } changeStatus(TransactionStatus.FAILED); } @@ -134,14 +140,12 @@ public abstract class AbstractDataTransaction

, D extends Objec this.onStatusChange(status); } - public static ListenableFuture> convertToLegacyCommitFuture( - CheckedFuture from ) { + public static ListenableFuture> convertToLegacyCommitFuture(final CheckedFuture from) { return Futures.transform(from, new AsyncFunction>() { @Override - public ListenableFuture> apply(Void input) throws Exception { - return Futures.immediateFuture(RpcResultBuilder. - success(TransactionStatus.COMMITED).build()); + public ListenableFuture> apply(final Void input) { + return SUCCESS_FUTURE; } - } ); + }); } } diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBeanImpl.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBeanImpl.java index b67855d731..58677103c2 100644 --- a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBeanImpl.java +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/md/sal/common/util/jmx/ThreadExecutorStatsMXBeanImpl.java @@ -16,6 +16,8 @@ import java.util.concurrent.ThreadPoolExecutor; import javax.annotation.Nullable; import org.opendaylight.yangtools.util.concurrent.CountingRejectedExecutionHandler; import org.opendaylight.yangtools.util.concurrent.TrackingLinkedBlockingQueue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * MXBean implementation of the ThreadExecutorStatsMXBean interface that retrieves statistics @@ -25,7 +27,7 @@ import org.opendaylight.yangtools.util.concurrent.TrackingLinkedBlockingQueue; */ public class ThreadExecutorStatsMXBeanImpl extends AbstractMXBean implements ThreadExecutorStatsMXBean { - + private static final Logger LOG = LoggerFactory.getLogger(ThreadExecutorStatsMXBeanImpl.class); private final ThreadPoolExecutor executor; /** @@ -36,14 +38,31 @@ public class ThreadExecutorStatsMXBeanImpl extends AbstractMXBean * @param mBeanType Used as the type property in the bean's ObjectName. * @param mBeanCategory Used as the Category property in the bean's ObjectName. */ - public ThreadExecutorStatsMXBeanImpl(Executor executor, String mBeanName, - String mBeanType, @Nullable String mBeanCategory) { + public ThreadExecutorStatsMXBeanImpl(final ThreadPoolExecutor executor, final String mBeanName, + final String mBeanType, @Nullable final String mBeanCategory) { super(mBeanName, mBeanType, mBeanCategory); + this.executor = Preconditions.checkNotNull(executor); + } + + /** + * Create a new bean for the statistics, which is already registered. + * + * @param executor + * @param mBeanName + * @param mBeanType + * @param mBeanCategory + * @return + */ + public static ThreadExecutorStatsMXBeanImpl create(final Executor executor, final String mBeanName, + final String mBeanType, @Nullable final String mBeanCategory) { + if (executor instanceof ThreadPoolExecutor) { + final ThreadExecutorStatsMXBeanImpl ret = new ThreadExecutorStatsMXBeanImpl((ThreadPoolExecutor) executor, mBeanName, mBeanType, mBeanCategory); + ret.registerMBean(); + return ret; + } - Preconditions.checkArgument(executor instanceof ThreadPoolExecutor, - "The ExecutorService of type {} is not an instanceof ThreadPoolExecutor", - executor.getClass()); - this.executor = (ThreadPoolExecutor)executor; + LOG.info("Executor {} is not supported", executor); + return null; } @Override diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreProperties.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreProperties.java deleted file mode 100644 index e69de29bb2..0000000000 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 0737d2020b..bf1eb056b5 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 @@ -53,6 +53,7 @@ import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl; import org.opendaylight.controller.cluster.raft.RaftActor; import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply; +import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; @@ -192,6 +193,7 @@ public class Shard extends RaftActor { .tell(new CaptureSnapshotReply(ReadDataReply.getNormalizedNodeByteString(message)), self()); + createSnapshotTransaction = null; // Send a PoisonPill instead of sending close transaction because we do not really need // a response getSender().tell(PoisonPill.getInstance(), self()); @@ -467,7 +469,7 @@ public class Shard extends RaftActor { } } else { - LOG.error("Unknown state received {}", data); + LOG.error("Unknown state received {} Class loader = {} CompositeNodeMod.ClassLoader = {}", data, data.getClass().getClassLoader(), CompositeModificationPayload.class.getClassLoader()); } // Update stats @@ -502,6 +504,8 @@ public class Shard extends RaftActor { // Since this will be done only on Recovery or when this actor is a Follower // we can safely commit everything in here. We not need to worry about event notifications // as they would have already been disabled on the follower + + LOG.info("Applying snapshot"); try { DOMStoreWriteTransaction transaction = store.newWriteOnlyTransaction(); NormalizedNodeMessages.Node serializedNode = NormalizedNodeMessages.Node.parseFrom(snapshot); @@ -516,6 +520,8 @@ public class Shard extends RaftActor { syncCommitTransaction(transaction); } catch (InvalidProtocolBufferException | InterruptedException | ExecutionException e) { LOG.error(e, "An exception occurred when applying snapshot"); + } finally { + LOG.info("Done applying snapshot"); } } @@ -525,9 +531,6 @@ public class Shard extends RaftActor { .tell(new EnableNotification(isLeader()), getSelf()); } - if (getLeaderId() != null) { - shardMBean.setLeader(getLeaderId()); - } shardMBean.setRaftState(getRaftState().name()); shardMBean.setCurrentTerm(getCurrentTerm()); @@ -543,6 +546,14 @@ public class Shard extends RaftActor { } } + @Override protected void onLeaderChanged(String oldLeader, String newLeader) { + if((oldLeader == null && newLeader == null) || (newLeader != null && newLeader.equals(oldLeader)) ){ + return; + } + LOG.info("Current state = {}, Leader = {}", getRaftState().name(), newLeader); + shardMBean.setLeader(newLeader); + } + @Override public String persistenceId() { return this.name.toString(); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/AbstractBaseMBean.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/AbstractBaseMBean.java deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java index 0a1964b053..74a91d08cf 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java @@ -74,7 +74,7 @@ public class ShardStats extends AbstractMXBean implements ShardStatsMXBean { } public void setDataStoreExecutor(ExecutorService dsExecutor) { - this.dataStoreExecutorStatsBean = new ThreadExecutorStatsMXBeanImpl(dsExecutor, + this.dataStoreExecutorStatsBean = ThreadExecutorStatsMXBeanImpl.create(dsExecutor, "notification-executor", getMBeanType(), getMBeanCategory()); } @@ -82,7 +82,7 @@ public class ShardStats extends AbstractMXBean implements ShardStatsMXBean { this.notificationManagerStatsBean = new QueuedNotificationManagerMXBeanImpl(manager, "notification-manager", getMBeanType(), getMBeanCategory()); - this.notificationExecutorStatsBean = new ThreadExecutorStatsMXBeanImpl(manager.getExecutor(), + this.notificationExecutorStatsBean = ThreadExecutorStatsMXBeanImpl.create(manager.getExecutor(), "data-store-executor", getMBeanType(), getMBeanCategory()); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMBean.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMBean.java deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractActorTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractActorTest.java index 4c550a768c..022ef9bbaf 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractActorTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractActorTest.java @@ -25,12 +25,16 @@ public abstract class AbstractActorTest { System.setProperty("shard.persistent", "false"); system = ActorSystem.create("test"); + + deletePersistenceFiles(); } @AfterClass public static void tearDownClass() throws IOException { JavaTestKit.shutdownActorSystem(system); system = null; + + deletePersistenceFiles(); } protected static void deletePersistenceFiles() throws IOException { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/CompositeModificationPayloadTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/CompositeModificationPayloadTest.java index be43911fe1..04d889fbe0 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/CompositeModificationPayloadTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/CompositeModificationPayloadTest.java @@ -7,9 +7,10 @@ import org.opendaylight.controller.cluster.datastore.modification.MutableComposi import org.opendaylight.controller.cluster.datastore.modification.WriteModification; import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; import org.opendaylight.controller.cluster.raft.messages.AppendEntries; +import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; -import org.opendaylight.controller.protobuff.messages.cluster.raft.AppendEntriesMessages; import org.opendaylight.controller.md.cluster.datastore.model.TestModel; +import org.opendaylight.controller.protobuff.messages.cluster.raft.AppendEntriesMessages; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import java.io.File; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java index 06bcac8d78..deb71c2df4 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java @@ -343,11 +343,16 @@ public class ShardTest extends AbstractActorTest { subject.tell(new CaptureSnapshot(-1,-1,-1,-1), getRef()); - waitForLogMessage(Logging.Debug.class, subject, "CaptureSnapshotReply received by actor"); + waitForLogMessage(Logging.Info.class, subject, "CaptureSnapshotReply received by actor"); + + subject.tell(new CaptureSnapshot(-1,-1,-1,-1), + getRef()); + + waitForLogMessage(Logging.Info.class, subject, "CaptureSnapshotReply received by actor"); + } }; - Thread.sleep(2000); deletePersistenceFiles(); }}; } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/InMemorySnapshotStore.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/InMemorySnapshotStore.java new file mode 100644 index 0000000000..0e492f0fbb --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/InMemorySnapshotStore.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.cluster.datastore.utils; + +import akka.dispatch.Futures; +import akka.japi.Option; +import akka.persistence.SelectedSnapshot; +import akka.persistence.SnapshotMetadata; +import akka.persistence.SnapshotSelectionCriteria; +import akka.persistence.snapshot.japi.SnapshotStore; +import com.google.common.collect.Iterables; +import scala.concurrent.Future; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class InMemorySnapshotStore extends SnapshotStore { + + Map> snapshots = new HashMap<>(); + + @Override public Future> doLoadAsync(String s, + SnapshotSelectionCriteria snapshotSelectionCriteria) { + List snapshotList = snapshots.get(s); + if(snapshotList == null){ + return Futures.successful(Option.none()); + } + + Snapshot snapshot = Iterables.getLast(snapshotList); + SelectedSnapshot selectedSnapshot = + new SelectedSnapshot(snapshot.getMetadata(), snapshot.getData()); + return Futures.successful(Option.some(selectedSnapshot)); + } + + @Override public Future doSaveAsync(SnapshotMetadata snapshotMetadata, Object o) { + List snapshotList = snapshots.get(snapshotMetadata.persistenceId()); + + if(snapshotList == null){ + snapshotList = new ArrayList<>(); + snapshots.put(snapshotMetadata.persistenceId(), snapshotList); + } + snapshotList.add(new Snapshot(snapshotMetadata, o)); + + return Futures.successful(null); + } + + @Override public void onSaved(SnapshotMetadata snapshotMetadata) throws Exception { + } + + @Override public void doDelete(SnapshotMetadata snapshotMetadata) throws Exception { + List snapshotList = snapshots.get(snapshotMetadata.persistenceId()); + + if(snapshotList == null){ + return; + } + + int deleteIndex = -1; + + for(int i=0;i snapshotList = snapshots.get(s); + + if(snapshotList == null){ + return; + } + + // TODO : This is a quick and dirty implementation. Do actual match later. + snapshotList.clear(); + snapshots.remove(s); + } + + private static class Snapshot { + private final SnapshotMetadata metadata; + private final Object data; + + private Snapshot(SnapshotMetadata metadata, Object data) { + this.metadata = metadata; + this.data = data; + } + + public SnapshotMetadata getMetadata() { + return metadata; + } + + public Object getData() { + return data; + } + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Client.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Client.java index 2671be80bb..a2b78c6c15 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Client.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Client.java @@ -13,7 +13,7 @@ import akka.actor.ActorSystem; import akka.actor.Props; import akka.actor.UntypedActor; import com.typesafe.config.ConfigFactory; -import org.opendaylight.controller.cluster.datastore.CompositeModificationPayload; +import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload; import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification; import org.opendaylight.controller.cluster.datastore.modification.WriteModification; import org.opendaylight.controller.cluster.example.messages.KeyValue; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Server.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Server.java index 0e6d535301..e6bdf5aac3 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Server.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Server.java @@ -12,7 +12,7 @@ import akka.actor.ActorSystem; import akka.actor.Props; import akka.actor.UntypedActor; import com.typesafe.config.ConfigFactory; -import org.opendaylight.controller.cluster.datastore.CompositeModificationPayload; +import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload; import org.opendaylight.controller.cluster.example.messages.KeyValue; import org.opendaylight.controller.cluster.raft.messages.AppendEntries; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application.conf b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application.conf index 794b376af8..f0dadc618b 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application.conf +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application.conf @@ -1,4 +1,6 @@ akka { + persistence.snapshot-store.plugin = "in-memory-snapshot-store" + loggers = ["akka.testkit.TestEventListener", "akka.event.slf4j.Slf4jLogger"] actor { @@ -14,6 +16,14 @@ akka { } } } + +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 diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java index b423bbd0e5..ac62974d29 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java @@ -7,10 +7,12 @@ */ package org.opendaylight.controller.config.yang.md.sal.dom.impl; +import java.util.EnumMap; +import java.util.Map; import java.util.concurrent.ExecutorService; - import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitDeadlockException; +import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean; import org.opendaylight.controller.md.sal.common.util.jmx.ThreadExecutorStatsMXBeanImpl; import org.opendaylight.controller.md.sal.dom.broker.impl.DOMDataBrokerImpl; import org.opendaylight.controller.md.sal.dom.broker.impl.jmx.CommitStatsMXBeanImpl; @@ -18,7 +20,6 @@ import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFac import org.opendaylight.controller.sal.core.spi.data.DOMStore; import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService; import org.opendaylight.yangtools.util.concurrent.SpecialExecutors; -import com.google.common.collect.ImmutableMap; /** * @@ -59,9 +60,10 @@ public final class DomInmemoryDataBrokerModule extends //we will default to InMemoryDOMDataStore creation configStore = InMemoryDOMDataStoreFactory.create("DOM-CFG", getSchemaServiceDependency()); } - ImmutableMap datastores = ImmutableMap - . builder().put(LogicalDatastoreType.OPERATIONAL, operStore) - .put(LogicalDatastoreType.CONFIGURATION, configStore).build(); + + final Map datastores = new EnumMap<>(LogicalDatastoreType.class); + datastores.put(LogicalDatastoreType.OPERATIONAL, operStore); + datastores.put(LogicalDatastoreType.CONFIGURATION, configStore); /* * We use a single-threaded executor for commits with a bounded queue capacity. If the @@ -88,29 +90,30 @@ public final class DomInmemoryDataBrokerModule extends DOMDataBrokerImpl newDataBroker = new DOMDataBrokerImpl(datastores, new DeadlockDetectingListeningExecutorService(commitExecutor, - TransactionCommitDeadlockException.DEADLOCK_EXECUTOR_FUNCTION, + TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, listenableFutureExecutor)); final CommitStatsMXBeanImpl commitStatsMXBean = new CommitStatsMXBeanImpl( newDataBroker.getCommitStatsTracker(), JMX_BEAN_TYPE); commitStatsMXBean.registerMBean(); - final ThreadExecutorStatsMXBeanImpl commitExecutorStatsMXBean = - new ThreadExecutorStatsMXBeanImpl(commitExecutor, "CommitExecutorStats", + final AbstractMXBean commitExecutorStatsMXBean = + ThreadExecutorStatsMXBeanImpl.create(commitExecutor, "CommitExecutorStats", JMX_BEAN_TYPE, null); - commitExecutorStatsMXBean.registerMBean(); - - final ThreadExecutorStatsMXBeanImpl commitFutureStatsMXBean = - new ThreadExecutorStatsMXBeanImpl(listenableFutureExecutor, + final AbstractMXBean commitFutureStatsMXBean = + ThreadExecutorStatsMXBeanImpl.create(listenableFutureExecutor, "CommitFutureExecutorStats", JMX_BEAN_TYPE, null); - commitFutureStatsMXBean.registerMBean(); newDataBroker.setCloseable(new AutoCloseable() { @Override public void close() { commitStatsMXBean.unregisterMBean(); - commitExecutorStatsMXBean.unregisterMBean(); - commitFutureStatsMXBean.unregisterMBean(); + if (commitExecutorStatsMXBean != null) { + commitExecutorStatsMXBean.unregisterMBean(); + } + if (commitFutureStatsMXBean != null) { + commitFutureStatsMXBean.unregisterMBean(); + } } }); diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedCompositeTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedCompositeTransaction.java index d3791a0878..15d53f5310 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedCompositeTransaction.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedCompositeTransaction.java @@ -6,14 +6,14 @@ */ package org.opendaylight.controller.md.sal.dom.broker.impl; +import com.google.common.base.Preconditions; +import java.util.Collection; +import java.util.Map; import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableMap; - /** * Composite DOM Transaction backed by {@link DOMStoreTransaction}. * @@ -29,7 +29,7 @@ import com.google.common.collect.ImmutableMap; abstract class AbstractDOMForwardedCompositeTransaction implements AsyncTransaction> { - private final ImmutableMap backingTxs; + private final Map backingTxs; private final Object identifier; /** @@ -41,7 +41,7 @@ abstract class AbstractDOMForwardedCompositeTransaction backingTxs) { + protected AbstractDOMForwardedCompositeTransaction(final Object identifier, final Map backingTxs) { this.identifier = Preconditions.checkNotNull(identifier, "Identifier should not be null"); this.backingTxs = Preconditions.checkNotNull(backingTxs, "Backing transactions should not be null"); } @@ -58,15 +58,17 @@ abstract class AbstractDOMForwardedCompositeTransaction getSubtransactions() { + protected Collection getSubtransactions() { return backingTxs.values(); } @@ -77,9 +79,8 @@ abstract class AbstractDOMForwardedCompositeTransaction * Type of {@link DOMStoreTransactionFactory} factory. */ -public abstract class AbstractDOMForwardedTransactionFactory implements DOMDataCommitImplementation, AutoCloseable { - - private final ImmutableMap storeTxFactories; - - private boolean closed; +abstract class AbstractDOMForwardedTransactionFactory implements DOMDataCommitImplementation, AutoCloseable { + @SuppressWarnings("rawtypes") + private static final AtomicIntegerFieldUpdater UPDATER = + AtomicIntegerFieldUpdater.newUpdater(AbstractDOMForwardedTransactionFactory.class, "closed"); + private final Map storeTxFactories; + private volatile int closed = 0; protected AbstractDOMForwardedTransactionFactory(final Map txFactories) { - this.storeTxFactories = ImmutableMap.copyOf(txFactories); + this.storeTxFactories = new EnumMap<>(txFactories); } /** @@ -74,17 +72,16 @@ public abstract class AbstractDOMForwardedTransactionFactory builder = ImmutableMap.builder(); + + final Map txns = new EnumMap<>(LogicalDatastoreType.class); for (Entry store : storeTxFactories.entrySet()) { - builder.put(store.getKey(), store.getValue().newReadOnlyTransaction()); + txns.put(store.getKey(), store.getValue().newReadOnlyTransaction()); } - return new DOMForwardedReadOnlyTransaction(newTransactionIdentifier(), builder.build()); + return new DOMForwardedReadOnlyTransaction(newTransactionIdentifier(), txns); } - - /** * Creates a new composite write-only transaction * @@ -124,14 +121,14 @@ public abstract class AbstractDOMForwardedTransactionFactory builder = ImmutableMap.builder(); + + final Map txns = new EnumMap<>(LogicalDatastoreType.class); for (Entry store : storeTxFactories.entrySet()) { - builder.put(store.getKey(), store.getValue().newWriteOnlyTransaction()); + txns.put(store.getKey(), store.getValue().newWriteOnlyTransaction()); } - return new DOMForwardedWriteTransaction(newTransactionIdentifier(), builder.build(), - this); + return new DOMForwardedWriteTransaction(newTransactionIdentifier(), txns, this); } /** @@ -177,15 +174,15 @@ public abstract class AbstractDOMForwardedTransactionFactory builder = ImmutableMap.builder(); + + final Map txns = new EnumMap<>(LogicalDatastoreType.class); for (Entry store : storeTxFactories.entrySet()) { - builder.put(store.getKey(), store.getValue().newReadWriteTransaction()); + txns.put(store.getKey(), store.getValue().newReadWriteTransaction()); } - return new DOMForwardedReadWriteTransaction(newTransactionIdentifier(), builder.build(), this); + return new DOMForwardedReadWriteTransaction(newTransactionIdentifier(), txns, this); } /** @@ -203,21 +200,19 @@ public abstract class AbstractDOMForwardedTransactionFactory implements DOMDataBroker, AutoCloseable { @@ -43,13 +41,13 @@ public class DOMDataBrokerImpl extends AbstractDOMForwardedTransactionFactory datastores, + public DOMDataBrokerImpl(final Map datastores, final ListeningExecutorService executor) { super(datastores); this.coordinator = new DOMDataCommitCoordinatorImpl(executor); } - public void setCloseable(AutoCloseable closeable) { + public void setCloseable(final AutoCloseable closeable) { this.closeable = closeable; } @@ -86,13 +84,14 @@ public class DOMDataBrokerImpl extends AbstractDOMForwardedTransactionFactory backingChainsBuilder = ImmutableMap - .builder(); + checkNotClosed(); + + final Map backingChains = new EnumMap<>(LogicalDatastoreType.class); for (Entry entry : getTxFactories().entrySet()) { - backingChainsBuilder.put(entry.getKey(), entry.getValue().createTransactionChain()); + backingChains.put(entry.getKey(), entry.getValue().createTransactionChain()); } - long chainId = chainNum.getAndIncrement(); - ImmutableMap backingChains = backingChainsBuilder.build(); + + final long chainId = chainNum.getAndIncrement(); LOG.debug("Transactoin chain {} created with listener {}, backing store chains {}", chainId, listener, backingChains); return new DOMDataBrokerTransactionChainImpl(chainId, backingChains, coordinator, listener); 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 227693ca4d..7cd6afa466 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 @@ -6,10 +6,11 @@ */ package org.opendaylight.controller.md.sal.dom.broker.impl; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.CheckedFuture; +import java.util.Map; import java.util.concurrent.atomic.AtomicLong; - -import javax.annotation.concurrent.GuardedBy; - import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; @@ -20,11 +21,6 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.CheckedFuture; - /** * NormalizedNode implementation of {@link org.opendaylight.controller.md.sal.common.api.data.TransactionChain} which is backed * by several {@link DOMStoreTransactionChain} differentiated by provided @@ -35,12 +31,12 @@ public class DOMDataBrokerTransactionChainImpl extends AbstractDOMForwardedTrans implements DOMTransactionChain, DOMDataCommitErrorListener { private static final Logger LOG = LoggerFactory.getLogger(DOMDataBrokerTransactionChainImpl.class); + private final AtomicLong txNum = new AtomicLong(); private final DOMDataCommitExecutor coordinator; private final TransactionChainListener listener; private final long chainId; - private final AtomicLong txNum = new AtomicLong(); - @GuardedBy("this") - private boolean failed = false; + + private volatile boolean failed = false; /** * @@ -58,7 +54,7 @@ public class DOMDataBrokerTransactionChainImpl extends AbstractDOMForwardedTrans * If any of arguments is null. */ public DOMDataBrokerTransactionChainImpl(final long chainId, - final ImmutableMap chains, + final Map chains, final DOMDataCommitExecutor coordinator, final TransactionChainListener listener) { super(chains); this.chainId = chainId; @@ -72,26 +68,30 @@ public class DOMDataBrokerTransactionChainImpl extends AbstractDOMForwardedTrans } @Override - public synchronized CheckedFuture submit( + public CheckedFuture submit( final DOMDataWriteTransaction transaction, final Iterable cohorts) { + checkNotClosed(); + return coordinator.submit(transaction, cohorts, Optional. of(this)); } @Override - public synchronized void close() { + public void close() { super.close(); + for (DOMStoreTransactionChain subChain : getTxFactories().values()) { subChain.close(); } if (!failed) { LOG.debug("Transaction chain {} successfully finished.", this); + // FIXME: this event should be emitted once all operations complete listener.onTransactionChainSuccessful(this); } } @Override - public synchronized void onCommitFailed(final DOMDataWriteTransaction tx, final Throwable cause) { + public void onCommitFailed(final DOMDataWriteTransaction tx, final Throwable cause) { failed = true; LOG.debug("Transaction chain {} failed.", this, cause); listener.onTransactionChainFailed(this, tx, cause); diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java index 3fde8d360f..77cf105ed6 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java @@ -6,13 +6,18 @@ */ package org.opendaylight.controller.md.sal.dom.broker.impl; -import java.util.List; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; +import com.google.common.collect.Iterables; +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.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.RejectedExecutionException; - -import javax.annotation.concurrent.GuardedBy; - +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; @@ -21,17 +26,6 @@ import org.opendaylight.yangtools.util.concurrent.MappingCheckedFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.base.Throwables; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList.Builder; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; - /** * * Implementation of blocking three phase commit coordinator, which which @@ -49,28 +43,8 @@ import com.google.common.util.concurrent.ListeningExecutorService; public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { private static final Logger LOG = LoggerFactory.getLogger(DOMDataCommitCoordinatorImpl.class); - - /** - * Runs AND binary operation between all booleans in supplied iteration of booleans. - * - * This method will stop evaluating iterables if first found is false. - */ - private static final Function, Boolean> AND_FUNCTION = new Function, Boolean>() { - - @Override - public Boolean apply(final Iterable input) { - for(boolean value : input) { - if(!value) { - return Boolean.FALSE; - } - } - return Boolean.TRUE; - } - }; - - private final ListeningExecutorService executor; - private final DurationStatsTracker commitStatsTracker = new DurationStatsTracker(); + private final ListeningExecutorService executor; /** * @@ -153,19 +127,17 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { } /** - * * Implementation of blocking three-phase commit-coordination tasks without - * support of cancelation. - * + * support of cancellation. */ - private static class CommitCoordinationTask implements Callable { - + private static final class CommitCoordinationTask implements Callable { + private static final AtomicReferenceFieldUpdater PHASE_UPDATER = + AtomicReferenceFieldUpdater.newUpdater(CommitCoordinationTask.class, CommitPhase.class, "currentPhase"); private final DOMDataWriteTransaction tx; private final Iterable cohorts; private final DurationStatsTracker commitStatTracker; - - @GuardedBy("this") - private CommitPhase currentPhase; + private final int cohortSize; + private volatile CommitPhase currentPhase = CommitPhase.SUBMITTED; public CommitCoordinationTask(final DOMDataWriteTransaction transaction, final Iterable cohorts, @@ -173,25 +145,26 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { final DurationStatsTracker commitStatTracker) { this.tx = Preconditions.checkNotNull(transaction, "transaction must not be null"); this.cohorts = Preconditions.checkNotNull(cohorts, "cohorts must not be null"); - this.currentPhase = CommitPhase.SUBMITTED; this.commitStatTracker = commitStatTracker; + this.cohortSize = Iterables.size(cohorts); } @Override public Void call() throws TransactionCommitFailedException { + final long startTime = commitStatTracker != null ? System.nanoTime() : 0; - long startTime = System.nanoTime(); try { canCommitBlocking(); preCommitBlocking(); commitBlocking(); return null; } catch (TransactionCommitFailedException e) { - LOG.warn("Tx: {} Error during phase {}, starting Abort", tx.getIdentifier(), currentPhase, e); - abortBlocking(e); + final CommitPhase phase = currentPhase; + LOG.warn("Tx: {} Error during phase {}, starting Abort", tx.getIdentifier(), phase, e); + abortBlocking(e, phase); throw e; } finally { - if(commitStatTracker != null) { + if (commitStatTracker != null) { commitStatTracker.addDuration(System.nanoTime() - startTime); } } @@ -210,78 +183,63 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { * */ private void canCommitBlocking() throws TransactionCommitFailedException { - final Boolean canCommitResult = canCommitAll().checkedGet(); - if (!canCommitResult) { - throw new TransactionCommitFailedException("Can Commit failed, no detailed cause available."); + for (ListenableFuture canCommit : canCommitAll()) { + try { + final Boolean result = (Boolean)canCommit.get(); + if (result == null || !result) { + throw new TransactionCommitFailedException("Can Commit failed, no detailed cause available."); + } + } catch (InterruptedException | ExecutionException e) { + throw TransactionCommitFailedExceptionMapper.CAN_COMMIT_ERROR_MAPPER.apply(e); + } } } /** * - * Invokes preCommit on underlying cohorts and blocks till - * all results are returned. + * Invokes canCommit on underlying cohorts and returns composite future + * which will contains {@link Boolean#TRUE} only and only if + * all cohorts returned true. * - * Valid state transition is from CAN_COMMIT to PRE_COMMIT, if current - * state is not CAN_COMMIT - * throws IllegalStateException. + * Valid state transition is from SUBMITTED to CAN_COMMIT, + * if currentPhase is not SUBMITTED throws IllegalStateException. * - * @throws TransactionCommitFailedException - * If one of cohorts failed preCommit + * @return List of all cohorts futures from can commit phase. * */ - private void preCommitBlocking() throws TransactionCommitFailedException { - preCommitAll().checkedGet(); + private ListenableFuture[] canCommitAll() { + changeStateFrom(CommitPhase.SUBMITTED, CommitPhase.CAN_COMMIT); + + final ListenableFuture[] ops = new ListenableFuture[cohortSize]; + int i = 0; + for (DOMStoreThreePhaseCommitCohort cohort : cohorts) { + ops[i++] = cohort.canCommit(); + } + return ops; } /** * - * Invokes commit on underlying cohorts and blocks till + * Invokes preCommit on underlying cohorts and blocks till * all results are returned. * - * Valid state transition is from PRE_COMMIT to COMMIT, if not throws - * IllegalStateException. + * Valid state transition is from CAN_COMMIT to PRE_COMMIT, if current + * state is not CAN_COMMIT + * throws IllegalStateException. * * @throws TransactionCommitFailedException * If one of cohorts failed preCommit * */ - private void commitBlocking() throws TransactionCommitFailedException { - commitAll().checkedGet(); - } - - /** - * Aborts transaction. - * - * Invokes {@link DOMStoreThreePhaseCommitCohort#abort()} on all - * cohorts, blocks - * for all results. If any of the abort failed throws - * IllegalStateException, - * which will contains originalCause as suppressed Exception. - * - * If aborts we're successful throws supplied exception - * - * @param originalCause - * Exception which should be used to fail transaction for - * consumers of transaction - * future and listeners of transaction failure. - * @throws TransactionCommitFailedException - * on invocation of this method. - * originalCa - * @throws IllegalStateException - * if abort failed. - */ - private void abortBlocking(final TransactionCommitFailedException originalCause) - throws TransactionCommitFailedException { - LOG.warn("Tx: {} Error during phase {}, starting Abort", tx.getIdentifier(), currentPhase, originalCause); - Exception cause = originalCause; + private void preCommitBlocking() throws TransactionCommitFailedException { + final ListenableFuture[] preCommitFutures = preCommitAll(); try { - abortAsyncAll().get(); + for(ListenableFuture future : preCommitFutures) { + future.get(); + } } catch (InterruptedException | ExecutionException e) { - LOG.error("Tx: {} Error during Abort.", tx.getIdentifier(), e); - cause = new IllegalStateException("Abort failed.", e); - cause.addSuppressed(e); + throw TransactionCommitFailedExceptionMapper.PRE_COMMIT_MAPPER.apply(e); } - Throwables.propagateIfPossible(cause, TransactionCommitFailedException.class); } /** @@ -295,27 +253,41 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { * state is not CAN_COMMIT * throws IllegalStateException. * - * @return Future which will complete once all cohorts completed - * preCommit. - * Future throws TransactionCommitFailedException - * If any of cohorts failed preCommit + * @return List of all cohorts futures from can commit phase. * */ - private CheckedFuture preCommitAll() { + private ListenableFuture[] preCommitAll() { changeStateFrom(CommitPhase.CAN_COMMIT, CommitPhase.PRE_COMMIT); - Builder> ops = ImmutableList.builder(); + + final ListenableFuture[] ops = new ListenableFuture[cohortSize]; + int i = 0; for (DOMStoreThreePhaseCommitCohort cohort : cohorts) { - ops.add(cohort.preCommit()); + ops[i++] = cohort.preCommit(); + } + return ops; + } + + /** + * + * Invokes commit on underlying cohorts and blocks till + * all results are returned. + * + * Valid state transition is from PRE_COMMIT to COMMIT, if not throws + * IllegalStateException. + * + * @throws TransactionCommitFailedException + * If one of cohorts failed preCommit + * + */ + private void commitBlocking() throws TransactionCommitFailedException { + final ListenableFuture[] commitFutures = commitAll(); + try { + for(ListenableFuture future : commitFutures) { + future.get(); + } + } catch (InterruptedException | ExecutionException e) { + throw TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER.apply(e); } - /* - * We are returing all futures as list, not only succeeded ones in - * order to fail composite future if any of them failed. - * See Futures.allAsList for this description. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - ListenableFuture compositeResult = (ListenableFuture) Futures.allAsList(ops.build()); - return MappingCheckedFuture.create(compositeResult, - TransactionCommitFailedExceptionMapper.PRE_COMMIT_MAPPER); } /** @@ -327,80 +299,80 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { * Valid state transition is from PRE_COMMIT to COMMIT, if not throws * IllegalStateException * - * @return Future which will complete once all cohorts completed - * commit. - * Future throws TransactionCommitFailedException - * If any of cohorts failed preCommit + * @return List of all cohorts futures from can commit phase. * */ - private CheckedFuture commitAll() { + private ListenableFuture[] commitAll() { changeStateFrom(CommitPhase.PRE_COMMIT, CommitPhase.COMMIT); - Builder> ops = ImmutableList.builder(); + + final ListenableFuture[] ops = new ListenableFuture[cohortSize]; + int i = 0; for (DOMStoreThreePhaseCommitCohort cohort : cohorts) { - ops.add(cohort.commit()); + ops[i++] = cohort.commit(); } - /* - * We are returing all futures as list, not only succeeded ones in - * order to fail composite future if any of them failed. - * See Futures.allAsList for this description. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - ListenableFuture compositeResult = (ListenableFuture) Futures.allAsList(ops.build()); - return MappingCheckedFuture.create(compositeResult, - TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER); + return ops; } /** + * Aborts transaction. * - * Invokes canCommit on underlying cohorts and returns composite future - * which will contains {@link Boolean#TRUE} only and only if - * all cohorts returned true. - * - * Valid state transition is from SUBMITTED to CAN_COMMIT, - * if currentPhase is not SUBMITTED throws IllegalStateException. + * Invokes {@link DOMStoreThreePhaseCommitCohort#abort()} on all + * cohorts, blocks + * for all results. If any of the abort failed throws + * IllegalStateException, + * which will contains originalCause as suppressed Exception. * - * @return Future which will complete once all cohorts completed - * preCommit. - * Future throws TransactionCommitFailedException - * If any of cohorts failed preCommit + * If aborts we're successful throws supplied exception * + * @param originalCause + * Exception which should be used to fail transaction for + * consumers of transaction + * future and listeners of transaction failure. + * @param phase phase in which the problem ensued + * @throws TransactionCommitFailedException + * on invocation of this method. + * originalCa + * @throws IllegalStateException + * if abort failed. */ - private CheckedFuture canCommitAll() { - changeStateFrom(CommitPhase.SUBMITTED, CommitPhase.CAN_COMMIT); - Builder> canCommitOperations = ImmutableList.builder(); - for (DOMStoreThreePhaseCommitCohort cohort : cohorts) { - canCommitOperations.add(cohort.canCommit()); + private void abortBlocking(final TransactionCommitFailedException originalCause, final CommitPhase phase) + throws TransactionCommitFailedException { + LOG.warn("Tx: {} Error during phase {}, starting Abort", tx.getIdentifier(), phase, originalCause); + Exception cause = originalCause; + try { + abortAsyncAll(phase).get(); + } catch (InterruptedException | ExecutionException e) { + LOG.error("Tx: {} Error during Abort.", tx.getIdentifier(), e); + cause = new IllegalStateException("Abort failed.", e); + cause.addSuppressed(e); } - ListenableFuture> allCanCommits = Futures.allAsList(canCommitOperations.build()); - ListenableFuture allSuccessFuture = Futures.transform(allCanCommits, AND_FUNCTION); - return MappingCheckedFuture.create(allSuccessFuture, - TransactionCommitFailedExceptionMapper.CAN_COMMIT_ERROR_MAPPER); - + Throwables.propagateIfPossible(cause, TransactionCommitFailedException.class); } /** - * * Invokes abort on underlying cohorts and returns future which - * completes - * once all abort on cohorts are completed. + * completes once all abort on cohorts are completed. * + * @param phase phase in which the problem ensued * @return Future which will complete once all cohorts completed * abort. - * */ - private ListenableFuture abortAsyncAll() { - changeStateFrom(currentPhase, CommitPhase.ABORT); - Builder> ops = ImmutableList.builder(); + private ListenableFuture abortAsyncAll(final CommitPhase phase) { + changeStateFrom(phase, CommitPhase.ABORT); + + final ListenableFuture[] ops = new ListenableFuture[cohortSize]; + int i = 0; for (DOMStoreThreePhaseCommitCohort cohort : cohorts) { - ops.add(cohort.abort()); + ops[i++] = cohort.abort(); } + /* - * We are returing all futures as list, not only succeeded ones in + * We are returning all futures as list, not only succeeded ones in * order to fail composite future if any of them failed. * See Futures.allAsList for this description. */ @SuppressWarnings({ "unchecked", "rawtypes" }) - ListenableFuture compositeResult = (ListenableFuture) Futures.allAsList(ops.build()); + ListenableFuture compositeResult = (ListenableFuture) Futures.allAsList(ops); return compositeResult; } @@ -423,14 +395,13 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { * @throws IllegalStateException * If currentState of task does not match expected state */ - private synchronized void changeStateFrom(final CommitPhase currentExpected, final CommitPhase newState) { - Preconditions.checkState(currentPhase.equals(currentExpected), - "Invalid state transition: Tx: %s current state: %s new state: %s", tx.getIdentifier(), - currentPhase, newState); - LOG.debug("Transaction {}: Phase {} Started ", tx.getIdentifier(), newState); - currentPhase = newState; - }; + private void changeStateFrom(final CommitPhase currentExpected, final CommitPhase newState) { + final boolean success = PHASE_UPDATER.compareAndSet(this, currentExpected, newState); + Preconditions.checkState(success, "Invalid state transition: Tx: %s expected: %s current: %s target: %s", + tx.getIdentifier(), currentExpected, currentPhase, newState); + LOG.debug("Transaction {}: Phase {} Started", tx.getIdentifier(), newState); + }; } } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadOnlyTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadOnlyTransaction.java index 5e2a417d28..124bf9f0be 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadOnlyTransaction.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadOnlyTransaction.java @@ -7,6 +7,9 @@ */ package org.opendaylight.controller.md.sal.dom.broker.impl; +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import java.util.Map; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; @@ -14,12 +17,7 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import com.google.common.base.Optional; -import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.CheckedFuture; - /** - * * Read Only Transaction, which is composed of several * {@link DOMStoreReadTransaction} transactions. Subtransaction is selected by * {@link LogicalDatastoreType} type parameter in @@ -30,7 +28,7 @@ class DOMForwardedReadOnlyTransaction extends DOMDataReadOnlyTransaction { protected DOMForwardedReadOnlyTransaction(final Object identifier, - final ImmutableMap backingTxs) { + final Map backingTxs) { super(identifier, backingTxs); } @@ -40,9 +38,10 @@ class DOMForwardedReadOnlyTransaction extends return getSubtransaction(store).read(path); } - @Override public CheckedFuture exists( - LogicalDatastoreType store, - YangInstanceIdentifier path) { + @Override + public CheckedFuture exists( + final LogicalDatastoreType store, + final YangInstanceIdentifier path) { return getSubtransaction(store).exists(path); } @@ -50,5 +49,4 @@ class DOMForwardedReadOnlyTransaction extends public void close() { closeSubtransactions(); } - } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java index 67351ec945..662d48afdb 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java @@ -6,6 +6,9 @@ * 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.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import java.util.Map; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; @@ -13,10 +16,6 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransactio import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import com.google.common.base.Optional; -import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.CheckedFuture; - /** * * Read-Write Transaction, which is composed of several @@ -35,12 +34,9 @@ import com.google.common.util.concurrent.CheckedFuture; * transactions. * */ - -class DOMForwardedReadWriteTransaction extends DOMForwardedWriteTransaction implements - DOMDataReadWriteTransaction { - +final class DOMForwardedReadWriteTransaction extends DOMForwardedWriteTransaction implements DOMDataReadWriteTransaction { protected DOMForwardedReadWriteTransaction(final Object identifier, - final ImmutableMap backingTxs, + final Map backingTxs, final DOMDataCommitImplementation commitImpl) { super(identifier, backingTxs, commitImpl); } @@ -51,9 +47,10 @@ class DOMForwardedReadWriteTransaction extends DOMForwardedWriteTransaction exists( - LogicalDatastoreType store, - YangInstanceIdentifier path) { + @Override + public CheckedFuture exists( + final LogicalDatastoreType store, + final YangInstanceIdentifier path) { return getSubtransaction(store).exists(path); } } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedWriteTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedWriteTransaction.java index 5d4ad4d803..8c84af11ff 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedWriteTransaction.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedWriteTransaction.java @@ -7,10 +7,15 @@ */ package org.opendaylight.controller.md.sal.dom.broker.impl; -import static com.google.common.base.Preconditions.checkState; - -import javax.annotation.concurrent.GuardedBy; - +import com.google.common.base.Preconditions; +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.Collection; +import java.util.Map; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; 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; @@ -21,18 +26,12 @@ 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 com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.ListenableFuture; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * - * * Read-Write Transaction, which is composed of several - * {@link DOMStoreWriteTransaction} transactions. Subtransaction is selected by + * {@link DOMStoreWriteTransaction} transactions. A sub-transaction is selected by * {@link LogicalDatastoreType} type parameter in: * *

    @@ -46,114 +45,106 @@ import com.google.common.util.concurrent.ListenableFuture; * invocation with all {@link org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort} for underlying * transactions. * - * @param - * Subtype of {@link DOMStoreWriteTransaction} which is used as + * @param Subtype of {@link DOMStoreWriteTransaction} which is used as * subtransaction. */ class DOMForwardedWriteTransaction extends AbstractDOMForwardedCompositeTransaction implements DOMDataWriteTransaction { + @SuppressWarnings("rawtypes") + private static final AtomicReferenceFieldUpdater IMPL_UPDATER = + AtomicReferenceFieldUpdater.newUpdater(DOMForwardedWriteTransaction.class, DOMDataCommitImplementation.class, "commitImpl"); + @SuppressWarnings("rawtypes") + private static final AtomicReferenceFieldUpdater FUTURE_UPDATER = + AtomicReferenceFieldUpdater.newUpdater(DOMForwardedWriteTransaction.class, Future.class, "commitFuture"); + private static final Logger LOG = LoggerFactory.getLogger(DOMForwardedWriteTransaction.class); + private static final Future CANCELLED_FUTURE = Futures.immediateCancelledFuture(); /** - * Implementation of real commit. - * - * Transaction can not be commited if commitImpl is null, - * so this seting this property to null is also used to - * prevent write to - * already commited / canceled transaction {@link #checkNotCanceled() - * - * + * Implementation of real commit. It also acts as an indication that + * the transaction is running -- which we flip atomically using + * {@link #IMPL_UPDATER}. */ - @GuardedBy("this") private volatile DOMDataCommitImplementation commitImpl; /** + * Future task of transaction commit. It starts off as null, but is + * set appropriately on {@link #submit()} and {@link #cancel()} via + * {@link AtomicReferenceFieldUpdater#lazySet(Object, Object)}. * - * Future task of transaction commit. - * - * This value is initially null, and is once updated if transaction - * is commited {@link #commit()}. - * If this future exists, transaction MUST not be commited again - * and all modifications should fail. See {@link #checkNotCommited()}. - * + * Lazy set is safe for use because it is only referenced to in the + * {@link #cancel()} slow path, where we will busy-wait for it. The + * fast path gets the benefit of a store-store barrier instead of the + * usual store-load barrier. */ - @GuardedBy("this") - private volatile CheckedFuture commitFuture; + private volatile Future commitFuture; protected DOMForwardedWriteTransaction(final Object identifier, - final ImmutableMap backingTxs, final DOMDataCommitImplementation commitImpl) { + final Map backingTxs, final DOMDataCommitImplementation commitImpl) { super(identifier, backingTxs); this.commitImpl = Preconditions.checkNotNull(commitImpl, "commitImpl must not be null."); } @Override public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode data) { - checkNotReady(); + checkRunning(commitImpl); getSubtransaction(store).write(path, data); } @Override public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) { - checkNotReady(); + checkRunning(commitImpl); getSubtransaction(store).delete(path); } @Override public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode data) { - checkNotReady(); + checkRunning(commitImpl); getSubtransaction(store).merge(path, data); } @Override - public synchronized boolean cancel() { - // Transaction is already canceled, we are safe to return true - final boolean cancelationResult; - if (commitImpl == null && commitFuture != null) { - // Transaction is submitted, we try to cancel future. - cancelationResult = commitFuture.cancel(false); - } else if(commitImpl == null) { + public boolean cancel() { + final DOMDataCommitImplementation impl = IMPL_UPDATER.getAndSet(this, null); + if (impl != null) { + LOG.trace("Transaction {} cancelled before submit", getIdentifier()); + FUTURE_UPDATER.lazySet(this, CANCELLED_FUTURE); return true; - } else { - cancelationResult = true; - commitImpl = null; } - return cancelationResult; + // The transaction is in process of being submitted or cancelled. Busy-wait + // for the corresponding future. + Future future; + do { + future = commitFuture; + } while (future == null); + + return future.cancel(false); } @Override - public synchronized ListenableFuture> commit() { + public ListenableFuture> commit() { return AbstractDataTransaction.convertToLegacyCommitFuture(submit()); } @Override - public CheckedFuture submit() { - checkNotReady(); + public CheckedFuture submit() { + final DOMDataCommitImplementation impl = IMPL_UPDATER.getAndSet(this, null); + checkRunning(impl); - ImmutableList.Builder cohortsBuilder = ImmutableList.builder(); - for (DOMStoreWriteTransaction subTx : getSubtransactions()) { - cohortsBuilder.add(subTx.ready()); - } - ImmutableList cohorts = cohortsBuilder.build(); - commitFuture = commitImpl.submit(this, cohorts); - - /* - *We remove reference to Commit Implementation in order - *to prevent memory leak - */ - commitImpl = null; - return commitFuture; - } + final Collection txns = getSubtransactions(); + final Collection cohorts = new ArrayList<>(txns.size()); - private void checkNotReady() { - checkNotCommited(); - checkNotCanceled(); - } + // FIXME: deal with errors thrown by backed (ready and submit can fail in theory) + for (DOMStoreWriteTransaction txn : txns) { + cohorts.add(txn.ready()); + } - private void checkNotCanceled() { - Preconditions.checkState(commitImpl != null, "Transaction was canceled."); + final CheckedFuture ret = impl.submit(this, cohorts); + FUTURE_UPDATER.lazySet(this, ret); + return ret; } - private void checkNotCommited() { - checkState(commitFuture == null, "Transaction was already submited."); + private void checkRunning(final DOMDataCommitImplementation impl) { + Preconditions.checkState(impl != null, "Transaction %s is no longer running", getIdentifier()); } -} \ No newline at end of file +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java index e57d08f173..674d2ff44a 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java +++ b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java @@ -1,12 +1,19 @@ package org.opendaylight.controller.md.sal.dom.broker.impl; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertEquals; import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION; import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL; - +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.ForwardingExecutorService; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import java.util.Collections; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @@ -15,7 +22,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; - import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -39,15 +45,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import com.google.common.base.Optional; -import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.ForwardingExecutorService; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; - public class DOMBrokerTest { private SchemaContext schemaContext; @@ -76,7 +73,7 @@ public class DOMBrokerTest { commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor()); futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB"); executor = new DeadlockDetectingListeningExecutorService(commitExecutor, - TransactionCommitDeadlockException.DEADLOCK_EXECUTOR_FUNCTION, futureExecutor); + TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor); domBroker = new DOMDataBrokerImpl(stores, executor); } @@ -215,19 +212,19 @@ public class DOMBrokerTest { TestDOMDataChangeListener dcListener = new TestDOMDataChangeListener() { @Override - public void onDataChanged( AsyncDataChangeEvent> change ) { + public void onDataChanged( final AsyncDataChangeEvent> change ) { DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction(); writeTx.put( OPERATIONAL, TestModel.TEST2_PATH, ImmutableNodes.containerNode( TestModel.TEST2_QNAME ) ); Futures.addCallback( writeTx.submit(), new FutureCallback() { @Override - public void onSuccess( Void result ) { + public void onSuccess( final Void result ) { commitCompletedLatch.countDown(); } @Override - public void onFailure( Throwable t ) { + public void onFailure( final Throwable t ) { caughtCommitEx.set( t ); commitCompletedLatch.countDown(); } @@ -271,7 +268,7 @@ public class DOMBrokerTest { TestDOMDataChangeListener dcListener = new TestDOMDataChangeListener() { @Override - public void onDataChanged( AsyncDataChangeEvent> change ) { + public void onDataChanged( final AsyncDataChangeEvent> change ) { DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction(); writeTx.put( OPERATIONAL, TestModel.TEST2_PATH, ImmutableNodes.containerNode( TestModel.TEST2_QNAME ) ); @@ -333,7 +330,7 @@ public class DOMBrokerTest { private final CountDownLatch latch = new CountDownLatch( 1 ); @Override - public void onDataChanged( AsyncDataChangeEvent> change ) { + public void onDataChanged( final AsyncDataChangeEvent> change ) { this.change = change; latch.countDown(); } @@ -347,7 +344,7 @@ public class DOMBrokerTest { ExecutorService delegate; - public CommitExecutorService( ExecutorService delegate ) { + public CommitExecutorService( final ExecutorService delegate ) { this.delegate = delegate; } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java index 3e74861816..74fa73afb9 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java @@ -8,14 +8,12 @@ package org.opendaylight.controller.md.sal.dom.store.impl; import static com.google.common.base.Preconditions.checkState; - import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -62,8 +60,7 @@ import org.slf4j.LoggerFactory; * to implement {@link DOMStore} contract. * */ -public class InMemoryDOMDataStore implements DOMStore, Identifiable, SchemaContextListener, - TransactionReadyPrototype,AutoCloseable { +public class InMemoryDOMDataStore extends TransactionReadyPrototype implements DOMStore, Identifiable, SchemaContextListener, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataStore.class); private static final ListenableFuture SUCCESSFUL_FUTURE = Futures.immediateFuture(null); @@ -82,29 +79,26 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch private final DataTree dataTree = InMemoryDataTreeFactory.getInstance().create(); private final ListenerTree listenerTree = ListenerTree.create(); private final AtomicLong txCounter = new AtomicLong(0); - private final ListeningExecutorService listeningExecutor; private final QueuedNotificationManager, DOMImmutableDataChangeEvent> dataChangeListenerNotificationManager; private final ExecutorService dataChangeListenerExecutor; - - private final ExecutorService domStoreExecutor; + private final ListeningExecutorService commitExecutor; private final boolean debugTransactions; private final String name; private volatile AutoCloseable closeable; - public InMemoryDOMDataStore(final String name, final ExecutorService domStoreExecutor, + public InMemoryDOMDataStore(final String name, final ListeningExecutorService commitExecutor, final ExecutorService dataChangeListenerExecutor) { - this(name, domStoreExecutor, dataChangeListenerExecutor, + this(name, commitExecutor, dataChangeListenerExecutor, InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE, false); } - public InMemoryDOMDataStore(final String name, final ExecutorService domStoreExecutor, + public InMemoryDOMDataStore(final String name, final ListeningExecutorService commitExecutor, final ExecutorService dataChangeListenerExecutor, final int maxDataChangeListenerQueueSize, final boolean debugTransactions) { this.name = Preconditions.checkNotNull(name); - this.domStoreExecutor = Preconditions.checkNotNull(domStoreExecutor); - this.listeningExecutor = MoreExecutors.listeningDecorator(this.domStoreExecutor); + this.commitExecutor = Preconditions.checkNotNull(commitExecutor); this.dataChangeListenerExecutor = Preconditions.checkNotNull(dataChangeListenerExecutor); this.debugTransactions = debugTransactions; @@ -114,7 +108,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch "DataChangeListenerQueueMgr"); } - public void setCloseable(AutoCloseable closeable) { + public void setCloseable(final AutoCloseable closeable) { this.closeable = closeable; } @@ -123,7 +117,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch } public ExecutorService getDomStoreExecutor() { - return domStoreExecutor; + return commitExecutor; } @Override @@ -158,7 +152,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch @Override public void close() { - ExecutorServiceUtil.tryGracefulShutdown(listeningExecutor, 30, TimeUnit.SECONDS); + ExecutorServiceUtil.tryGracefulShutdown(commitExecutor, 30, TimeUnit.SECONDS); ExecutorServiceUtil.tryGracefulShutdown(dataChangeListenerExecutor, 30, TimeUnit.SECONDS); if(closeable != null) { @@ -215,80 +209,95 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch } @Override - public DOMStoreThreePhaseCommitCohort ready(final SnapshotBackedWriteTransaction writeTx) { - LOG.debug("Tx: {} is submitted. Modifications: {}", writeTx.getIdentifier(), writeTx.getMutatedView()); - return new ThreePhaseCommitImpl(writeTx); + protected void transactionAborted(final SnapshotBackedWriteTransaction tx) { + LOG.debug("Tx: {} is closed.", tx.getIdentifier()); + } + + @Override + protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction tx, final DataTreeModification tree) { + LOG.debug("Tx: {} is submitted. Modifications: {}", tx.getIdentifier(), tree); + return new ThreePhaseCommitImpl(tx, tree); } private Object nextIdentifier() { return name + "-" + txCounter.getAndIncrement(); } - private class DOMStoreTransactionChainImpl implements DOMStoreTransactionChain, TransactionReadyPrototype { - + private class DOMStoreTransactionChainImpl extends TransactionReadyPrototype implements DOMStoreTransactionChain { + @GuardedBy("this") + private SnapshotBackedWriteTransaction allocatedTransaction; + @GuardedBy("this") + private DataTreeSnapshot readySnapshot; @GuardedBy("this") - private SnapshotBackedWriteTransaction latestOutstandingTx; - private boolean chainFailed = false; + @GuardedBy("this") private void checkFailed() { Preconditions.checkState(!chainFailed, "Transaction chain is failed."); } - @Override - public synchronized DOMStoreReadTransaction newReadOnlyTransaction() { - final DataTreeSnapshot snapshot; + @GuardedBy("this") + private DataTreeSnapshot getSnapshot() { checkFailed(); - if (latestOutstandingTx != null) { - checkState(latestOutstandingTx.isReady(), "Previous transaction in chain must be ready."); - snapshot = latestOutstandingTx.getMutatedView(); + + if (allocatedTransaction != null) { + Preconditions.checkState(readySnapshot != null, "Previous transaction %s is not ready yet", allocatedTransaction.getIdentifier()); + return readySnapshot; } else { - snapshot = dataTree.takeSnapshot(); + return dataTree.takeSnapshot(); } + } + + @GuardedBy("this") + private T recordTransaction(final T transaction) { + allocatedTransaction = transaction; + readySnapshot = null; + return transaction; + } + + @Override + public synchronized DOMStoreReadTransaction newReadOnlyTransaction() { + final DataTreeSnapshot snapshot = getSnapshot(); return new SnapshotBackedReadTransaction(nextIdentifier(), getDebugTransactions(), snapshot); } @Override public synchronized DOMStoreReadWriteTransaction newReadWriteTransaction() { - final DataTreeSnapshot snapshot; - checkFailed(); - if (latestOutstandingTx != null) { - checkState(latestOutstandingTx.isReady(), "Previous transaction in chain must be ready."); - snapshot = latestOutstandingTx.getMutatedView(); - } else { - snapshot = dataTree.takeSnapshot(); - } - final SnapshotBackedReadWriteTransaction ret = new SnapshotBackedReadWriteTransaction(nextIdentifier(), - getDebugTransactions(), snapshot, this); - latestOutstandingTx = ret; - return ret; + final DataTreeSnapshot snapshot = getSnapshot(); + return recordTransaction(new SnapshotBackedReadWriteTransaction(nextIdentifier(), + getDebugTransactions(), snapshot, this)); } @Override public synchronized DOMStoreWriteTransaction newWriteOnlyTransaction() { - final DataTreeSnapshot snapshot; - checkFailed(); - if (latestOutstandingTx != null) { - checkState(latestOutstandingTx.isReady(), "Previous transaction in chain must be ready."); - snapshot = latestOutstandingTx.getMutatedView(); - } else { - snapshot = dataTree.takeSnapshot(); + final DataTreeSnapshot snapshot = getSnapshot(); + return recordTransaction(new SnapshotBackedWriteTransaction(nextIdentifier(), + getDebugTransactions(), snapshot, this)); + } + + @Override + protected synchronized void transactionAborted(final SnapshotBackedWriteTransaction tx) { + if (tx.equals(allocatedTransaction)) { + Preconditions.checkState(readySnapshot == null, "Unexpected abort of transaction %s with ready snapshot %s", tx, readySnapshot); + allocatedTransaction = null; } - final SnapshotBackedWriteTransaction ret = new SnapshotBackedWriteTransaction(nextIdentifier(), - getDebugTransactions(), snapshot, this); - latestOutstandingTx = ret; - return ret; } @Override - public DOMStoreThreePhaseCommitCohort ready(final SnapshotBackedWriteTransaction tx) { - DOMStoreThreePhaseCommitCohort storeCohort = InMemoryDOMDataStore.this.ready(tx); - return new ChainedTransactionCommitImpl(tx, storeCohort, this); + protected synchronized DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction tx, final DataTreeModification tree) { + Preconditions.checkState(tx.equals(allocatedTransaction), "Mis-ordered ready transaction %s last allocated was %s", tx, allocatedTransaction); + if (readySnapshot != null) { + // The snapshot should have been cleared + LOG.warn("Uncleared snapshot {} encountered, overwritten with transaction {} snapshot {}", readySnapshot, tx, tree); + } + + final DOMStoreThreePhaseCommitCohort cohort = InMemoryDOMDataStore.this.transactionReady(tx, tree); + readySnapshot = tree; + return new ChainedTransactionCommitImpl(tx, cohort, this); } @Override public void close() { - // FIXME: this call doesn't look right here - listeningExecutor is shared and owned // by the outer class. //listeningExecutor.shutdownNow(); @@ -297,31 +306,30 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch protected synchronized void onTransactionFailed(final SnapshotBackedWriteTransaction transaction, final Throwable t) { chainFailed = true; - } public synchronized void onTransactionCommited(final SnapshotBackedWriteTransaction transaction) { - // If committed transaction is latestOutstandingTx we clear - // latestOutstandingTx - // field in order to base new transactions on Datastore Data Tree - // directly. - if (transaction.equals(latestOutstandingTx)) { - latestOutstandingTx = null; + // If the committed transaction was the one we allocated last, + // we clear it and the ready snapshot, so the next transaction + // allocated refers to the data tree directly. + if (transaction.equals(allocatedTransaction)) { + if (readySnapshot == null) { + LOG.warn("Transaction {} committed while no ready snapshot present", transaction); + } + + allocatedTransaction = null; + readySnapshot = null; } } - } private static class ChainedTransactionCommitImpl implements DOMStoreThreePhaseCommitCohort { - private final SnapshotBackedWriteTransaction transaction; private final DOMStoreThreePhaseCommitCohort delegate; - private final DOMStoreTransactionChainImpl txChain; protected ChainedTransactionCommitImpl(final SnapshotBackedWriteTransaction transaction, final DOMStoreThreePhaseCommitCohort delegate, final DOMStoreTransactionChainImpl txChain) { - super(); this.transaction = transaction; this.delegate = delegate; this.txChain = txChain; @@ -355,29 +363,26 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch public void onSuccess(final Void result) { txChain.onTransactionCommited(transaction); } - }); return commitFuture; } - } private class ThreePhaseCommitImpl implements DOMStoreThreePhaseCommitCohort { - private final SnapshotBackedWriteTransaction transaction; private final DataTreeModification modification; private ResolveDataChangeEventsTask listenerResolver; private DataTreeCandidate candidate; - public ThreePhaseCommitImpl(final SnapshotBackedWriteTransaction writeTransaction) { + public ThreePhaseCommitImpl(final SnapshotBackedWriteTransaction writeTransaction, final DataTreeModification modification) { this.transaction = writeTransaction; - this.modification = transaction.getMutatedView(); + this.modification = modification; } @Override public ListenableFuture canCommit() { - return listeningExecutor.submit(new Callable() { + return commitExecutor.submit(new Callable() { @Override public Boolean call() throws TransactionCommitFailedException { try { @@ -401,7 +406,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch @Override public ListenableFuture preCommit() { - return listeningExecutor.submit(new Callable() { + return commitExecutor.submit(new Callable() { @Override public Void call() { candidate = dataTree.prepare(modification); @@ -425,7 +430,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch * The commit has to occur atomically with regard to listener * registrations. */ - synchronized (this) { + synchronized (InMemoryDOMDataStore.this) { dataTree.commit(candidate); listenerResolver.resolve(dataChangeListenerNotificationManager); } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreFactory.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreFactory.java index dc1482c6ab..2ee8e182c2 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreFactory.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStoreFactory.java @@ -7,6 +7,8 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import java.util.concurrent.ExecutorService; import javax.annotation.Nullable; import org.opendaylight.controller.sal.core.api.model.SchemaService; @@ -57,7 +59,7 @@ public final class InMemoryDOMDataStoreFactory { @Nullable final InMemoryDOMDataStoreConfigProperties properties) { InMemoryDOMDataStoreConfigProperties actualProperties = properties; - if(actualProperties == null) { + if (actualProperties == null) { actualProperties = InMemoryDOMDataStoreConfigProperties.getDefault(); } @@ -65,21 +67,18 @@ public final class InMemoryDOMDataStoreFactory { // task execution time to get higher throughput as DataChangeListeners typically provide // much of the business logic for a data model. If the executor queue size limit is reached, // subsequent submitted notifications will block the calling thread. - int dclExecutorMaxQueueSize = actualProperties.getMaxDataChangeExecutorQueueSize(); int dclExecutorMaxPoolSize = actualProperties.getMaxDataChangeExecutorPoolSize(); ExecutorService dataChangeListenerExecutor = SpecialExecutors.newBlockingBoundedFastThreadPool( dclExecutorMaxPoolSize, dclExecutorMaxQueueSize, name + "-DCL" ); - ExecutorService domStoreExecutor = SpecialExecutors.newBoundedSingleThreadExecutor( - actualProperties.getMaxDataStoreExecutorQueueSize(), "DOMStore-" + name ); - - InMemoryDOMDataStore dataStore = new InMemoryDOMDataStore(name, - domStoreExecutor, dataChangeListenerExecutor, + final ListeningExecutorService commitExecutor = MoreExecutors.sameThreadExecutor(); + final InMemoryDOMDataStore dataStore = new InMemoryDOMDataStore(name, + commitExecutor, dataChangeListenerExecutor, actualProperties.getMaxDataChangeListenerQueueSize(), debugTransactions); - if(schemaService != null) { + if (schemaService != null) { schemaService.registerSchemaContextListener(dataStore); } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java index 2ae7425bbb..30fa6da58b 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java @@ -8,16 +8,13 @@ package org.opendaylight.controller.md.sal.dom.store.impl; import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; - import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,9 +24,7 @@ import org.slf4j.LoggerFactory; * and executed according to {@link TransactionReadyPrototype}. * */ -class SnapshotBackedReadWriteTransaction extends SnapshotBackedWriteTransaction - implements DOMStoreReadWriteTransaction { - +final class SnapshotBackedReadWriteTransaction extends SnapshotBackedWriteTransaction implements DOMStoreReadWriteTransaction { private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadWriteTransaction.class); /** @@ -49,16 +44,18 @@ class SnapshotBackedReadWriteTransaction extends SnapshotBackedWriteTransaction LOG.debug("Tx: {} Read: {}", getIdentifier(), path); checkNotNull(path, "Path must not be null."); - DataTreeModification dataView = getMutatedView(); - if(dataView == null) { - return Futures.immediateFailedCheckedFuture(new ReadFailedException("Transaction is closed")); - } - + final Optional> result; try { - return Futures.immediateCheckedFuture(dataView.readNode(path)); + result = readSnapshotNode(path); } catch (Exception e) { LOG.error("Tx: {} Failed Read of {}", getIdentifier(), path, e); - return Futures.immediateFailedCheckedFuture(new ReadFailedException("Read failed",e)); + return Futures.immediateFailedCheckedFuture(new ReadFailedException("Read failed", e)); + } + + if (result == null) { + return Futures.immediateFailedCheckedFuture(new ReadFailedException("Transaction is closed")); + } else { + return Futures.immediateCheckedFuture(result); } } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java index 6129df7478..60a23403b3 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedWriteTransaction.java @@ -8,11 +8,11 @@ package org.opendaylight.controller.md.sal.dom.store.impl; import static com.google.common.base.Preconditions.checkState; - import com.google.common.base.Objects.ToStringHelper; +import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; - +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -29,11 +29,16 @@ import org.slf4j.LoggerFactory; * */ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction implements DOMStoreWriteTransaction { - private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedWriteTransaction.class); - private DataTreeModification mutableTree; - private boolean ready = false; - private TransactionReadyPrototype readyImpl; + private static final AtomicReferenceFieldUpdater READY_UPDATER = + AtomicReferenceFieldUpdater.newUpdater(SnapshotBackedWriteTransaction.class, TransactionReadyPrototype.class, "readyImpl"); + private static final AtomicReferenceFieldUpdater TREE_UPDATER = + AtomicReferenceFieldUpdater.newUpdater(SnapshotBackedWriteTransaction.class, DataTreeModification.class, "mutableTree"); + + // non-null when not ready + private volatile TransactionReadyPrototype readyImpl; + // non-null when not committed/closed + private volatile DataTreeModification mutableTree; /** * Creates new write-only transaction. @@ -48,27 +53,23 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction impleme public SnapshotBackedWriteTransaction(final Object identifier, final boolean debug, final DataTreeSnapshot snapshot, final TransactionReadyPrototype readyImpl) { super(identifier, debug); - mutableTree = snapshot.newModification(); this.readyImpl = Preconditions.checkNotNull(readyImpl, "readyImpl must not be null."); + mutableTree = snapshot.newModification(); LOG.debug("Write Tx: {} allocated with snapshot {}", identifier, snapshot); } - @Override - public void close() { - LOG.debug("Store transaction: {} : Closed", getIdentifier()); - this.mutableTree = null; - this.readyImpl = null; - } - @Override public void write(final YangInstanceIdentifier path, final NormalizedNode data) { checkNotReady(); + + final DataTreeModification tree = mutableTree; + LOG.debug("Tx: {} Write: {}:{}", getIdentifier(), path, data); + try { - LOG.debug("Tx: {} Write: {}:{}", getIdentifier(), path, data); - mutableTree.write(path, data); + tree.write(path, data); // FIXME: Add checked exception } catch (Exception e) { - LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, mutableTree, e); + LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, tree, e); // Rethrow original ones if they are subclasses of RuntimeException // or Error Throwables.propagateIfPossible(e); @@ -80,12 +81,15 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction impleme @Override public void merge(final YangInstanceIdentifier path, final NormalizedNode data) { checkNotReady(); + + final DataTreeModification tree = mutableTree; + LOG.debug("Tx: {} Merge: {}:{}", getIdentifier(), path, data); + try { - LOG.debug("Tx: {} Merge: {}:{}", getIdentifier(), path, data); - mutableTree.merge(path, data); + tree.merge(path, data); // FIXME: Add checked exception } catch (Exception e) { - LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, mutableTree, e); + LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, tree, e); // Rethrow original ones if they are subclasses of RuntimeException // or Error Throwables.propagateIfPossible(e); @@ -97,12 +101,15 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction impleme @Override public void delete(final YangInstanceIdentifier path) { checkNotReady(); + + final DataTreeModification tree = mutableTree; + LOG.debug("Tx: {} Delete: {}", getIdentifier(), path); + try { - LOG.debug("Tx: {} Delete: {}", getIdentifier(), path); - mutableTree.delete(path); + tree.delete(path); // FIXME: Add checked exception } catch (Exception e) { - LOG.error("Tx: {}, failed to delete {} in {}", getIdentifier(), path, mutableTree, e); + LOG.error("Tx: {}, failed to delete {} in {}", getIdentifier(), path, tree, e); // Rethrow original ones if they are subclasses of RuntimeException // or Error Throwables.propagateIfPossible(e); @@ -111,30 +118,49 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction impleme } } - protected final boolean isReady() { - return ready; + /** + * Exposed for {@link SnapshotBackedReadWriteTransaction}'s sake only. The contract does + * not allow data access after the transaction has been closed or readied. + * + * @param path Path to read + * @return null if the the transaction has been closed; + */ + protected final Optional> readSnapshotNode(final YangInstanceIdentifier path) { + return readyImpl == null ? null : mutableTree.readNode(path); } - protected final void checkNotReady() { - checkState(!ready, "Transaction %s is ready. No further modifications allowed.", getIdentifier()); + private final void checkNotReady() { + checkState(readyImpl != null, "Transaction %s is no longer open. No further modifications allowed.", getIdentifier()); } @Override - public synchronized DOMStoreThreePhaseCommitCohort ready() { - checkState(!ready, "Transaction %s is already ready.", getIdentifier()); - ready = true; + public DOMStoreThreePhaseCommitCohort ready() { + final TransactionReadyPrototype wasReady = READY_UPDATER.getAndSet(this, null); + checkState(wasReady != null, "Transaction %s is no longer open", getIdentifier()); + LOG.debug("Store transaction: {} : Ready", getIdentifier()); - mutableTree.ready(); - return readyImpl.ready(this); + + final DataTreeModification tree = mutableTree; + TREE_UPDATER.lazySet(this, null); + tree.ready(); + return wasReady.transactionReady(this, tree); } - protected DataTreeModification getMutatedView() { - return mutableTree; + @Override + public void close() { + final TransactionReadyPrototype wasReady = READY_UPDATER.getAndSet(this, null); + if (wasReady != null) { + LOG.debug("Store transaction: {} : Closed", getIdentifier()); + TREE_UPDATER.lazySet(this, null); + wasReady.transactionAborted(this); + } else { + LOG.debug("Store transaction: {} : Closed after submit", getIdentifier()); + } } @Override protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) { - return toStringHelper.add("ready", isReady()); + return toStringHelper.add("ready", readyImpl == null); } /** @@ -146,7 +172,14 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction impleme * providing underlying logic for applying implementation. * */ - public static interface TransactionReadyPrototype { + abstract static class TransactionReadyPrototype { + /** + * Called when a transaction is closed without being readied. This is not invoked for + * transactions which are ready. + * + * @param tx Transaction which got aborted. + */ + protected abstract void transactionAborted(final SnapshotBackedWriteTransaction tx); /** * Returns a commit coordinator associated with supplied transactions. @@ -155,8 +188,10 @@ class SnapshotBackedWriteTransaction extends AbstractDOMStoreTransaction impleme * * @param tx * Transaction on which ready was invoked. + * @param tree + * Modified data tree which has been constructed. * @return DOMStoreThreePhaseCommitCohort associated with transaction */ - DOMStoreThreePhaseCommitCohort ready(SnapshotBackedWriteTransaction tx); + protected abstract DOMStoreThreePhaseCommitCohort transactionReady(SnapshotBackedWriteTransaction tx, DataTreeModification tree); } } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/jmx/InMemoryDataStoreStats.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/jmx/InMemoryDataStoreStats.java index b3608eceef..e00be2446a 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/jmx/InMemoryDataStoreStats.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/jmx/InMemoryDataStoreStats.java @@ -9,7 +9,7 @@ package org.opendaylight.controller.md.sal.dom.store.impl.jmx; import java.util.concurrent.ExecutorService; - +import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean; import org.opendaylight.controller.md.sal.common.util.jmx.QueuedNotificationManagerMXBeanImpl; import org.opendaylight.controller.md.sal.common.util.jmx.ThreadExecutorStatsMXBeanImpl; import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager; @@ -21,24 +21,28 @@ import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager; */ public class InMemoryDataStoreStats implements AutoCloseable { - private final ThreadExecutorStatsMXBeanImpl notificationExecutorStatsBean; - private final ThreadExecutorStatsMXBeanImpl dataStoreExecutorStatsBean; + private final AbstractMXBean notificationExecutorStatsBean; + private final AbstractMXBean dataStoreExecutorStatsBean; private final QueuedNotificationManagerMXBeanImpl notificationManagerStatsBean; - public InMemoryDataStoreStats(String mBeanType, QueuedNotificationManager manager, - ExecutorService dataStoreExecutor) { + public InMemoryDataStoreStats(final String mBeanType, final QueuedNotificationManager manager, + final ExecutorService dataStoreExecutor) { - this.notificationManagerStatsBean = new QueuedNotificationManagerMXBeanImpl(manager, + notificationManagerStatsBean = new QueuedNotificationManagerMXBeanImpl(manager, "notification-manager", mBeanType, null); notificationManagerStatsBean.registerMBean(); - this.notificationExecutorStatsBean = new ThreadExecutorStatsMXBeanImpl(manager.getExecutor(), + notificationExecutorStatsBean = ThreadExecutorStatsMXBeanImpl.create(manager.getExecutor(), "notification-executor", mBeanType, null); - this.notificationExecutorStatsBean.registerMBean(); + if (notificationExecutorStatsBean != null) { + notificationExecutorStatsBean.registerMBean(); + } - this.dataStoreExecutorStatsBean = new ThreadExecutorStatsMXBeanImpl(dataStoreExecutor, + dataStoreExecutorStatsBean = ThreadExecutorStatsMXBeanImpl.create(dataStoreExecutor, "data-store-executor", mBeanType, null); - this.dataStoreExecutorStatsBean.registerMBean(); + if (dataStoreExecutorStatsBean != null) { + dataStoreExecutorStatsBean.registerMBean(); + } } @Override diff --git a/opendaylight/md-sal/samples/clustering-test-app/configuration/pom.xml b/opendaylight/md-sal/samples/clustering-test-app/configuration/pom.xml new file mode 100644 index 0000000000..8d4bbbd64c --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/configuration/pom.xml @@ -0,0 +1,55 @@ + + + + + 4.0.0 + + clustering-it + org.opendaylight.controller.samples + 1.1-SNAPSHOT + + clustering-it-config + jar + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + + attach-artifact + + package + + + + ${project.build.directory}/classes/initial/20-clustering-test-app.xml + xml + config + + + ${project.build.directory}/classes/initial/module-shards.conf + xml + testmoduleshardconf + + + ${project.build.directory}/classes/initial/modules.conf + xml + testmoduleconf + + + + + + + + + diff --git a/opendaylight/md-sal/samples/clustering-test-app/configuration/src/main/resources/initial/20-clustering-test-app.xml b/opendaylight/md-sal/samples/clustering-test-app/configuration/src/main/resources/initial/20-clustering-test-app.xml new file mode 100644 index 0000000000..f01970908c --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/configuration/src/main/resources/initial/20-clustering-test-app.xml @@ -0,0 +1,47 @@ + + + + + + + + + + prefix:clustering-it-provider + + clustering-it-provider + + + binding:binding-rpc-registry + binding-rpc-broker + + + binding:binding-async-data-broker + binding-data-broker + + + + binding:binding-notification-service + + binding-notification-broker + + + + + + + + + urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28 + urn:opendaylight:params:xml:ns:yang:controller:config:clustering-it-provider?module=clustering-it-provider&revision=2014-08-19 + + + + + diff --git a/opendaylight/md-sal/samples/clustering-test-app/configuration/src/main/resources/initial/module-shards.conf b/opendaylight/md-sal/samples/clustering-test-app/configuration/src/main/resources/initial/module-shards.conf new file mode 100644 index 0000000000..59b0be1bce --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/configuration/src/main/resources/initial/module-shards.conf @@ -0,0 +1,117 @@ +# This file describes which shards live on which members +# The format for a module-shards is as follows, +# { +# name = "" +# shards = [ +# { +# name="" +# replicas = [ +# "" +# ] +# ] +# } +# +# For Helium we support only one shard per module. Beyond Helium +# we will support more than 1 +# The replicas section is a collection of member names. This information +# will be used to decide on which members replicas of a particular shard will be +# located. Once replication is integrated with the distributed data store then +# this section can have multiple entries. +# +# + + +module-shards = [ + { + name = "default" + shards = [ + { + name="default" + replicas = [ + "member-1", + "member-2", + "member-3" + ] + } + ] + }, + { + name = "topology" + shards = [ + { + name="topology" + replicas = [ + "member-1", + "member-2", + "member-3" + ] + } + ] + }, + { + name = "inventory" + shards = [ + { + name="inventory" + replicas = [ + "member-1", + "member-2", + "member-3" + ] + } + ] + }, + { + name = "toaster" + shards = [ + { + name="toaster" + replicas = [ + "member-1", + "member-2", + "member-3" + ] + } + ] + } + { + name = "car" + shards = [ + { + name="car" + replicas = [ + "member-1", + "member-2", + "member-3" + ] + } + ] + } + { + name = "people" + shards = [ + { + name="people" + replicas = [ + "member-1", + "member-2", + "member-3" + ] + } + ] + } + { + name = "car-people" + shards = [ + { + name="car-people" + replicas = [ + "member-1", + "member-2", + "member-3" + ] + } + ] + } + +] diff --git a/opendaylight/md-sal/samples/clustering-test-app/configuration/src/main/resources/initial/modules.conf b/opendaylight/md-sal/samples/clustering-test-app/configuration/src/main/resources/initial/modules.conf new file mode 100644 index 0000000000..eda60d35a1 --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/configuration/src/main/resources/initial/modules.conf @@ -0,0 +1,47 @@ +# This file should describe all the modules that need to be placed in a separate shard +# The format of the configuration is as follows +# { +# name = "" +# namespace = "" +# shard-strategy = "module" +# } +# +# Note that at this time the only shard-strategy we support is module which basically +# will put all the data of a single module in two shards (one for config and one for +# operational data) + +modules = [ + { + name = "inventory" + namespace = "urn:opendaylight:inventory" + shard-strategy = "module" + }, + + { + name = "topology" + namespace = "urn:TBD:params:xml:ns:yang:network-topology" + shard-strategy = "module" + }, + + { + name = "toaster" + namespace = "http://netconfcentral.org/ns/toaster" + shard-strategy = "module" + }, + { + name = "car" + namespace = "urn:opendaylight:params:xml:ns:yang:controller:config:sal-clustering-it:car" + shard-strategy = "module" + } + { + name = "people" + namespace = "urn:opendaylight:params:xml:ns:yang:controller:config:sal-clustering-it:people" + shard-strategy = "module" + } + + { + name = "car-people" + namespace = "urn:opendaylight:params:xml:ns:yang:controller:config:sal-clustering-it:car-people" + shard-strategy = "module" + } +] diff --git a/opendaylight/md-sal/samples/clustering-test-app/model/pom.xml b/opendaylight/md-sal/samples/clustering-test-app/model/pom.xml new file mode 100644 index 0000000000..a23e32df2b --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/model/pom.xml @@ -0,0 +1,115 @@ + + + 4.0.0 + + clustering-it + org.opendaylight.controller.samples + 1.1-SNAPSHOT + + clustering-it-model + bundle + + + + + org.apache.felix + maven-bundle-plugin + ${bundle.plugin.version} + true + + + org.opendaylight.controller.sal-clustering-it-model + * + + + + + org.opendaylight.yangtools + yang-maven-plugin + ${yangtools.version} + + + + generate-sources + + + src/main/yang + + + org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl + target/generated-sources/sal + + + true + + + + + + org.opendaylight.yangtools + maven-sal-api-gen-plugin + ${yangtools.version} + jar + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.opendaylight.yangtools + yang-maven-plugin + [0.5,) + + generate-sources + + + + + + + + + + + + + + + + org.opendaylight.yangtools + yang-binding + ${yangtools.version} + + + org.opendaylight.yangtools + yang-common + ${yangtools.version} + + + org.opendaylight.yangtools.model + ietf-inet-types + ${ietf-inet-types.version} + + + org.opendaylight.yangtools.model + ietf-yang-types + ${ietf-yang-types.version} + + + org.opendaylight.yangtools.model + yang-ext + ${yang-ext.version} + + + diff --git a/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car-people.yang b/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car-people.yang new file mode 100644 index 0000000000..899724658f --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car-people.yang @@ -0,0 +1,42 @@ +module car-people { + + yang-version 1; + + namespace "urn:opendaylight:params:xml:ns:yang:controller:config:sal-clustering-it:car-people"; + + prefix car; + + import ietf-inet-types { prefix "inet"; revision-date 2010-09-24; } + import car { prefix "c"; revision-date 2014-08-18; } + import people { prefix "people"; revision-date 2014-08-18; } + + organization "Netconf Central"; + + contact + "Harman Singh "; + + description + "YANG model for car for test application"; + + revision "2014-08-18" { + description + "Clustering sample app"; + } + + container car-people { + description + "Top-level container for all people car map"; + + list car-person { + key "car-id person-id"; + description "A mapping of cars and people."; + leaf car-id { + type c:car-id; + } + + leaf person-id { + type people:person-id; + } + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car-purchase.yang b/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car-purchase.yang new file mode 100644 index 0000000000..f6a8797098 --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car-purchase.yang @@ -0,0 +1,60 @@ +module car-purchase { + + yang-version 1; + + namespace "urn:opendaylight:params:xml:ns:yang:controller:config:sal-clustering-it:car-purchase"; + + prefix cp; + + import ietf-inet-types { prefix "inet"; revision-date 2010-09-24; } + import car { prefix "car"; revision-date 2014-08-18; } + import people { prefix "person"; revision-date 2014-08-18; } + import yang-ext {prefix "ext"; revision-date "2013-07-09";} + + organization "Netconf Central"; + + contact + "Harman Singh "; + + description + "YANG model for car purchase for test application"; + + revision "2014-08-18" { + description + "Clustering sample app"; + } + + rpc buy-car { + description + "buy a new car"; + input { + leaf person { + ext:context-reference "person:person-context"; + type person:person-ref; + description "A reference to a particular person."; + } + + leaf car-id { + type car:car-id; + description "identifier of car."; + } + leaf person-id { + type person:person-id; + description "identifier of person."; + } + } + } + + notification carBought { + description + "Indicates that a person bought a car."; + leaf car-id { + type car:car-id; + description "identifier of car."; + } + leaf person-id { + type person:person-id; + description "identifier of person."; + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car.yang b/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car.yang new file mode 100644 index 0000000000..d9cfb6b1d5 --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car.yang @@ -0,0 +1,64 @@ +module car { + + yang-version 1; + + namespace "urn:opendaylight:params:xml:ns:yang:controller:config:sal-clustering-it:car"; + + prefix car; + + import ietf-inet-types { prefix "inet"; revision-date 2010-09-24; } + + organization "Netconf Central"; + + contact + "Harman Singh "; + + description + "YANG model for car for test application"; + + revision "2014-08-18" { + description + "Clustering sample app"; + } + + typedef car-id { + type inet:uri; + description "An identifier for car entry."; + } + + grouping car-entry { + description "Describes the contents of a car entry - + Details of the car manufacturer, model etc"; + leaf id { + type car-id; + description "identifier of single list of entries."; + } + + leaf model { + type string; + } + leaf manufacturer { + type string; + } + + leaf year { + type uint32; + } + + leaf category { + type string; + } + } + + container cars { + description + "Top-level container for all car objects."; + list car-entry { + key "id"; + description "A list of cars (as defined by the 'grouping car-entry')."; + uses car-entry; + } + } + + +} \ No newline at end of file diff --git a/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/people.yang b/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/people.yang new file mode 100644 index 0000000000..6c8f24703f --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/people.yang @@ -0,0 +1,80 @@ +module people { + + yang-version 1; + + namespace "urn:opendaylight:params:xml:ns:yang:controller:config:sal-clustering-it:people"; + + prefix people; + + import ietf-inet-types { prefix "inet"; revision-date 2010-09-24; } + + organization "Netconf Central"; + + contact + "Harman Singh "; + + description + "YANG model for person for test application"; + + revision "2014-08-18" { + description + "Clustering sample app"; + } + + typedef person-id { + type inet:uri; + description "An identifier for person."; + } + + typedef person-ref { + type instance-identifier; + description "A reference that points to an people:people/person in the data tree."; + } + identity person-context { + description "A person-context is a classifier for person elements which allows an RPC to provide a service on behalf of a particular element in the data tree."; + } + + grouping person { + description "Describes the details of the person"; + + leaf id { + type person-id; + description "identifier of single list of entries."; + } + + leaf gender { + type string; + } + + leaf age { + type uint32; + } + + leaf address { + type string; + } + + leaf contactNo { + type string; + } + } + + container people { + description + "Top-level container for all people"; + + list person { + key "id"; + description "A list of people (as defined by the 'grouping person')."; + uses person; + } + } + + rpc add-person { + description + "Add a person entry into database"; + input { + uses person; + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/samples/clustering-test-app/pom.xml b/opendaylight/md-sal/samples/clustering-test-app/pom.xml new file mode 100644 index 0000000000..863bbecdf9 --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + org.opendaylight.controller.samples + sal-samples + 1.1-SNAPSHOT + + clustering-it + pom + + configuration + model + provider + + diff --git a/opendaylight/md-sal/samples/clustering-test-app/provider/pom.xml b/opendaylight/md-sal/samples/clustering-test-app/provider/pom.xml new file mode 100644 index 0000000000..093b681125 --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/provider/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + + clustering-it + org.opendaylight.controller.samples + 1.1-SNAPSHOT + + clustering-it-provider + bundle + + + + + org.apache.felix + maven-bundle-plugin + ${bundle.plugin.version} + true + + + org.opendaylight.controller.config.yang.config.clustering_it_provider + * + + + + + org.opendaylight.yangtools + yang-maven-plugin + ${yangtools.version} + + + config + + generate-sources + + + + + org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + ${jmxGeneratorPath} + + urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang + + + + org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl + ${salGeneratorPath} + + + true + + + + + + org.opendaylight.controller + yang-jmx-generator-plugin + ${config.version} + + + org.opendaylight.yangtools + maven-sal-api-gen-plugin + ${yangtools.version} + + + + + + + + + org.opendaylight.controller.samples + clustering-it-model + ${version} + + + org.opendaylight.controller + config-api + ${config.version} + + + org.opendaylight.controller + sal-binding-config + ${mdsal.version} + + + org.opendaylight.controller + sal-binding-api + ${mdsal.version} + + + org.opendaylight.controller + sal-common-util + ${mdsal.version} + + + equinoxSDK381 + org.eclipse.osgi + 3.8.1.v20120830-144521 + + + diff --git a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/listener/PeopleCarListener.java b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/listener/PeopleCarListener.java new file mode 100644 index 0000000000..4737d6eb49 --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/listener/PeopleCarListener.java @@ -0,0 +1,70 @@ +/* + * 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.clustering.it.listener; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.people.rev140818.CarPeople; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.people.rev140818.car.people.CarPerson; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.people.rev140818.car.people.CarPersonBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.people.rev140818.car.people.CarPersonKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.purchase.rev140818.CarBought; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.purchase.rev140818.CarPurchaseListener; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class PeopleCarListener implements CarPurchaseListener { + + private static final Logger log = LoggerFactory.getLogger(PeopleCarListener.class); + + private DataBroker dataProvider; + + + + public void setDataProvider(final DataBroker salDataProvider) { + this.dataProvider = salDataProvider; + } + + @Override + public void onCarBought(CarBought notification) { + log.info("onCarBought notification : Adding car person entry"); + + final CarPersonBuilder carPersonBuilder = new CarPersonBuilder(); + carPersonBuilder.setCarId(notification.getCarId()); + carPersonBuilder.setPersonId(notification.getPersonId()); + CarPersonKey key = new CarPersonKey(notification.getCarId(), notification.getPersonId()); + carPersonBuilder.setKey(key); + final CarPerson carPerson = carPersonBuilder.build(); + + InstanceIdentifier carPersonIId = + InstanceIdentifier.builder(CarPeople.class).child(CarPerson.class, carPerson.getKey()).build(); + + + WriteTransaction tx = dataProvider.newWriteOnlyTransaction(); + tx.put(LogicalDatastoreType.CONFIGURATION, carPersonIId, carPerson); + + Futures.addCallback(tx.submit(), new FutureCallback() { + @Override + public void onSuccess(final Void result) { + log.info("Car bought, entry added to map of people and car [{}]", carPerson); + } + + @Override + public void onFailure(final Throwable t) { + log.info("Car bought, Failed entry addition to map of people and car [{}]", carPerson); + } + }); + + } +} diff --git a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/PeopleProvider.java b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/PeopleProvider.java new file mode 100644 index 0000000000..e0d3f75349 --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/PeopleProvider.java @@ -0,0 +1,91 @@ +/* + * 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.clustering.it.provider; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.SettableFuture; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.purchase.rev140818.CarPurchaseService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.people.rev140818.AddPersonInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.people.rev140818.People; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.people.rev140818.PeopleService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.people.rev140818.PersonContext; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.people.rev140818.people.Person; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.people.rev140818.people.PersonBuilder; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.Future; + +public class PeopleProvider implements PeopleService, AutoCloseable { + + private static final Logger log = LoggerFactory.getLogger(PeopleProvider.class); + + private DataBroker dataProvider; + + private BindingAwareBroker.RoutedRpcRegistration rpcRegistration; + + public void setDataProvider(final DataBroker salDataProvider) { + this.dataProvider = salDataProvider; + } + + + public void setRpcRegistration(BindingAwareBroker.RoutedRpcRegistration rpcRegistration) { + this.rpcRegistration = rpcRegistration; + } + + @Override + public Future> addPerson(AddPersonInput input) { + log.info("RPC addPerson : adding person [{}]", input); + + PersonBuilder builder = new PersonBuilder(input); + final Person person = builder.build(); + final SettableFuture> futureResult = SettableFuture.create(); + + // Each entry will be identifiable by a unique key, we have to create that identifier + final InstanceIdentifier.InstanceIdentifierBuilder personIdBuilder = + InstanceIdentifier.builder(People.class) + .child(Person.class, person.getKey()); + final InstanceIdentifier personId = personIdBuilder.build(); + // Place entry in data store tree + WriteTransaction tx = dataProvider.newWriteOnlyTransaction(); + tx.put(LogicalDatastoreType.CONFIGURATION, personId, person); + + Futures.addCallback(tx.submit(), new FutureCallback() { + @Override + public void onSuccess(final Void result) { + log.info("RPC addPerson : person added successfully [{}]", person); + rpcRegistration.registerPath(PersonContext.class, personId); + log.info("RPC addPerson : routed rpc registered for instance ID [{}]", personId); + futureResult.set(RpcResultBuilder.success().build()); + } + + @Override + public void onFailure(final Throwable t) { + log.info("RPC addPerson : person addition failed [{}]", person); + futureResult.set(RpcResultBuilder.failed() + .withError(RpcError.ErrorType.APPLICATION, t.getMessage()).build()); + } + }); + return futureResult; + } + + @Override + public void close() throws Exception { + + } +} diff --git a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/PurchaseCarProvider.java b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/PurchaseCarProvider.java new file mode 100644 index 0000000000..74a0aa68ed --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/PurchaseCarProvider.java @@ -0,0 +1,52 @@ +/* + * 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.clustering.it.provider; + +import com.google.common.util.concurrent.SettableFuture; +import org.opendaylight.controller.sal.binding.api.NotificationProviderService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.purchase.rev140818.BuyCarInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.purchase.rev140818.CarBoughtBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.purchase.rev140818.CarPurchaseService; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.Future; + + +public class PurchaseCarProvider implements CarPurchaseService, AutoCloseable{ + + private static final Logger log = LoggerFactory.getLogger(PurchaseCarProvider.class); + + private NotificationProviderService notificationProvider; + + + public void setNotificationProvider(final NotificationProviderService salService) { + this.notificationProvider = salService; + } + + + @Override + public Future> buyCar(BuyCarInput input) { + log.info("Routed RPC buyCar : generating notification for buying car [{}]", input); + SettableFuture> futureResult = SettableFuture.create(); + CarBoughtBuilder carBoughtBuilder = new CarBoughtBuilder(); + carBoughtBuilder.setCarId(input.getCarId()); + carBoughtBuilder.setPersonId(input.getPersonId()); + notificationProvider.publish(carBoughtBuilder.build()); + futureResult.set(RpcResultBuilder.success().build()); + return futureResult; + } + + @Override + public void close() throws Exception { + + } +} diff --git a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/config/yang/config/clustering_it_provider/ClusteringItProviderModule.java b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/config/yang/config/clustering_it_provider/ClusteringItProviderModule.java new file mode 100644 index 0000000000..d91d40a34d --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/config/yang/config/clustering_it_provider/ClusteringItProviderModule.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.config.yang.config.clustering_it_provider; + + +import org.opendaylight.controller.clustering.it.listener.PeopleCarListener; +import org.opendaylight.controller.clustering.it.provider.PeopleProvider; +import org.opendaylight.controller.clustering.it.provider.PurchaseCarProvider; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.binding.api.NotificationProviderService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.purchase.rev140818.CarPurchaseService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.people.rev140818.PeopleService; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.NotificationListener; + +public class ClusteringItProviderModule extends org.opendaylight.controller.config.yang.config.clustering_it_provider.AbstractClusteringItProviderModule { + public ClusteringItProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public ClusteringItProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.config.clustering_it_provider.ClusteringItProviderModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + // add custom validation form module attributes here. + } + + @Override + public java.lang.AutoCloseable createInstance() { + DataBroker dataBrokerService = getDataBrokerDependency(); + NotificationProviderService notificationProvider = getNotificationServiceDependency(); + + // Add routed RPC registration for car purchase + final PurchaseCarProvider purchaseCar = new PurchaseCarProvider(); + purchaseCar.setNotificationProvider(notificationProvider); + + final BindingAwareBroker.RoutedRpcRegistration purchaseCarRpc = getRpcRegistryDependency() + .addRoutedRpcImplementation(CarPurchaseService.class, purchaseCar); + + // Add people provider registration + final PeopleProvider people = new PeopleProvider(); + people.setDataProvider(dataBrokerService); + + people.setRpcRegistration(purchaseCarRpc); + + final BindingAwareBroker.RpcRegistration peopleRpcReg = getRpcRegistryDependency() + .addRpcImplementation(PeopleService.class, people); + + + + final PeopleCarListener peopleCarListener = new PeopleCarListener(); + peopleCarListener.setDataProvider(dataBrokerService); + + final ListenerRegistration listenerReg = + getNotificationServiceDependency().registerNotificationListener( peopleCarListener ); + + // Wrap toaster as AutoCloseable and close registrations to md-sal at + // close() + final class AutoCloseableToaster implements AutoCloseable { + + @Override + public void close() throws Exception { + peopleRpcReg.close(); + purchaseCarRpc.close(); + people.close(); + purchaseCar.close(); + listenerReg.close(); + } + } + + AutoCloseable ret = new AutoCloseableToaster(); + return ret; + } + +} diff --git a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/config/yang/config/clustering_it_provider/ClusteringItProviderModuleFactory.java b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/config/yang/config/clustering_it_provider/ClusteringItProviderModuleFactory.java new file mode 100644 index 0000000000..642263c773 --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/config/yang/config/clustering_it_provider/ClusteringItProviderModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: clustering-it-provider yang module local name: clustering-it-provider +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Tue Aug 19 14:44:46 PDT 2014 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.controller.config.yang.config.clustering_it_provider; +public class ClusteringItProviderModuleFactory extends org.opendaylight.controller.config.yang.config.clustering_it_provider.AbstractClusteringItProviderModuleFactory { + +} diff --git a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/yang/clustering-it-provider.yang b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/yang/clustering-it-provider.yang new file mode 100644 index 0000000000..ff3f9a8e5c --- /dev/null +++ b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/yang/clustering-it-provider.yang @@ -0,0 +1,60 @@ +module clustering-it-provider { + + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:config:clustering-it-provider"; + prefix "clustering-it-provider"; + + import config { prefix config; revision-date 2013-04-05; } + import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28; } + + description + "This module contains the base YANG definitions for + clustering-it-provider implementation."; + + revision "2014-08-19" { + description + "Initial revision."; + } + + // This is the definition of the service implementation as a module identity. + identity clustering-it-provider { + base config:module-type; + + // Specifies the prefix for generated java classes. + config:java-name-prefix ClusteringItProvider; + } + + // Augments the 'configuration' choice node under modules/module. + augment "/config:modules/config:module/config:configuration" { + case clustering-it-provider { + when "/config:modules/config:module/config:type = 'clustering-it-provider'"; + + container rpc-registry { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity mdsal:binding-rpc-registry; + } + } + } + + container notification-service { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity mdsal:binding-notification-service; + } + } + } + + container data-broker { + uses config:service-ref { + refine type { + mandatory false; + config:required-identity mdsal:binding-async-data-broker; + } + } + } + } + } +} diff --git a/opendaylight/md-sal/samples/pom.xml b/opendaylight/md-sal/samples/pom.xml index ae7d323480..d13200e4e8 100644 --- a/opendaylight/md-sal/samples/pom.xml +++ b/opendaylight/md-sal/samples/pom.xml @@ -17,6 +17,7 @@ toaster-provider toaster-config l2switch + clustering-test-app scm:git:ssh://git.opendaylight.org:29418/controller.git diff --git a/opendaylight/netconf/netconf-client/pom.xml b/opendaylight/netconf/netconf-client/pom.xml index bf27ed6f4d..6bb67d0681 100644 --- a/opendaylight/netconf/netconf-client/pom.xml +++ b/opendaylight/netconf/netconf-client/pom.xml @@ -24,6 +24,12 @@ ${project.groupId} netconf-util + + ${project.groupId} + netconf-util + test-jar + test + com.google.guava guava @@ -36,6 +42,10 @@ org.slf4j slf4j-api + + org.opendaylight.yangtools + mockito-configuration + diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java index e2ac49c3ef..cbbee1f655 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.netconf.client; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import io.netty.channel.Channel; @@ -70,8 +71,8 @@ public class NetconfClientSessionNegotiator extends logger.debug("Netconf session {} should use exi.", session); NetconfStartExiMessage startExiMessage = (NetconfStartExiMessage) sessionPreferences.getStartExiMessage(); tryToInitiateExi(session, startExiMessage); - // Exi is not supported, release session immediately } else { + // Exi is not supported, release session immediately logger.debug("Netconf session {} isn't capable of using exi.", session); negotiationSuccessful(session); } @@ -117,6 +118,7 @@ public class NetconfClientSessionNegotiator extends private long extractSessionId(final Document doc) { final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, doc, XPathConstants.NODE); + Preconditions.checkState(sessionIdNode != null, ""); String textContent = sessionIdNode.getTextContent(); if (textContent == null || textContent.equals("")) { throw new IllegalStateException("Session id not received from server"); diff --git a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientConfigurationTest.java b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientConfigurationTest.java new file mode 100644 index 0000000000..592cdad4c1 --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientConfigurationTest.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.netconf.client; + +import com.google.common.base.Optional; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.netconf.client.NetconfClientSessionListener; +import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionListener; +import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; +import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder; +import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; +import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader; +import org.opendaylight.protocol.framework.ReconnectStrategy; + +import java.net.InetSocketAddress; + +public class NetconfClientConfigurationTest { + @Test + public void testNetconfClientConfiguration() throws Exception { + Long timeout = 200L; + NetconfHelloMessageAdditionalHeader header = new NetconfHelloMessageAdditionalHeader("a", "host", "port", "trans", "id"); + NetconfClientSessionListener listener = new SimpleNetconfClientSessionListener(); + InetSocketAddress address = InetSocketAddress.createUnresolved("host", 830); + ReconnectStrategy strategy = Mockito.mock(ReconnectStrategy.class); + AuthenticationHandler handler = Mockito.mock(AuthenticationHandler.class); + NetconfClientConfiguration cfg = NetconfClientConfigurationBuilder.create(). + withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH). + withAddress(address). + withConnectionTimeoutMillis(timeout). + withReconnectStrategy(strategy). + withAdditionalHeader(header). + withSessionListener(listener). + withAuthHandler(handler).build(); + + Assert.assertEquals(timeout, cfg.getConnectionTimeoutMillis()); + Assert.assertEquals(Optional.fromNullable(header), cfg.getAdditionalHeader()); + Assert.assertEquals(listener, cfg.getSessionListener()); + Assert.assertEquals(handler, cfg.getAuthHandler()); + Assert.assertEquals(strategy, cfg.getReconnectStrategy()); + Assert.assertEquals(NetconfClientConfiguration.NetconfClientProtocol.SSH, cfg.getProtocol()); + Assert.assertEquals(address, cfg.getAddress()); + } +} diff --git a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcherImplTest.java b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcherImplTest.java new file mode 100644 index 0000000000..5a2ec5656f --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcherImplTest.java @@ -0,0 +1,104 @@ +/* + * 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.netconf.client; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelPromise; +import io.netty.channel.EventLoopGroup; +import io.netty.util.HashedWheelTimer; +import io.netty.util.Timer; +import io.netty.util.concurrent.GenericFutureListener; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl; +import org.opendaylight.controller.netconf.client.NetconfClientSessionListener; +import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionListener; +import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; +import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder; +import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration; +import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder; +import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; +import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader; +import org.opendaylight.protocol.framework.ReconnectStrategy; +import org.opendaylight.protocol.framework.ReconnectStrategyFactory; + +import java.net.InetSocketAddress; +import java.util.concurrent.Future; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; + +public class NetconfClientDispatcherImplTest { + @Test + public void testNetconfClientDispatcherImpl() throws Exception { + EventLoopGroup bossGroup = Mockito.mock(EventLoopGroup.class); + EventLoopGroup workerGroup = Mockito.mock(EventLoopGroup.class); + Timer timer = new HashedWheelTimer(); + + ChannelFuture chf = Mockito.mock(ChannelFuture.class); + Channel ch = Mockito.mock(Channel.class); + doReturn(ch).when(chf).channel(); + Throwable thr = Mockito.mock(Throwable.class); + doReturn(chf).when(workerGroup).register(any(Channel.class)); + + ChannelPromise promise = Mockito.mock(ChannelPromise.class); + doReturn(promise).when(chf).addListener(any(GenericFutureListener.class)); + doReturn(thr).when(chf).cause(); + + Long timeout = 200L; + NetconfHelloMessageAdditionalHeader header = new NetconfHelloMessageAdditionalHeader("a", "host", "port", "trans", "id"); + NetconfClientSessionListener listener = new SimpleNetconfClientSessionListener(); + InetSocketAddress address = InetSocketAddress.createUnresolved("host", 830); + ReconnectStrategyFactory reconnectStrategyFactory = Mockito.mock(ReconnectStrategyFactory.class); + AuthenticationHandler handler = Mockito.mock(AuthenticationHandler.class); + ReconnectStrategy reconnect = Mockito.mock(ReconnectStrategy.class); + + doReturn(5).when(reconnect).getConnectTimeout(); + doReturn("").when(reconnect).toString(); + doReturn("").when(handler).toString(); + doReturn("").when(reconnectStrategyFactory).toString(); + doReturn(reconnect).when(reconnectStrategyFactory).createReconnectStrategy(); + + NetconfReconnectingClientConfiguration cfg = NetconfReconnectingClientConfigurationBuilder.create(). + withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH). + withAddress(address). + withConnectionTimeoutMillis(timeout). + withReconnectStrategy(reconnect). + withAdditionalHeader(header). + withSessionListener(listener). + withConnectStrategyFactory(reconnectStrategyFactory). + withAuthHandler(handler).build(); + + NetconfReconnectingClientConfiguration cfg2 = NetconfReconnectingClientConfigurationBuilder.create(). + withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP). + withAddress(address). + withConnectionTimeoutMillis(timeout). + withReconnectStrategy(reconnect). + withAdditionalHeader(header). + withSessionListener(listener). + withConnectStrategyFactory(reconnectStrategyFactory). + withAuthHandler(handler).build(); + + NetconfClientDispatcherImpl dispatcher = new NetconfClientDispatcherImpl(bossGroup, workerGroup, timer); + Future sshSession = dispatcher.createClient(cfg); + Future tcpSession = dispatcher.createClient(cfg2); + + Future sshReconn = dispatcher.createReconnectingClient(cfg); + Future tcpReconn = dispatcher.createReconnectingClient(cfg2); + + assertNotNull(sshSession); + assertNotNull(tcpSession); + assertNotNull(sshReconn); + assertNotNull(tcpReconn); + + } +} diff --git a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactoryTest.java b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactoryTest.java new file mode 100644 index 0000000000..0557a0c268 --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactoryTest.java @@ -0,0 +1,43 @@ +/* + * 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.netconf.client; + +import com.google.common.base.Optional; +import io.netty.channel.Channel; +import io.netty.util.HashedWheelTimer; +import io.netty.util.Timer; +import io.netty.util.concurrent.Promise; +import org.apache.sshd.common.SessionListener; +import org.junit.Test; +import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader; +import org.opendaylight.protocol.framework.SessionListenerFactory; +import org.opendaylight.protocol.framework.SessionNegotiator; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +public class NetconfClientSessionNegotiatorFactoryTest { + @Test + public void testGetSessionNegotiator() throws Exception { + NetconfClientSessionListener sessionListener = mock(NetconfClientSessionListener.class); + Timer timer = new HashedWheelTimer(); + SessionListenerFactory listenerFactory = mock(SessionListenerFactory.class); + doReturn(sessionListener).when(listenerFactory).getSessionListener(); + + Channel channel = mock(Channel.class); + Promise promise = mock(Promise.class); + NetconfClientSessionNegotiatorFactory negotiatorFactory = new NetconfClientSessionNegotiatorFactory(timer, + Optional.absent(), 200L); + + SessionNegotiator sessionNegotiator = negotiatorFactory.getSessionNegotiator(listenerFactory, channel, promise); + assertNotNull(sessionNegotiator); + } +} diff --git a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorTest.java b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorTest.java new file mode 100644 index 0000000000..333e9deae4 --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorTest.java @@ -0,0 +1,155 @@ +/* + * 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.netconf.client; + +import com.google.common.base.Optional; +import io.netty.channel.*; +import io.netty.handler.ssl.SslHandler; +import io.netty.util.HashedWheelTimer; +import io.netty.util.concurrent.GenericFutureListener; +import io.netty.util.concurrent.Promise; +import org.apache.mina.handler.demux.ExceptionHandler; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.internal.util.collections.Sets; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.opendaylight.controller.netconf.api.NetconfClientSessionPreferences; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import io.netty.util.Timer; +import org.opendaylight.controller.netconf.nettyutil.handler.ChunkedFramingMechanismEncoder; +import org.opendaylight.controller.netconf.nettyutil.handler.NetconfXMLToHelloMessageDecoder; +import org.opendaylight.controller.netconf.nettyutil.handler.NetconfXMLToMessageDecoder; +import org.opendaylight.controller.netconf.nettyutil.handler.exi.NetconfStartExiMessage; +import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage; +import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader; +import org.opendaylight.controller.netconf.util.test.XmlFileLoader; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.openexi.proc.common.EXIOptions; +import org.w3c.dom.Document; +import java.util.Set; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; + +public class NetconfClientSessionNegotiatorTest { + + private NetconfHelloMessage helloMessage; + private ChannelPipeline pipeline; + private ChannelFuture future; + private Channel channel; + private ChannelInboundHandlerAdapter channelInboundHandlerAdapter; + + @Before + public void setUp() throws Exception { + helloMessage = NetconfHelloMessage.createClientHello(Sets.newSet("exi:1.0"), Optional.absent()); + pipeline = mockChannelPipeline(); + future = mockChannelFuture(); + channel = mockChannel(); + System.out.println("setup done"); + } + + private ChannelHandler mockChannelHandler() { + ChannelHandler handler = mock(ChannelHandler.class); + return handler; + } + + private Channel mockChannel() { + Channel channel = mock(Channel.class); + ChannelHandler channelHandler = mockChannelHandler(); + doReturn("").when(channel).toString(); + doReturn(future).when(channel).close(); + doReturn(future).when(channel).writeAndFlush(anyObject()); + doReturn(true).when(channel).isOpen(); + doReturn(pipeline).when(channel).pipeline(); + doReturn("").when(pipeline).toString(); + doReturn(pipeline).when(pipeline).remove(any(ChannelHandler.class)); + doReturn(channelHandler).when(pipeline).remove(anyString()); + return channel; + } + + private ChannelFuture mockChannelFuture() { + ChannelFuture future = mock(ChannelFuture.class); + doReturn(future).when(future).addListener(any(GenericFutureListener.class)); + return future; + } + + private ChannelPipeline mockChannelPipeline() { + ChannelPipeline pipeline = mock(ChannelPipeline.class); + ChannelHandler handler = mock(ChannelHandler.class); + doReturn(pipeline).when(pipeline).addAfter(anyString(), anyString(), any(ChannelHandler.class)); + doReturn(null).when(pipeline).get(SslHandler.class); + doReturn(pipeline).when(pipeline).addLast(anyString(), any(ChannelHandler.class)); + doReturn(handler).when(pipeline).replace(anyString(), anyString(), any(ChunkedFramingMechanismEncoder.class)); + + NetconfXMLToHelloMessageDecoder messageDecoder = new NetconfXMLToHelloMessageDecoder(); + doReturn(messageDecoder).when(pipeline).replace(anyString(), anyString(), any(NetconfXMLToMessageDecoder.class)); + doReturn(pipeline).when(pipeline).replace(any(ChannelHandler.class), anyString(), any(NetconfClientSession.class)); + return pipeline; + } + + private NetconfClientSessionNegotiator createNetconfClientSessionNegotiator(Promise promise, + NetconfMessage startExi) { + ChannelProgressivePromise progressivePromise = mock(ChannelProgressivePromise.class); + NetconfClientSessionPreferences preferences = new NetconfClientSessionPreferences(helloMessage, startExi); + doReturn(progressivePromise).when(promise).setFailure(any(Throwable.class)); + + long timeout = 10L; + NetconfClientSessionListener sessionListener = mock(NetconfClientSessionListener.class); + Timer timer = new HashedWheelTimer(); + return new NetconfClientSessionNegotiator(preferences, promise, channel, timer, sessionListener, timeout); + } + + @Test + public void testNetconfClientSessionNegotiator() throws Exception { + Promise promise = mock(Promise.class); + doReturn(promise).when(promise).setSuccess(anyObject()); + NetconfClientSessionNegotiator negotiator = createNetconfClientSessionNegotiator(promise, null); + + negotiator.channelActive(null); + Set caps = Sets.newSet("a", "b"); + NetconfHelloMessage helloServerMessage = NetconfHelloMessage.createServerHello(caps, 10); + negotiator.handleMessage(helloServerMessage); + verify(promise).setSuccess(anyObject()); + } + + @Test + public void testNetconfClientSessionNegotiatorWithEXI() throws Exception { + Promise promise = mock(Promise.class); + EXIOptions exiOptions = new EXIOptions(); + NetconfStartExiMessage exiMessage = NetconfStartExiMessage.create(exiOptions, "msg-id"); + doReturn(promise).when(promise).setSuccess(anyObject()); + NetconfClientSessionNegotiator negotiator = createNetconfClientSessionNegotiator(promise, exiMessage); + + negotiator.channelActive(null); + Set caps = Sets.newSet("exi:1.0"); + NetconfHelloMessage helloMessage = NetconfHelloMessage.createServerHello(caps, 10); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + channelInboundHandlerAdapter = ((ChannelInboundHandlerAdapter) invocationOnMock.getArguments()[2]); + return null; + } + }).when(pipeline).addAfter(anyString(), anyString(), any(ChannelHandler.class)); + + ChannelHandlerContext handlerContext = mock(ChannelHandlerContext.class); + doReturn(pipeline).when(handlerContext).pipeline(); + negotiator.handleMessage(helloMessage); + Document expectedResult = XmlFileLoader.xmlFileToDocument("netconfMessages/rpc-reply_ok.xml"); + channelInboundHandlerAdapter.channelRead(handlerContext, new NetconfMessage(expectedResult)); + + verify(promise).setSuccess(anyObject()); + + // two calls for exiMessage, 2 for hello message + verify(pipeline, times(4)).replace(anyString(), anyString(), any(ChannelHandler.class)); + } +} diff --git a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionTest.java b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionTest.java new file mode 100644 index 0000000000..4175190e14 --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionTest.java @@ -0,0 +1,69 @@ +/* + * 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.netconf.client; + +import com.google.common.collect.Lists; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelPipeline; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.netconf.client.NetconfClientSession; +import org.opendaylight.controller.netconf.client.NetconfClientSessionListener; +import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXICodec; +import org.openexi.proc.common.EXIOptions; + +import java.util.ArrayList; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; + +public class NetconfClientSessionTest { + + @Mock + ChannelHandler channelHandler; + + @Mock + Channel channel; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testNetconfClientSession() throws Exception { + NetconfClientSessionListener sessionListener = mock(NetconfClientSessionListener.class); + long sessId = 20L; + Collection caps = Lists.newArrayList("cap1", "cap2"); + + NetconfEXICodec codec = new NetconfEXICodec(new EXIOptions()); + ChannelPipeline pipeline = mock(ChannelPipeline.class); + + Mockito.doReturn(pipeline).when(channel).pipeline(); + Mockito.doReturn(channelHandler).when(pipeline).replace(anyString(), anyString(), any(ChannelHandler.class)); + Mockito.doReturn("").when(channelHandler).toString(); + + NetconfClientSession session = new NetconfClientSession(sessionListener, channel, sessId, caps); + session.addExiHandlers(codec); + session.stopExiCommunication(); + + assertEquals(caps, session.getServerCapabilities()); + assertEquals(session, session.thisInstance()); + + Mockito.verify(pipeline, Mockito.times(4)).replace(anyString(), anyString(), Mockito.any(ChannelHandler.class)); + } +} diff --git a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfReconnectingClientConfigurationTest.java b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfReconnectingClientConfigurationTest.java new file mode 100644 index 0000000000..e79a370ec7 --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfReconnectingClientConfigurationTest.java @@ -0,0 +1,58 @@ +/* + * 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.netconf.client; + +import com.google.common.base.Optional; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.config.yang.protocol.framework.NeverReconnectStrategyFactoryModule; +import org.opendaylight.controller.netconf.client.NetconfClientSessionListener; +import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionListener; +import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; +import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration; +import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder; +import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; +import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader; +import org.opendaylight.protocol.framework.ReconnectStrategy; +import org.opendaylight.protocol.framework.ReconnectStrategyFactory; + +import java.net.InetSocketAddress; + +public class NetconfReconnectingClientConfigurationTest { + @Test + public void testNetconfReconnectingClientConfiguration() throws Exception { + Long timeout = 200L; + NetconfHelloMessageAdditionalHeader header = new NetconfHelloMessageAdditionalHeader("a", "host", "port", "trans", "id"); + NetconfClientSessionListener listener = new SimpleNetconfClientSessionListener(); + InetSocketAddress address = InetSocketAddress.createUnresolved("host", 830); + ReconnectStrategyFactory strategy = Mockito.mock(ReconnectStrategyFactory.class); + AuthenticationHandler handler = Mockito.mock(AuthenticationHandler.class); + ReconnectStrategy reconnect = Mockito.mock(ReconnectStrategy.class); + + NetconfReconnectingClientConfiguration cfg = NetconfReconnectingClientConfigurationBuilder.create(). + withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH). + withAddress(address). + withConnectionTimeoutMillis(timeout). + withReconnectStrategy(reconnect). + withAdditionalHeader(header). + withSessionListener(listener). + withConnectStrategyFactory(strategy). + withAuthHandler(handler).build(); + + Assert.assertEquals(timeout, cfg.getConnectionTimeoutMillis()); + Assert.assertEquals(Optional.fromNullable(header), cfg.getAdditionalHeader()); + Assert.assertEquals(listener, cfg.getSessionListener()); + Assert.assertEquals(handler, cfg.getAuthHandler()); + Assert.assertEquals(strategy, cfg.getConnectStrategyFactory()); + Assert.assertEquals(NetconfClientConfiguration.NetconfClientProtocol.SSH, cfg.getProtocol()); + Assert.assertEquals(address, cfg.getAddress()); + Assert.assertEquals(reconnect, cfg.getReconnectStrategy()); + } +} diff --git a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/SimpleNetconfClientSessionListenerTest.java b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/SimpleNetconfClientSessionListenerTest.java new file mode 100644 index 0000000000..e067cc225f --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/SimpleNetconfClientSessionListenerTest.java @@ -0,0 +1,79 @@ +/* + * 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.netconf.client; +import io.netty.channel.*; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.Promise; +import org.junit.Before; +import org.junit.Test; +import org.mockito.internal.util.collections.Sets; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage; + +import java.util.Set; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.*; + +public class SimpleNetconfClientSessionListenerTest { + + private Channel channel; + private ChannelFuture channelFuture; + Set caps; + private NetconfHelloMessage helloMessage; + private NetconfMessage message; + private NetconfClientSessionListener sessionListener; + private NetconfClientSession clientSession; + + @Before + public void setUp() throws Exception { + channel = mock(Channel.class); + channelFuture = mock(ChannelFuture.class); + doReturn(channelFuture).when(channel).writeAndFlush(anyObject()); + caps = Sets.newSet("a", "b"); + helloMessage = NetconfHelloMessage.createServerHello(caps, 10); + message = new NetconfMessage(helloMessage.getDocument()); + sessionListener = mock(NetconfClientSessionListener.class); + clientSession = new NetconfClientSession(sessionListener, channel, 20L, caps); + } + + @Test + public void testSessionDown() throws Exception { + SimpleNetconfClientSessionListener simpleListener = new SimpleNetconfClientSessionListener(); + Future promise = simpleListener.sendRequest(message); + simpleListener.onSessionUp(clientSession); + verify(channel, times(1)).writeAndFlush(anyObject()); + + simpleListener.onSessionDown(clientSession, new Exception()); + assertFalse(promise.isSuccess()); + } + + @Test + public void testSendRequest() throws Exception { + SimpleNetconfClientSessionListener simpleListener = new SimpleNetconfClientSessionListener(); + Future promise = simpleListener.sendRequest(message); + simpleListener.onSessionUp(clientSession); + verify(channel, times(1)).writeAndFlush(anyObject()); + + simpleListener.sendRequest(message); + assertFalse(promise.isSuccess()); + } + + @Test + public void testOnMessage() throws Exception { + SimpleNetconfClientSessionListener simpleListener = new SimpleNetconfClientSessionListener(); + Future promise = simpleListener.sendRequest(message); + simpleListener.onSessionUp(clientSession); + verify(channel, times(1)).writeAndFlush(anyObject()); + + simpleListener.onMessage(clientSession, message); + assertTrue(promise.isSuccess()); + } +} diff --git a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/SshClientChannelInitializerTest.java b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/SshClientChannelInitializerTest.java new file mode 100644 index 0000000000..0830c2967b --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/SshClientChannelInitializerTest.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.netconf.client; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelPipeline; +import io.netty.util.concurrent.Promise; +import org.junit.Test; +import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; +import org.opendaylight.protocol.framework.SessionListenerFactory; +import org.opendaylight.protocol.framework.SessionNegotiator; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; + +public class SshClientChannelInitializerTest { + @Test + public void test() throws Exception { + + AuthenticationHandler authenticationHandler = mock(AuthenticationHandler.class); + NetconfClientSessionNegotiatorFactory negotiatorFactory = mock(NetconfClientSessionNegotiatorFactory.class); + NetconfClientSessionListener sessionListener = mock(NetconfClientSessionListener.class); + + SessionNegotiator sessionNegotiator = mock(SessionNegotiator.class); + doReturn("").when(sessionNegotiator).toString(); + doReturn(sessionNegotiator).when(negotiatorFactory).getSessionNegotiator(any(SessionListenerFactory.class), any(Channel.class), any(Promise.class)); + ChannelPipeline pipeline = mock(ChannelPipeline.class); + doReturn(pipeline).when(pipeline).addAfter(anyString(), anyString(), any(ChannelHandler.class)); + Channel channel = mock(Channel.class); + doReturn(pipeline).when(channel).pipeline(); + doReturn("").when(channel).toString(); + doReturn(pipeline).when(pipeline).addFirst(any(ChannelHandler.class)); + doReturn(pipeline).when(pipeline).addLast(anyString(), any(ChannelHandler.class)); + + Promise promise = mock(Promise.class); + doReturn("").when(promise).toString(); + + SshClientChannelInitializer initializer = new SshClientChannelInitializer(authenticationHandler, negotiatorFactory, + sessionListener); + initializer.initialize(channel, promise); + verify(pipeline, times(1)).addFirst(any(ChannelHandler.class)); + } +} diff --git a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/TcpClientChannelInitializerTest.java b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/TcpClientChannelInitializerTest.java new file mode 100644 index 0000000000..e355cf45e7 --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/TcpClientChannelInitializerTest.java @@ -0,0 +1,43 @@ +/* + * 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.netconf.client; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelPipeline; +import io.netty.util.concurrent.Promise; +import org.junit.Test; +import org.opendaylight.controller.netconf.nettyutil.AbstractChannelInitializer; +import org.opendaylight.protocol.framework.SessionListenerFactory; +import org.opendaylight.protocol.framework.SessionNegotiator; + +import static org.mockito.Mockito.*; + +public class TcpClientChannelInitializerTest { + @Test + public void testInitializeSessionNegotiator() throws Exception { + NetconfClientSessionNegotiatorFactory factory = mock(NetconfClientSessionNegotiatorFactory.class); + SessionNegotiator sessionNegotiator = mock(SessionNegotiator.class); + doReturn("").when(sessionNegotiator).toString(); + doReturn(sessionNegotiator).when(factory).getSessionNegotiator(any(SessionListenerFactory.class), any(Channel.class), any(Promise.class)); + NetconfClientSessionListener listener = mock(NetconfClientSessionListener.class); + TcpClientChannelInitializer initializer = new TcpClientChannelInitializer(factory, listener); + ChannelPipeline pipeline = mock(ChannelPipeline.class); + doReturn(pipeline).when(pipeline).addAfter(anyString(), anyString(), any(ChannelHandler.class)); + Channel channel = mock(Channel.class); + doReturn(pipeline).when(channel).pipeline(); + doReturn("").when(channel).toString(); + + Promise promise = mock(Promise.class); + doReturn("").when(promise).toString(); + + initializer.initializeSessionNegotiator(channel, promise); + verify(pipeline, times(1)).addAfter(anyString(), anyString(), any(ChannelHandler.class)); + } +} diff --git a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/test/TestingNetconfClient.java b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/TestingNetconfClient.java similarity index 92% rename from opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/test/TestingNetconfClient.java rename to opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/TestingNetconfClient.java index 18ed18e4ae..d7209d9295 100644 --- a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/test/TestingNetconfClient.java +++ b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/TestingNetconfClient.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.netconf.client.test; +package org.opendaylight.controller.netconf.client; import com.google.common.base.Optional; import com.google.common.base.Preconditions; @@ -26,11 +26,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.opendaylight.controller.netconf.api.NetconfMessage; -import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; -import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl; -import org.opendaylight.controller.netconf.client.NetconfClientSession; -import org.opendaylight.controller.netconf.client.NetconfClientSessionListener; -import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionListener; import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol; import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder; diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java index c5281d01f8..5f8bc06e10 100644 --- a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java @@ -59,7 +59,7 @@ import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl; import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionListener; import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder; -import org.opendaylight.controller.netconf.client.test.TestingNetconfClient; +import org.opendaylight.controller.netconf.client.TestingNetconfClient; import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl; import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService; import org.opendaylight.controller.netconf.mapping.api.Capability; diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfConfigPersisterITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfConfigPersisterITTest.java index 4b49c0928b..d8eb841a79 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfConfigPersisterITTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfConfigPersisterITTest.java @@ -36,7 +36,7 @@ import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import org.opendaylight.controller.config.persist.api.Persister; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification; -import org.opendaylight.controller.netconf.client.test.TestingNetconfClient; +import org.opendaylight.controller.netconf.client.TestingNetconfClient; import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl; import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshotImpl; diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITMonitoringTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITMonitoringTest.java index 72a2f8f7ac..a9558c06cd 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITMonitoringTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITMonitoringTest.java @@ -28,7 +28,7 @@ import java.util.Set; import org.junit.Test; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession; -import org.opendaylight.controller.netconf.client.test.TestingNetconfClient; +import org.opendaylight.controller.netconf.client.TestingNetconfClient; import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl; import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshotImpl; import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService; diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java index 67ccf0c02c..4fe5f2a950 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java @@ -34,7 +34,7 @@ import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl; import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionListener; import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder; -import org.opendaylight.controller.netconf.client.test.TestingNetconfClient; +import org.opendaylight.controller.netconf.client.TestingNetconfClient; import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword; import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java index a7a9d7494a..4c0730863f 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java @@ -41,7 +41,7 @@ import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleMX import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; -import org.opendaylight.controller.netconf.client.test.TestingNetconfClient; +import org.opendaylight.controller.netconf.client.TestingNetconfClient; import org.opendaylight.controller.netconf.util.test.XmlFileLoader; import org.opendaylight.controller.netconf.util.xml.XmlElement; import org.opendaylight.controller.netconf.util.xml.XmlUtil; diff --git a/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java b/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java index d0d587fb84..08441b4ce5 100644 --- a/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java +++ b/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java @@ -38,7 +38,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types. public class JaxBSerializerTest { @Test - public void testName() throws Exception { + public void testSerialization() throws Exception { final NetconfMonitoringService service = new NetconfMonitoringService() { @@ -53,29 +53,29 @@ public class JaxBSerializerTest { } }; final NetconfState model = new NetconfState(service); - final String xml = XmlUtil.toString(new JaxBSerializer().toXml(model)); + final String xml = XmlUtil.toString(new JaxBSerializer().toXml(model)).replaceAll("\\s", ""); assertThat(xml, CoreMatchers.containsString( - "\n" + - "yang\n" + - "id\n" + - "NETCONF\n" + - "localhost\n" + - "v1\n" + - "\n")); + "" + + "yang" + + "id" + + "NETCONF" + + "localhost" + + "v1" + + "")); assertThat(xml, CoreMatchers.containsString( - "\n" + - "1\n" + - "0\n" + - "0\n" + - "loginTime\n" + - "0\n" + - "0\n" + - "client\n" + - "address/port\n" + - "ncme:netconf-tcp\n" + - "username\n" + + "" + + "1" + + "0" + + "0" + + "loginTime" + + "0" + + "0" + + "client" + + "address/port" + + "ncme:netconf-tcp" + + "username" + "")); } diff --git a/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfXMLToHelloMessageDecoderTest.java b/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfXMLToHelloMessageDecoderTest.java index f0c0d6341b..ac6370685a 100644 --- a/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfXMLToHelloMessageDecoderTest.java +++ b/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfXMLToHelloMessageDecoderTest.java @@ -36,7 +36,7 @@ public class NetconfXMLToHelloMessageDecoderTest { assertThat(out.get(0), CoreMatchers.instanceOf(NetconfHelloMessage.class)); final NetconfHelloMessage hello = (NetconfHelloMessage) out.get(0); assertTrue(hello.getAdditionalHeader().isPresent()); - assertEquals("[tomas;10.0.0.0:10000;tcp;client;]\n", hello.getAdditionalHeader().get().toFormattedString()); + assertEquals("[tomas;10.0.0.0:10000;tcp;client;]" + System.lineSeparator(), hello.getAdditionalHeader().get().toFormattedString()); assertThat(XmlUtil.toString(hello.getDocument()), CoreMatchers.containsString(" moduleBuilders, final boolean exi) { + private NetconfServerDispatcher createDispatcher(final Map moduleBuilders, final boolean exi, final int generateConfigsTimeout) { final Set capabilities = Sets.newHashSet(Collections2.transform(moduleBuilders.keySet(), new Function() { @Override @@ -115,7 +115,7 @@ public class NetconfDeviceSimulator implements Closeable { : Sets.newHashSet(XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0, XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1); final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory( - hashedWheelTimer, simulatedOperationProvider, idProvider, CONNECTION_TIMEOUT_MILLIS, commitNotifier, new LoggingMonitoringService(), serverCapabilities); + hashedWheelTimer, simulatedOperationProvider, idProvider, generateConfigsTimeout, commitNotifier, new LoggingMonitoringService(), serverCapabilities); final NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer( serverNegotiatorFactory); @@ -153,7 +153,7 @@ public class NetconfDeviceSimulator implements Closeable { public List start(final Main.Params params) { final Map moduleBuilders = parseSchemasToModuleBuilders(params); - final NetconfServerDispatcher dispatcher = createDispatcher(moduleBuilders, params.exi); + final NetconfServerDispatcher dispatcher = createDispatcher(moduleBuilders, params.exi, params.generateConfigsTimeout); int currentPort = params.startingPort; diff --git a/opendaylight/netconf/netconf-util/pom.xml b/opendaylight/netconf/netconf-util/pom.xml index df4d389705..bed58beb0f 100644 --- a/opendaylight/netconf/netconf-util/pom.xml +++ b/opendaylight/netconf/netconf-util/pom.xml @@ -46,6 +46,10 @@ xmlunit test + + org.opendaylight.yangtools + mockito-configuration + diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfHelloMessage.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfHelloMessage.java index 33934d10ba..15223cb60b 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfHelloMessage.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfHelloMessage.java @@ -64,10 +64,12 @@ public final class NetconfHelloMessage extends NetconfMessage { Document doc = XmlUtil.newDocument(); Element helloElement = doc.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, HELLO_TAG); - Element capabilitiesElement = doc.createElement(XmlNetconfConstants.CAPABILITIES); + Element capabilitiesElement = doc.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, + XmlNetconfConstants.CAPABILITIES); for (String capability : Sets.newHashSet(capabilities)) { - Element capElement = doc.createElement(XmlNetconfConstants.CAPABILITY); + Element capElement = doc.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, + XmlNetconfConstants.CAPABILITY); capElement.setTextContent(capability); capabilitiesElement.appendChild(capElement); } @@ -80,7 +82,8 @@ public final class NetconfHelloMessage extends NetconfMessage { public static NetconfHelloMessage createServerHello(Set capabilities, long sessionId) throws NetconfDocumentedException { Document doc = createHelloMessageDoc(capabilities); - Element sessionIdElement = doc.createElement(XmlNetconfConstants.SESSION_ID); + Element sessionIdElement = doc.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, + XmlNetconfConstants.SESSION_ID); sessionIdElement.setTextContent(Long.toString(sessionId)); doc.getDocumentElement().appendChild(sessionIdElement); return new NetconfHelloMessage(doc); diff --git a/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/CloseableUtilTest.java b/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/CloseableUtilTest.java new file mode 100644 index 0000000000..8d41ad7607 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/CloseableUtilTest.java @@ -0,0 +1,44 @@ +/* + * 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.netconf.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; + +import com.google.common.collect.Lists; +import org.junit.Test; + +public class CloseableUtilTest { + + @Test + public void testCloseAllFail() throws Exception { + final AutoCloseable failingCloseable = new AutoCloseable() { + @Override + public void close() throws Exception { + throw new RuntimeException("testing failing close"); + } + }; + + try { + CloseableUtil.closeAll(Lists.newArrayList(failingCloseable, failingCloseable)); + fail("Exception with suppressed should be thrown"); + } catch (final RuntimeException e) { + assertEquals(1, e.getSuppressed().length); + } + } + + @Test + public void testCloseAll() throws Exception { + final AutoCloseable failingCloseable = mock(AutoCloseable.class); + doNothing().when(failingCloseable).close(); + CloseableUtil.closeAll(Lists.newArrayList(failingCloseable, failingCloseable)); + } +} \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/xml/HardcodedNamespaceResolverTest.java b/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/xml/HardcodedNamespaceResolverTest.java new file mode 100644 index 0000000000..f083cc1dbd --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/xml/HardcodedNamespaceResolverTest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import org.junit.Test; + +public class HardcodedNamespaceResolverTest { + + @Test + public void testResolver() throws Exception { + final HardcodedNamespaceResolver hardcodedNamespaceResolver = new HardcodedNamespaceResolver("prefix", "namespace"); + + assertEquals("namespace", hardcodedNamespaceResolver.getNamespaceURI("prefix")); + try{ + hardcodedNamespaceResolver.getNamespaceURI("unknown"); + fail("Unknown namespace lookup should fail"); + } catch(IllegalStateException e) {} + + assertNull(hardcodedNamespaceResolver.getPrefix("any")); + assertNull(hardcodedNamespaceResolver.getPrefixes("any")); + } +} \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/xml/XmlElementTest.java b/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/xml/XmlElementTest.java new file mode 100644 index 0000000000..a88de956e2 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/xml/XmlElementTest.java @@ -0,0 +1,144 @@ +/* + * 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.netconf.util.xml; + +import static org.hamcrest.CoreMatchers.both; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import com.google.common.base.Optional; + +public class XmlElementTest { + + private final String elementAsString = "" + + "" + + "deepValue" + + "" + + "innerNamespaceValue" + + "b:valueWithPrefix" + + ""; + private Document document; + private Element element; + private XmlElement xmlElement; + + @Before + public void setUp() throws Exception { + document = XmlUtil.readXmlToDocument(elementAsString); + element = document.getDocumentElement(); + xmlElement = XmlElement.fromDomElement(element); + } + + @Test + public void testConstruct() throws Exception { + final XmlElement fromString = XmlElement.fromString(elementAsString); + assertEquals(fromString, xmlElement); + XmlElement.fromDomDocument(document); + XmlElement.fromDomElement(element); + XmlElement.fromDomElementWithExpected(element, "top"); + XmlElement.fromDomElementWithExpected(element, "top", "namespace"); + + try { + XmlElement.fromString("notXml"); + fail(); + } catch (final NetconfDocumentedException e) {} + + try { + XmlElement.fromDomElementWithExpected(element, "notTop"); + fail(); + } catch (final NetconfDocumentedException e) {} + + try { + XmlElement.fromDomElementWithExpected(element, "top", "notNamespace"); + fail(); + } catch (final NetconfDocumentedException e) {} + } + + @Test + public void testGetters() throws Exception { + assertEquals(element, xmlElement.getDomElement()); + assertEquals(element.getElementsByTagName("inner").getLength(), xmlElement.getElementsByTagName("inner").getLength()); + + assertEquals("top", xmlElement.getName()); + assertTrue(xmlElement.hasNamespace()); + assertEquals("namespace", xmlElement.getNamespace()); + assertEquals("namespace", xmlElement.getNamespaceAttribute()); + assertEquals(Optional.of("namespace"), xmlElement.getNamespaceOptionally()); + + assertEquals("value1", xmlElement.getAttribute("attr1", "attrNamespace")); + assertEquals("value2", xmlElement.getAttribute("attr2")); + assertEquals(2 + 2/*Namespace definition*/, xmlElement.getAttributes().size()); + + assertEquals(3, xmlElement.getChildElements().size()); + assertEquals(1, xmlElement.getChildElements("inner").size()); + assertTrue(xmlElement.getOnlyChildElementOptionally("inner").isPresent()); + assertTrue(xmlElement.getOnlyChildElementWithSameNamespaceOptionally("inner").isPresent()); + assertEquals(0, xmlElement.getChildElements("unknown").size()); + assertFalse(xmlElement.getOnlyChildElementOptionally("unknown").isPresent()); + assertEquals(1, xmlElement.getChildElementsWithSameNamespace("inner").size()); + assertEquals(0, xmlElement.getChildElementsWithSameNamespace("innerNamespace").size()); + assertEquals(1, xmlElement.getChildElementsWithinNamespace("innerNamespace", "innerNamespace").size()); + assertTrue(xmlElement.getOnlyChildElementOptionally("innerNamespace", "innerNamespace").isPresent()); + assertFalse(xmlElement.getOnlyChildElementOptionally("innerNamespace", "unknownNamespace").isPresent()); + + final XmlElement noNamespaceElement = XmlElement.fromString(""); + assertFalse(noNamespaceElement.hasNamespace()); + try { + noNamespaceElement.getNamespace(); + fail(); + } catch (final MissingNameSpaceException e) {} + + final XmlElement inner = xmlElement.getOnlyChildElement("inner"); + final XmlElement deepInner = inner.getOnlyChildElementWithSameNamespaceOptionally().get(); + assertEquals(deepInner, inner.getOnlyChildElementWithSameNamespace()); + assertEquals(Optional.absent(), xmlElement.getOnlyChildElementOptionally("unknown")); + assertEquals("deepValue", deepInner.getTextContent()); + assertEquals("deepValue", deepInner.getOnlyTextContentOptionally().get()); + assertEquals("deepValue", deepInner.getOnlyTextContentOptionally().get()); + } + + @Test + public void testExtractNamespaces() throws Exception { + final XmlElement innerPrefixed = xmlElement.getOnlyChildElement("innerPrefixed"); + Map.Entry namespaceOfTextContent = innerPrefixed.findNamespaceOfTextContent(); + + assertNotNull(namespaceOfTextContent); + assertEquals("b", namespaceOfTextContent.getKey()); + assertEquals("prefixedValueNamespace", namespaceOfTextContent.getValue()); + final XmlElement innerNamespace = xmlElement.getOnlyChildElement("innerNamespace"); + namespaceOfTextContent = innerNamespace.findNamespaceOfTextContent(); + + assertEquals("", namespaceOfTextContent.getKey()); + assertEquals("innerNamespace", namespaceOfTextContent.getValue()); + } + + @Test + public void testUnrecognisedElements() throws Exception { + xmlElement.checkUnrecognisedElements(xmlElement.getOnlyChildElement("inner"), xmlElement.getOnlyChildElement("innerPrefixed"), xmlElement.getOnlyChildElement("innerNamespace")); + + try { + xmlElement.checkUnrecognisedElements(xmlElement.getOnlyChildElement("inner")); + fail(); + } catch (final NetconfDocumentedException e) { + assertThat(e.getMessage(), both(containsString("innerNamespace")).and(containsString("innerNamespace"))); + } + } +} diff --git a/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/xml/XmlUtilTest.java b/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/xml/XmlUtilTest.java new file mode 100644 index 0000000000..3796dd996a --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/xml/XmlUtilTest.java @@ -0,0 +1,74 @@ +/* + * 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.netconf.util.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.common.base.Optional; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import org.custommonkey.xmlunit.Diff; +import org.custommonkey.xmlunit.XMLUnit; +import org.junit.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXParseException; + +public class XmlUtilTest { + + private final String xml = "\n" + + "value\n" + + "prefix:value\n" + + "prefix:value\n" + + ""; + + @Test + public void testCreateElement() throws Exception { + final Document document = XmlUtil.newDocument(); + final Element top = XmlUtil.createElement(document, "top", Optional.of("namespace")); + + top.appendChild(XmlUtil.createTextElement(document, "innerText", "value", Optional.of("namespace"))); + top.appendChild(XmlUtil.createTextElementWithNamespacedContent(document, "innerPrefixedText", "pref", "prefixNamespace", "value", Optional.of("namespace"))); + top.appendChild(XmlUtil.createTextElementWithNamespacedContent(document, "innerPrefixedText", "pref", "prefixNamespace", "value", Optional.of("randomNamespace"))); + + document.appendChild(top); + assertEquals("top", XmlUtil.createDocumentCopy(document).getDocumentElement().getTagName()); + + XMLUnit.setIgnoreAttributeOrder(true); + XMLUnit.setIgnoreWhitespace(true); + + final Diff diff = XMLUnit.compareXML(XMLUnit.buildControlDocument(xml), document); + assertTrue(diff.toString(), diff.similar()); + } + + @Test + public void testLoadSchema() throws Exception { + XmlUtil.loadSchema(); + try { + XmlUtil.loadSchema(getClass().getResourceAsStream("/netconfMessages/commit.xml")); + fail("Input stream does not contain xsd"); + } catch (final IllegalStateException e) { + assertTrue(e.getCause() instanceof SAXParseException); + } + + } + + @Test + public void testXPath() throws Exception { + final XPathExpression correctXPath = XMLNetconfUtil.compileXPath("/top/innerText"); + try { + XMLNetconfUtil.compileXPath("!@(*&$!"); + fail("Incorrect xpath should fail"); + } catch (IllegalStateException e) {} + final Object value = XmlUtil.evaluateXPath(correctXPath, XmlUtil.readXmlToDocument("value"), XPathConstants.NODE); + assertEquals("value", ((Element) value).getTextContent()); + } +} \ No newline at end of file diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronLoadBalancerPool.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronLoadBalancerPool.java index 12c80fe70c..a2df680b07 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronLoadBalancerPool.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronLoadBalancerPool.java @@ -14,8 +14,8 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +//import javax.xml.bind.annotation.XmlElementWrapper; import java.io.Serializable; -import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -34,7 +34,7 @@ import java.util.List; * healthmonitor_id String * admin_state_up Bool * status String - * members List + * members List * http://docs.openstack.org/api/openstack-network/2.0/openstack-network.pdf */ @@ -71,13 +71,10 @@ public class NeutronLoadBalancerPool extends ConfigurationObject implements Seri @XmlElement (name="status") String loadBalancerPoolStatus; - @XmlElement (name="members") - List loadBalancerPoolMembers; - - HashMap member; + @XmlElement(name="members") + List loadBalancerPoolMembers; public NeutronLoadBalancerPool() { - member = new HashMap(); } public String getLoadBalancerPoolID() { @@ -152,14 +149,27 @@ public class NeutronLoadBalancerPool extends ConfigurationObject implements Seri this.loadBalancerPoolStatus = loadBalancerPoolStatus; } - public List getLoadBalancerPoolMembers() { + public List getLoadBalancerPoolMembers() { + /* + * Update the pool_id of the member to that this.loadBalancerPoolID + */ + for (NeutronLoadBalancerPoolMember member: loadBalancerPoolMembers) + member.setPoolID(loadBalancerPoolID); return loadBalancerPoolMembers; } - public void setLoadBalancerPoolMembers(List loadBalancerPoolMembers) { + public void setLoadBalancerPoolMembers(List loadBalancerPoolMembers) { this.loadBalancerPoolMembers = loadBalancerPoolMembers; } + public void addLoadBalancerPoolMember(NeutronLoadBalancerPoolMember loadBalancerPoolMember) { + this.loadBalancerPoolMembers.add(loadBalancerPoolMember); + } + + public void removeLoadBalancerPoolMember(NeutronLoadBalancerPoolMember loadBalancerPoolMember) { + this.loadBalancerPoolMembers.remove(loadBalancerPoolMember); + } + public NeutronLoadBalancerPool extractFields(List fields) { NeutronLoadBalancerPool ans = new NeutronLoadBalancerPool(); Iterator i = fields.iterator(); @@ -198,4 +208,4 @@ public class NeutronLoadBalancerPool extends ConfigurationObject implements Seri } return ans; } -} \ No newline at end of file +} diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronLoadBalancerPoolMember.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronLoadBalancerPoolMember.java index 577c3bb528..683d45fcf2 100644 --- a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronLoadBalancerPoolMember.java +++ b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronLoadBalancerPoolMember.java @@ -10,11 +10,18 @@ package org.opendaylight.controller.networkconfig.neutron; import org.opendaylight.controller.configuration.ConfigurationObject; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; import java.io.Serializable; import java.util.Iterator; import java.util.List; +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) + public class NeutronLoadBalancerPoolMember extends ConfigurationObject implements Serializable { private static final long serialVersionUID = 1L; @@ -46,9 +53,20 @@ public class NeutronLoadBalancerPoolMember extends ConfigurationObject implemen @XmlElement (name="status") String poolMemberStatus; + String poolID; + public NeutronLoadBalancerPoolMember() { } + @XmlTransient + public String getPoolID() { + return poolID; + } + + public void setPoolID(String poolID) { + this.poolID = poolID; + } + public String getPoolMemberID() { return poolMemberID; } @@ -121,6 +139,9 @@ public class NeutronLoadBalancerPoolMember extends ConfigurationObject implemen if (s.equals("id")) { ans.setPoolMemberID(this.getPoolMemberID()); } + if (s.equals("pool_id")) { + ans.setPoolID(this.getPoolID()); + } if (s.equals("tenant_id")) { ans.setPoolMemberTenantID(this.getPoolMemberTenantID()); } @@ -148,6 +169,7 @@ public class NeutronLoadBalancerPoolMember extends ConfigurationObject implemen @Override public String toString() { return "NeutronLoadBalancerPoolMember{" + "poolMemberID='" + poolMemberID + '\'' + + ", poolID='" + poolID + '\'' + ", poolMemberTenantID='" + poolMemberTenantID + '\'' + ", poolMemberAddress='" + poolMemberAddress + '\'' + ", poolMemberProtoPort=" + poolMemberProtoPort + diff --git a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerNorthbound.java b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerNorthbound.java index 748dffc8cf..863b3cbdc7 100644 --- a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerNorthbound.java +++ b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerNorthbound.java @@ -38,8 +38,8 @@ import java.util.Iterator; import java.util.List; /** - * Neutron Northbound REST APIs for LoadBalancer Policies.
    - * This class provides REST APIs for managing neutron LoadBalancer Policies + * Neutron Northbound REST APIs for LoadBalancers.
    + * This class provides REST APIs for managing neutron LoadBalancers * *
    *
    @@ -87,15 +87,13 @@ public class NeutronLoadBalancerNorthbound { @QueryParam("page_reverse") String pageReverse // sorting not supported ) { - INeutronLoadBalancerCRUD loadBalancerPoolInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerCRUD( - this); - // INeutronLoadBalancerRuleCRUD firewallRuleInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerRuleCRUD(this); + INeutronLoadBalancerCRUD loadBalancerInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerCRUD(this); - if (loadBalancerPoolInterface == null) { + if (loadBalancerInterface == null) { throw new ServiceUnavailableException("LoadBalancer CRUD Interface " + RestMessages.SERVICEUNAVAILABLE.toString()); } - List allLoadBalancers = loadBalancerPoolInterface.getAllNeutronLoadBalancers(); + List allLoadBalancers = loadBalancerInterface.getAllNeutronLoadBalancers(); // List allLoadBalancerRules = firewallRuleInterface.getAllNeutronLoadBalancerRules(); List ans = new ArrayList(); // List rules = new ArrayList(); @@ -128,7 +126,7 @@ public class NeutronLoadBalancerNorthbound { /** * Returns a specific LoadBalancer */ - @Path("{loadBalancerPoolID}") + @Path("{loadBalancerID}") @GET @Produces({ MediaType.APPLICATION_JSON }) @@ -137,25 +135,25 @@ public class NeutronLoadBalancerNorthbound { @ResponseCode(code = 401, condition = "Unauthorized"), @ResponseCode(code = 404, condition = "Not Found"), @ResponseCode(code = 501, condition = "Not Implemented") }) - public Response showLoadBalancer(@PathParam("loadBalancerPoolID") String loadBalancerPoolID, + public Response showLoadBalancer(@PathParam("loadBalancerID") String loadBalancerID, // return fields @QueryParam("fields") List fields) { - INeutronLoadBalancerCRUD loadBalancerPoolInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerCRUD( + INeutronLoadBalancerCRUD loadBalancerInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerCRUD( this); - if (loadBalancerPoolInterface == null) { + if (loadBalancerInterface == null) { throw new ServiceUnavailableException("LoadBalancer CRUD Interface " + RestMessages.SERVICEUNAVAILABLE.toString()); } - if (!loadBalancerPoolInterface.neutronLoadBalancerExists(loadBalancerPoolID)) { + if (!loadBalancerInterface.neutronLoadBalancerExists(loadBalancerID)) { throw new ResourceNotFoundException("LoadBalancer UUID does not exist."); } if (fields.size() > 0) { - NeutronLoadBalancer ans = loadBalancerPoolInterface.getNeutronLoadBalancer(loadBalancerPoolID); + NeutronLoadBalancer ans = loadBalancerInterface.getNeutronLoadBalancer(loadBalancerID); return Response.status(200).entity( new NeutronLoadBalancerRequest(extractFields(ans, fields))).build(); } else { - return Response.status(200).entity(new NeutronLoadBalancerRequest(loadBalancerPoolInterface.getNeutronLoadBalancer( - loadBalancerPoolID))).build(); + return Response.status(200).entity(new NeutronLoadBalancerRequest(loadBalancerInterface.getNeutronLoadBalancer( + loadBalancerID))).build(); } } @@ -175,9 +173,9 @@ public class NeutronLoadBalancerNorthbound { @ResponseCode(code = 409, condition = "Conflict"), @ResponseCode(code = 501, condition = "Not Implemented") }) public Response createLoadBalancers(final NeutronLoadBalancerRequest input) { - INeutronLoadBalancerCRUD loadBalancerPoolInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerCRUD( + INeutronLoadBalancerCRUD loadBalancerInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerCRUD( this); - if (loadBalancerPoolInterface == null) { + if (loadBalancerInterface == null) { throw new ServiceUnavailableException("LoadBalancer CRUD Interface " + RestMessages.SERVICEUNAVAILABLE.toString()); } @@ -187,11 +185,9 @@ public class NeutronLoadBalancerNorthbound { /* * Verify that the LoadBalancer doesn't already exist. */ - if (loadBalancerPoolInterface.neutronLoadBalancerExists(singleton.getLoadBalancerID())) { + if (loadBalancerInterface.neutronLoadBalancerExists(singleton.getLoadBalancerID())) { throw new BadRequestException("LoadBalancer UUID already exists"); } - loadBalancerPoolInterface.addNeutronLoadBalancer(singleton); - Object[] instances = ServiceHelper.getGlobalInstances(INeutronLoadBalancerAware.class, this, null); if (instances != null) { for (Object instance : instances) { @@ -202,7 +198,7 @@ public class NeutronLoadBalancerNorthbound { } } } - loadBalancerPoolInterface.addNeutronLoadBalancer(singleton); + loadBalancerInterface.addNeutronLoadBalancer(singleton); if (instances != null) { for (Object instance : instances) { INeutronLoadBalancerAware service = (INeutronLoadBalancerAware) instance; @@ -218,10 +214,10 @@ public class NeutronLoadBalancerNorthbound { NeutronLoadBalancer test = i.next(); /* - * Verify that the firewall policy doesn't already exist + * Verify that the loadbalancer doesn't already exist */ - if (loadBalancerPoolInterface.neutronLoadBalancerExists(test.getLoadBalancerID())) { + if (loadBalancerInterface.neutronLoadBalancerExists(test.getLoadBalancerID())) { throw new BadRequestException("Load Balancer Pool UUID already is already created"); } if (testMap.containsKey(test.getLoadBalancerID())) { @@ -243,7 +239,7 @@ public class NeutronLoadBalancerNorthbound { i = bulk.iterator(); while (i.hasNext()) { NeutronLoadBalancer test = i.next(); - loadBalancerPoolInterface.addNeutronLoadBalancer(test); + loadBalancerInterface.addNeutronLoadBalancer(test); if (instances != null) { for (Object instance : instances) { INeutronLoadBalancerAware service = (INeutronLoadBalancerAware) instance; @@ -258,7 +254,7 @@ public class NeutronLoadBalancerNorthbound { /** * Updates a LoadBalancer Policy */ - @Path("{loadBalancerPoolID}") + @Path("{loadBalancerID}") @PUT @Produces({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON }) @@ -271,10 +267,10 @@ public class NeutronLoadBalancerNorthbound { @ResponseCode(code = 404, condition = "Not Found"), @ResponseCode(code = 501, condition = "Not Implemented") }) public Response updateLoadBalancer( - @PathParam("loadBalancerPoolID") String loadBalancerPoolID, final NeutronLoadBalancerRequest input) { - INeutronLoadBalancerCRUD loadBalancerPoolInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerCRUD( + @PathParam("loadBalancerID") String loadBalancerID, final NeutronLoadBalancerRequest input) { + INeutronLoadBalancerCRUD loadBalancerInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerCRUD( this); - if (loadBalancerPoolInterface == null) { + if (loadBalancerInterface == null) { throw new ServiceUnavailableException("LoadBalancer CRUD Interface " + RestMessages.SERVICEUNAVAILABLE.toString()); } @@ -282,14 +278,14 @@ public class NeutronLoadBalancerNorthbound { /* * verify the LoadBalancer exists and there is only one delta provided */ - if (!loadBalancerPoolInterface.neutronLoadBalancerExists(loadBalancerPoolID)) { + if (!loadBalancerInterface.neutronLoadBalancerExists(loadBalancerID)) { throw new ResourceNotFoundException("LoadBalancer UUID does not exist."); } if (!input.isSingleton()) { throw new BadRequestException("Only singleton edit supported"); } NeutronLoadBalancer delta = input.getSingleton(); - NeutronLoadBalancer original = loadBalancerPoolInterface.getNeutronLoadBalancer(loadBalancerPoolID); + NeutronLoadBalancer original = loadBalancerInterface.getNeutronLoadBalancer(loadBalancerID); /* * updates restricted by Neutron @@ -318,23 +314,23 @@ public class NeutronLoadBalancerNorthbound { /* * update the object and return it */ - loadBalancerPoolInterface.updateNeutronLoadBalancer(loadBalancerPoolID, delta); - NeutronLoadBalancer updatedLoadBalancer = loadBalancerPoolInterface.getNeutronLoadBalancer( - loadBalancerPoolID); + loadBalancerInterface.updateNeutronLoadBalancer(loadBalancerID, delta); + NeutronLoadBalancer updatedLoadBalancer = loadBalancerInterface.getNeutronLoadBalancer( + loadBalancerID); if (instances != null) { for (Object instance : instances) { INeutronLoadBalancerAware service = (INeutronLoadBalancerAware) instance; service.neutronLoadBalancerUpdated(updatedLoadBalancer); } } - return Response.status(200).entity(new NeutronLoadBalancerRequest(loadBalancerPoolInterface.getNeutronLoadBalancer( - loadBalancerPoolID))).build(); + return Response.status(200).entity(new NeutronLoadBalancerRequest(loadBalancerInterface.getNeutronLoadBalancer( + loadBalancerID))).build(); } /** * Deletes a LoadBalancer */ - @Path("{loadBalancerPoolID}") + @Path("{loadBalancerID}") @DELETE @StatusCodes({ @ResponseCode(code = 204, condition = "No Content"), @@ -343,10 +339,10 @@ public class NeutronLoadBalancerNorthbound { @ResponseCode(code = 409, condition = "Conflict"), @ResponseCode(code = 501, condition = "Not Implemented") }) public Response deleteLoadBalancer( - @PathParam("loadBalancerPoolID") String loadBalancerPoolID) { - INeutronLoadBalancerCRUD loadBalancerPoolInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerCRUD( + @PathParam("loadBalancerID") String loadBalancerID) { + INeutronLoadBalancerCRUD loadBalancerInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerCRUD( this); - if (loadBalancerPoolInterface == null) { + if (loadBalancerInterface == null) { throw new ServiceUnavailableException("LoadBalancer CRUD Interface " + RestMessages.SERVICEUNAVAILABLE.toString()); } @@ -354,13 +350,13 @@ public class NeutronLoadBalancerNorthbound { /* * verify the LoadBalancer exists and it isn't currently in use */ - if (!loadBalancerPoolInterface.neutronLoadBalancerExists(loadBalancerPoolID)) { + if (!loadBalancerInterface.neutronLoadBalancerExists(loadBalancerID)) { throw new ResourceNotFoundException("LoadBalancer UUID does not exist."); } - if (loadBalancerPoolInterface.neutronLoadBalancerInUse(loadBalancerPoolID)) { + if (loadBalancerInterface.neutronLoadBalancerInUse(loadBalancerID)) { return Response.status(409).build(); } - NeutronLoadBalancer singleton = loadBalancerPoolInterface.getNeutronLoadBalancer(loadBalancerPoolID); + NeutronLoadBalancer singleton = loadBalancerInterface.getNeutronLoadBalancer(loadBalancerID); Object[] instances = ServiceHelper.getGlobalInstances(INeutronLoadBalancerAware.class, this, null); if (instances != null) { for (Object instance : instances) { @@ -372,7 +368,7 @@ public class NeutronLoadBalancerNorthbound { } } - loadBalancerPoolInterface.removeNeutronLoadBalancer(loadBalancerPoolID); + loadBalancerInterface.removeNeutronLoadBalancer(loadBalancerID); if (instances != null) { for (Object instance : instances) { INeutronLoadBalancerAware service = (INeutronLoadBalancerAware) instance; diff --git a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/INeutronLoadBalancerPoolMemberRequest.java b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerPoolMemberRequest.java similarity index 82% rename from opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/INeutronLoadBalancerPoolMemberRequest.java rename to opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerPoolMemberRequest.java index 9d6616373c..9b949da72e 100644 --- a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/INeutronLoadBalancerPoolMemberRequest.java +++ b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerPoolMemberRequest.java @@ -12,7 +12,7 @@ import org.opendaylight.controller.networkconfig.neutron.NeutronLoadBalancerPool import javax.xml.bind.annotation.XmlElement; import java.util.List; -public class INeutronLoadBalancerPoolMemberRequest { +public class NeutronLoadBalancerPoolMemberRequest { /** * See OpenStack Network API v2.0 Reference for description of @@ -25,15 +25,15 @@ public class INeutronLoadBalancerPoolMemberRequest { @XmlElement(name="members") List bulkRequest; - INeutronLoadBalancerPoolMemberRequest() { + NeutronLoadBalancerPoolMemberRequest() { } - INeutronLoadBalancerPoolMemberRequest(List bulk) { + NeutronLoadBalancerPoolMemberRequest(List bulk) { bulkRequest = bulk; singletonLoadBalancerPoolMember = null; } - INeutronLoadBalancerPoolMemberRequest(NeutronLoadBalancerPoolMember group) { + NeutronLoadBalancerPoolMemberRequest(NeutronLoadBalancerPoolMember group) { singletonLoadBalancerPoolMember = group; } diff --git a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerPoolMembersNorthbound.java b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerPoolMembersNorthbound.java index ff56fa0a9d..f8f3cd8c53 100644 --- a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerPoolMembersNorthbound.java +++ b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerPoolMembersNorthbound.java @@ -1,46 +1,51 @@ /* - * Copyright (C) 2014 Red Hat, Inc. + * Copyright (C) 2014 SDN Hub, LLC. * * 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 + * + * Authors : Srini Seetharaman */ package org.opendaylight.controller.networkconfig.neutron.northbound; import org.codehaus.enunciate.jaxrs.ResponseCode; import org.codehaus.enunciate.jaxrs.StatusCodes; +import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolCRUD; import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolMemberAware; -import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolMemberCRUD; import org.opendaylight.controller.networkconfig.neutron.NeutronCRUDInterfaces; +import org.opendaylight.controller.networkconfig.neutron.NeutronLoadBalancerPool; import org.opendaylight.controller.networkconfig.neutron.NeutronLoadBalancerPoolMember; import org.opendaylight.controller.northbound.commons.RestMessages; import org.opendaylight.controller.northbound.commons.exception.BadRequestException; +import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException; import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException; import org.opendaylight.controller.sal.utils.ServiceHelper; import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; + import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; - -@Path("/pools/{loadBalancerPoolID}/members") +@Path("/pools/{loadBalancerPoolUUID}/members") public class NeutronLoadBalancerPoolMembersNorthbound { - private NeutronLoadBalancerPoolMember extractFields(NeutronLoadBalancerPoolMember o, List fields) { return o.extractFields(fields); } /** - * Returns a list of all LoadBalancerPool + * Returns a list of all LoadBalancerPoolMembers in specified pool */ @GET @Produces({MediaType.APPLICATION_JSON}) @@ -50,8 +55,12 @@ public class NeutronLoadBalancerPoolMembersNorthbound { @ResponseCode(code = 501, condition = "Not Implemented")}) public Response listMembers( + //Path param + @PathParam("loadBalancerPoolUUID") String loadBalancerPoolUUID, + // return fields @QueryParam("fields") List fields, + // OpenStack LoadBalancerPool attributes @QueryParam("id") String queryLoadBalancerPoolMemberID, @QueryParam("tenant_id") String queryLoadBalancerPoolMemberTenantID, @@ -68,20 +77,24 @@ public Response listMembers( @QueryParam("page_reverse") String pageReverse // sorting not supported ) { - INeutronLoadBalancerPoolMemberCRUD loadBalancerPoolMemberInterface = NeutronCRUDInterfaces - .getINeutronLoadBalancerPoolMemberCRUD(this); - if (loadBalancerPoolMemberInterface == null) { + INeutronLoadBalancerPoolCRUD loadBalancerPoolInterface = NeutronCRUDInterfaces + .getINeutronLoadBalancerPoolCRUD(this); + if (loadBalancerPoolInterface == null) { throw new ServiceUnavailableException("LoadBalancerPool CRUD Interface " + RestMessages.SERVICEUNAVAILABLE.toString()); } - List allLoadBalancerPoolMembers = loadBalancerPoolMemberInterface - .getAllNeutronLoadBalancerPoolMembers(); + if (!loadBalancerPoolInterface.neutronLoadBalancerPoolExists(loadBalancerPoolUUID)) { + throw new ResourceNotFoundException("loadBalancerPool UUID does not exist."); + } + List members = + loadBalancerPoolInterface.getNeutronLoadBalancerPool(loadBalancerPoolUUID).getLoadBalancerPoolMembers(); List ans = new ArrayList(); - Iterator i = allLoadBalancerPoolMembers.iterator(); + Iterator i = members.iterator(); while (i.hasNext()) { NeutronLoadBalancerPoolMember nsg = i.next(); if ((queryLoadBalancerPoolMemberID == null || queryLoadBalancerPoolMemberID.equals(nsg.getPoolMemberID())) && + loadBalancerPoolUUID.equals(nsg.getPoolID()) && (queryLoadBalancerPoolMemberTenantID == null || queryLoadBalancerPoolMemberTenantID.equals(nsg.getPoolMemberTenantID())) && (queryLoadBalancerPoolMemberAddress == null || @@ -102,13 +115,57 @@ public Response listMembers( } } return Response.status(200).entity( - new INeutronLoadBalancerPoolMemberRequest(ans)).build(); + new NeutronLoadBalancerPoolMemberRequest(ans)).build(); +} + +/** + * Returns a specific LoadBalancerPoolMember + */ + +@Path("{loadBalancerPoolMemberUUID}") +@GET +@Produces({ MediaType.APPLICATION_JSON }) +//@TypeHint(OpenStackLoadBalancerPoolMembers.class) +@StatusCodes({ + @ResponseCode(code = 200, condition = "Operation successful"), + @ResponseCode(code = 401, condition = "Unauthorized"), + @ResponseCode(code = 404, condition = "Not Found"), + @ResponseCode(code = 501, condition = "Not Implemented") }) +public Response showLoadBalancerPoolMember( + @PathParam("loadBalancerPoolUUID") String loadBalancerPoolUUID, + @PathParam("loadBalancerPoolMemberUUID") String loadBalancerPoolMemberUUID, + // return fields + @QueryParam("fields") List fields ) { + + INeutronLoadBalancerPoolCRUD loadBalancerPoolInterface = NeutronCRUDInterfaces + .getINeutronLoadBalancerPoolCRUD(this); + if (loadBalancerPoolInterface == null) { + throw new ServiceUnavailableException("LoadBalancerPool CRUD Interface " + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + if (!loadBalancerPoolInterface.neutronLoadBalancerPoolExists(loadBalancerPoolUUID)) { + throw new ResourceNotFoundException("loadBalancerPool UUID does not exist."); + } + List members = + loadBalancerPoolInterface.getNeutronLoadBalancerPool(loadBalancerPoolUUID).getLoadBalancerPoolMembers(); + for (NeutronLoadBalancerPoolMember ans: members) { + if (!ans.getPoolMemberID().equals(loadBalancerPoolMemberUUID)) + continue; + + if (fields.size() > 0) { + return Response.status(200).entity( + new NeutronLoadBalancerPoolMemberRequest(extractFields(ans, fields))).build(); + } else { + return Response.status(200).entity( + new NeutronLoadBalancerPoolMemberRequest(ans)).build(); + } + } + return Response.status(204).build(); } /** * Adds a Member to an LBaaS Pool member */ -@Path("/pools/{loadBalancerPoolID}/members") @PUT @Produces({MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_JSON}) @@ -117,25 +174,34 @@ public Response listMembers( @ResponseCode(code = 401, condition = "Unauthorized"), @ResponseCode(code = 404, condition = "Not Found"), @ResponseCode(code = 501, condition = "Not Implemented")}) -public Response createLoadBalancerPoolMember( INeutronLoadBalancerPoolMemberRequest input) { +public Response createLoadBalancerPoolMember( + @PathParam("loadBalancerPoolUUID") String loadBalancerPoolUUID, + final NeutronLoadBalancerPoolMemberRequest input) { - INeutronLoadBalancerPoolMemberCRUD loadBalancerPoolMemberInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerPoolMemberCRUD( - this); - if (loadBalancerPoolMemberInterface == null) { - throw new ServiceUnavailableException("LoadBalancerPoolMember CRUD Interface " + INeutronLoadBalancerPoolCRUD loadBalancerPoolInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerPoolCRUD(this); + if (loadBalancerPoolInterface == null) { + throw new ServiceUnavailableException("LoadBalancerPool CRUD Interface " + RestMessages.SERVICEUNAVAILABLE.toString()); } + // Verify that the loadBalancerPool exists, for the member to be added to its cache + if (!loadBalancerPoolInterface.neutronLoadBalancerPoolExists(loadBalancerPoolUUID)) { + throw new ResourceNotFoundException("loadBalancerPool UUID does not exist."); + } + NeutronLoadBalancerPool singletonPool = loadBalancerPoolInterface.getNeutronLoadBalancerPool(loadBalancerPoolUUID); + if (input.isSingleton()) { NeutronLoadBalancerPoolMember singleton = input.getSingleton(); + singleton.setPoolID(loadBalancerPoolUUID); + String loadBalancerPoolMemberUUID = singleton.getPoolMemberID(); /* * Verify that the LoadBalancerPoolMember doesn't already exist. */ - if (loadBalancerPoolMemberInterface.neutronLoadBalancerPoolMemberExists( - singleton.getPoolMemberID())) { - throw new BadRequestException("LoadBalancerPoolMember UUID already exists"); + List members = singletonPool.getLoadBalancerPoolMembers(); + for (NeutronLoadBalancerPoolMember member: members) { + if (member.getPoolMemberID().equals(loadBalancerPoolMemberUUID)) + throw new BadRequestException("LoadBalancerPoolMember UUID already exists"); } - loadBalancerPoolMemberInterface.addNeutronLoadBalancerPoolMember(singleton); Object[] instances = ServiceHelper.getGlobalInstances(INeutronLoadBalancerPoolMemberAware.class, this, null); if (instances != null) { @@ -147,13 +213,18 @@ public Response createLoadBalancerPoolMember( INeutronLoadBalancerPoolMemberReq } } } - loadBalancerPoolMemberInterface.addNeutronLoadBalancerPoolMember(singleton); if (instances != null) { for (Object instance : instances) { INeutronLoadBalancerPoolMemberAware service = (INeutronLoadBalancerPoolMemberAware) instance; service.neutronLoadBalancerPoolMemberCreated(singleton); } } + + /** + * Add the member from the neutron load balancer pool as well + */ + singletonPool.addLoadBalancerPoolMember(singleton); + } else { List bulk = input.getBulk(); Iterator i = bulk.iterator(); @@ -161,15 +232,17 @@ public Response createLoadBalancerPoolMember( INeutronLoadBalancerPoolMemberReq Object[] instances = ServiceHelper.getGlobalInstances(INeutronLoadBalancerPoolMemberAware.class, this, null); while (i.hasNext()) { NeutronLoadBalancerPoolMember test = i.next(); + String loadBalancerPoolMemberUUID = test.getPoolMemberID(); /* - * Verify that the firewall doesn't already exist + * Verify that the LoadBalancerPoolMember doesn't already exist. */ - - if (loadBalancerPoolMemberInterface.neutronLoadBalancerPoolMemberExists( - test.getPoolMemberID())) { - throw new BadRequestException("Load Balancer PoolMember UUID already is already created"); + List members = singletonPool.getLoadBalancerPoolMembers(); + for (NeutronLoadBalancerPoolMember member: members) { + if (member.getPoolMemberID().equals(loadBalancerPoolMemberUUID)) + throw new BadRequestException("LoadBalancerPoolMember UUID already exists"); } + if (testMap.containsKey(test.getPoolMemberID())) { throw new BadRequestException("Load Balancer PoolMember UUID already exists"); } @@ -189,15 +262,105 @@ public Response createLoadBalancerPoolMember( INeutronLoadBalancerPoolMemberReq i = bulk.iterator(); while (i.hasNext()) { NeutronLoadBalancerPoolMember test = i.next(); - loadBalancerPoolMemberInterface.addNeutronLoadBalancerPoolMember(test); if (instances != null) { for (Object instance : instances) { INeutronLoadBalancerPoolMemberAware service = (INeutronLoadBalancerPoolMemberAware) instance; service.neutronLoadBalancerPoolMemberCreated(test); } } + singletonPool.addLoadBalancerPoolMember(test); } } return Response.status(201).entity(input).build(); } + +/** + * Updates a LB member pool + */ + +@Path("{loadBalancerPoolMemberUUID}") +@PUT +@Produces({ MediaType.APPLICATION_JSON }) +@Consumes({ MediaType.APPLICATION_JSON }) +@StatusCodes({ + @ResponseCode(code = 200, condition = "Operation successful"), + @ResponseCode(code = 400, condition = "Bad Request"), + @ResponseCode(code = 401, condition = "Unauthorized"), + @ResponseCode(code = 403, condition = "Forbidden"), + @ResponseCode(code = 404, condition = "Not Found"), + @ResponseCode(code = 501, condition = "Not Implemented") }) +public Response updateLoadBalancerPoolMember( + @PathParam("loadBalancerPoolUUID") String loadBalancerPoolUUID, + @PathParam("loadBalancerPoolMemberUUID") String loadBalancerPoolMemberUUID, + final NeutronLoadBalancerPoolMemberRequest input) { + + //TODO: Implement update LB member pool + return Response.status(501).entity(input).build(); +} + +/** + * Deletes a LoadBalancerPoolMember + */ + +@Path("{loadBalancerPoolMemberUUID}") +@DELETE +@StatusCodes({ + @ResponseCode(code = 204, condition = "No Content"), + @ResponseCode(code = 401, condition = "Unauthorized"), + @ResponseCode(code = 403, condition = "Forbidden"), + @ResponseCode(code = 404, condition = "Not Found"), + @ResponseCode(code = 501, condition = "Not Implemented") }) +public Response deleteLoadBalancerPoolMember( + @PathParam("loadBalancerPoolUUID") String loadBalancerPoolUUID, + @PathParam("loadBalancerPoolMemberUUID") String loadBalancerPoolMemberUUID) { + INeutronLoadBalancerPoolCRUD loadBalancerPoolInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerPoolCRUD(this); + if (loadBalancerPoolInterface == null) { + throw new ServiceUnavailableException("LoadBalancerPool CRUD Interface " + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + + // Verify that the loadBalancerPool exists, for the member to be removed from its cache + if (!loadBalancerPoolInterface.neutronLoadBalancerPoolExists(loadBalancerPoolUUID)) { + throw new ResourceNotFoundException("loadBalancerPool UUID does not exist."); + } + + //Verify that the LB pool member exists + NeutronLoadBalancerPoolMember singleton = null; + List members = + loadBalancerPoolInterface.getNeutronLoadBalancerPool(loadBalancerPoolUUID).getLoadBalancerPoolMembers(); + for (NeutronLoadBalancerPoolMember member: members) { + if (member.getPoolMemberID().equals(loadBalancerPoolMemberUUID)) { + singleton = member; + break; + } + } + if (singleton == null) + throw new BadRequestException("LoadBalancerPoolMember UUID does not exist."); + + Object[] instances = ServiceHelper.getGlobalInstances(INeutronLoadBalancerPoolMemberAware.class, this, null); + if (instances != null) { + for (Object instance : instances) { + INeutronLoadBalancerPoolMemberAware service = (INeutronLoadBalancerPoolMemberAware) instance; + int status = service.canDeleteNeutronLoadBalancerPoolMember(singleton); + if (status < 200 || status > 299) { + return Response.status(status).build(); + } + } + } + + if (instances != null) { + for (Object instance : instances) { + INeutronLoadBalancerPoolMemberAware service = (INeutronLoadBalancerPoolMemberAware) instance; + service.neutronLoadBalancerPoolMemberDeleted(singleton); + } + } + + /** + * Remove the member from the neutron load balancer pool + */ + NeutronLoadBalancerPool singletonPool = loadBalancerPoolInterface.getNeutronLoadBalancerPool(loadBalancerPoolUUID); + singletonPool.removeLoadBalancerPoolMember(singleton); + + return Response.status(204).build(); +} } diff --git a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerPoolNorthbound.java b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerPoolNorthbound.java index fc5357ccb5..7802dbb906 100644 --- a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerPoolNorthbound.java +++ b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronLoadBalancerPoolNorthbound.java @@ -1,9 +1,11 @@ /* - * Copyright (C) 2014 Red Hat, Inc. + * Copyright (C) 2014 SDN Hub, LLC. * * 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 + * + * Authors : Srini Seetharaman */ package org.opendaylight.controller.networkconfig.neutron.northbound; @@ -13,8 +15,10 @@ import org.codehaus.enunciate.jaxrs.ResponseCode; import org.codehaus.enunciate.jaxrs.StatusCodes; import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolAware; import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolCRUD; +import org.opendaylight.controller.networkconfig.neutron.INeutronLoadBalancerPoolMemberCRUD; import org.opendaylight.controller.networkconfig.neutron.NeutronCRUDInterfaces; import org.opendaylight.controller.networkconfig.neutron.NeutronLoadBalancerPool; +import org.opendaylight.controller.networkconfig.neutron.NeutronLoadBalancerPoolMember; import org.opendaylight.controller.northbound.commons.RestMessages; import org.opendaylight.controller.northbound.commons.exception.BadRequestException; import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException; @@ -22,6 +26,7 @@ import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailab import org.opendaylight.controller.sal.utils.ServiceHelper; import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; @@ -31,6 +36,7 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; + import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -53,6 +59,13 @@ import java.util.List; * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration * */ + +/** + * For now, the LB pool member data is maintained with the INeutronLoadBalancerPoolCRUD, + * although there may be an overlap with INeutronLoadBalancerPoolMemberCRUD's cache. + * TODO: Consolidate and maintain a single copy + */ + @Path("/pools") public class NeutronLoadBalancerPoolNorthbound { @@ -83,7 +96,7 @@ public class NeutronLoadBalancerPoolNorthbound { @QueryParam("healthmonitor_id") String queryLoadBalancerPoolHealthMonitorID, @QueryParam("admin_state_up") String queryLoadBalancerIsAdminStateUp, @QueryParam("status") String queryLoadBalancerPoolStatus, - @QueryParam("members") List queryLoadBalancerPoolMembers, + @QueryParam("members") List queryLoadBalancerPoolMembers, // pagination @QueryParam("limit") String limit, @QueryParam("marker") String marker, @@ -217,7 +230,7 @@ public class NeutronLoadBalancerPoolNorthbound { NeutronLoadBalancerPool test = i.next(); /* - * Verify that the firewall doesn't already exist + * Verify that the loadBalancerPool doesn't already exist */ if (loadBalancerPoolInterface.neutronLoadBalancerPoolExists(test.getLoadBalancerPoolID())) { @@ -328,4 +341,73 @@ public class NeutronLoadBalancerPoolNorthbound { } return Response.status(200).entity(new NeutronLoadBalancerPoolRequest(loadBalancerPoolInterface.getNeutronLoadBalancerPool(loadBalancerPoolID))).build(); } + + /** + * Deletes a LoadBalancerPool + */ + + @Path("{loadBalancerPoolUUID}") + @DELETE + @StatusCodes({ + @ResponseCode(code = 204, condition = "No Content"), + @ResponseCode(code = 401, condition = "Unauthorized"), + @ResponseCode(code = 404, condition = "Not Found"), + @ResponseCode(code = 409, condition = "Conflict"), + @ResponseCode(code = 501, condition = "Not Implemented") }) + public Response deleteLoadBalancerPool( + @PathParam("loadBalancerPoolUUID") String loadBalancerPoolUUID) { + INeutronLoadBalancerPoolCRUD loadBalancerPoolInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerPoolCRUD(this); + if (loadBalancerPoolInterface == null) { + throw new ServiceUnavailableException("LoadBalancerPool CRUD Interface " + + RestMessages.SERVICEUNAVAILABLE.toString()); + } + + /* + * verify the LoadBalancerPool exists and it isn't currently in use + */ + if (!loadBalancerPoolInterface.neutronLoadBalancerPoolExists(loadBalancerPoolUUID)) { + throw new ResourceNotFoundException("LoadBalancerPool UUID does not exist."); + } + if (loadBalancerPoolInterface.neutronLoadBalancerPoolInUse(loadBalancerPoolUUID)) { + return Response.status(409).build(); + } + NeutronLoadBalancerPool singleton = loadBalancerPoolInterface.getNeutronLoadBalancerPool(loadBalancerPoolUUID); + Object[] instances = ServiceHelper.getGlobalInstances(INeutronLoadBalancerPoolAware.class, this, null); + if (instances != null) { + for (Object instance : instances) { + INeutronLoadBalancerPoolAware service = (INeutronLoadBalancerPoolAware) instance; + int status = service.canDeleteNeutronLoadBalancerPool(singleton); + if (status < 200 || status > 299) { + return Response.status(status).build(); + } + } + } + + /* + * remove it and return 204 status + */ + loadBalancerPoolInterface.removeNeutronLoadBalancerPool(loadBalancerPoolUUID); + if (instances != null) { + for (Object instance : instances) { + INeutronLoadBalancerPoolAware service = (INeutronLoadBalancerPoolAware) instance; + service.neutronLoadBalancerPoolDeleted(singleton); + } + } + + /* + * remove corresponding members from the member cache too + */ + INeutronLoadBalancerPoolMemberCRUD loadBalancerPoolMemberInterface = NeutronCRUDInterfaces.getINeutronLoadBalancerPoolMemberCRUD(this); + if (loadBalancerPoolMemberInterface != null) { + List allLoadBalancerPoolMembers = new + ArrayList(loadBalancerPoolMemberInterface.getAllNeutronLoadBalancerPoolMembers()); + Iterator i = allLoadBalancerPoolMembers.iterator(); + while (i.hasNext()) { + NeutronLoadBalancerPoolMember member = i.next(); + if (member.getPoolID() == loadBalancerPoolUUID) + loadBalancerPoolMemberInterface.removeNeutronLoadBalancerPoolMember(member.getPoolMemberID()); + } + } + return Response.status(204).build(); + } } diff --git a/opendaylight/topologymanager/implementation/src/main/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImpl.java b/opendaylight/topologymanager/implementation/src/main/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImpl.java index b0e87c48f3..659ee7dd81 100644 --- a/opendaylight/topologymanager/implementation/src/main/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImpl.java +++ b/opendaylight/topologymanager/implementation/src/main/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImpl.java @@ -8,25 +8,6 @@ package org.opendaylight.controller.topologymanager.internal; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.util.ArrayList; -import java.util.Dictionary; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.LinkedBlockingQueue; - import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.felix.dm.Component; import org.eclipse.osgi.framework.console.CommandInterpreter; @@ -64,6 +45,25 @@ import org.osgi.framework.FrameworkUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.LinkedBlockingQueue; + /** * The class describes TopologyManager which is the central repository of the * network topology. It provides service for applications to interact with @@ -654,12 +654,14 @@ public class TopologyManagerImpl implements // all except the creation time stamp because that should // be set only when the edge is created TimeStamp timeStamp = null; - for (Property prop : oldProps) { - if (prop instanceof TimeStamp) { - TimeStamp tsProp = (TimeStamp) prop; - if (tsProp.getTimeStampName().equals("creation")) { - timeStamp = tsProp; - break; + if (oldProps != null) { + for (Property prop : oldProps) { + if (prop instanceof TimeStamp) { + TimeStamp tsProp = (TimeStamp) prop; + if (tsProp.getTimeStampName().equals("creation")) { + timeStamp = tsProp; + break; + } } } } @@ -679,7 +681,9 @@ public class TopologyManagerImpl implements if (prop instanceof TimeStamp) { TimeStamp t = (TimeStamp) prop; if (t.getTimeStampName().equals("creation")) { - i.remove(); + if (timeStamp != null) { + i.remove(); + } break; } } diff --git a/opendaylight/topologymanager/implementation/src/test/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImplTest.java b/opendaylight/topologymanager/implementation/src/test/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImplTest.java index fa01fa6a60..d1338bf695 100644 --- a/opendaylight/topologymanager/implementation/src/test/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImplTest.java +++ b/opendaylight/topologymanager/implementation/src/test/java/org/opendaylight/controller/topologymanager/internal/TopologyManagerImplTest.java @@ -8,21 +8,11 @@ package org.opendaylight.controller.topologymanager.internal; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentMap; - import org.junit.Assert; import org.junit.Test; import org.opendaylight.controller.sal.core.Bandwidth; import org.opendaylight.controller.sal.core.ConstructionException; +import org.opendaylight.controller.sal.core.Description; import org.opendaylight.controller.sal.core.Edge; import org.opendaylight.controller.sal.core.Host; import org.opendaylight.controller.sal.core.Latency; @@ -32,6 +22,7 @@ import org.opendaylight.controller.sal.core.NodeConnector; import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType; import org.opendaylight.controller.sal.core.Property; import org.opendaylight.controller.sal.core.State; +import org.opendaylight.controller.sal.core.TimeStamp; import org.opendaylight.controller.sal.core.UpdateType; import org.opendaylight.controller.sal.packet.address.EthernetAddress; import org.opendaylight.controller.sal.topology.TopoEdgeUpdate; @@ -47,6 +38,17 @@ import org.opendaylight.controller.switchmanager.Switch; import org.opendaylight.controller.switchmanager.SwitchConfig; import org.opendaylight.controller.topologymanager.TopologyUserLinkConfig; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + public class TopologyManagerImplTest { /** * Mockup of switch manager that only maintains existence of node @@ -733,4 +735,35 @@ public class TopologyManagerImplTest { Assert.assertTrue(nodeNCmap.isEmpty()); } + + @Test + public void bug1348FixTest() throws ConstructionException { + TopologyManagerImpl topoManagerImpl = new TopologyManagerImpl(); + TestSwitchManager swMgr = new TestSwitchManager(); + topoManagerImpl.setSwitchManager(swMgr); + topoManagerImpl.nonClusterObjectCreate(); + + NodeConnector headnc1 = NodeConnectorCreator.createOFNodeConnector( + (short) 1, NodeCreator.createOFNode(1000L)); + NodeConnector tailnc1 = NodeConnectorCreator.createOFNodeConnector( + (short) 2, NodeCreator.createOFNode(2000L)); + Edge edge = new Edge(headnc1, tailnc1); + List updatedEdges = new ArrayList<>(); + Set edgeProps = new HashSet<>(); + edgeProps.add(new TimeStamp(System.currentTimeMillis(), "creation")); + edgeProps.add(new Latency(Latency.LATENCY100ns)); + edgeProps.add(new State(State.EDGE_UP)); + edgeProps.add(new Bandwidth(Bandwidth.BW100Gbps)); + edgeProps.add(new Description("Test edge")); + updatedEdges.add(new TopoEdgeUpdate(edge, edgeProps, UpdateType.CHANGED)); + + try { + topoManagerImpl.edgeUpdate(updatedEdges); + } catch (Exception e) { + Assert.fail("Exception was raised when trying to update edge properties: " + e.getMessage()); + } + + Assert.assertEquals(1, topoManagerImpl.getEdges().size()); + Assert.assertNotNull(topoManagerImpl.getEdges().get(edge)); + } }