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