Replacing InstanceIdentifier in VPP renderer model
[groupbasedpolicy.git] / renderers / vpp / src / main / java / org / opendaylight / groupbasedpolicy / renderer / vpp / iface / InterfaceManager.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.vpp.iface;
10
11 import javax.annotation.Nonnull;
12 import javax.annotation.Nullable;
13 import java.util.concurrent.ExecutionException;
14 import com.google.common.base.Optional;
15 import com.google.common.base.Preconditions;
16 import com.google.common.base.Strings;
17 import com.google.common.eventbus.Subscribe;
18 import com.google.common.util.concurrent.AsyncFunction;
19 import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.ListenableFuture;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.ConfigCommand;
25 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.LoopbackCommand;
26 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.TapPortCommand;
27 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.VhostUserCommand;
28 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.VhostUserCommand.VhostUserCommandBuilder;
29 import org.opendaylight.groupbasedpolicy.renderer.vpp.event.NodeOperEvent;
30 import org.opendaylight.groupbasedpolicy.renderer.vpp.event.VppEndpointConfEvent;
31 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.GbpNetconfTransaction;
32 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.General.Operations;
33 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.MountedDataBrokerProvider;
34 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory;
35 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppRendererProcessingException;
36 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.LocationType;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCase;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCaseBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes.InterfaceTypeChoice;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.LoopbackCase;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.TapCase;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425._interface.attributes._interface.type.choice.VhostUserCase;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VhostUserRole;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VppInterfaceAugmentation;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.L2;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.L2Builder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.Interconnection;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.interconnection.BridgeBased;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.l2.base.attributes.interconnection.BridgeBasedBuilder;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
57 import org.opendaylight.yangtools.yang.binding.DataObject;
58 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 public class InterfaceManager implements AutoCloseable {
63
64     private static final Logger LOG = LoggerFactory.getLogger(InterfaceManager.class);
65     private final MountedDataBrokerProvider mountDataProvider;
66     private final VppEndpointLocationProvider vppEndpointLocationProvider;
67
68     public InterfaceManager(@Nonnull MountedDataBrokerProvider mountDataProvider, @Nonnull DataBroker dataProvider) {
69         this.mountDataProvider = Preconditions.checkNotNull(mountDataProvider);
70         this.vppEndpointLocationProvider = new VppEndpointLocationProvider(dataProvider);
71     }
72
73     @Subscribe
74     @SuppressWarnings("OptionalGetWithoutIsPresent")
75     public synchronized void vppEndpointChanged(VppEndpointConfEvent event) {
76         try {
77             switch (event.getDtoModificationType()) {
78                 case CREATED: {
79                     vppEndpointCreated(event.getAfter().get()).get();
80                 }
81                 break;
82                 case UPDATED:
83                     vppEndpointUpdated(event.getBefore().get(), event.getAfter().get()).get();
84                     break;
85                 case DELETED:
86                     vppEndpointDeleted(event.getBefore().get()).get();
87                     break;
88             }
89         } catch (InterruptedException | ExecutionException e) {
90             LOG.warn("Failed to update Vpp Endpoint. {}", event, e);
91         }
92     }
93
94     private ListenableFuture<Void> vppEndpointCreated(VppEndpoint vppEndpoint) {
95         InterfaceTypeChoice interfaceTypeChoice = vppEndpoint.getInterfaceTypeChoice();
96         LOG.trace("Creating VPP endpoint {}, type of {}", vppEndpoint, interfaceTypeChoice);
97         Optional<ConfigCommand> potentialIfaceCommand = Optional.absent();
98         if (interfaceTypeChoice instanceof VhostUserCase) {
99             potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.PUT);
100         } else if (interfaceTypeChoice instanceof TapCase) {
101             potentialIfaceCommand = createTapInterfaceWithoutBdCommand(vppEndpoint, Operations.PUT);
102         } else if (interfaceTypeChoice instanceof LoopbackCase){
103             potentialIfaceCommand = createLoopbackWithoutBdCommand(vppEndpoint, Operations.PUT);
104         }
105
106         if (!potentialIfaceCommand.isPresent()) {
107             LOG.debug("Interface/PUT command was not created for VppEndpoint point {}", vppEndpoint);
108             return Futures.immediateFuture(null);
109         }
110         ConfigCommand ifaceWithoutBdCommand = potentialIfaceCommand.get();
111         InstanceIdentifier<Node> vppNodeIid = VppIidFactory.getNetconfNodeIid(vppEndpoint.getVppNodeId());
112         Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
113         if (!potentialVppDataProvider.isPresent()) {
114             final String message = "Cannot get data broker for mount point " + vppNodeIid;
115             LOG.warn(message);
116             return Futures.immediateFailedFuture(new VppRendererProcessingException(message));
117         }
118         DataBroker vppDataBroker = potentialVppDataProvider.get();
119         return createInterfaceWithEndpointLocation(ifaceWithoutBdCommand, vppDataBroker, vppEndpoint, vppNodeIid);
120     }
121
122     public ListenableFuture<Void> createInterfaceOnVpp(final ConfigCommand createIfaceWithoutBdCommand,
123                                                        final DataBroker vppDataBroker) {
124         final boolean transactionState = GbpNetconfTransaction.write(vppDataBroker, createIfaceWithoutBdCommand,
125                 GbpNetconfTransaction.RETRY_COUNT);
126         if (transactionState) {
127             LOG.trace("Creating Interface on VPP: {}", createIfaceWithoutBdCommand);
128             return Futures.immediateFuture(null);
129         } else {
130             final String message = "Failed to create Interface on VPP: " + createIfaceWithoutBdCommand;
131             LOG.warn(message);
132             return Futures.immediateFailedFuture(new VppRendererProcessingException(message));
133         }
134     }
135
136     private ListenableFuture<Void> createInterfaceWithEndpointLocation(final ConfigCommand createIfaceWithoutBdCommand,
137                                                                        final DataBroker vppDataBroker,
138                                                                        final VppEndpoint vppEndpoint,
139                                                                        final InstanceIdentifier<?> vppNodeIid) {
140         final boolean transactionState = GbpNetconfTransaction.write(vppDataBroker, createIfaceWithoutBdCommand,
141                 GbpNetconfTransaction.RETRY_COUNT);
142         if (transactionState) {
143             LOG.debug("Create interface on VPP command was successful. VPP: {} Command: {}", vppNodeIid,
144                     createIfaceWithoutBdCommand);
145             return vppEndpointLocationProvider.createLocationForVppEndpoint(vppEndpoint);
146         } else {
147             final String message = "Create interface on VPP command was not successful. VPP: " + vppNodeIid
148             + " Command: " + createIfaceWithoutBdCommand;
149             LOG.warn(message);
150             return Futures.immediateFailedFuture(new VppRendererProcessingException(message));
151         }
152     }
153
154     private ListenableFuture<Void> vppEndpointUpdated(@Nonnull final VppEndpoint oldVppEndpoint,
155                                                       @Nonnull final VppEndpoint newVppEndpoint)
156             throws ExecutionException, InterruptedException {
157         if(!oldVppEndpoint.equals(newVppEndpoint)) {
158             LOG.debug("Updating vpp endpoint, old EP: {} new EP: {}", oldVppEndpoint, newVppEndpoint);
159             return Futures.transform(vppEndpointDeleted(oldVppEndpoint),
160                     (AsyncFunction<Void, Void>) input -> vppEndpointCreated(newVppEndpoint));
161         }
162         LOG.debug("Update skipped, provided before/after vpp endpoints are equal");
163         return Futures.immediateFuture(null);
164     }
165
166     private ListenableFuture<Void> vppEndpointDeleted(@Nonnull VppEndpoint vppEndpoint) {
167         InterfaceTypeChoice interfaceTypeChoice = vppEndpoint.getInterfaceTypeChoice();
168         LOG.trace("Deleting VPP endpoint {}, type of {}", vppEndpoint, interfaceTypeChoice.toString());
169         Optional<ConfigCommand> potentialIfaceCommand = Optional.absent();
170         if (interfaceTypeChoice instanceof VhostUserCase) {
171             potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.DELETE);
172         } else if (interfaceTypeChoice instanceof TapCase) {
173             potentialIfaceCommand = createTapInterfaceWithoutBdCommand(vppEndpoint, Operations.DELETE);
174         } else if (interfaceTypeChoice instanceof LoopbackCase){
175             potentialIfaceCommand = createLoopbackWithoutBdCommand(vppEndpoint, Operations.DELETE);
176         }
177
178         if (!potentialIfaceCommand.isPresent()) {
179             LOG.debug("Interface/DELETE command was not created for VppEndpoint point {}", vppEndpoint);
180             return Futures.immediateFuture(null);
181         }
182         ConfigCommand ifaceWithoutBdCommand = potentialIfaceCommand.get();
183         InstanceIdentifier<Node> vppNodeIid = VppIidFactory.getNetconfNodeIid(vppEndpoint.getVppNodeId());
184         Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
185         if (!potentialVppDataProvider.isPresent()) {
186             final String message = "Cannot get data broker for mount point " + vppNodeIid;
187             LOG.warn(message);
188             return Futures.immediateFailedFuture(new VppRendererProcessingException(message));
189         }
190         DataBroker vppDataBroker = potentialVppDataProvider.get();
191         return deleteIfaceOnVpp(ifaceWithoutBdCommand, vppDataBroker, vppEndpoint, vppNodeIid);
192     }
193
194     private ListenableFuture<Void> deleteIfaceOnVpp(ConfigCommand deleteIfaceWithoutBdCommand,
195             DataBroker vppDataBroker, VppEndpoint vppEndpoint, InstanceIdentifier<?> vppNodeIid) {
196         final boolean transactionState = GbpNetconfTransaction.delete(vppDataBroker, deleteIfaceWithoutBdCommand,
197                 GbpNetconfTransaction.RETRY_COUNT);
198         if (transactionState) {
199             LOG.debug("Delete interface on VPP command was successful: VPP: {} Command: {}", vppNodeIid,
200                     deleteIfaceWithoutBdCommand);
201             return vppEndpointLocationProvider.deleteLocationForVppEndpoint(vppEndpoint);
202         } else {
203             final String message = "Delete interface on VPP command was not successful: VPP: " + vppNodeIid +
204                     " Command: " + deleteIfaceWithoutBdCommand;
205             LOG.warn(message);
206             return Futures.immediateFailedFuture(new VppRendererProcessingException(message));
207         }
208     }
209
210     @Subscribe
211     public synchronized void vppNodeChanged(NodeOperEvent event) {
212         switch (event.getDtoModificationType()) {
213             case CREATED:
214                 if (event.isAfterConnected()) {
215                     // TODO read VppEndpoints or cache them during vppEndpointChanged()
216                 }
217                 break;
218             case UPDATED:
219                 if (!event.isBeforeConnected() && event.isAfterConnected()) {
220                     // TODO reconciliation - diff between disconnected snapshot and current snapshot
221                 }
222                 break;
223             case DELETED:
224                 if (event.isBeforeConnected()) {
225                     // TODO we could do snapshot of VppEndpoints
226                     // which can be used for reconciliation
227                 }
228                 break;
229         }
230     }
231
232     public static Optional<ConfigCommand> createInterfaceWithoutBdCommand(@Nonnull VppEndpoint vppEp,
233             @Nonnull Operations operations) {
234         if (!hasNodeAndInterface(vppEp)) {
235             LOG.debug("Interface command is not created for {}", vppEp);
236             return Optional.absent();
237         }
238         VhostUserCommandBuilder builder = VhostUserCommand.builder();
239         builder.setName(vppEp.getVppInterfaceName());
240         InterfaceTypeChoice interfaceTypeChoice = vppEp.getInterfaceTypeChoice();
241         if (interfaceTypeChoice instanceof VhostUserCase) {
242             VhostUserCase vhostUserIface = (VhostUserCase) interfaceTypeChoice;
243             String socket = vhostUserIface.getSocket();
244             if (Strings.isNullOrEmpty(socket)) {
245                 LOG.debug("Vhost user interface command is not created because socket is missing. {}", vppEp);
246                 return Optional.absent();
247             }
248             builder.setSocket(socket);
249             builder.setRole(VhostUserRole.Client);
250         }
251         VhostUserCommand vhostUserCommand =
252                 builder.setOperation(operations).setDescription(vppEp.getDescription()).build();
253         return Optional.of(vhostUserCommand);
254     }
255
256     private static Optional<ConfigCommand> createTapInterfaceWithoutBdCommand(@Nonnull VppEndpoint vppEp,
257             @Nonnull Operations operation) {
258         if (!hasNodeAndInterface(vppEp)) {
259             LOG.debug("Interface command is not created for {}", vppEp);
260             return Optional.absent();
261         }
262         TapPortCommand.TapPortCommandBuilder builder = TapPortCommand.builder();
263         InterfaceTypeChoice interfaceTypeChoice = vppEp.getInterfaceTypeChoice();
264         if (interfaceTypeChoice instanceof TapCase) {
265             TapCase tapIface = (TapCase) interfaceTypeChoice;
266             String name = tapIface.getName();
267             if (Strings.isNullOrEmpty(name)) {
268                 LOG.debug("Tap interface command is not created because name is missing. {}", vppEp);
269                 return Optional.absent();
270             }
271             builder.setTapName(name);
272             builder.setPhysAddress(tapIface.getPhysicalAddress());
273         }
274         TapPortCommand tapPortCommand = builder
275                 .setOperation(operation)
276                 .setDescription(vppEp.getDescription())
277                 .setInterfaceName(vppEp.getVppInterfaceName())
278                 .build();
279         return Optional.of(tapPortCommand);
280     }
281
282     private static Optional<ConfigCommand> createLoopbackWithoutBdCommand(@Nonnull VppEndpoint vppEp,
283         @Nonnull Operations operation) {
284         if (!hasNodeAndInterface(vppEp)) {
285             LOG.debug("Interface command is not created for {}", vppEp);
286             return Optional.absent();
287         }
288         LoopbackCommand.LoopbackCommandBuilder builder = LoopbackCommand.builder();
289         LoopbackCase loopIface = (LoopbackCase) vppEp.getInterfaceTypeChoice();
290
291         builder.setPhysAddress(loopIface.getPhysAddress());
292         builder.setBvi(loopIface.isBvi());
293         builder.setIpAddress(loopIface.getIpAddress());
294         builder.setIpPrefix(loopIface.getIpPrefix());
295
296         LoopbackCommand loopbackCommand = builder
297             .setOperation(operation)
298             .setDescription(vppEp.getDescription())
299             .setInterfaceName(vppEp.getVppInterfaceName())
300             .build();
301
302         return Optional.of(loopbackCommand);
303     }
304
305     /**
306      * Adds bridge domain to an interface if the interface exist.<br>
307      * It rewrites bridge domain in case it already exist.<br>
308      * {@link VppEndpointLocationProvider#VPP_ENDPOINT_LOCATION_PROVIDER} will update location
309      * when the interface is created successfully.<br>
310      * If the interface does not exist or other problems occur {@link ListenableFuture} will fail
311      * as {@link Futures#immediateFailedFuture(Throwable)} with {@link Exception}
312      * containing message in {@link Exception#getMessage()}
313      *
314      * @param bridgeDomainName bridge domain
315      * @param addrEpWithLoc    {@link AddressEndpointWithLocation} containing
316      *                         {@link ExternalLocationCase} where
317      *                         {@link ExternalLocationCase#getExternalNodeMountPoint()} MUST NOT be {@code null}
318      *                         and {@link ExternalLocationCase#getExternalNodeConnector()} MUST NOT be {@code null}
319      * @return {@link ListenableFuture}
320      */
321     public synchronized ListenableFuture<Void> addBridgeDomainToInterface(@Nonnull String bridgeDomainName,
322                                                                           @Nonnull AddressEndpointWithLocation addrEpWithLoc,
323                                                                           boolean enableBvi) {
324         ExternalLocationCase epLoc = resolveAndValidateLocation(addrEpWithLoc);
325         InstanceIdentifier<?> vppNodeIid = epLoc.getExternalNodeMountPoint();
326         String interfacePath = epLoc.getExternalNodeConnector();
327
328         Optional<InstanceIdentifier<Interface>> optInterfaceIid =
329                 VppPathMapper.interfaceToInstanceIdentifier(interfacePath);
330         if (!optInterfaceIid.isPresent()) {
331             return Futures.immediateFailedFuture(
332                     new Exception("Cannot resolve interface instance-identifier for interface path" + interfacePath));
333         }
334         InstanceIdentifier<Interface> interfaceIid = optInterfaceIid.get();
335         Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
336         if (!potentialVppDataProvider.isPresent()) {
337             return Futures.immediateFailedFuture(new Exception("Cannot get data broker for mount point " + vppNodeIid));
338         }
339         final DataBroker mountpoint = potentialVppDataProvider.get();
340         Optional<Interface> optInterface = GbpNetconfTransaction.read(mountpoint, LogicalDatastoreType.CONFIGURATION,
341                 interfaceIid, GbpNetconfTransaction.RETRY_COUNT);
342
343         if (!optInterface.isPresent()) {
344             return Futures.immediateFailedFuture(new Exception("Interface "
345                     + interfaceIid.firstKeyOf(Interface.class) + " does not exist on node " + vppNodeIid));
346         }
347         String existingBridgeDomain = resolveBridgeDomain(optInterface.get());
348         if (bridgeDomainName.equals(existingBridgeDomain)) {
349             LOG.debug("Bridge domain {} already exists on interface {}", bridgeDomainName, interfacePath);
350             String bridgeDomainPath = VppPathMapper.bridgeDomainToRestPath(bridgeDomainName);
351             if (!bridgeDomainPath.equals(epLoc.getExternalNode())) {
352                 return vppEndpointLocationProvider.replaceLocationForEndpoint(new ExternalLocationCaseBuilder()
353                         .setExternalNode(bridgeDomainPath)
354                         .setExternalNodeMountPoint(vppNodeIid)
355                         .setExternalNodeConnector(interfacePath)
356                         .build(), addrEpWithLoc.getKey());
357             }
358             return Futures.immediateFuture(null);
359         }
360         InstanceIdentifier<L2> l2Iid =
361                 interfaceIid.builder().augmentation(VppInterfaceAugmentation.class).child(L2.class).build();
362         Optional<L2> optL2 = GbpNetconfTransaction.read(mountpoint, LogicalDatastoreType.CONFIGURATION,
363                 l2Iid, GbpNetconfTransaction.RETRY_COUNT);
364         L2Builder l2Builder = (optL2.isPresent()) ? new L2Builder(optL2.get()) : new L2Builder();
365         L2 l2 = l2Builder.setInterconnection(new BridgeBasedBuilder()
366                 .setBridgeDomain(bridgeDomainName)
367                 .setBridgedVirtualInterface(enableBvi)
368                 .build()).build();
369         LOG.debug("Adding bridge domain {} to interface {}", bridgeDomainName, interfacePath);
370         final boolean transactionState = GbpNetconfTransaction.write(mountpoint, l2Iid, l2,
371                 GbpNetconfTransaction.RETRY_COUNT);
372         if (transactionState) {
373             LOG.debug("Adding bridge domain {} to interface {} successful", bridgeDomainName, interfacePath);
374             String bridgeDomainPath = VppPathMapper.bridgeDomainToRestPath(bridgeDomainName);
375             return vppEndpointLocationProvider.replaceLocationForEndpoint(new ExternalLocationCaseBuilder()
376                     .setExternalNode(bridgeDomainPath)
377                     .setExternalNodeMountPoint(vppNodeIid)
378                     .setExternalNodeConnector(interfacePath)
379                     .build(), addrEpWithLoc.getKey());
380         } else {
381             final String message = "Adding bridge domain " + bridgeDomainName + " to interface " + interfacePath + " failed";
382             LOG.warn(message);
383             return Futures.immediateFailedFuture(new VppRendererProcessingException(message));
384         }
385     }
386
387     public ListenableFuture<Void> configureInterface(DataBroker mountPoint, InterfaceKey ifaceKey, @Nullable String bridgeDomainName,
388                                                      @Nullable Boolean enableBvi) {
389         L2Builder l2Builder = readL2ForInterface(mountPoint, ifaceKey);
390         L2 l2 = l2Builder.setInterconnection(new BridgeBasedBuilder()
391             .setBridgeDomain(bridgeDomainName)
392             .setBridgedVirtualInterface(enableBvi)
393             .build()).build();
394         final boolean transactionState = GbpNetconfTransaction.write(mountPoint, VppIidFactory.getL2ForInterfaceIid(ifaceKey),
395                 l2, GbpNetconfTransaction.RETRY_COUNT);
396         if (transactionState) {
397             LOG.debug("Adding bridge domain {} to interface {}", bridgeDomainName, VppIidFactory.getInterfaceIID(ifaceKey));
398             return Futures.immediateFuture(null);
399         } else {
400             final String message = "Failed to add bridge domain " + bridgeDomainName + " to interface "
401                     + VppIidFactory.getInterfaceIID(ifaceKey);
402             LOG.warn(message);
403             return Futures.immediateFailedFuture(new VppRendererProcessingException(message));
404         }
405     }
406
407     public ListenableFuture<Void> removeInterfaceFromBridgeDomain(DataBroker mountPoint, InterfaceKey ifaceKey) {
408         L2Builder l2Builder = readL2ForInterface(mountPoint, ifaceKey);
409         if (l2Builder.getInterconnection() == null || !(l2Builder.getInterconnection() instanceof BridgeBased)) {
410             LOG.warn("Interface already not in bridge domain {} ", ifaceKey);
411             return Futures.immediateFuture(null);
412         }
413         BridgeBased bridgeBased = new BridgeBasedBuilder((BridgeBased) l2Builder.getInterconnection()).setBridgeDomain(
414                 null).build();
415         L2 l2 = l2Builder.setInterconnection(bridgeBased).build();
416         final boolean transactionState = GbpNetconfTransaction.write(mountPoint, VppIidFactory.getL2ForInterfaceIid(ifaceKey),
417                 l2, GbpNetconfTransaction.RETRY_COUNT);
418         if (transactionState) {
419             LOG.debug("Removing bridge domain from interface {}", VppIidFactory.getInterfaceIID(ifaceKey));
420             return Futures.immediateFuture(null);
421         } else {
422             final String message = "Failed to remove bridge domain from interface "
423                     + VppIidFactory.getInterfaceIID(ifaceKey);
424             LOG.warn(message);
425             return Futures.immediateFailedFuture(new VppRendererProcessingException(message));
426         }
427
428     }
429
430     private L2Builder readL2ForInterface(DataBroker mountpoint, InterfaceKey ifaceKey) {
431         InstanceIdentifier<L2> l2Iid = VppIidFactory.getL2ForInterfaceIid(ifaceKey);
432         final ReadWriteTransaction rwTxRead = mountpoint.newReadWriteTransaction();
433         Optional<L2> optL2 = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, l2Iid, rwTxRead);
434         return  (optL2.isPresent()) ? new L2Builder(optL2.get()) : new L2Builder();
435     }
436
437     /**
438      * Removes bridge domain (if exist) from an interface (if exist).<br>
439      * {@link VppEndpointLocationProvider#VPP_ENDPOINT_LOCATION_PROVIDER} will update endpoint
440      * location.
441      * <p>
442      * If the interface does not exist or other problems occur {@link ListenableFuture} will fail
443      * as {@link Futures#immediateFailedFuture(Throwable)} with {@link Exception}
444      * containing message in {@link Exception#getMessage()}
445      *
446      * @param addrEpWithLoc {@link AddressEndpointWithLocation} containing
447      *                      {@link ExternalLocationCase} where
448      *                      {@link ExternalLocationCase#getExternalNodeMountPoint()} MUST NOT be {@code null}
449      *                      and {@link ExternalLocationCase#getExternalNodeConnector()} MUST NOT be {@code null}
450      * @return {@link ListenableFuture}
451      */
452     public synchronized
453     @Nonnull
454     ListenableFuture<Void> deleteBridgeDomainFromInterface(
455             @Nonnull AddressEndpointWithLocation addrEpWithLoc) {
456         ExternalLocationCase epLoc = resolveAndValidateLocation(addrEpWithLoc);
457         InstanceIdentifier<?> vppNodeIid = epLoc.getExternalNodeMountPoint();
458         String interfacePath = epLoc.getExternalNodeConnector();
459
460         Optional<InstanceIdentifier<Interface>> optInterfaceIid =
461                 VppPathMapper.interfaceToInstanceIdentifier(interfacePath);
462         if (!optInterfaceIid.isPresent()) {
463             return Futures.immediateFailedFuture(
464                     new Exception("Cannot resolve interface instance-identifier for interface path" + interfacePath));
465         }
466         InstanceIdentifier<Interface> interfaceIid = optInterfaceIid.get();
467
468         Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
469         if (!potentialVppDataProvider.isPresent()) {
470             return Futures.immediateFailedFuture(new Exception("Cannot get data broker for mount point " + vppNodeIid));
471         }
472         final DataBroker mountpoint = potentialVppDataProvider.get();
473         final Optional<Interface> optInterface = GbpNetconfTransaction.read(mountpoint,
474                 LogicalDatastoreType.CONFIGURATION, interfaceIid, GbpNetconfTransaction.RETRY_COUNT);
475         if (!optInterface.isPresent()) {
476             // interface does not exist so we consider job done
477             return Futures.immediateFuture(null);
478         }
479         String existingBridgeDomain = resolveBridgeDomain(optInterface.get());
480         if (Strings.isNullOrEmpty(existingBridgeDomain)) {
481             LOG.debug("Bridge domain does not exist therefore it is considered as deleted for interface {}",
482                     interfacePath);
483             // bridge domain does not exist on interface so we consider job done
484             return vppEndpointLocationProvider.replaceLocationForEndpoint(new ExternalLocationCaseBuilder()
485                     .setExternalNode(null)
486                     .setExternalNodeMountPoint(vppNodeIid)
487                     .setExternalNodeConnector(interfacePath)
488                     .build(), addrEpWithLoc.getKey());
489         }
490         InstanceIdentifier<L2> l2Iid =
491                 interfaceIid.builder().augmentation(VppInterfaceAugmentation.class).child(L2.class).build();
492         LOG.debug("Deleting bridge domain from interface {}", interfacePath);
493         final boolean transactionState = GbpNetconfTransaction.delete(mountpoint, l2Iid,
494                 GbpNetconfTransaction.RETRY_COUNT);
495         if (transactionState) {
496             return vppEndpointLocationProvider.replaceLocationForEndpoint(new ExternalLocationCaseBuilder()
497                     .setExternalNode(null)
498                     .setExternalNodeMountPoint(vppNodeIid)
499                     .setExternalNodeConnector(interfacePath)
500                     .build(), addrEpWithLoc.getKey());
501         } else {
502             final String message = "Failed to delete bridge domain from interface " + interfacePath;
503             LOG.warn(message);
504             return Futures.immediateFailedFuture(new VppRendererProcessingException(message));
505         }
506     }
507
508     private static ExternalLocationCase resolveAndValidateLocation(AddressEndpointWithLocation addrEpWithLoc) {
509         LocationType locationType = addrEpWithLoc.getAbsoluteLocation().getLocationType();
510         if (!(locationType instanceof ExternalLocationCase)) {
511             throw new IllegalArgumentException("Endpoint does not have external location " + addrEpWithLoc);
512         }
513         ExternalLocationCase result = (ExternalLocationCase) locationType;
514         if (result.getExternalNodeMountPoint() == null || result.getExternalNodeConnector() == null) {
515             throw new IllegalArgumentException(
516                     "Endpoint does not have external-node-mount-point or external-node-connector " + addrEpWithLoc);
517         }
518         return result;
519     }
520
521     private static @Nullable String resolveBridgeDomain(@Nonnull Interface iface) {
522         VppInterfaceAugmentation vppInterfaceAugmentation = iface.getAugmentation(VppInterfaceAugmentation.class);
523         L2 existingL2 = vppInterfaceAugmentation.getL2();
524         if (existingL2 != null) {
525             Interconnection interconnection = existingL2.getInterconnection();
526             if (interconnection instanceof BridgeBased) {
527                 return ((BridgeBased) interconnection).getBridgeDomain();
528             }
529         }
530         return null;
531     }
532
533     private static boolean hasNodeAndInterface(VppEndpoint vppEp) {
534         if (vppEp.getVppNodeId() == null) {
535             LOG.debug("vpp-node is missing. {}", vppEp);
536             return false;
537         }
538         if (Strings.isNullOrEmpty(vppEp.getVppInterfaceName())) {
539             LOG.debug("vpp-interface-name is missing. {}", vppEp);
540             return false;
541         }
542         return true;
543     }
544
545     @Override
546     public void close() throws Exception {
547         vppEndpointLocationProvider.close();
548     }
549 }