Merge "Resolved policy contracts"
[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                     String nodePath = VppPathMapper.bridgeDomainToRestPath(bridgeDomainName);
255                     if (!nodePath.equals(epLoc.getExternalNode())) {
256                         vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(nodePath,
257                                 epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
258                     }
259                     return Futures.immediateFuture(null);
260                 }
261
262                 InstanceIdentifier<L2> l2Iid =
263                         interfaceIid.builder().augmentation(VppInterfaceAugmentation.class).child(L2.class).build();
264                 L2 l2 = new L2Builder()
265                     .setInterconnection(new BridgeBasedBuilder().setBridgeDomain(bridgeDomainName).build()).build();
266                 rwTx.merge(LogicalDatastoreType.CONFIGURATION, l2Iid, l2);
267                 return Futures.transform(rwTx.submit(), new Function<Void, Void>() {
268
269                     @Override
270                     public Void apply(Void input) {
271                         String nodePath = VppPathMapper.bridgeDomainToRestPath(bridgeDomainName);
272                         vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(nodePath,
273                                 epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
274                         return null;
275                     }
276                 }, netconfWorker);
277             }
278         }, netconfWorker);
279     }
280
281     /**
282      * <p>
283      * Removes bridge domain (if exist) from an interface (if exist).<br>
284      * {@link VppEndpointLocationProvider#VPP_ENDPOINT_LOCATION_PROVIDER} will update endpoint
285      * location.
286      * <p>
287      * If the interface does not exist or other problems occur {@link ListenableFuture} will fail
288      * as {@link Futures#immediateFailedFuture(Throwable)} with {@link Exception}
289      * containing message in {@link Exception#getMessage()}
290      * 
291      * @param addrEpWithLoc {@link AddressEndpointWithLocation} containing
292      *        {@link ExternalLocationCase} where
293      *        {@link ExternalLocationCase#getExternalNodeMountPoint()} MUST NOT be {@code null}
294      *        and {@link ExternalLocationCase#getExternalNodeConnector()} MUST NOT be {@code null}
295      * @return {@link ListenableFuture}
296      */
297     public synchronized @Nonnull ListenableFuture<Void> deleteBridgeDomainFromInterface(
298             @Nonnull AddressEndpointWithLocation addrEpWithLoc) {
299         ExternalLocationCase epLoc = resolveAndValidateLocation(addrEpWithLoc);
300         InstanceIdentifier<?> vppNodeIid = epLoc.getExternalNodeMountPoint();
301         String interfacePath = epLoc.getExternalNodeConnector();
302
303         Optional<InstanceIdentifier<Interface>> optInterfaceIid =
304                 VppPathMapper.interfaceToInstanceIdentifier(interfacePath);
305         if (!optInterfaceIid.isPresent()) {
306             return Futures.immediateFailedFuture(
307                     new Exception("Cannot resolve interface instance-identifier for interface path" + interfacePath));
308         }
309         InstanceIdentifier<Interface> interfaceIid = optInterfaceIid.get();
310
311         Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
312         if (!potentialVppDataProvider.isPresent()) {
313             return Futures.immediateFailedFuture(new Exception("Cannot get data broker for mount point " + vppNodeIid));
314         }
315
316         ReadWriteTransaction rwTx = potentialVppDataProvider.get().newReadWriteTransaction();
317         CheckedFuture<Optional<Interface>, ReadFailedException> futureIface =
318                 rwTx.read(LogicalDatastoreType.CONFIGURATION, interfaceIid);
319         return Futures.transform(futureIface, new AsyncFunction<Optional<Interface>, Void>() {
320
321             @Override
322             public ListenableFuture<Void> apply(Optional<Interface> optIface) throws Exception {
323                 if (!optIface.isPresent()) {
324                     // interface does not exist so we consider job done
325                     return Futures.immediateFuture(null);
326                 }
327
328                 String existingBridgeDomain = resolveBridgeDomain(optIface.get());
329                 if (Strings.isNullOrEmpty(existingBridgeDomain)) {
330                     // bridge domain does not exist on interface so we consider job done
331                     vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(null,
332                             epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
333                     return Futures.immediateFuture(null);
334                 }
335
336                 InstanceIdentifier<L2> l2Iid =
337                         interfaceIid.builder().augmentation(VppInterfaceAugmentation.class).child(L2.class).build();
338                 rwTx.delete(LogicalDatastoreType.CONFIGURATION, l2Iid);
339                 return Futures.transform(rwTx.submit(), new Function<Void, Void>() {
340
341                     @Override
342                     public Void apply(Void input) {
343                         vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(null,
344                                 epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
345                         return null;
346                     }
347                 }, netconfWorker);
348             }
349         }, netconfWorker);
350     }
351
352     private static ExternalLocationCase resolveAndValidateLocation(AddressEndpointWithLocation addrEpWithLoc) {
353         LocationType locationType = addrEpWithLoc.getAbsoluteLocation().getLocationType();
354         if (!(locationType instanceof ExternalLocationCase)) {
355             throw new IllegalArgumentException("Endpoint does not have external location " + addrEpWithLoc);
356         }
357         ExternalLocationCase result = (ExternalLocationCase) locationType;
358         if (result.getExternalNodeMountPoint() == null || result.getExternalNodeConnector() == null) {
359             throw new IllegalArgumentException(
360                     "Endpoint does not have external-node-mount-point or external-node-connector " + addrEpWithLoc);
361         }
362         return result;
363     }
364
365     private static @Nullable String resolveBridgeDomain(@Nonnull Interface iface) {
366         VppInterfaceAugmentation vppInterfaceAugmentation = iface.getAugmentation(VppInterfaceAugmentation.class);
367         L2 existingL2 = vppInterfaceAugmentation.getL2();
368         if (existingL2 != null) {
369             Interconnection interconnection = existingL2.getInterconnection();
370             if (interconnection instanceof BridgeBased) {
371                 return ((BridgeBased) interconnection).getBridgeDomain();
372             }
373         }
374         return null;
375     }
376
377     private static boolean hasNodeAndInterface(VppEndpoint vppEp) {
378         if (vppEp.getVppNodePath() == null) {
379             LOG.trace("vpp-node is missing. {}", vppEp);
380             return false;
381         }
382         if (Strings.isNullOrEmpty(vppEp.getVppInterfaceName())) {
383             LOG.trace("vpp-interface-name is missing. {}", vppEp);
384             return false;
385         }
386         return true;
387     }
388
389     @Override
390     public void close() throws Exception {
391         vppEndpointLocationProvider.close();
392     }
393
394 }