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