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