Merge "Fixed bug in update forwarding"
[groupbasedpolicy.git] / renderers / vpp / src / main / java / org / opendaylight / groupbasedpolicy / renderer / vpp / iface / InterfaceManager.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.vpp.iface;
10
11 import java.util.concurrent.ExecutorService;
12
13 import javax.annotation.Nonnull;
14 import javax.annotation.Nullable;
15
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
20 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.ConfigCommand;
21 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.VhostUserCommand;
22 import org.opendaylight.groupbasedpolicy.renderer.vpp.commands.VhostUserCommand.VhostUserCommandBuilder;
23 import org.opendaylight.groupbasedpolicy.renderer.vpp.event.NodeOperEvent;
24 import org.opendaylight.groupbasedpolicy.renderer.vpp.event.VppEndpointConfEvent;
25 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.General.Operations;
26 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.MountedDataBrokerProvider;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.LocationType;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCase;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint.InterfaceTypeChoice;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.VhostUserCase;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUserRole;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2Builder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.base.attributes.Interconnection;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.base.attributes.interconnection.BridgeBased;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.base.attributes.interconnection.BridgeBasedBuilder;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 import com.google.common.base.Function;
46 import com.google.common.base.Optional;
47 import com.google.common.base.Preconditions;
48 import com.google.common.base.Strings;
49 import com.google.common.eventbus.Subscribe;
50 import com.google.common.util.concurrent.AsyncFunction;
51 import com.google.common.util.concurrent.CheckedFuture;
52 import com.google.common.util.concurrent.FutureCallback;
53 import com.google.common.util.concurrent.Futures;
54 import com.google.common.util.concurrent.ListenableFuture;
55
56 public class InterfaceManager implements AutoCloseable {
57
58     private static final Logger LOG = LoggerFactory.getLogger(InterfaceManager.class);
59     private final MountedDataBrokerProvider mountDataProvider;
60     private final VppEndpointLocationProvider vppEndpointLocationProvider;
61     private final ExecutorService netconfWorker;
62
63     public InterfaceManager(@Nonnull MountedDataBrokerProvider mountDataProvider, @Nonnull DataBroker dataProvider, @Nonnull ExecutorService netconfWorker) {
64         this.mountDataProvider = Preconditions.checkNotNull(mountDataProvider);
65         this.netconfWorker = Preconditions.checkNotNull(netconfWorker);
66         this.vppEndpointLocationProvider = new VppEndpointLocationProvider(dataProvider);
67     }
68
69     @Subscribe
70     public synchronized void vppEndpointChanged(VppEndpointConfEvent event) {
71         switch (event.getDtoModificationType()) {
72             case CREATED:
73                 vppEndpointCreated(event.getAfter().get());
74                 break;
75             case UPDATED:
76                 vppEndpointDeleted(event.getBefore().get());
77                 vppEndpointCreated(event.getAfter().get());
78                 break;
79             case DELETED:
80                 vppEndpointDeleted(event.getBefore().get());
81                 break;
82         }
83     }
84
85     private void vppEndpointCreated(VppEndpoint vppEndpoint) {
86         Optional<ConfigCommand> potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.PUT);
87         if (!potentialIfaceCommand.isPresent()) {
88             return;
89         }
90         ConfigCommand ifaceWithoutBdCommand = potentialIfaceCommand.get();
91         InstanceIdentifier<?> vppNodeIid = vppEndpoint.getVppNodePath();
92         Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
93         if (!potentialVppDataProvider.isPresent()) {
94             LOG.debug("Cannot get data broker for mount point {}", vppNodeIid);
95             return;
96         }
97         DataBroker vppDataBroker = potentialVppDataProvider.get();
98         createIfaceOnVpp(ifaceWithoutBdCommand, vppDataBroker, vppEndpoint, vppNodeIid);
99     }
100
101     private void createIfaceOnVpp(ConfigCommand createIfaceWithoutBdCommand, DataBroker vppDataBroker,
102             VppEndpoint vppEndpoint, InstanceIdentifier<?> vppNodeIid) {
103         ReadWriteTransaction rwTx = vppDataBroker.newReadWriteTransaction();
104         createIfaceWithoutBdCommand.execute(rwTx);
105         Futures.addCallback(rwTx.submit(), new FutureCallback<Void>() {
106
107             @Override
108             public void onSuccess(Void result) {
109                 LOG.debug("Create interface on VPP command was successful:\nVPP: {}\nCommand: {}", vppNodeIid,
110                         createIfaceWithoutBdCommand);
111                 vppEndpointLocationProvider.createLocationForVppEndpoint(vppEndpoint);
112             }
113
114             @Override
115             public void onFailure(Throwable t) {
116                 LOG.error("Create interface on VPP command was NOT successful:\nVPP: {}\nCommand: {}", vppNodeIid,
117                         createIfaceWithoutBdCommand, t);
118             }
119         }, netconfWorker);
120     }
121
122     private void vppEndpointDeleted(VppEndpoint vppEndpoint) {
123         Optional<ConfigCommand> potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.DELETE);
124         if (!potentialIfaceCommand.isPresent()) {
125             return;
126         }
127         ConfigCommand ifaceWithoutBdCommand = potentialIfaceCommand.get();
128         InstanceIdentifier<?> vppNodeIid = vppEndpoint.getVppNodePath();
129         Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
130         if (!potentialVppDataProvider.isPresent()) {
131             LOG.debug("Cannot get data broker for mount point {}", vppNodeIid);
132             return;
133         }
134         DataBroker vppDataBroker = potentialVppDataProvider.get();
135         deleteIfaceOnVpp(ifaceWithoutBdCommand, vppDataBroker, vppEndpoint, vppNodeIid);
136     }
137
138     private void deleteIfaceOnVpp(ConfigCommand deleteIfaceWithoutBdCommand, DataBroker vppDataBroker,
139             VppEndpoint vppEndpoint, InstanceIdentifier<?> vppNodeIid) {
140         ReadWriteTransaction rwTx = vppDataBroker.newReadWriteTransaction();
141         deleteIfaceWithoutBdCommand.execute(rwTx);
142         Futures.addCallback(rwTx.submit(), new FutureCallback<Void>() {
143
144             @Override
145             public void onSuccess(Void result) {
146                 LOG.debug("Delete interface on VPP command was successful:\nVPP: {}\nCommand: {}", vppNodeIid,
147                         deleteIfaceWithoutBdCommand);
148                 vppEndpointLocationProvider.deleteLocationForVppEndpoint(vppEndpoint);
149             }
150
151             @Override
152             public void onFailure(Throwable t) {
153                 LOG.error("Delete interface on VPP command was NOT successful:\nVPP: {}\nCommand: {}", vppNodeIid,
154                         deleteIfaceWithoutBdCommand, t);
155             }
156         }, netconfWorker);
157     }
158
159     @Subscribe
160     public synchronized void vppNodeChanged(NodeOperEvent event) {
161         switch (event.getDtoModificationType()) {
162             case CREATED:
163                 if (event.isAfterConnected()) {
164                     // TODO read VppEndpoints or cache them during vppEndpointChanged()
165                 }
166                 break;
167             case UPDATED:
168                 if (!event.isBeforeConnected() && event.isAfterConnected()) {
169                     // TODO reconciliation - diff between disconnected snapshot and current snapshot
170                 }
171                 break;
172             case DELETED:
173                 if (event.isBeforeConnected()) {
174                     // TODO we could do snapshot of VppEndpoints 
175                     // which can be used for reconciliation
176                 }
177                 break;
178         }
179     }
180
181     private static Optional<ConfigCommand> createInterfaceWithoutBdCommand(@Nonnull VppEndpoint vppEp,
182             @Nonnull Operations operations) {
183         if (!hasNodeAndInterface(vppEp)) {
184             LOG.debug("Interface command is not created for {}", vppEp);
185             return Optional.absent();
186         }
187         VhostUserCommandBuilder builder = VhostUserCommand.builder();
188         builder.setName(vppEp.getVppInterfaceName());
189         InterfaceTypeChoice interfaceTypeChoice = vppEp.getInterfaceTypeChoice();
190         if (interfaceTypeChoice instanceof VhostUserCase) {
191             VhostUserCase vhostUserIface = (VhostUserCase) interfaceTypeChoice;
192             String socket = vhostUserIface.getSocket();
193             if (Strings.isNullOrEmpty(socket)) {
194                 LOG.debug("Vhost user interface command is not created because socket is missing. {}", vppEp);
195                 return Optional.absent();
196             }
197             builder.setSocket(socket);
198             builder.setRole(VhostUserRole.Client);
199         }
200         VhostUserCommand vhostUserCommand =
201                 builder.setOperation(operations).setDescription(vppEp.getDescription()).build();
202         return Optional.of(vhostUserCommand);
203     }
204
205     /**
206      * Adds bridge domain to an interface if the interface exist.<br>
207      * It rewrites bridge domain in case it already exist.<br>
208      * {@link VppEndpointLocationProvider#VPP_ENDPOINT_LOCATION_PROVIDER} will update location
209      * when the interface is created successfully.<br>
210      * If the interface does not exist or other problems occur {@link ListenableFuture} will fail
211      * as {@link Futures#immediateFailedFuture(Throwable)} with {@link Exception}
212      * containing message in {@link Exception#getMessage()}
213      * 
214      * @param bridgeDomainName bridge domain
215      * @param addrEpWithLoc {@link AddressEndpointWithLocation} containing
216      *        {@link ExternalLocationCase} where
217      *        {@link ExternalLocationCase#getExternalNodeMountPoint()} MUST NOT be {@code null}
218      *        and {@link ExternalLocationCase#getExternalNodeConnector()} MUST NOT be {@code null}
219      * @return {@link ListenableFuture}
220      */
221     public synchronized @Nonnull ListenableFuture<Void> addBridgeDomainToInterface(@Nonnull String bridgeDomainName,
222             @Nonnull AddressEndpointWithLocation addrEpWithLoc) {
223         ExternalLocationCase epLoc = resolveAndValidateLocation(addrEpWithLoc);
224         InstanceIdentifier<?> vppNodeIid = epLoc.getExternalNodeMountPoint();
225         String interfacePath = epLoc.getExternalNodeConnector();
226
227         Optional<InstanceIdentifier<Interface>> optInterfaceIid =
228                 VppPathMapper.interfaceToInstanceIdentifier(interfacePath);
229         if (!optInterfaceIid.isPresent()) {
230             return Futures.immediateFailedFuture(
231                     new Exception("Cannot resolve interface instance-identifier for interface path" + interfacePath));
232         }
233         InstanceIdentifier<Interface> interfaceIid = optInterfaceIid.get();
234
235         Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
236         if (!potentialVppDataProvider.isPresent()) {
237             return Futures.immediateFailedFuture(new Exception("Cannot get data broker for mount point " + vppNodeIid));
238         }
239
240         ReadWriteTransaction rwTx = potentialVppDataProvider.get().newReadWriteTransaction();
241         CheckedFuture<Optional<Interface>, ReadFailedException> futureIface =
242                 rwTx.read(LogicalDatastoreType.CONFIGURATION, interfaceIid);
243         return Futures.transform(futureIface, new AsyncFunction<Optional<Interface>, Void>() {
244
245             @Override
246             public ListenableFuture<Void> apply(Optional<Interface> optIface) throws Exception {
247                 if (!optIface.isPresent()) {
248                     return Futures.immediateFailedFuture(new Exception("Iterface "
249                             + interfaceIid.firstKeyOf(Interface.class) + " does not exist on node " + vppNodeIid));
250                 }
251
252                 String existingBridgeDomain = resolveBridgeDomain(optIface.get());
253                 if (bridgeDomainName.equals(existingBridgeDomain)) {
254                     LOG.debug("Bridge domain {} already exists on interface {}", bridgeDomainName, interfacePath);
255                     String nodePath = VppPathMapper.bridgeDomainToRestPath(bridgeDomainName);
256                     if (!nodePath.equals(epLoc.getExternalNode())) {
257                         vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(nodePath,
258                                 epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
259                     }
260                     return Futures.immediateFuture(null);
261                 }
262
263                 InstanceIdentifier<L2> l2Iid =
264                         interfaceIid.builder().augmentation(VppInterfaceAugmentation.class).child(L2.class).build();
265                 L2 l2 = new L2Builder()
266                     .setInterconnection(new BridgeBasedBuilder().setBridgeDomain(bridgeDomainName).build()).build();
267                 rwTx.merge(LogicalDatastoreType.CONFIGURATION, l2Iid, l2);
268                 LOG.debug("Adding bridge domain {} to interface {}", bridgeDomainName, interfacePath);
269                 return Futures.transform(rwTx.submit(), new Function<Void, Void>() {
270
271                     @Override
272                     public Void apply(Void input) {
273                         String nodePath = VppPathMapper.bridgeDomainToRestPath(bridgeDomainName);
274                         vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(nodePath,
275                                 epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
276                         return null;
277                     }
278                 }, netconfWorker);
279             }
280         }, netconfWorker);
281     }
282
283     /**
284      * <p>
285      * Removes bridge domain (if exist) from an interface (if exist).<br>
286      * {@link VppEndpointLocationProvider#VPP_ENDPOINT_LOCATION_PROVIDER} will update endpoint
287      * location.
288      * <p>
289      * If the interface does not exist or other problems occur {@link ListenableFuture} will fail
290      * as {@link Futures#immediateFailedFuture(Throwable)} with {@link Exception}
291      * containing message in {@link Exception#getMessage()}
292      * 
293      * @param addrEpWithLoc {@link AddressEndpointWithLocation} containing
294      *        {@link ExternalLocationCase} where
295      *        {@link ExternalLocationCase#getExternalNodeMountPoint()} MUST NOT be {@code null}
296      *        and {@link ExternalLocationCase#getExternalNodeConnector()} MUST NOT be {@code null}
297      * @return {@link ListenableFuture}
298      */
299     public synchronized @Nonnull ListenableFuture<Void> deleteBridgeDomainFromInterface(
300             @Nonnull AddressEndpointWithLocation addrEpWithLoc) {
301         ExternalLocationCase epLoc = resolveAndValidateLocation(addrEpWithLoc);
302         InstanceIdentifier<?> vppNodeIid = epLoc.getExternalNodeMountPoint();
303         String interfacePath = epLoc.getExternalNodeConnector();
304
305         Optional<InstanceIdentifier<Interface>> optInterfaceIid =
306                 VppPathMapper.interfaceToInstanceIdentifier(interfacePath);
307         if (!optInterfaceIid.isPresent()) {
308             return Futures.immediateFailedFuture(
309                     new Exception("Cannot resolve interface instance-identifier for interface path" + interfacePath));
310         }
311         InstanceIdentifier<Interface> interfaceIid = optInterfaceIid.get();
312
313         Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
314         if (!potentialVppDataProvider.isPresent()) {
315             return Futures.immediateFailedFuture(new Exception("Cannot get data broker for mount point " + vppNodeIid));
316         }
317
318         ReadWriteTransaction rwTx = potentialVppDataProvider.get().newReadWriteTransaction();
319         CheckedFuture<Optional<Interface>, ReadFailedException> futureIface =
320                 rwTx.read(LogicalDatastoreType.CONFIGURATION, interfaceIid);
321         return Futures.transform(futureIface, new AsyncFunction<Optional<Interface>, Void>() {
322
323             @Override
324             public ListenableFuture<Void> apply(Optional<Interface> optIface) throws Exception {
325                 if (!optIface.isPresent()) {
326                     // interface does not exist so we consider job done
327                     return Futures.immediateFuture(null);
328                 }
329
330                 String existingBridgeDomain = resolveBridgeDomain(optIface.get());
331                 if (Strings.isNullOrEmpty(existingBridgeDomain)) {
332                     LOG.debug("Bridge domain does not exist therefore it is cosidered as"
333                             + "deleted for interface {}", interfacePath);
334                     // bridge domain does not exist on interface so we consider job done
335                     vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(null,
336                             epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
337                     return Futures.immediateFuture(null);
338                 }
339
340                 InstanceIdentifier<L2> l2Iid =
341                         interfaceIid.builder().augmentation(VppInterfaceAugmentation.class).child(L2.class).build();
342                 rwTx.delete(LogicalDatastoreType.CONFIGURATION, l2Iid);
343                 LOG.debug("Deleting bridge domain from interface {}", interfacePath);
344                 return Futures.transform(rwTx.submit(), new Function<Void, Void>() {
345
346                     @Override
347                     public Void apply(Void input) {
348                         vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(null,
349                                 epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
350                         return null;
351                     }
352                 }, netconfWorker);
353             }
354         }, netconfWorker);
355     }
356
357     private static ExternalLocationCase resolveAndValidateLocation(AddressEndpointWithLocation addrEpWithLoc) {
358         LocationType locationType = addrEpWithLoc.getAbsoluteLocation().getLocationType();
359         if (!(locationType instanceof ExternalLocationCase)) {
360             throw new IllegalArgumentException("Endpoint does not have external location " + addrEpWithLoc);
361         }
362         ExternalLocationCase result = (ExternalLocationCase) locationType;
363         if (result.getExternalNodeMountPoint() == null || result.getExternalNodeConnector() == null) {
364             throw new IllegalArgumentException(
365                     "Endpoint does not have external-node-mount-point or external-node-connector " + addrEpWithLoc);
366         }
367         return result;
368     }
369
370     private static @Nullable String resolveBridgeDomain(@Nonnull Interface iface) {
371         VppInterfaceAugmentation vppInterfaceAugmentation = iface.getAugmentation(VppInterfaceAugmentation.class);
372         L2 existingL2 = vppInterfaceAugmentation.getL2();
373         if (existingL2 != null) {
374             Interconnection interconnection = existingL2.getInterconnection();
375             if (interconnection instanceof BridgeBased) {
376                 return ((BridgeBased) interconnection).getBridgeDomain();
377             }
378         }
379         return null;
380     }
381
382     private static boolean hasNodeAndInterface(VppEndpoint vppEp) {
383         if (vppEp.getVppNodePath() == null) {
384             LOG.trace("vpp-node is missing. {}", vppEp);
385             return false;
386         }
387         if (Strings.isNullOrEmpty(vppEp.getVppInterfaceName())) {
388             LOG.trace("vpp-interface-name is missing. {}", vppEp);
389             return false;
390         }
391         return true;
392     }
393
394     @Override
395     public void close() throws Exception {
396         vppEndpointLocationProvider.close();
397     }
398
399 }