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 java.util.concurrent.ExecutorService;
13 import javax.annotation.Nonnull;
14 import javax.annotation.Nullable;
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;
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;
56 public class InterfaceManager implements AutoCloseable {
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;
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);
70 public synchronized void vppEndpointChanged(VppEndpointConfEvent event) {
71 switch (event.getDtoModificationType()) {
73 vppEndpointCreated(event.getAfter().get());
76 vppEndpointDeleted(event.getBefore().get());
77 vppEndpointCreated(event.getAfter().get());
80 vppEndpointDeleted(event.getBefore().get());
85 private void vppEndpointCreated(VppEndpoint vppEndpoint) {
86 Optional<ConfigCommand> potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.PUT);
87 if (!potentialIfaceCommand.isPresent()) {
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);
97 DataBroker vppDataBroker = potentialVppDataProvider.get();
98 createIfaceOnVpp(ifaceWithoutBdCommand, vppDataBroker, vppEndpoint, vppNodeIid);
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>() {
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);
115 public void onFailure(Throwable t) {
116 LOG.error("Create interface on VPP command was NOT successful:\nVPP: {}\nCommand: {}", vppNodeIid,
117 createIfaceWithoutBdCommand, t);
122 private void vppEndpointDeleted(VppEndpoint vppEndpoint) {
123 Optional<ConfigCommand> potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.DELETE);
124 if (!potentialIfaceCommand.isPresent()) {
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);
134 DataBroker vppDataBroker = potentialVppDataProvider.get();
135 deleteIfaceOnVpp(ifaceWithoutBdCommand, vppDataBroker, vppEndpoint, vppNodeIid);
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>() {
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);
152 public void onFailure(Throwable t) {
153 LOG.error("Delete interface on VPP command was NOT successful:\nVPP: {}\nCommand: {}", vppNodeIid,
154 deleteIfaceWithoutBdCommand, t);
160 public synchronized void vppNodeChanged(NodeOperEvent event) {
161 switch (event.getDtoModificationType()) {
163 if (event.isAfterConnected()) {
164 // TODO read VppEndpoints or cache them during vppEndpointChanged()
168 if (!event.isBeforeConnected() && event.isAfterConnected()) {
169 // TODO reconciliation - diff between disconnected snapshot and current snapshot
173 if (event.isBeforeConnected()) {
174 // TODO we could do snapshot of VppEndpoints
175 // which can be used for reconciliation
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();
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();
197 builder.setSocket(socket);
198 builder.setRole(VhostUserRole.Client);
200 VhostUserCommand vhostUserCommand =
201 builder.setOperation(operations).setDescription(vppEp.getDescription()).build();
202 return Optional.of(vhostUserCommand);
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()}
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}
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();
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));
233 InstanceIdentifier<Interface> interfaceIid = optInterfaceIid.get();
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));
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>() {
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));
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());
259 return Futures.immediateFuture(null);
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>() {
270 public Void apply(Void input) {
271 String nodePath = VppPathMapper.bridgeDomainToRestPath(bridgeDomainName);
272 vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(nodePath,
273 epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
283 * Removes bridge domain (if exist) from an interface (if exist).<br>
284 * {@link VppEndpointLocationProvider#VPP_ENDPOINT_LOCATION_PROVIDER} will update endpoint
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()}
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}
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();
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));
309 InstanceIdentifier<Interface> interfaceIid = optInterfaceIid.get();
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));
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>() {
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);
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);
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>() {
342 public Void apply(Void input) {
343 vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(null,
344 epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
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);
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);
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();
377 private static boolean hasNodeAndInterface(VppEndpoint vppEp) {
378 if (vppEp.getVppNodePath() == null) {
379 LOG.trace("vpp-node is missing. {}", vppEp);
382 if (Strings.isNullOrEmpty(vppEp.getVppInterfaceName())) {
383 LOG.trace("vpp-interface-name is missing. {}", vppEp);
390 public void close() throws Exception {
391 vppEndpointLocationProvider.close();