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