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