2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.groupbasedpolicy.renderer.vpp.iface;
11 import javax.annotation.Nonnull;
12 import javax.annotation.Nullable;
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;
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;
54 public class InterfaceManager implements AutoCloseable {
56 private static final Logger LOG = LoggerFactory.getLogger(InterfaceManager.class);
57 private final MountedDataBrokerProvider mountDataProvider;
58 private final VppEndpointLocationProvider vppEndpointLocationProvider;
60 public InterfaceManager(@Nonnull MountedDataBrokerProvider mountDataProvider, @Nonnull DataBroker dataProvider) {
61 this.mountDataProvider = Preconditions.checkNotNull(mountDataProvider);
62 this.vppEndpointLocationProvider = new VppEndpointLocationProvider(dataProvider);
66 public synchronized void vppEndpointChanged(VppEndpointConfEvent event) {
67 switch (event.getDtoModificationType()) {
69 vppEndpointCreated(event.getAfter().get());
72 vppEndpointDeleted(event.getBefore().get());
73 vppEndpointCreated(event.getAfter().get());
76 vppEndpointDeleted(event.getBefore().get());
81 private void vppEndpointCreated(VppEndpoint vppEndpoint) {
82 Optional<ConfigCommand> potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.PUT);
83 if (!potentialIfaceCommand.isPresent()) {
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);
93 DataBroker vppDataBroker = potentialVppDataProvider.get();
94 createIfaceOnVpp(ifaceWithoutBdCommand, vppDataBroker, vppEndpoint, vppNodeIid);
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>() {
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);
111 public void onFailure(Throwable t) {
112 LOG.error("Create interface on VPP command was NOT successful:\nVPP: {}\nCommand: {}", vppNodeIid,
113 createIfaceWithoutBdCommand, t);
118 private void vppEndpointDeleted(VppEndpoint vppEndpoint) {
119 Optional<ConfigCommand> potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.DELETE);
120 if (!potentialIfaceCommand.isPresent()) {
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);
130 DataBroker vppDataBroker = potentialVppDataProvider.get();
131 deleteIfaceOnVpp(ifaceWithoutBdCommand, vppDataBroker, vppEndpoint, vppNodeIid);
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>() {
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);
148 public void onFailure(Throwable t) {
149 LOG.error("Delete interface on VPP command was NOT successful:\nVPP: {}\nCommand: {}", vppNodeIid,
150 deleteIfaceWithoutBdCommand, t);
156 public synchronized void vppNodeChanged(NodeOperEvent event) {
157 switch (event.getDtoModificationType()) {
159 if (event.isAfterConnected()) {
160 // TODO read VppEndpoints or cache them during vppEndpointChanged()
164 if (!event.isBeforeConnected() && event.isAfterConnected()) {
165 // TODO reconciliation - diff between disconnected snapshot and current snapshot
169 if (event.isBeforeConnected()) {
170 // TODO we could do snapshot of VppEndpoints
171 // which can be used for reconciliation
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();
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();
193 builder.setSocket(socket);
194 builder.setRole(VhostUserRole.Client);
196 VhostUserCommand vhostUserCommand =
197 builder.setOperation(operations).setDescription(vppEp.getDescription()).build();
198 return Optional.of(vhostUserCommand);
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()}
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}
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();
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));
229 InstanceIdentifier<Interface> interfaceIid = optInterfaceIid.get();
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));
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>() {
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));
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());
255 return Futures.immediateFuture(null);
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>() {
266 public Void apply(Void input) {
267 String nodePath = VppPathMapper.bridgeDomainToRestPath(bridgeDomainName);
268 vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(nodePath,
269 addrEpWithLoc.getKey());
279 * Removes bridge domain (if exist) from an interface (if exist).<br>
280 * {@link VppEndpointLocationProvider#VPP_ENDPOINT_LOCATION_PROVIDER} will update endpoint
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()}
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}
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();
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));
305 InstanceIdentifier<Interface> interfaceIid = optInterfaceIid.get();
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));
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>() {
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);
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);
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>() {
337 public Void apply(Void input) {
338 vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(null, addrEpWithLoc.getKey());
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);
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);
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();
371 private static boolean hasNodeAndInterface(VppEndpoint vppEp) {
372 if (vppEp.getVppNodePath() == null) {
373 LOG.trace("vpp-node is missing. {}", vppEp);
376 if (Strings.isNullOrEmpty(vppEp.getVppInterfaceName())) {
377 LOG.trace("vpp-interface-name is missing. {}", vppEp);
384 public void close() throws Exception {
385 vppEndpointLocationProvider.close();