From a010c5c22bdde839c6f30d141b63abf532122603 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Fri, 4 Mar 2016 12:32:10 +0100 Subject: MountPoint for data-tree based DataBroker. Change-Id: I6a15e79747484790607d82d4024971763b4bae54 Signed-off-by: Marek Gradzki --- v3po/impl/pom.xml | 5 + .../io/fd/honeycomb/v3po/impl/V3poProvider.java | 17 ++ .../data/VppDataBrokerInitializationProvider.java | 249 +++++++++++++++++++++ .../ns/yang/v3po/impl/rev141210/V3poModule.java | 13 +- .../VppDataBrokerInitializationProviderTest.java | 74 ++++++ .../yang/v3po/impl/rev141210/V3poModuleTest.java | 2 +- 6 files changed, 351 insertions(+), 9 deletions(-) create mode 100644 v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProvider.java create mode 100644 v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProviderTest.java (limited to 'v3po') diff --git a/v3po/impl/pom.xml b/v3po/impl/pom.xml index 9315c15cc..ff27d1202 100644 --- a/v3po/impl/pom.xml +++ b/v3po/impl/pom.xml @@ -41,6 +41,11 @@ 1.0.0-SNAPSHOT + + org.opendaylight.mdsal.model + ietf-topology + + junit diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poProvider.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poProvider.java index b761000de..54acce3f2 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poProvider.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poProvider.java @@ -16,15 +16,18 @@ package io.fd.honeycomb.v3po.impl; +import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; +import io.fd.honeycomb.v3po.impl.data.VppDataBrokerInitializationProvider; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; +import javax.annotation.Nonnull; 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; @@ -32,6 +35,7 @@ import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFaile import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration; import org.opendaylight.controller.sal.binding.api.BindingAwareProvider; +import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.EthernetCsmacd; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.SoftwareLoopback; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType; @@ -55,12 +59,18 @@ import org.slf4j.LoggerFactory; public class V3poProvider implements BindingAwareProvider, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(V3poProvider.class); + private final Broker domBroker; private RpcRegistration v3poService; private VppIetfInterfaceListener vppInterfaceListener; private VppBridgeDomainListener vppBridgeDomainListener; private vppApi api; private DataBroker db; VppPollOperDataImpl vppPollOperData; + private VppDataBrokerInitializationProvider vppDataBrokerInitializationProvider; + + public V3poProvider(@Nonnull final Broker domBroker) { + this.domBroker = Preconditions.checkNotNull(domBroker, "domBroker should not be null"); + } private void initializeVppConfig() { @@ -176,6 +186,10 @@ public class V3poProvider implements BindingAwareProvider, AutoCloseable { v3poService = session.addRpcImplementation(V3poService.class, vppPollOperData); startOperationalUpdateTimer(); + + // TODO make configurable + vppDataBrokerInitializationProvider = new VppDataBrokerInitializationProvider(db); + domBroker.registerProvider(vppDataBrokerInitializationProvider); } @Override @@ -187,5 +201,8 @@ public class V3poProvider implements BindingAwareProvider, AutoCloseable { if (api != null) { api.close(); } + if (vppDataBrokerInitializationProvider != null) { + vppDataBrokerInitializationProvider.close(); + } } } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProvider.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProvider.java new file mode 100644 index 000000000..9a643564a --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProvider.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed 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. + */ + +package io.fd.honeycomb.v3po.impl.data; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import io.fd.honeycomb.v3po.impl.LoggingFuturesCallBack; +import io.fd.honeycomb.v3po.impl.trans.DefaultVppWriter; +import io.fd.honeycomb.v3po.impl.trans.VppInterfacesReader; +import java.util.Collection; +import java.util.Collections; +import javassist.ClassPool; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; +import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.controller.sal.core.api.Provider; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.concepts.ObjectRegistration; +import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy; +import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; +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.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType; +import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Creates VppDataBroker which uses DataTree instead of DataStore internally in order to obtain better control over the + * data processing in Honeycomb agent + */ +public final class VppDataBrokerInitializationProvider implements Provider, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(VppDataBrokerInitializationProvider.class); + + private final TopologyId VPP_TOPOLOGY_ID = TopologyId.getDefaultInstance("vpp-topology"); + private final NodeId VPP_TOPOLOGY_NODE_ID = NodeId.getDefaultInstance("vpp"); + private final DataBroker bindingBroker; + private final InstanceIdentifier mountPointPath; + private ObjectRegistration mountPointRegistration; + + public VppDataBrokerInitializationProvider(@Nonnull final DataBroker bindingBroker) { + this.bindingBroker = Preconditions.checkNotNull(bindingBroker, "bindingBroker should not be null"); + this.mountPointPath = getMountPointPath(); + } + + // TODO make configurable + private InstanceIdentifier getMountPointPath() { + final InstanceIdentifier networkTopology = + InstanceIdentifier.builder(NetworkTopology.class).build(); + final KeyedInstanceIdentifier topology = + networkTopology.child(Topology.class, new TopologyKey(VPP_TOPOLOGY_ID)); + return topology.child(Node.class, new NodeKey(VPP_TOPOLOGY_NODE_ID)); + } + + @Override + public void onSessionInitiated(final Broker.ProviderSession providerSession) { + LOG.info("Session initialized, providerSession={}", providerSession); + Preconditions.checkState(!isMountPointRegistered(), "Mount point is already registered"); + + final DOMMountPointService mountPointService = providerSession.getService(DOMMountPointService.class); + final SchemaService schemaService = providerSession.getService(SchemaService.class); + + final SchemaContext globalContext = schemaService.getGlobalContext(); + final BindingNormalizedNodeSerializer serializer = initSerializer(globalContext); + final YangInstanceIdentifier path = serializer.toYangInstanceIdentifier(mountPointPath); + + final DOMMountPointService.DOMMountPointBuilder mountPointBuilder = mountPointService.createMountPoint(path); + mountPointBuilder.addInitialSchemaContext(globalContext); + + final DOMDataBroker broker = initVppDataBroker(globalContext, serializer); + mountPointBuilder.addService(DOMDataBroker.class, broker); + + mountPointRegistration = mountPointBuilder.register(); + final DOMMountPoint mountPoint = mountPointRegistration.getInstance(); + LOG.debug("Created mountPoint: identifier={}, schemaContext={}", mountPoint.getIdentifier(), + mountPoint.getSchemaContext()); + + createMountPointPlaceholder(); + + initialVppStateSynchronization(broker); + } + + @Override + public Collection getProviderFunctionality() { + return Collections.EMPTY_LIST; + } + + private boolean isMountPointRegistered() { + final ReadOnlyTransaction readTx = bindingBroker.newReadOnlyTransaction(); + try { + final Optional cfgPlaceholder = + readTx.read(LogicalDatastoreType.CONFIGURATION, mountPointPath).checkedGet(); + final Optional operPlaceholder = + readTx.read(LogicalDatastoreType.OPERATIONAL, mountPointPath).checkedGet(); + return cfgPlaceholder.isPresent() || operPlaceholder.isPresent(); + } catch (ReadFailedException e) { + throw new IllegalStateException("Failed to read mountpoint placeholder data", e); + } + } + + private BindingNormalizedNodeSerializer initSerializer(final SchemaContext globalContext) { + final JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault()); + // TODO this produces ClassNotFoundException + //final GeneratedClassLoadingStrategy loading = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(); + + // FIXME get global class loader instance + final GeneratedClassLoadingStrategy loadingStrategy = + new GeneratedClassLoadingStrategy() { + @Override + public Class loadClass(final String fullyQualifiedName) + throws ClassNotFoundException { + return Class.forName(fullyQualifiedName); + } + }; + final DataObjectSerializerGenerator generator = StreamWriterGenerator.create(utils); + + // TODO make configurable: + final BindingNormalizedNodeCodecRegistry serializer = new BindingNormalizedNodeCodecRegistry(generator); + final BindingRuntimeContext context = BindingRuntimeContext.create(loadingStrategy, globalContext); + serializer.onBindingRuntimeContextUpdated(context); + return serializer; + } + + private DOMDataBroker initVppDataBroker(final SchemaContext globalContext, + final BindingNormalizedNodeSerializer serializer) { + final ReadableVppDataTree operationalData = + new VppOperationalDataTree(serializer, new VppInterfacesReader()); // TODO make configurable + + final DataTree dataTree = + InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION); // TODO make configurable + dataTree.setSchemaContext(globalContext); + + final VppDataTree configDataProxy = new VppConfigDataTree(serializer, dataTree, + new DefaultVppWriter()); // TODO make configurable + return new VppDataBroker(operationalData, configDataProxy); + } + + /** + * Writes placeholder data into MD-SAL's global datastore to indicate the presence of VPP mountpoint. + */ + private void createMountPointPlaceholder() { + final NodeBuilder nodeBuilder = new NodeBuilder(); + nodeBuilder.setKey(new NodeKey(VPP_TOPOLOGY_NODE_ID)); + final Node node = nodeBuilder.build(); + + final WriteTransaction writeTx = bindingBroker.newWriteOnlyTransaction(); + writeTx.merge(LogicalDatastoreType.CONFIGURATION, mountPointPath, node, true); + writeTx.merge(LogicalDatastoreType.OPERATIONAL, mountPointPath, node, true); + + try { + writeTx.submit().checkedGet(); + } catch (TransactionCommitFailedException e) { + throw new IllegalStateException("Failed to create mountpoint placeholder", e); + } + } + + // TODO operational and config models are not 1-1 + // decide what part of operational data should be written to config during initialization + private void initialVppStateSynchronization(final DOMDataBroker broker) { + // read from operational + final DOMDataReadOnlyTransaction readTx = broker.newReadOnlyTransaction(); + + final YangInstanceIdentifier interfacesID = YangInstanceIdentifier.of(Interfaces.QNAME); + + final ListenableFuture writeFuture = Futures.transform( + readTx.read(LogicalDatastoreType.OPERATIONAL, interfacesID), + new AsyncFunction>, Void>() { + @Override + public ListenableFuture apply(final Optional> readResult) + throws Exception { + if (readResult.isPresent()) { + final DOMDataWriteTransaction writeTx = broker.newWriteOnlyTransaction(); + writeTx.put(LogicalDatastoreType.CONFIGURATION, interfacesID, readResult.get()); + return writeTx.submit(); + } else { + return Futures + .immediateFailedFuture( + new IllegalStateException("Failed to read data from VPP.")); + } + } + }); + + Futures.addCallback(writeFuture, + new LoggingFuturesCallBack("Initializing VPP config DataTree failed", LOG)); + } + + @Override + public void close() throws Exception { + if (mountPointRegistration != null) { + mountPointRegistration.close(); + } + // remove MD-SAL placeholder data for VPP mount point: + final WriteTransaction rwTx = bindingBroker.newWriteOnlyTransaction(); + // does not fail if data is not present: + rwTx.delete(LogicalDatastoreType.CONFIGURATION, mountPointPath); + try { + rwTx.submit().checkedGet(); + } catch (TransactionCommitFailedException e) { + throw new IllegalStateException("Failed to remove mountpoint's placeholder from MD-SAL's global datastore", + e); + } + } +} diff --git a/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java index f51eb0575..390545563 100644 --- a/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java +++ b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java @@ -31,14 +31,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.impl.schema.Builders; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class V3poModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractV3poModule { - private static final Logger LOG = LoggerFactory.getLogger(V3poModule.class); - public V3poModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); @@ -53,14 +49,14 @@ public class V3poModule extends @Override public void customValidation() { - // add custom validation form module attributes here.AbstractModule + // add custom validation form module attributes here } @Override public java.lang.AutoCloseable createInstance() { - getDomBrokerDependency().registerProvider(new InitializationProvider()); - - V3poProvider provider = new V3poProvider(); + final Broker domBroker = getDomBrokerDependency(); + domBroker.registerProvider(new InitializationProvider()); + final V3poProvider provider = new V3poProvider(domBroker); getBrokerDependency().registerProvider(provider); return provider; } @@ -109,4 +105,5 @@ public class V3poModule extends } } + } diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProviderTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProviderTest.java new file mode 100644 index 000000000..782a7b6fe --- /dev/null +++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/data/VppDataBrokerInitializationProviderTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed 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. + */ + +package io.fd.honeycomb.v3po.impl.data; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.common.util.concurrent.CheckedFuture; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +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.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class VppDataBrokerInitializationProviderTest { + + @Mock + private DataBroker bindingBroker; + @Mock + private WriteTransaction writeTx; + + private VppDataBrokerInitializationProvider provider; + + @Before + public void setUp() throws Exception { + initMocks(this); + doReturn(writeTx).when(bindingBroker).newWriteOnlyTransaction(); + provider = new VppDataBrokerInitializationProvider(bindingBroker); + } + + @Test + public void testGetProviderFunctionality() { + assertNotNull(provider.getProviderFunctionality()); + } + + @Test + public void testClose() throws Exception { + doReturn(mock(CheckedFuture.class)).when(writeTx).submit(); + provider.close(); + verify(writeTx).delete(eq(LogicalDatastoreType.CONFIGURATION), any(InstanceIdentifier.class)); + verify(writeTx).submit(); + } + + @Test(expected = IllegalStateException.class) + public void testCloseFailed() throws Exception { + doReturn(writeTx).when(bindingBroker).newWriteOnlyTransaction(); + doThrow(TransactionCommitFailedException.class).when(writeTx).submit(); + provider.close(); + verify(writeTx).delete(eq(LogicalDatastoreType.CONFIGURATION), any(InstanceIdentifier.class)); + } +} \ No newline at end of file diff --git a/v3po/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java b/v3po/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java index 4f14a671c..74d36bb9d 100644 --- a/v3po/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java +++ b/v3po/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java @@ -40,7 +40,7 @@ public class V3poModuleTest { } - // @Test + @Test public void testCreateInstance() throws Exception { // configure mocks DependencyResolver dependencyResolver = mock(DependencyResolver.class); -- cgit 1.2.3-korg