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