diff options
19 files changed, 874 insertions, 152 deletions
@@ -46,5 +46,6 @@ <module>vpp-integration</module> <module>acl</module> <module>dhcp</module> + <module>samples</module> </modules> </project>
\ No newline at end of file diff --git a/release-notes/pom.xml b/release-notes/pom.xml index 6c1dbfe2d..03a66847d 100644 --- a/release-notes/pom.xml +++ b/release-notes/pom.xml @@ -87,6 +87,7 @@ <!--<project-snat-snapshot-version>1.0</project-snat-snapshot-version>--> <project-snat-snapshot-version>1.0-SNAPSHOT</project-snat-snapshot-version> <project-git-web>https://git.fd.io/cgit/hc2vpp/tree</project-git-web> + <project-infra-git-web>https://git.fd.io/cgit/honeycomb/tree</project-infra-git-web> </attributes> </configuration> </execution> diff --git a/release-notes/src/main/asciidoc/devel_guide/devel_plugin_vpp_tutorial.adoc b/release-notes/src/main/asciidoc/devel_guide/devel_plugin_vpp_tutorial.adoc index ee148e5e5..c55e85ccc 100644 --- a/release-notes/src/main/asciidoc/devel_guide/devel_plugin_vpp_tutorial.adoc +++ b/release-notes/src/main/asciidoc/devel_guide/devel_plugin_vpp_tutorial.adoc @@ -73,12 +73,23 @@ Also add vpp-translate-utils dependency so that writing translation code is easi [source,xml,subs="+attributes"] ---- <dependency> - <groupId>io.fd.honeycomb.vpp</groupId> + <groupId>io.fd.hc2vpp.vpp</groupId> <artifactId>vpp-translate-utils</artifactId> <version>{project-version}</version> </dependency> ---- +To use advanced features such as context caching, use dependency to translate-utils + +[source,xml,subs="+attributes"] +---- +<dependency> + <groupId>io.fd.honeycomb</groupId> + <artifactId>translate-utils</artifactId> + <version>${project.version}</version> +</dependency> +---- + Do not rebuild yet, since the APIs for this plugin have changed and the compilation would fail. But make sure to update the project if using an IDE to pick up the Jvpp dependency. === Updating the customizers @@ -91,36 +102,50 @@ Rename it to VxlanReadCustomzier. Update the code to: [source,java] ---- -package io.fd.honeycomb.tutorial.read; +package io.fd.hc2vpp.samples.read; + +import com.google.common.base.Optional; import com.google.common.base.Preconditions; +import io.fd.hc2vpp.common.translate.util.AddressTranslator; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; import io.fd.honeycomb.translate.read.ReadContext; import io.fd.honeycomb.translate.read.ReadFailedException; import io.fd.honeycomb.translate.spi.read.ListReaderCustomizer; -import io.fd.honeycomb.translate.v3po.util.NamingContext; -import io.fd.honeycomb.translate.v3po.util.TranslateUtils; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.VxlansBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnel; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnelBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnelKey; +import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager; +import io.fd.vpp.jvpp.core.dto.VxlanTunnelDetails; +import io.fd.vpp.jvpp.core.dto.VxlanTunnelDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.VxlanTunnelDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.VxlansBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnelBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnelKey; import org.opendaylight.yangtools.concepts.Builder; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.openvpp.jvpp.VppBaseCallException; -import org.openvpp.jvpp.core.dto.VxlanTunnelDetails; -import org.openvpp.jvpp.core.dto.VxlanTunnelDetailsReplyDump; -import org.openvpp.jvpp.core.dto.VxlanTunnelDump; -import org.openvpp.jvpp.core.future.FutureJVppCore; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + /** * Reader for {@link VxlanTunnel} list node from our YANG model. */ public final class VxlanReadCustomizer implements - ListReaderCustomizer<VxlanTunnel, VxlanTunnelKey, VxlanTunnelBuilder> { + ListReaderCustomizer<VxlanTunnel, VxlanTunnelKey, VxlanTunnelBuilder>, + // provides utility methods to translate binary data + ByteDataTranslator, + // provides utility methods to translate Ipv4,Ipv6,Mac addresses. + // in case that just one address family processing is needed,use *address-family-name*Translator, + // for ex Ipv4Translator + AddressTranslator, + // provides utility methods to consume results of jvpp api calls + JvppReplyConsumer { // JVpp core. This is the Java API for VPP's core API. private final FutureJVppCore jVppCore; @@ -135,9 +160,38 @@ public final class VxlanReadCustomizer implements // backed by context storage that makes the lookup and storing easier. private final NamingContext vxlanNamingContext; + // Dump manager that provides intelligent caching based on provided contextual key + private DumpCacheManager<VxlanTunnelDetailsReplyDump, Integer> dumpManager; + public VxlanReadCustomizer(final FutureJVppCore jVppCore, final NamingContext vxlanNamingContext) { this.jVppCore = jVppCore; this.vxlanNamingContext = vxlanNamingContext; + + this.dumpManager = new DumpCacheManager.DumpCacheManagerBuilder<VxlanTunnelDetailsReplyDump, Integer>() + // executor handles dumping of data itself, based on provided lambda + // instanceIdentifier - identifier of entity that we are caching, should be the one passed as parameter + // to getAllIds or readCurrentAttributes. Caching is by default performed based on this key + // param - can be anything that needs to be bind to request + .withExecutor((instanceIdentifier, param) -> { + // creates dump request + final VxlanTunnelDump vxlanTunnelDump = new VxlanTunnelDump(); + // binds parameters, in this case index of interface + vxlanTunnelDump.swIfIndex = param; + // perform dump action with default timeout and either return result or throw ReadFailedException + // identified by provided instanceIdentifier + return getReplyForRead(jVppCore.vxlanTunnelDump(vxlanTunnelDump).toCompletableFuture(), instanceIdentifier); + }) + // this provides type-awareness for caching, so multiple DumpManagers can be used withing the same + // customizer, using same instance identifiers, as long as they handle different data types + .acceptOnly(VxlanTunnelDetailsReplyDump.class) + + // either acceptOnly is required or custom cache key factory must be provided to tell manager, + // how to produce keys. can be used to change caching scope of data + //.withCacheKeyFactory() + + // serves as post-dump processing of any kind, triggered only once after calling executor + //.withPostProcessingFunction() + .build(); } /** @@ -148,24 +202,14 @@ public final class VxlanReadCustomizer implements public List<VxlanTunnelKey> getAllIds(@Nonnull final InstanceIdentifier<VxlanTunnel> id, @Nonnull final ReadContext context) throws ReadFailedException { - // Create Dump request - final VxlanTunnelDump vxlanTunnelDump = new VxlanTunnelDump(); - // Set Dump request attributes - // Set interface index to 0, so all interfaces are dumped and we can get the list of all IDs - vxlanTunnelDump.swIfIndex = 0; - final VxlanTunnelDetailsReplyDump reply; - try { - reply = TranslateUtils.getReplyForRead(jVppCore.vxlanTunnelDump(vxlanTunnelDump).toCompletableFuture(), id); - } catch (VppBaseCallException e) { - throw new ReadFailedException(id, e); - } - // Check for empty response (no vxlan tunnels to read) - if (reply == null || reply.vxlanTunnelDetails == null) { + final Optional<VxlanTunnelDetailsReplyDump> dump = dumpManager.getDump(id, context.getModificationCache(), 0); + + if (!dump.isPresent()) { return Collections.emptyList(); } - return reply.vxlanTunnelDetails.stream() + return dump.get().vxlanTunnelDetails.stream() // Need a name of an interface here. Use context to look it up from index // In case the naming context does not contain such mapping, it creates an artificial one .map(a -> new VxlanTunnelKey(vxlanNamingContext.getName(a.swIfIndex, context.getMappingContext()))) @@ -196,31 +240,23 @@ public final class VxlanReadCustomizer implements // The ID received here contains the name of a particular interface that should be read // It was either requested directly by HC users or is one of the IDs from getAllIds that could have been invoked // just before this method invocation + final String vxlanName = id.firstKeyOf(VxlanTunnel.class).getId(); - // Create Dump request - final VxlanTunnelDump vxlanTunnelDump = new VxlanTunnelDump(); - // Set Dump request attributes - // Set the vxlan index from naming context // Naming context must contain the mapping because: // 1. The vxlan tunnel was created in VPP using HC + this plugin meaning we stored the mapping in write customizer // 2. The vxlan tunnel was already present in VPP, but HC reconciliation mechanism took care of that (as long as proper Initializer is provided by this plugin) - final String vxlanName = id.firstKeyOf(VxlanTunnel.class).getId(); - vxlanTunnelDump.swIfIndex = vxlanNamingContext.getIndex(vxlanName, ctx.getMappingContext()); - final VxlanTunnelDetailsReplyDump reply; - try { - reply = TranslateUtils.getReplyForRead(jVppCore.vxlanTunnelDump(vxlanTunnelDump).toCompletableFuture(), id); - } catch (VppBaseCallException e) { - throw new ReadFailedException(id, e); - } + final Optional<VxlanTunnelDetailsReplyDump> dump = dumpManager.getDump(id, ctx.getModificationCache(), + vxlanNamingContext.getIndex(vxlanName, ctx.getMappingContext())); + - Preconditions.checkState(reply != null && reply.vxlanTunnelDetails != null); - final VxlanTunnelDetails singleVxlanDetail = reply.vxlanTunnelDetails.stream().findFirst().get(); + Preconditions.checkState(dump.isPresent() && dump.get().vxlanTunnelDetails != null); + final VxlanTunnelDetails singleVxlanDetail = dump.get().vxlanTunnelDetails.stream().findFirst().get(); // Now translate all attributes into provided builder - final Boolean isIpv6 = TranslateUtils.byteToBoolean(singleVxlanDetail.isIpv6); - builder.setSrc(TranslateUtils.arrayToIpAddress(isIpv6, singleVxlanDetail.srcAddress)); - builder.setDst(TranslateUtils.arrayToIpAddress(isIpv6, singleVxlanDetail.dstAddress)); + final Boolean isIpv6 = byteToBoolean(singleVxlanDetail.isIpv6); + builder.setSrc(arrayToIpAddress(isIpv6, singleVxlanDetail.srcAddress)); + builder.setDst(arrayToIpAddress(isIpv6, singleVxlanDetail.dstAddress)); // There are additional attributes of a vxlan tunnel that wont be used here } } @@ -230,21 +266,22 @@ The '"ReaderFactory also needs to be updated: [source,java] ---- -package io.fd.honeycomb.tutorial.read; +package io.fd.hc2vpp.samples.read; import com.google.inject.Inject; +import io.fd.hc2vpp.common.translate.util.NamingContext; import io.fd.honeycomb.translate.impl.read.GenericListReader; import io.fd.honeycomb.translate.read.ReaderFactory; import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; -import io.fd.honeycomb.translate.v3po.util.NamingContext; -import javax.annotation.Nonnull; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.SamplePluginState; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.SamplePluginStateBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.Vxlans; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.VxlansBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnel; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.SamplePluginState; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.SamplePluginStateBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.Vxlans; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.VxlansBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnel; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.openvpp.jvpp.core.future.FutureJVppCore; + +import javax.annotation.Nonnull; /** * Factory producing readers for sample-plugin plugin's data. @@ -292,26 +329,32 @@ Rename to VxlanWriteCustomizer. Update the code to: [source,java] ---- -package io.fd.honeycomb.tutorial.write; +package io.fd.hc2vpp.samples.write; + +import io.fd.hc2vpp.common.translate.util.AddressTranslator; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; -import io.fd.honeycomb.translate.v3po.util.NamingContext; -import io.fd.honeycomb.translate.v3po.util.TranslateUtils; import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; -import javax.annotation.Nonnull; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnel; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnelKey; +import io.fd.vpp.jvpp.core.dto.VxlanAddDelTunnel; +import io.fd.vpp.jvpp.core.dto.VxlanAddDelTunnelReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnelKey; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.openvpp.jvpp.VppBaseCallException; -import org.openvpp.jvpp.core.dto.VxlanAddDelTunnel; -import org.openvpp.jvpp.core.dto.VxlanAddDelTunnelReply; -import org.openvpp.jvpp.core.future.FutureJVppCore; + +import javax.annotation.Nonnull; /** * Writer for {@link VxlanTunnel} list node from our YANG model. */ -public final class VxlanWriteCustomizer implements ListWriterCustomizer<VxlanTunnel, VxlanTunnelKey> { +public final class VxlanWriteCustomizer implements ListWriterCustomizer<VxlanTunnel, VxlanTunnelKey>, + ByteDataTranslator, + AddressTranslator, + JvppReplyConsumer { /** * JVpp APIs @@ -337,22 +380,18 @@ public final class VxlanWriteCustomizer implements ListWriterCustomizer<VxlanTun vxlanAddDelTunnel.isAdd = 1; // dataAfter is the new vxlanTunnel configuration final boolean isIpv6 = dataAfter.getSrc().getIpv6Address() != null; - vxlanAddDelTunnel.isIpv6 = TranslateUtils.booleanToByte(isIpv6); - vxlanAddDelTunnel.srcAddress = TranslateUtils.ipAddressToArray(isIpv6, dataAfter.getSrc()); - vxlanAddDelTunnel.dstAddress = TranslateUtils.ipAddressToArray(isIpv6, dataAfter.getDst()); + vxlanAddDelTunnel.isIpv6 = booleanToByte(isIpv6); + vxlanAddDelTunnel.srcAddress = ipAddressToArray(isIpv6, dataAfter.getSrc()); + vxlanAddDelTunnel.dstAddress = ipAddressToArray(isIpv6, dataAfter.getDst()); // There are other input parameters that are not exposed by our YANG model, default values will be used - try { - final VxlanAddDelTunnelReply replyForWrite = TranslateUtils - .getReplyForWrite(jvppCore.vxlanAddDelTunnel(vxlanAddDelTunnel).toCompletableFuture(), id); - // VPP returns the index of new vxlan tunnel - final int newVxlanTunnelIndex = replyForWrite.swIfIndex; - // It's important to store it in context so that reader knows to which name a vxlan tunnel is mapped - vxlanTunnelNamingContext.addName(newVxlanTunnelIndex, dataAfter.getId(), writeContext.getMappingContext()); - } catch (VppBaseCallException e) { - throw new WriteFailedException.CreateFailedException(id, dataAfter, e); - } + final VxlanAddDelTunnelReply replyForWrite = getReplyForWrite(jvppCore.vxlanAddDelTunnel(vxlanAddDelTunnel).toCompletableFuture(), id); + + // VPP returns the index of new vxlan tunnel + final int newVxlanTunnelIndex = replyForWrite.swIfIndex; + // It's important to store it in context so that reader knows to which name a vxlan tunnel is mapped + vxlanTunnelNamingContext.addName(newVxlanTunnelIndex, dataAfter.getId(), writeContext.getMappingContext()); } @Override @@ -376,19 +415,14 @@ public final class VxlanWriteCustomizer implements ListWriterCustomizer<VxlanTun // Vxlan tunnel is identified by its attributes when deleting, not index, so set all attributes // dataBefore is the vxlan tunnel that's being deleted final boolean isIpv6 = dataBefore.getSrc().getIpv6Address() != null; - vxlanAddDelTunnel.isIpv6 = TranslateUtils.booleanToByte(isIpv6); - vxlanAddDelTunnel.srcAddress = TranslateUtils.ipAddressToArray(isIpv6, dataBefore.getSrc()); - vxlanAddDelTunnel.dstAddress = TranslateUtils.ipAddressToArray(isIpv6, dataBefore.getDst()); + vxlanAddDelTunnel.isIpv6 = booleanToByte(isIpv6); + vxlanAddDelTunnel.srcAddress = ipAddressToArray(isIpv6, dataBefore.getSrc()); + vxlanAddDelTunnel.dstAddress = ipAddressToArray(isIpv6, dataBefore.getDst()); // There are other input parameters that are not exposed by our YANG model, default values will be used - try { - final VxlanAddDelTunnelReply replyForWrite = TranslateUtils - .getReplyForWrite(jvppCore.vxlanAddDelTunnel(vxlanAddDelTunnel).toCompletableFuture(), id); - // It's important to remove the mapping from context - vxlanTunnelNamingContext.removeName(dataBefore.getId(), writeContext.getMappingContext()); - } catch (VppBaseCallException e) { - throw new WriteFailedException.DeleteFailedException(id, e); - } + final VxlanAddDelTunnelReply replyForWrite = getReplyForWrite(jvppCore.vxlanAddDelTunnel(vxlanAddDelTunnel).toCompletableFuture(), id); + // It's important to remove the mapping from context + vxlanTunnelNamingContext.removeName(dataBefore.getId(), writeContext.getMappingContext()); } } ---- @@ -397,19 +431,19 @@ The '"WriterFactory also needs to be updated: [source,java] ---- -package io.fd.honeycomb.tutorial.write; +package io.fd.hc2vpp.samples.write; import com.google.inject.Inject; +import io.fd.hc2vpp.common.translate.util.NamingContext; import io.fd.honeycomb.translate.impl.write.GenericWriter; -import io.fd.honeycomb.translate.v3po.util.NamingContext; import io.fd.honeycomb.translate.write.WriterFactory; import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; -import javax.annotation.Nonnull; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.SamplePlugin; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.Vxlans; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnel; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.SamplePlugin; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.Vxlans; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnel; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.openvpp.jvpp.core.future.FutureJVppCore; +import javax.annotation.Nonnull; /** * Factory producing writers for sample-plugin plugin's data. @@ -453,17 +487,15 @@ and the code needs to look like: [source,java] ---- -package io.fd.honeycomb.tutorial; +package io.fd.hc2vpp.samples; import com.google.inject.AbstractModule; import com.google.inject.multibindings.Multibinder; -import io.fd.honeycomb.data.init.DataTreeInitializer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.hc2vpp.samples.read.ModuleStateReaderFactory; +import io.fd.hc2vpp.samples.write.ModuleWriterFactory; import io.fd.honeycomb.translate.read.ReaderFactory; -import io.fd.honeycomb.translate.v3po.util.NamingContext; import io.fd.honeycomb.translate.write.WriterFactory; -import io.fd.honeycomb.tutorial.init.ConfigDataInitializer; -import io.fd.honeycomb.tutorial.read.ModuleStateReaderFactory; -import io.fd.honeycomb.tutorial.write.ModuleWriterFactory; import net.jmob.guice.conf.core.ConfigurationModule; /** @@ -493,11 +525,6 @@ public final class Module extends AbstractModule { final Multibinder<WriterFactory> writerFactoryBinder = Multibinder.newSetBinder(binder(), WriterFactory.class); writerFactoryBinder.addBinding().to(ModuleWriterFactory.class); - // create initializer binding - // can hold multiple binding for separate yang modules - final Multibinder<DataTreeInitializer> initializerBinder = - Multibinder.newSetBinder(binder(), DataTreeInitializer.class); - initializerBinder.addBinding().to(ConfigDataInitializer.class); // Disable notification producer for now // Multibinder.newSetBinder(binder(), ManagedNotificationProducer.class).addBinding() @@ -526,32 +553,13 @@ In order to add this new plugin into vpp-integration: </dependency> ---- -* modify Main of vpp-integration distribution to include sample-plugin (/home/mmarsale/Projects/honeycomb/vpp-integration/minimal-distribution/src/main/java/io/fd/honeycomb/vpp/integration/distro/Main.java): -[source,java] +* modify Modify vpp-integration-distribution pom.xml property <distribution.modules> +[source,xml,subs="+attributes"] ---- -package io.fd.honeycomb.vpp.integration.distro; - -import com.google.common.collect.Lists; -import com.google.inject.Module; -import io.fd.honeycomb.vpp.distro.VppCommonModule; -import java.util.List; - -public class Main { - - public static void main(String[] args) { - final List<Module> sampleModules = Lists.newArrayList(io.fd.honeycomb.infra.distro.Main.BASE_MODULES); - - // All the plugins should be listed here - sampleModules.add(new VppCommonModule()); -// Comment out V3po and Lisp module for the time being, since V3po and sample-plugin are in conflict over vxlan tunnel management -// a plugin implementing VPP's API that's not yet covered by V3po or LISP plugin would not have to do this -// sampleModules.add(new V3poModule()); -// sampleModules.add(new LispModule()); - sampleModules.add(new io.fd.honeycomb.tutorial.Module()); - - io.fd.honeycomb.infra.distro.Main.init(sampleModules); - } -} + <distribution.modules> + some.module.full.package.name.ModuleClass, + io.fd.hc2vpp.samples.Module //add your plugin module class +</distribution.modules> ---- Now just rebuild the honeycomb project. @@ -559,24 +567,27 @@ Now just rebuild the honeycomb project. == Verifying distribution At this point, the vpp-integration distribution with sample-plugin can now be started. But first, make sure that a compatible version of VPP is installed and running. Next, start honeycomb with: - sudo vpp-integration/minimal-distribution/target/vpp-integration-distribution-1.16.9-hc/vpp-integration-distribution-1.16.9/honeycomb +[source,xml,subs="+attributes"] +---- +sudo hc2vpp/vpp-integration/minimal-distribution/target/vpp-integration-distribution-{project-version}-hc/vpp-integration-distribution-{project-version} +---- === Testing over RESTCONF Reading vxlans operational data (should return empty vxlans container at first): - curl -u admin:admin http://localhost:8181/restconf/operational/sample-plugin:sample-plugin-state + curl -u admin:admin http://localhost:8183/restconf/operational/sample-plugin:sample-plugin-state Adding a vxlan tunnel: - curl -H 'Content-Type: application/json' -H 'Accept: application/json' -u admin:admin -X PUT -d '{"vxlans":{"vxlan-tunnel": [{"id":"vxlan-test-tunnel", "src":"10.0.0.1", "dst":"10.0.0.2"}]}}' http://localhost:8181/restconf/config/sample-plugin:sample-plugin/vxlans + curl -H 'Content-Type: application/json' -H 'Accept: application/json' -u admin:admin -X PUT -d '{"vxlans":{"vxlan-tunnel": [{"id":"vxlan-test-tunnel", "src":"10.0.0.1", "dst":"10.0.0.2"}]}}' http://localhost:8183/restconf/config/sample-plugin:sample-plugin/vxlans Reading vxlans config data (data that we posted to Honeycomb): - curl -u admin:admin http://localhost:8181/restconf/config/sample-plugin:sample-plugin + curl -u admin:admin http://localhost:8183/restconf/config/sample-plugin:sample-plugin Reading vxlans operational data (data coming from VPP being transformed by ReaderCustomizer on the fly): - curl -u admin:admin http://localhost:8181/restconf/operational/sample-plugin:sample-plugin-state + curl -u admin:admin http://localhost:8183/restconf/operational/sample-plugin:sample-plugin-state Verifying vxlan tunnel existence in VPP: @@ -594,7 +605,7 @@ vxlan_tunnel0 1 up Deleting a vxlan tunnel: - curl -u admin:admin -X DELETE http://localhost:8181/restconf/config/sample-plugin:sample-plugin/vxlans/vxlan-tunnel/vxlan-test-tunnel + curl -u admin:admin -X DELETE http://localhost:8183/restconf/config/sample-plugin:sample-plugin/vxlans/vxlan-tunnel/vxlan-test-tunnel Disclaimer: The vxlan tunnel will be removed from Honeycomb, and delete command will be executed on VPP, but VPP will just disable that interface and keep it as some sort of placeholder for next vxlan tunnel (that's VPPs behavior, so a vxlan tunnel cant be really deleted). So that's why you would still see the tunnel in VPP's CLI after delete. diff --git a/release-notes/src/main/asciidoc/release_notes.adoc b/release-notes/src/main/asciidoc/release_notes.adoc index 2762501fd..11dce0848 100644 --- a/release-notes/src/main/asciidoc/release_notes.adoc +++ b/release-notes/src/main/asciidoc/release_notes.adoc @@ -6,18 +6,27 @@ Release notes for honeycomb for VPP {project-version}. Based on VPP {project-vpp https://wiki.fd.io/view/Honeycomb/Release_Plans/Release_Plan_{project-public-version}[Release plan] == Features -Adding to the list of existing features: https://wiki.fd.io/view/Honeycomb/Releases/1609#Features[1609 feature list]: +Adding to the list of existing features: https://wiki.fd.io/view/Honeycomb/Releases/1701#Features[1701 feature list]: === New features * https://jira.fd.io/browse/HONEYCOMB-132[NAT management] * https://jira.fd.io/browse/HONEYCOMB-257[iOAM management] +* https://jira.fd.io/browse/HONEYCOMB-58[Static routing management] +* https://jira.fd.io/browse/HONEYCOMB-303[Stateful acl's management] +* https://jira.fd.io/browse/HONEYCOMB-46[NSH management] +* https://jira.fd.io/browse/HONEYCOMB-242[Port mirroring] +* https://jira.fd.io/browse/HONEYCOMB-281[Loopback interface management] +* https://jira.fd.io/browse/HONEYCOMB-102[Ipv6 support] === Removed features === Modified features -* ACL management improvements +* ACL management improvements: +* Previous ACL implementation that used classfier has been disabled in favor of implementation + based on acl plugin (which provides also stateful acls) === Deprecated features +* ACL management in terms of ietf-acl model based on vpp classfier == Backlog diff --git a/release-notes/src/main/asciidoc/user_guide/user_guide.adoc b/release-notes/src/main/asciidoc/user_guide/user_guide.adoc index 0f72c8bc9..16cf90253 100644 --- a/release-notes/src/main/asciidoc/user_guide/user_guide.adoc +++ b/release-notes/src/main/asciidoc/user_guide/user_guide.adoc @@ -13,17 +13,15 @@ link:user_honeycomb_and_ODL.html[Mounting Honeycomb in ODL] Honeycomb's configuration files present within its distribution: * Honeycomb infra: -** {project-git-web}/infra/minimal-distribution/src/main/resources/honeycomb-minimal-resources/config/honeycomb.json?h={project-branch}[Honeycomb base configuration] +** {project-infra-git-web}/infra/minimal-distribution/src/main/resources/honeycomb-minimal-resources/config/honeycomb.json?h={project-branch}[Honeycomb base configuration] * Honeycomb vpp plugins commons: ** {project-git-web}/vpp-common/vpp-common-integration/src/main/resources/honeycomb-minimal-resources/config/jvpp.json?h={project-branch}[VPP plugins common configuration] * V3PO plugin: ** {project-git-web}/v3po/v3po2vpp/src/main/resources/honeycomb-minimal-resources/config/v3po.json?h={project-branch}[V3PO plugin for Honeycomb configuration] * LISP plugin ** {project-git-web}/lisp/lisp2vpp/src/main/resources/honeycomb-minimal-resources/config/lisp.json?h={project-branch}[LISP plugin for Honeycomb configuration] -* NSH plugin: -** {project-git-web}/nsh/impl/src/main/resources/honeycomb-minimal-resources/config/vppnsh.json?h={project-branch}[NSH_SFC plugin for Honeycomb configuration] -* iOAM plugin: -** {project-git-web}/ioam/impl/src/main/resources/honeycomb-minimal-resources/config/vppioam.json?h={project-branch}[IOAM plugin for Honeycomb configuration] +* Routing plugin: +** {project-git-web}/routing/routing-impl/src/main/resources/honeycomb-minimal-resources/config/routing.json?h={project-branch}[Routing plugin for Honeycomb configuration] === YANG models @@ -37,6 +35,10 @@ Honeycomb's configuration files present within its distribution: ** {project-git-web}/nat/nat-api/src/main/yang?h={project-branch}[NAT YANG models] * iOAM plugin ** {project-git-web}/ioam/api/src/main/yang?h={project-branch}[IOAM YANG models] +* Routing plugin +** {project-git-web}/routing/routing-api/src/main/yang?h={project-branch}[Routing YANG models] +* ACL plugin +** {project-git-web}/acl/acl-api/src/main/yang?h={project-branch}[ACL YANG models] * Context models ** {project-git-web}/vpp-common/naming-context-api/src/main/yang?h={project-branch}[Context YANG models] @@ -45,10 +47,14 @@ Honeycomb's configuration files present within its distribution: * V3PO plugin ** {project-git-web}/v3po/postman_rest_collection.json?h={project-branch}[V3PO postman collection] * LISP plugin -** {project-git-web}/lisp/lisp_postman_rest_collection.json?h={project-branch}[LISP postman collection] +** {project-git-web}/lisp/lisp_postman_collection.json?h={project-branch}[LISP postman collection] * NSH plugin -** {project-git-web}/nsh/nsh_postman_rest_collection.json?h={project-branch}[NSH postman collection] +** {project-git-web}/nsh/nsh_postman_collection.json?h={project-branch}[NSH postman collection] * NAT plugin ** {project-git-web}/nat/postman_rest_collection.json?h={project-branch}[NAT postman collection] * iOAM plugin -** {project-git-web}/ioam/ioam_trace_postman_collection.json?h={project-branch}[IOAM postman collection] +** {project-git-web}/ioam/ioam_postman_collection.json?h={project-branch}[IOAM postman collection] +* Routing plugin +** {project-git-web}/routing/routing_postman_collection.json?h={project-branch}[Routing postman collection] +* Stateful ACL plugin +** {project-git-web}/acl/acl_postman_collection.json?h={project-branch}[Stateful ACL postman collection] diff --git a/samples/asciidoc/Readme.adoc b/samples/asciidoc/Readme.adoc new file mode 100644 index 000000000..82961d0fe --- /dev/null +++ b/samples/asciidoc/Readme.adoc @@ -0,0 +1,3 @@ += samples + +Overview of samples
\ No newline at end of file diff --git a/samples/pom.xml b/samples/pom.xml new file mode 100644 index 000000000..a2e058e49 --- /dev/null +++ b/samples/pom.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>hc2vpp-aggregator</artifactId> + <groupId>io.fd.hc2vpp</groupId> + <version>1.17.04-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <groupId>io.fd.hc2vpp.samples</groupId> + <artifactId>samples</artifactId> + <packaging>pom</packaging> + + <modules> + <module>samples-api</module> + <module>samples-impl</module> + </modules> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-install-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> +</project>
\ No newline at end of file diff --git a/samples/samples-api/asciidoc/Readme.adoc b/samples/samples-api/asciidoc/Readme.adoc new file mode 100644 index 000000000..7bc14dc6f --- /dev/null +++ b/samples/samples-api/asciidoc/Readme.adoc @@ -0,0 +1,3 @@ += samples-api + +Overview of samples-api
\ No newline at end of file diff --git a/samples/samples-api/pom.xml b/samples/samples-api/pom.xml new file mode 100644 index 000000000..a6fa1f63b --- /dev/null +++ b/samples/samples-api/pom.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <groupId>io.fd.honeycomb.common</groupId> + <artifactId>api-parent</artifactId> + <version>1.17.04-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <groupId>io.fd.hc2vpp.common</groupId> + <artifactId>samples-api</artifactId> + <version>1.17.01-SNAPSHOT</version> + <packaging>bundle</packaging> + + <dependencies> + <dependency> + <groupId>org.opendaylight.mdsal.model</groupId> + <artifactId>ietf-inet-types-2013-07-15</artifactId> + </dependency> + </dependencies> +</project>
\ No newline at end of file diff --git a/samples/samples-api/src/main/yang/sample-plugin.yang b/samples/samples-api/src/main/yang/sample-plugin.yang new file mode 100644 index 000000000..099ab65b8 --- /dev/null +++ b/samples/samples-api/src/main/yang/sample-plugin.yang @@ -0,0 +1,42 @@ +module sample-plugin { + + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:sample:plugin"; + prefix "samples"; + + revision 2016-12-14 { + description "Sample model for demonstration of transation code"; + } + + import ietf-inet-types { + prefix "inet"; + } + + grouping sample-plugin-params { + container vxlans { + list vxlan-tunnel { + + key id; + leaf id { + type string; + } + + leaf src { + type inet:ip-address; + } + leaf dst { + type inet:ip-address; + } + } + } + } + + container sample-plugin-state { + config false; + uses sample-plugin-params; + } + + container sample-plugin { + uses sample-plugin-params; + } +}
\ No newline at end of file diff --git a/samples/samples-impl/asciidoc/Readme.adoc b/samples/samples-impl/asciidoc/Readme.adoc new file mode 100644 index 000000000..893c0ebfb --- /dev/null +++ b/samples/samples-impl/asciidoc/Readme.adoc @@ -0,0 +1,3 @@ += samples-impl + +Overview of samples-impl
\ No newline at end of file diff --git a/samples/samples-impl/pom.xml b/samples/samples-impl/pom.xml new file mode 100644 index 000000000..72938cbca --- /dev/null +++ b/samples/samples-impl/pom.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <groupId>io.fd.hc2vpp.common</groupId> + <artifactId>vpp-impl-parent</artifactId> + <version>1.17.04-SNAPSHOT</version> + <relativePath>../../vpp-common/vpp-impl-parent</relativePath> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>samples-impl</artifactId> + <version>1.17.01-SNAPSHOT</version> + + <dependencies> + <!-- Api classes generated from yang model --> + <dependency> + <groupId>io.fd.hc2vpp.common</groupId> + <artifactId>samples-api</artifactId> + <version>${project.version}</version> + </dependency> + <!-- Infrastructure communication interfaces --> + <dependency> + <groupId>io.fd.honeycomb</groupId> + <artifactId>translate-spi</artifactId> + <version>${project.version}</version> + </dependency> + <!-- Vpp api java wrapped --> + <dependency> + <groupId>io.fd.vpp</groupId> + <artifactId>jvpp-core</artifactId> + </dependency> + <!-- Infrastructure utils for translation code --> + <dependency> + <groupId>io.fd.honeycomb</groupId> + <artifactId>translate-utils</artifactId> + <version>${project.version}</version> + </dependency> + <!-- Jvpp specific utils for translation code --> + <dependency> + <groupId>io.fd.hc2vpp.common</groupId> + <artifactId>vpp-translate-utils</artifactId> + <version>${project.version}</version> + </dependency> + <!-- Generic implementations for communication interfaces--> + <dependency> + <groupId>io.fd.honeycomb</groupId> + <artifactId>translate-impl</artifactId> + <version>${project.version}</version> + </dependency> + <!-- Initialization interfaces for infrastructure--> + <dependency> + <groupId>io.fd.honeycomb</groupId> + <artifactId>cfg-init</artifactId> + <version>${project.version}</version> + </dependency> + <!-- Google juice injection --> + <dependency> + <groupId>com.google.inject</groupId> + <artifactId>guice</artifactId> + </dependency> + <!-- Google juice multibindings --> + <dependency> + <groupId>com.google.inject.extensions</groupId> + <artifactId>guice-multibindings</artifactId> + </dependency> + <!-- Configuration injection --> + <dependency> + <groupId>net.jmob</groupId> + <artifactId>guice.conf</artifactId> + </dependency> + </dependencies> + +</project>
\ No newline at end of file diff --git a/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/Module.java b/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/Module.java new file mode 100644 index 000000000..d6f38caaf --- /dev/null +++ b/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/Module.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 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.hc2vpp.samples; + +import com.google.inject.AbstractModule; +import com.google.inject.multibindings.Multibinder; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.hc2vpp.samples.read.ModuleStateReaderFactory; +import io.fd.hc2vpp.samples.write.ModuleWriterFactory; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.write.WriterFactory; +import net.jmob.guice.conf.core.ConfigurationModule; + + +/** + * Module class instantiating sample-plugin plugin components. + */ +public final class Module extends AbstractModule { + + @Override + protected void configure() { + // requests injection of properties + install(ConfigurationModule.create()); + requestInjection(ModuleConfiguration.class); + + // bind naming context instance for reader and writer factories + // the first parameter is artificial name prefix in cases a name needs to be reconstructed for a vxlan tunnel + // that is present in VPP but not in Honeycomb (could be extracted into configuration) + // the second parameter is just the naming context ID (could be extracted into configuration) + binder().bind(NamingContext.class).toInstance(new NamingContext("vxlan-tunnel", "vxlan-tunnel-context")); + + // creates reader factory binding + // can hold multiple binding for separate yang modules + final Multibinder<ReaderFactory> readerFactoryBinder = Multibinder.newSetBinder(binder(), ReaderFactory.class); + readerFactoryBinder.addBinding().to(ModuleStateReaderFactory.class); + + // create writer factory binding + // can hold multiple binding for separate yang modules + final Multibinder<WriterFactory> writerFactoryBinder = Multibinder.newSetBinder(binder(), WriterFactory.class); + writerFactoryBinder.addBinding().to(ModuleWriterFactory.class); + + + // Disable notification producer for now +// Multibinder.newSetBinder(binder(), ManagedNotificationProducer.class).addBinding() +// .to(SampleNotificationProducer.class); + } +}
\ No newline at end of file diff --git a/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/ModuleConfiguration.java b/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/ModuleConfiguration.java new file mode 100644 index 000000000..6766213ab --- /dev/null +++ b/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/ModuleConfiguration.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017 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.hc2vpp.samples; + + +import net.jmob.guice.conf.core.BindConfig; +import net.jmob.guice.conf.core.InjectConfig; +import net.jmob.guice.conf.core.Syntax; + +@BindConfig(value = "module", syntax = Syntax.JSON) +public class ModuleConfiguration { + + // inject property from resource file + @InjectConfig(value = "some.prop.name") + String somePropValue; +} diff --git a/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/read/ModuleStateReaderFactory.java b/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/read/ModuleStateReaderFactory.java new file mode 100644 index 000000000..83e91a2ad --- /dev/null +++ b/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/read/ModuleStateReaderFactory.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017 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.hc2vpp.samples.read; + +import com.google.inject.Inject; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.impl.read.GenericListReader; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.SamplePluginState; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.SamplePluginStateBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.Vxlans; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.VxlansBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnel; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import javax.annotation.Nonnull; + +/** + * Factory producing readers for sample-plugin plugin's data. + */ +public final class ModuleStateReaderFactory implements ReaderFactory { + + public static final InstanceIdentifier<SamplePluginState> ROOT_STATE_CONTAINER_ID = + InstanceIdentifier.create(SamplePluginState.class); + + /** + * Injected vxlan naming context shared with writer, provided by this plugin + */ + @Inject + private NamingContext vxlanNamingContext; + /** + * Injected jvpp core APIs, provided by Honeycomb's infrastructure + */ + @Inject + private FutureJVppCore jvppCore; + + @Override + public void init(@Nonnull final ModifiableReaderRegistryBuilder registry) { + // register reader that only delegate read's to its children + registry.addStructuralReader(ROOT_STATE_CONTAINER_ID, SamplePluginStateBuilder.class); + // register reader that only delegate read's to its children + registry.addStructuralReader(ROOT_STATE_CONTAINER_ID.child(Vxlans.class), VxlansBuilder.class); + + // just adds reader to the structure + // use addAfter/addBefore if you want to add specific order to readers on the same level of tree + // use subtreeAdd if you want to handle multiple nodes in single customizer/subtreeAddAfter/subtreeAddBefore if you also want to add order + // be aware that instance identifier passes to subtreeAdd/subtreeAddAfter/subtreeAddBefore should define subtree, + // therefore it should be relative from handled node down - InstanceIdentifier.create(HandledNode), not parent.child(HandledNode.class) + registry.add(new GenericListReader<>( + // What part of subtree this reader handles is identified by an InstanceIdentifier + ROOT_STATE_CONTAINER_ID.child(Vxlans.class).child(VxlanTunnel.class), + // Customizer (the actual translation code to do the heavy lifting) + new VxlanReadCustomizer(jvppCore, vxlanNamingContext))); + } +}
\ No newline at end of file diff --git a/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/read/VxlanReadCustomizer.java b/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/read/VxlanReadCustomizer.java new file mode 100644 index 000000000..133155b6f --- /dev/null +++ b/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/read/VxlanReadCustomizer.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017 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.hc2vpp.samples.read; + + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import io.fd.hc2vpp.common.translate.util.AddressTranslator; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ListReaderCustomizer; +import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager; +import io.fd.vpp.jvpp.core.dto.VxlanTunnelDetails; +import io.fd.vpp.jvpp.core.dto.VxlanTunnelDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.VxlanTunnelDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.VxlansBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnelBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnelKey; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + + +/** + * Reader for {@link VxlanTunnel} list node from our YANG model. + */ +public final class VxlanReadCustomizer implements + ListReaderCustomizer<VxlanTunnel, VxlanTunnelKey, VxlanTunnelBuilder>, + // provides utility methods to translate binary data + ByteDataTranslator, + // provides utility methods to translate Ipv4,Ipv6,Mac addresses. + // in case that just one address family processing is needed,use *address-family-name*Translator, + // for ex Ipv4Translator + AddressTranslator, + // provides utility methods to consume results of jvpp api calls + JvppReplyConsumer { + + // JVpp core. This is the Java API for VPP's core API. + private final FutureJVppCore jVppCore; + // Naming context for interfaces + // Honeycomb provides a "context" storage for plugins. This storage is used for storing metadata required during + // data translation (just like in this plugin). An example of such metadata would be interface identifier. In Honeycomb + // we use string names for interfaces, however VPP uses only indices (that are created automatically). + // This means that translation layer has to store the mapping between HC interface name <-> VPP' interface index. + // And since vxlan tunnel is a type of interface in VPP, the same applies here + // + // Honeycomb provides a couple utilities on top of context storage such as NamingContext. It is just a map + // backed by context storage that makes the lookup and storing easier. + private final NamingContext vxlanNamingContext; + + // Dump manager that provides intelligent caching based on provided contextual key + private DumpCacheManager<VxlanTunnelDetailsReplyDump, Integer> dumpManager; + + public VxlanReadCustomizer(final FutureJVppCore jVppCore, final NamingContext vxlanNamingContext) { + this.jVppCore = jVppCore; + this.vxlanNamingContext = vxlanNamingContext; + + this.dumpManager = new DumpCacheManager.DumpCacheManagerBuilder<VxlanTunnelDetailsReplyDump, Integer>() + // executor handles dumping of data itself, based on provided lambda + // instanceIdentifier - identifier of entity that we are caching, should be the one passed as parameter + // to getAllIds or readCurrentAttributes. Caching is by default performed based on this key + // param - can be anything that needs to be bind to request + .withExecutor((instanceIdentifier, param) -> { + // creates dump request + final VxlanTunnelDump vxlanTunnelDump = new VxlanTunnelDump(); + // binds parameters, in this case index of interface + vxlanTunnelDump.swIfIndex = param; + // perform dump action with default timeout and either return result or throw ReadFailedException + // identified by provided instanceIdentifier + return getReplyForRead(jVppCore.vxlanTunnelDump(vxlanTunnelDump).toCompletableFuture(), instanceIdentifier); + }) + // this provides type-awareness for caching, so multiple DumpManagers can be used withing the same + // customizer, using same instance identifiers, as long as they handle different data types + .acceptOnly(VxlanTunnelDetailsReplyDump.class) + + // either acceptOnly is required or custom cache key factory must be provided to tell manager, + // how to produce keys. can be used to change caching scope of data + //.withCacheKeyFactory() + + // serves as post-dump processing of any kind, triggered only once after calling executor + //.withPostProcessingFunction() + .build(); + } + + /** + * Provide a list of IDs for all VXLANs in VPP + */ + @Nonnull + @Override + public List<VxlanTunnelKey> getAllIds(@Nonnull final InstanceIdentifier<VxlanTunnel> id, + @Nonnull final ReadContext context) + throws ReadFailedException { + + final Optional<VxlanTunnelDetailsReplyDump> dump = dumpManager.getDump(id, context.getModificationCache(), 0); + + if (!dump.isPresent()) { + return Collections.emptyList(); + } + + return dump.get().vxlanTunnelDetails.stream() + // Need a name of an interface here. Use context to look it up from index + // In case the naming context does not contain such mapping, it creates an artificial one + .map(a -> new VxlanTunnelKey(vxlanNamingContext.getName(a.swIfIndex, context.getMappingContext()))) + .collect(Collectors.toList()); + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<VxlanTunnel> readData) { + // Just set the readValue into parent builder + // The cast has to be performed here + ((VxlansBuilder) builder).setVxlanTunnel(readData); + } + + @Nonnull + @Override + public VxlanTunnelBuilder getBuilder(@Nonnull final InstanceIdentifier<VxlanTunnel> id) { + // Setting key from id is not necessary, builder will take care of that + return new VxlanTunnelBuilder(); + } + + /** + * Read all the attributes of a single VXLAN tunnel + */ + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<VxlanTunnel> id, + @Nonnull final VxlanTunnelBuilder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + // The ID received here contains the name of a particular interface that should be read + // It was either requested directly by HC users or is one of the IDs from getAllIds that could have been invoked + // just before this method invocation + final String vxlanName = id.firstKeyOf(VxlanTunnel.class).getId(); + + // Naming context must contain the mapping because: + // 1. The vxlan tunnel was created in VPP using HC + this plugin meaning we stored the mapping in write customizer + // 2. The vxlan tunnel was already present in VPP, but HC reconciliation mechanism took care of that (as long as proper Initializer is provided by this plugin) + + final Optional<VxlanTunnelDetailsReplyDump> dump = dumpManager.getDump(id, ctx.getModificationCache(), + vxlanNamingContext.getIndex(vxlanName, ctx.getMappingContext())); + + + Preconditions.checkState(dump.isPresent() && dump.get().vxlanTunnelDetails != null); + final VxlanTunnelDetails singleVxlanDetail = dump.get().vxlanTunnelDetails.stream().findFirst().get(); + + // Now translate all attributes into provided builder + final Boolean isIpv6 = byteToBoolean(singleVxlanDetail.isIpv6); + builder.setSrc(arrayToIpAddress(isIpv6, singleVxlanDetail.srcAddress)); + builder.setDst(arrayToIpAddress(isIpv6, singleVxlanDetail.dstAddress)); + // There are additional attributes of a vxlan tunnel that wont be used here + } +}
\ No newline at end of file diff --git a/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/write/ModuleWriterFactory.java b/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/write/ModuleWriterFactory.java new file mode 100644 index 000000000..346141429 --- /dev/null +++ b/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/write/ModuleWriterFactory.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 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.hc2vpp.samples.write; + +import com.google.inject.Inject; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.impl.write.GenericWriter; +import io.fd.honeycomb.translate.write.WriterFactory; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.SamplePlugin; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.Vxlans; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnel; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import javax.annotation.Nonnull; + +/** + * Factory producing writers for sample-plugin plugin's data. + */ +public final class ModuleWriterFactory implements WriterFactory { + + private static final InstanceIdentifier<SamplePlugin> ROOT_CONTAINER_ID = InstanceIdentifier.create(SamplePlugin.class); + + /** + * Injected vxlan naming context shared with writer, provided by this plugin + */ + @Inject + private NamingContext vxlanNamingContext; + /** + * Injected jvpp core APIs, provided by Honeycomb's infrastructure + */ + @Inject + private FutureJVppCore jvppCore; + + @Override + public void init(@Nonnull final ModifiableWriterRegistryBuilder registry) { + // Unlike ReaderFactory, there's no need to add structural writers, just the writers that actually do something + + // register writer for vxlan tunnel + registry.add(new GenericWriter<>( + // What part of subtree this writer handles is identified by an InstanceIdentifier + ROOT_CONTAINER_ID.child(Vxlans.class).child(VxlanTunnel.class), + // Customizer (the actual translation code to do the heavy lifting) + new VxlanWriteCustomizer(jvppCore, vxlanNamingContext))); + } +}
\ No newline at end of file diff --git a/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/write/VxlanWriteCustomizer.java b/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/write/VxlanWriteCustomizer.java new file mode 100644 index 000000000..77c67b290 --- /dev/null +++ b/samples/samples-impl/src/main/java/io/fd/hc2vpp/samples/write/VxlanWriteCustomizer.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017 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.hc2vpp.samples.write; + + +import io.fd.hc2vpp.common.translate.util.AddressTranslator; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.VxlanAddDelTunnel; +import io.fd.vpp.jvpp.core.dto.VxlanAddDelTunnelReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev161214.sample.plugin.params.vxlans.VxlanTunnelKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import javax.annotation.Nonnull; + +/** + * Writer for {@link VxlanTunnel} list node from our YANG model. + */ +public final class VxlanWriteCustomizer implements ListWriterCustomizer<VxlanTunnel, VxlanTunnelKey>, + ByteDataTranslator, + AddressTranslator, + JvppReplyConsumer { + + /** + * JVpp APIs + */ + private final FutureJVppCore jvppCore; + /** + * Shared vxlan tunnel naming context + */ + private final NamingContext vxlanTunnelNamingContext; + + public VxlanWriteCustomizer(final FutureJVppCore jvppCore, final NamingContext vxlanTunnelNamingContext) { + this.jvppCore = jvppCore; + this.vxlanTunnelNamingContext = vxlanTunnelNamingContext; + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<VxlanTunnel> id, + @Nonnull final VxlanTunnel dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + // Create and set vxlan tunnel add request + final VxlanAddDelTunnel vxlanAddDelTunnel = new VxlanAddDelTunnel(); + // 1 for add, 0 for delete + vxlanAddDelTunnel.isAdd = 1; + // dataAfter is the new vxlanTunnel configuration + final boolean isIpv6 = dataAfter.getSrc().getIpv6Address() != null; + vxlanAddDelTunnel.isIpv6 = booleanToByte(isIpv6); + vxlanAddDelTunnel.srcAddress = ipAddressToArray(isIpv6, dataAfter.getSrc()); + vxlanAddDelTunnel.dstAddress = ipAddressToArray(isIpv6, dataAfter.getDst()); + // There are other input parameters that are not exposed by our YANG model, default values will be used + + + final VxlanAddDelTunnelReply replyForWrite = getReplyForWrite(jvppCore.vxlanAddDelTunnel(vxlanAddDelTunnel).toCompletableFuture(), id); + + // VPP returns the index of new vxlan tunnel + final int newVxlanTunnelIndex = replyForWrite.swIfIndex; + // It's important to store it in context so that reader knows to which name a vxlan tunnel is mapped + vxlanTunnelNamingContext.addName(newVxlanTunnelIndex, dataAfter.getId(), writeContext.getMappingContext()); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<VxlanTunnel> id, + @Nonnull final VxlanTunnel dataBefore, + @Nonnull final VxlanTunnel dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + // Not supported at VPP API level, throw exception + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, + new UnsupportedOperationException("Vxlan tunnel update is not supported by VPP")); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<VxlanTunnel> id, + @Nonnull final VxlanTunnel dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + // Create and set vxlan tunnel add request + final VxlanAddDelTunnel vxlanAddDelTunnel = new VxlanAddDelTunnel(); + // 1 for add, 0 for delete + vxlanAddDelTunnel.isAdd = 0; + // Vxlan tunnel is identified by its attributes when deleting, not index, so set all attributes + // dataBefore is the vxlan tunnel that's being deleted + final boolean isIpv6 = dataBefore.getSrc().getIpv6Address() != null; + vxlanAddDelTunnel.isIpv6 = booleanToByte(isIpv6); + vxlanAddDelTunnel.srcAddress = ipAddressToArray(isIpv6, dataBefore.getSrc()); + vxlanAddDelTunnel.dstAddress = ipAddressToArray(isIpv6, dataBefore.getDst()); + // There are other input parameters that are not exposed by our YANG model, default values will be used + + final VxlanAddDelTunnelReply replyForWrite = getReplyForWrite(jvppCore.vxlanAddDelTunnel(vxlanAddDelTunnel).toCompletableFuture(), id); + // It's important to remove the mapping from context + vxlanTunnelNamingContext.removeName(dataBefore.getId(), writeContext.getMappingContext()); + } +}
\ No newline at end of file diff --git a/samples/samples-impl/src/main/resources/module.json b/samples/samples-impl/src/main/resources/module.json new file mode 100644 index 000000000..da70698d2 --- /dev/null +++ b/samples/samples-impl/src/main/resources/module.json @@ -0,0 +1,3 @@ +{ + "some.prop.name":"someValue" +}
\ No newline at end of file |