HONEYCOMB-58 - Routing Api
[honeycomb.git] / release-notes / src / main / asciidoc / devel_guide / devel_plugin_vpp_tutorial.adoc
1 = Developing plugins for VPP
2
3 link:release_notes.html[< Home]
4
5 Honeycomb's primary use case is to provide an agent for VPP. This section provides a tutorial for how to develop a Honeycomb plugin that translates YANG modeled data into VPP binary API invocation.
6
7 == Analyzing VPP's API
8 For this tutorial, VPP's VXLAN management API. Honeycomb already contains VXLAN management translation code inside V3PO plugin. This will be a simplified version.
9
10 Looking at VPP's API definition file, there are 3 calls related to VXLAN:
11
12 vxlan_add_del_tunnel - Creates and Deletes VXLAN tunnel (Update not supported)
13 vxlan_tunnel_dump - Reads all VXLAN tunnels
14 These are the shared-memory, binary APIs of VPP that would be difficult to use from Java. But VPP contains a jvpp component, that's completely generated from VPP's API definition file and allows Java applications to manage VPP in plain Java using JNI in the background. Honeycomb provides a component that can be included in a distribution.
15
16 == Updating sample-plugin to manage VPP
17
18 This tutorial starts where the previous one left and will continue to modify the sample plugin in order to be able to manage VPP's VXLAN tunnels.
19
20 === Updating YANG models
21 YANG models need to reflect the intent of managing VXLAN tunnels in VPP. As mentioned before, VPP exposes 2 calls to manage VXLAN tunnels. Each vxlan tunnel has a set of attributes, but for simplicity, only 2 of them will be exposed in YANG : source IP address and destination IP address. Rest of attributes will be set to default values in the code.
22
23 So let's update the sample-plugin-params grouping to:
24
25 [source,yang]
26 ----
27 grouping sample-plugin-params {
28     container vxlans {
29         list vxlan-tunnel {
30
31             key id;
32             leaf id {
33                 type string;
34             }
35
36             leaf src {
37               type inet:ip-address;
38             }
39             leaf dst {
40               type inet:ip-address;
41             }
42         }
43     }
44 }
45 ----
46
47 Since ietf-inet-types YANG model is used for the ip-address type, it needs to be imported (after the prefix statement):
48
49 [source,yang]
50 ----
51 import ietf-inet-types { prefix "inet"; }
52 ----
53
54 NOTE: The reason this works is that there are some general YANG models such as ietf-inet-types added to *-api module in its pom.xml.
55
56 Now rebuild the *-api module.
57
58 === JVpp dependency
59 Another important thing that the plugin needs is dependency to VPP's JVpp (Java APIs). To do so, just update *-impl's pom.xml with:
60
61 [source,xml,subs="+attributes"]
62 ----
63 <!-- VPP's core Java APIs -->
64 <dependency>
65     <groupId>io.fd.vpp</groupId>
66     <artifactId>jvpp-core</artifactId>
67     <version>{project-vpp-snapshot-version}</version>
68 </dependency>
69 ----
70
71 Also add vpp-translate-utils dependency so that writing translation code is easier:
72
73 [source,xml,subs="+attributes"]
74 ----
75 <dependency>
76     <groupId>io.fd.honeycomb.vpp</groupId>
77     <artifactId>vpp-translate-utils</artifactId>
78     <version>{project-version}</version>
79 </dependency>
80 ----
81
82 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.
83
84 === Updating the customizers
85
86 First of all, remove CrudService interface and ElementCrudService class. Will not be needed now.
87
88 ==== Changes to ElementStateCustomizer
89
90 Rename it to VxlanReadCustomzier. Update the code to:
91
92 [source,java]
93 ----
94 package io.fd.honeycomb.tutorial.read;
95
96 import com.google.common.base.Preconditions;
97 import io.fd.honeycomb.translate.read.ReadContext;
98 import io.fd.honeycomb.translate.read.ReadFailedException;
99 import io.fd.honeycomb.translate.spi.read.ListReaderCustomizer;
100 import io.fd.honeycomb.translate.v3po.util.NamingContext;
101 import io.fd.honeycomb.translate.v3po.util.TranslateUtils;
102 import java.util.Collections;
103 import java.util.List;
104 import java.util.stream.Collectors;
105 import javax.annotation.Nonnull;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.VxlansBuilder;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnel;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnelBuilder;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnelKey;
110 import org.opendaylight.yangtools.concepts.Builder;
111 import org.opendaylight.yangtools.yang.binding.DataObject;
112 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
113 import org.openvpp.jvpp.VppBaseCallException;
114 import org.openvpp.jvpp.core.dto.VxlanTunnelDetails;
115 import org.openvpp.jvpp.core.dto.VxlanTunnelDetailsReplyDump;
116 import org.openvpp.jvpp.core.dto.VxlanTunnelDump;
117 import org.openvpp.jvpp.core.future.FutureJVppCore;
118
119 /**
120  * Reader for {@link VxlanTunnel} list node from our YANG model.
121  */
122 public final class VxlanReadCustomizer implements
123         ListReaderCustomizer<VxlanTunnel, VxlanTunnelKey, VxlanTunnelBuilder> {
124
125     // JVpp core. This is the Java API for VPP's core API.
126     private final FutureJVppCore jVppCore;
127     // Naming context for interfaces
128     // Honeycomb provides a "context" storage for plugins. This storage is used for storing metadata required during
129     // data translation (just like in this plugin). An example of such metadata would be interface identifier. In Honeycomb
130     // we use string names for interfaces, however VPP uses only indices (that are created automatically).
131     // This means that translation layer has to store the mapping between HC interface name <-> VPP' interface index.
132     // And since vxlan tunnel is a type of interface in VPP, the same applies here
133     //
134     // Honeycomb provides a couple utilities on top of context storage such as NamingContext. It is just a map
135     // backed by context storage that makes the lookup and storing easier.
136     private final NamingContext vxlanNamingContext;
137
138     public VxlanReadCustomizer(final FutureJVppCore jVppCore, final NamingContext vxlanNamingContext) {
139         this.jVppCore = jVppCore;
140         this.vxlanNamingContext = vxlanNamingContext;
141     }
142
143     /**
144      * Provide a list of IDs for all VXLANs in VPP
145      */
146     @Nonnull
147     @Override
148     public List<VxlanTunnelKey> getAllIds(@Nonnull final InstanceIdentifier<VxlanTunnel> id,
149                                           @Nonnull final ReadContext context)
150             throws ReadFailedException {
151         // Create Dump request
152         final VxlanTunnelDump vxlanTunnelDump = new VxlanTunnelDump();
153         // Set Dump request attributes
154         // Set interface index to 0, so all interfaces are dumped and we can get the list of all IDs
155         vxlanTunnelDump.swIfIndex = 0;
156         final VxlanTunnelDetailsReplyDump reply;
157         try {
158             reply = TranslateUtils.getReplyForRead(jVppCore.vxlanTunnelDump(vxlanTunnelDump).toCompletableFuture(), id);
159         } catch (VppBaseCallException e) {
160             throw new ReadFailedException(id, e);
161         }
162
163         // Check for empty response (no vxlan tunnels to read)
164         if (reply == null || reply.vxlanTunnelDetails == null) {
165             return Collections.emptyList();
166         }
167
168         return reply.vxlanTunnelDetails.stream()
169                 // Need a name of an interface here. Use context to look it up from index
170                 // In case the naming context does not contain such mapping, it creates an artificial one
171                 .map(a -> new VxlanTunnelKey(vxlanNamingContext.getName(a.swIfIndex, context.getMappingContext())))
172                 .collect(Collectors.toList());
173     }
174
175     @Override
176     public void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<VxlanTunnel> readData) {
177         // Just set the readValue into parent builder
178         // The cast has to be performed here
179         ((VxlansBuilder) builder).setVxlanTunnel(readData);
180     }
181
182     @Nonnull
183     @Override
184     public VxlanTunnelBuilder getBuilder(@Nonnull final InstanceIdentifier<VxlanTunnel> id) {
185         // Setting key from id is not necessary, builder will take care of that
186         return new VxlanTunnelBuilder();
187     }
188
189     /**
190      * Read all the attributes of a single VXLAN tunnel
191      */
192     @Override
193     public void readCurrentAttributes(@Nonnull final InstanceIdentifier<VxlanTunnel> id,
194                                       @Nonnull final VxlanTunnelBuilder builder,
195                                       @Nonnull final ReadContext ctx) throws ReadFailedException {
196         // The ID received here contains the name of a particular interface that should be read
197         // It was either requested directly by HC users or is one of the IDs from getAllIds that could have been invoked
198         // just before this method invocation
199
200         // Create Dump request
201         final VxlanTunnelDump vxlanTunnelDump = new VxlanTunnelDump();
202         // Set Dump request attributes
203         // Set the vxlan index from naming context
204         // Naming context must contain the mapping because:
205         // 1. The vxlan tunnel was created in VPP using HC + this plugin meaning we stored the mapping in write customizer
206         // 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)
207         final String vxlanName = id.firstKeyOf(VxlanTunnel.class).getId();
208         vxlanTunnelDump.swIfIndex = vxlanNamingContext.getIndex(vxlanName, ctx.getMappingContext());
209
210         final VxlanTunnelDetailsReplyDump reply;
211         try {
212             reply = TranslateUtils.getReplyForRead(jVppCore.vxlanTunnelDump(vxlanTunnelDump).toCompletableFuture(), id);
213         } catch (VppBaseCallException e) {
214             throw new ReadFailedException(id, e);
215         }
216
217         Preconditions.checkState(reply != null && reply.vxlanTunnelDetails != null);
218         final VxlanTunnelDetails singleVxlanDetail = reply.vxlanTunnelDetails.stream().findFirst().get();
219
220         // Now translate all attributes into provided builder
221         final Boolean isIpv6 = TranslateUtils.byteToBoolean(singleVxlanDetail.isIpv6);
222         builder.setSrc(TranslateUtils.arrayToIpAddress(isIpv6, singleVxlanDetail.srcAddress));
223         builder.setDst(TranslateUtils.arrayToIpAddress(isIpv6, singleVxlanDetail.dstAddress));
224         // There are additional attributes of a vxlan tunnel that wont be used here
225     }
226 }
227 ----
228
229 The '"ReaderFactory also needs to be updated:
230
231 [source,java]
232 ----
233 package io.fd.honeycomb.tutorial.read;
234
235 import com.google.inject.Inject;
236 import io.fd.honeycomb.translate.impl.read.GenericListReader;
237 import io.fd.honeycomb.translate.read.ReaderFactory;
238 import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder;
239 import io.fd.honeycomb.translate.v3po.util.NamingContext;
240 import javax.annotation.Nonnull;
241 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.SamplePluginState;
242 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.SamplePluginStateBuilder;
243 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.Vxlans;
244 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.VxlansBuilder;
245 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnel;
246 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
247 import org.openvpp.jvpp.core.future.FutureJVppCore;
248
249 /**
250  * Factory producing readers for sample-plugin plugin's data.
251  */
252 public final class ModuleStateReaderFactory implements ReaderFactory {
253
254     public static final InstanceIdentifier<SamplePluginState> ROOT_STATE_CONTAINER_ID =
255             InstanceIdentifier.create(SamplePluginState.class);
256
257     /**
258      * Injected vxlan naming context shared with writer, provided by this plugin
259      */
260     @Inject
261     private NamingContext vxlanNamingContext;
262     /**
263      * Injected jvpp core APIs, provided by Honeycomb's infrastructure
264      */
265     @Inject
266     private FutureJVppCore jvppCore;
267
268     @Override
269     public void init(@Nonnull final ModifiableReaderRegistryBuilder registry) {
270         // register reader that only delegate read's to its children
271         registry.addStructuralReader(ROOT_STATE_CONTAINER_ID, SamplePluginStateBuilder.class);
272         // register reader that only delegate read's to its children
273         registry.addStructuralReader(ROOT_STATE_CONTAINER_ID.child(Vxlans.class), VxlansBuilder.class);
274
275         // just adds reader to the structure
276         // use addAfter/addBefore if you want to add specific order to readers on the same level of tree
277         // use subtreeAdd if you want to handle multiple nodes in single customizer/subtreeAddAfter/subtreeAddBefore if you also want to add order
278         // be aware that instance identifier passes to subtreeAdd/subtreeAddAfter/subtreeAddBefore should define subtree,
279         // therefore it should be relative from handled node down - InstanceIdentifier.create(HandledNode), not parent.child(HandledNode.class)
280         registry.add(new GenericListReader<>(
281                 // What part of subtree this reader handles is identified by an InstanceIdentifier
282                 ROOT_STATE_CONTAINER_ID.child(Vxlans.class).child(VxlanTunnel.class),
283                 // Customizer (the actual translation code to do the heavy lifting)
284                 new VxlanReadCustomizer(jvppCore, vxlanNamingContext)));
285     }
286 }
287 ----
288
289 ==== Changes to ElementCustomizer
290
291 Rename to VxlanWriteCustomizer. Update the code to:
292
293 [source,java]
294 ----
295 package io.fd.honeycomb.tutorial.write;
296
297 import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer;
298 import io.fd.honeycomb.translate.v3po.util.NamingContext;
299 import io.fd.honeycomb.translate.v3po.util.TranslateUtils;
300 import io.fd.honeycomb.translate.write.WriteContext;
301 import io.fd.honeycomb.translate.write.WriteFailedException;
302 import javax.annotation.Nonnull;
303 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnel;
304 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnelKey;
305 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
306 import org.openvpp.jvpp.VppBaseCallException;
307 import org.openvpp.jvpp.core.dto.VxlanAddDelTunnel;
308 import org.openvpp.jvpp.core.dto.VxlanAddDelTunnelReply;
309 import org.openvpp.jvpp.core.future.FutureJVppCore;
310
311 /**
312  * Writer for {@link VxlanTunnel} list node from our YANG model.
313  */
314 public final class VxlanWriteCustomizer implements ListWriterCustomizer<VxlanTunnel, VxlanTunnelKey> {
315
316     /**
317      * JVpp APIs
318      */
319     private final FutureJVppCore jvppCore;
320     /**
321      * Shared vxlan tunnel naming context
322      */
323     private final NamingContext vxlanTunnelNamingContext;
324
325     public VxlanWriteCustomizer(final FutureJVppCore jvppCore, final NamingContext vxlanTunnelNamingContext) {
326         this.jvppCore = jvppCore;
327         this.vxlanTunnelNamingContext = vxlanTunnelNamingContext;
328     }
329
330     @Override
331     public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<VxlanTunnel> id,
332                                        @Nonnull final VxlanTunnel dataAfter,
333                                        @Nonnull final WriteContext writeContext) throws WriteFailedException {
334         // Create and set vxlan tunnel add request
335         final VxlanAddDelTunnel vxlanAddDelTunnel = new VxlanAddDelTunnel();
336         // 1 for add, 0 for delete
337         vxlanAddDelTunnel.isAdd = 1;
338         // dataAfter is the new vxlanTunnel configuration
339         final boolean isIpv6 = dataAfter.getSrc().getIpv6Address() != null;
340         vxlanAddDelTunnel.isIpv6 = TranslateUtils.booleanToByte(isIpv6);
341         vxlanAddDelTunnel.srcAddress = TranslateUtils.ipAddressToArray(isIpv6, dataAfter.getSrc());
342         vxlanAddDelTunnel.dstAddress = TranslateUtils.ipAddressToArray(isIpv6, dataAfter.getDst());
343         // There are other input parameters that are not exposed by our YANG model, default values will be used
344
345         try {
346             final VxlanAddDelTunnelReply replyForWrite = TranslateUtils
347                     .getReplyForWrite(jvppCore.vxlanAddDelTunnel(vxlanAddDelTunnel).toCompletableFuture(), id);
348
349             // VPP returns the index of new vxlan tunnel
350             final int newVxlanTunnelIndex = replyForWrite.swIfIndex;
351             // It's important to store it in context so that reader knows to which name a vxlan tunnel is mapped
352             vxlanTunnelNamingContext.addName(newVxlanTunnelIndex, dataAfter.getId(), writeContext.getMappingContext());
353         } catch (VppBaseCallException e) {
354             throw new WriteFailedException.CreateFailedException(id, dataAfter, e);
355         }
356     }
357
358     @Override
359     public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<VxlanTunnel> id,
360                                         @Nonnull final VxlanTunnel dataBefore,
361                                         @Nonnull final VxlanTunnel dataAfter, @Nonnull final WriteContext writeContext)
362             throws WriteFailedException {
363         // Not supported at VPP API level, throw exception
364         throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter,
365                 new UnsupportedOperationException("Vxlan tunnel update is not supported by VPP"));
366     }
367
368     @Override
369     public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<VxlanTunnel> id,
370                                         @Nonnull final VxlanTunnel dataBefore,
371                                         @Nonnull final WriteContext writeContext) throws WriteFailedException {
372         // Create and set vxlan tunnel add request
373         final VxlanAddDelTunnel vxlanAddDelTunnel = new VxlanAddDelTunnel();
374         // 1 for add, 0 for delete
375         vxlanAddDelTunnel.isAdd = 0;
376         // Vxlan tunnel is identified by its attributes when deleting, not index, so set all attributes
377         // dataBefore is the vxlan tunnel that's being deleted
378         final boolean isIpv6 = dataBefore.getSrc().getIpv6Address() != null;
379         vxlanAddDelTunnel.isIpv6 = TranslateUtils.booleanToByte(isIpv6);
380         vxlanAddDelTunnel.srcAddress = TranslateUtils.ipAddressToArray(isIpv6, dataBefore.getSrc());
381         vxlanAddDelTunnel.dstAddress = TranslateUtils.ipAddressToArray(isIpv6, dataBefore.getDst());
382         // There are other input parameters that are not exposed by our YANG model, default values will be used
383
384         try {
385             final VxlanAddDelTunnelReply replyForWrite = TranslateUtils
386                     .getReplyForWrite(jvppCore.vxlanAddDelTunnel(vxlanAddDelTunnel).toCompletableFuture(), id);
387             // It's important to remove the mapping from context
388             vxlanTunnelNamingContext.removeName(dataBefore.getId(), writeContext.getMappingContext());
389         } catch (VppBaseCallException e) {
390             throw new WriteFailedException.DeleteFailedException(id, e);
391         }
392     }
393 }
394 ----
395
396 The '"WriterFactory also needs to be updated:
397
398 [source,java]
399 ----
400 package io.fd.honeycomb.tutorial.write;
401
402 import com.google.inject.Inject;
403 import io.fd.honeycomb.translate.impl.write.GenericWriter;
404 import io.fd.honeycomb.translate.v3po.util.NamingContext;
405 import io.fd.honeycomb.translate.write.WriterFactory;
406 import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder;
407 import javax.annotation.Nonnull;
408 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.SamplePlugin;
409 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.Vxlans;
410 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.sample.plugin.rev160918.sample.plugin.params.vxlans.VxlanTunnel;
411 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
412 import org.openvpp.jvpp.core.future.FutureJVppCore;
413
414 /**
415  * Factory producing writers for sample-plugin plugin's data.
416  */
417 public final class ModuleWriterFactory implements WriterFactory {
418
419     private static final InstanceIdentifier<SamplePlugin> ROOT_CONTAINER_ID = InstanceIdentifier.create(SamplePlugin.class);
420
421     /**
422      * Injected vxlan naming context shared with writer, provided by this plugin
423      */
424     @Inject
425     private NamingContext vxlanNamingContext;
426     /**
427      * Injected jvpp core APIs, provided by Honeycomb's infrastructure
428      */
429     @Inject
430     private FutureJVppCore jvppCore;
431
432     @Override
433     public void init(@Nonnull final ModifiableWriterRegistryBuilder registry) {
434         // Unlike ReaderFactory, there's no need to add structural writers, just the writers that actually do something
435
436         // register writer for vxlan tunnel
437         registry.add(new GenericWriter<>(
438                 // What part of subtree this writer handles is identified by an InstanceIdentifier
439                 ROOT_CONTAINER_ID.child(Vxlans.class).child(VxlanTunnel.class),
440                 // Customizer (the actual translation code to do the heavy lifting)
441                 new VxlanWriteCustomizer(jvppCore, vxlanNamingContext)));
442     }
443 }
444 ----
445
446 ==== Changes to Module
447 The module needs to be updated to:
448
449 * Include new instance of naming context
450 * Remove crud service
451
452 and the code needs to look like:
453
454 [source,java]
455 ----
456 package io.fd.honeycomb.tutorial;
457
458 import com.google.inject.AbstractModule;
459 import com.google.inject.multibindings.Multibinder;
460 import io.fd.honeycomb.data.init.DataTreeInitializer;
461 import io.fd.honeycomb.translate.read.ReaderFactory;
462 import io.fd.honeycomb.translate.v3po.util.NamingContext;
463 import io.fd.honeycomb.translate.write.WriterFactory;
464 import io.fd.honeycomb.tutorial.init.ConfigDataInitializer;
465 import io.fd.honeycomb.tutorial.read.ModuleStateReaderFactory;
466 import io.fd.honeycomb.tutorial.write.ModuleWriterFactory;
467 import net.jmob.guice.conf.core.ConfigurationModule;
468
469 /**
470  * Module class instantiating sample-plugin plugin components.
471  */
472 public final class Module extends AbstractModule {
473
474     @Override
475     protected void configure() {
476         // requests injection of properties
477         install(ConfigurationModule.create());
478         requestInjection(ModuleConfiguration.class);
479
480         // bind naming context instance for reader and writer factories
481         // the first parameter is artificial name prefix in cases a name needs to be reconstructed for a vxlan tunnel
482         // that is present in VPP but not in Honeycomb (could be extracted into configuration)
483         // the second parameter is just the naming context ID (could be extracted into configuration)
484         binder().bind(NamingContext.class).toInstance(new NamingContext("vxlan-tunnel", "vxlan-tunnel-context"));
485
486         // creates reader factory binding
487         // can hold multiple binding for separate yang modules
488         final Multibinder<ReaderFactory> readerFactoryBinder = Multibinder.newSetBinder(binder(), ReaderFactory.class);
489         readerFactoryBinder.addBinding().to(ModuleStateReaderFactory.class);
490
491         // create writer factory binding
492         // can hold multiple binding for separate yang modules
493         final Multibinder<WriterFactory> writerFactoryBinder = Multibinder.newSetBinder(binder(), WriterFactory.class);
494         writerFactoryBinder.addBinding().to(ModuleWriterFactory.class);
495
496         // create initializer binding
497         // can hold multiple binding for separate yang modules
498         final Multibinder<DataTreeInitializer> initializerBinder =
499                 Multibinder.newSetBinder(binder(), DataTreeInitializer.class);
500         initializerBinder.addBinding().to(ConfigDataInitializer.class);
501
502         // Disable notification producer for now
503 //        Multibinder.newSetBinder(binder(), ManagedNotificationProducer.class).addBinding()
504 //                .to(SampleNotificationProducer.class);
505     }
506 }
507 ----
508 *Now it's time to rebuild the plugin using mvn clean install to make the jars available for integrating them with vpp-integration distribution in next sections*
509
510 == Integrating with vpp-integration distribution
511 The vxlan tunnel management plugin can now be integrated with any honeycomb distribution. Honeycomb provides a vpp-integration distribution, where all VPP related plugins integrate to create a distribution with all available VPP related features.
512
513 This distribution comes with honeycomb infrastructure + common components for VPP Honeycomb plugins (e.g. Java APIs for VPP).
514
515 In order to add this new plugin into vpp-integration:
516
517 * clone honeycomb codebase (since that's the home of vpp-integration distribution)
518 * add a dependency for this sample plugin in vpp-integration distribution (honeycomb/vpp-integration/minimal-distribution/pom.xml):
519
520 [source,xml,subs="+attributes"]
521 ----
522 <dependency>
523   <groupId>io.fd.honeycomb.tutorial</groupId>
524   <artifactId>sample-plugin-impl</artifactId>
525   <version>{project-version}</version>
526 </dependency>
527 ----
528
529 * 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):
530 [source,java]
531 ----
532 package io.fd.honeycomb.vpp.integration.distro;
533
534 import com.google.common.collect.Lists;
535 import com.google.inject.Module;
536 import io.fd.honeycomb.vpp.distro.VppCommonModule;
537 import java.util.List;
538
539 public class Main {
540
541     public static void main(String[] args) {
542         final List<Module> sampleModules = Lists.newArrayList(io.fd.honeycomb.infra.distro.Main.BASE_MODULES);
543
544         // All the plugins should be listed here
545         sampleModules.add(new VppCommonModule());
546 //        Comment out V3po and Lisp module for the time being, since V3po and sample-plugin are in conflict over vxlan tunnel management
547 //         a plugin implementing VPP's API that's not yet covered by V3po or LISP plugin would not have to do this
548 //        sampleModules.add(new V3poModule());
549 //        sampleModules.add(new LispModule());
550         sampleModules.add(new io.fd.honeycomb.tutorial.Module());
551
552         io.fd.honeycomb.infra.distro.Main.init(sampleModules);
553     }
554 }
555 ----
556
557 Now just rebuild the honeycomb project.
558
559 == Verifying distribution
560 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:
561
562  sudo vpp-integration/minimal-distribution/target/vpp-integration-distribution-1.16.9-hc/vpp-integration-distribution-1.16.9/honeycomb
563
564 === Testing over RESTCONF
565 Reading vxlans operational data (should return empty vxlans container at first):
566
567  curl -u admin:admin  http://localhost:8181/restconf/operational/sample-plugin:sample-plugin-state
568
569 Adding a vxlan tunnel:
570
571  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
572
573 Reading vxlans config data (data that we posted to Honeycomb):
574
575  curl -u admin:admin  http://localhost:8181/restconf/config/sample-plugin:sample-plugin
576
577 Reading vxlans operational data (data coming from VPP being transformed by ReaderCustomizer on the fly):
578
579  curl -u admin:admin  http://localhost:8181/restconf/operational/sample-plugin:sample-plugin-state
580
581 Verifying vxlan tunnel existence in VPP:
582
583  telnet 0 5002
584  show interface
585
586 should show:
587
588 [source]
589 ----
590 Name                             Idx       State          Counter          Count
591 local0                            0        down
592 vxlan_tunnel0                     1         up
593 ----
594
595 Deleting a vxlan tunnel:
596
597  curl -u admin:admin -X DELETE http://localhost:8181/restconf/config/sample-plugin:sample-plugin/vxlans/vxlan-tunnel/vxlan-test-tunnel
598
599 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.
600
601 ==== Testing over NETCONF
602 Netconf testing guide including Notifications, can be found in Honeycomb/Running_Honeycomb.
603
604 NOTE: Netconf and Restconf are equivalent interfaces to Honeycomb, being capable of providing the same APIs. The only difference is with notifications. Only NETCONF is capable of emitting the notifications.
605
606 == Full working sample
607
608 Full working sample on github: https://github.com/marosmars/honeycomb-samples/tree/vpp-plugin
609
610 [NOTE]
611 ====
612 just a note on what further work for this plugin might contain:
613
614 * unit tests
615 * POSTMAN REST collection with sample requests
616 * logging
617 ====