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.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCaseBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint.InterfaceTypeChoice;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.vpp.endpoint._interface.type.choice.VhostUserCase;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VhostUserRole;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2Builder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.base.attributes.Interconnection;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.base.attributes.interconnection.BridgeBased;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.base.attributes.interconnection.BridgeBasedBuilder;
42 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
46 import com.google.common.base.Function;
47 import com.google.common.base.Optional;
48 import com.google.common.base.Preconditions;
49 import com.google.common.base.Strings;
50 import com.google.common.eventbus.Subscribe;
51 import com.google.common.util.concurrent.AsyncFunction;
52 import com.google.common.util.concurrent.CheckedFuture;
53 import com.google.common.util.concurrent.FutureCallback;
54 import com.google.common.util.concurrent.Futures;
55 import com.google.common.util.concurrent.ListenableFuture;
57 public class InterfaceManager implements AutoCloseable {
59 private static final Logger LOG = LoggerFactory.getLogger(InterfaceManager.class);
60 private final MountedDataBrokerProvider mountDataProvider;
61 private final VppEndpointLocationProvider vppEndpointLocationProvider;
62 private final ExecutorService netconfWorker;
64 public InterfaceManager(@Nonnull MountedDataBrokerProvider mountDataProvider, @Nonnull DataBroker dataProvider, @Nonnull ExecutorService netconfWorker) {
65 this.mountDataProvider = Preconditions.checkNotNull(mountDataProvider);
66 this.netconfWorker = Preconditions.checkNotNull(netconfWorker);
67 this.vppEndpointLocationProvider = new VppEndpointLocationProvider(dataProvider);
71 public synchronized void vppEndpointChanged(VppEndpointConfEvent event) {
72 switch (event.getDtoModificationType()) {
74 vppEndpointCreated(event.getAfter().get());
77 vppEndpointDeleted(event.getBefore().get());
78 vppEndpointCreated(event.getAfter().get());
81 vppEndpointDeleted(event.getBefore().get());
86 private void vppEndpointCreated(VppEndpoint vppEndpoint) {
87 Optional<ConfigCommand> potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.PUT);
88 if (!potentialIfaceCommand.isPresent()) {
91 ConfigCommand ifaceWithoutBdCommand = potentialIfaceCommand.get();
92 InstanceIdentifier<?> vppNodeIid = vppEndpoint.getVppNodePath();
93 Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
94 if (!potentialVppDataProvider.isPresent()) {
95 LOG.debug("Cannot get data broker for mount point {}", vppNodeIid);
98 DataBroker vppDataBroker = potentialVppDataProvider.get();
99 createIfaceOnVpp(ifaceWithoutBdCommand, vppDataBroker, vppEndpoint, vppNodeIid);
102 private void createIfaceOnVpp(ConfigCommand createIfaceWithoutBdCommand, DataBroker vppDataBroker,
103 VppEndpoint vppEndpoint, InstanceIdentifier<?> vppNodeIid) {
104 ReadWriteTransaction rwTx = vppDataBroker.newReadWriteTransaction();
105 createIfaceWithoutBdCommand.execute(rwTx);
106 Futures.addCallback(rwTx.submit(), new FutureCallback<Void>() {
109 public void onSuccess(Void result) {
110 LOG.debug("Create interface on VPP command was successful:\nVPP: {}\nCommand: {}", vppNodeIid,
111 createIfaceWithoutBdCommand);
112 vppEndpointLocationProvider.createLocationForVppEndpoint(vppEndpoint);
116 public void onFailure(Throwable t) {
117 LOG.error("Create interface on VPP command was NOT successful:\nVPP: {}\nCommand: {}", vppNodeIid,
118 createIfaceWithoutBdCommand, t);
123 private void vppEndpointDeleted(VppEndpoint vppEndpoint) {
124 Optional<ConfigCommand> potentialIfaceCommand = createInterfaceWithoutBdCommand(vppEndpoint, Operations.DELETE);
125 if (!potentialIfaceCommand.isPresent()) {
128 ConfigCommand ifaceWithoutBdCommand = potentialIfaceCommand.get();
129 InstanceIdentifier<?> vppNodeIid = vppEndpoint.getVppNodePath();
130 Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
131 if (!potentialVppDataProvider.isPresent()) {
132 LOG.debug("Cannot get data broker for mount point {}", vppNodeIid);
135 DataBroker vppDataBroker = potentialVppDataProvider.get();
136 deleteIfaceOnVpp(ifaceWithoutBdCommand, vppDataBroker, vppEndpoint, vppNodeIid);
139 private void deleteIfaceOnVpp(ConfigCommand deleteIfaceWithoutBdCommand, DataBroker vppDataBroker,
140 VppEndpoint vppEndpoint, InstanceIdentifier<?> vppNodeIid) {
141 ReadWriteTransaction rwTx = vppDataBroker.newReadWriteTransaction();
142 deleteIfaceWithoutBdCommand.execute(rwTx);
143 Futures.addCallback(rwTx.submit(), new FutureCallback<Void>() {
146 public void onSuccess(Void result) {
147 LOG.debug("Delete interface on VPP command was successful:\nVPP: {}\nCommand: {}", vppNodeIid,
148 deleteIfaceWithoutBdCommand);
149 vppEndpointLocationProvider.deleteLocationForVppEndpoint(vppEndpoint);
153 public void onFailure(Throwable t) {
154 LOG.error("Delete interface on VPP command was NOT successful:\nVPP: {}\nCommand: {}", vppNodeIid,
155 deleteIfaceWithoutBdCommand, t);
161 public synchronized void vppNodeChanged(NodeOperEvent event) {
162 switch (event.getDtoModificationType()) {
164 if (event.isAfterConnected()) {
165 // TODO read VppEndpoints or cache them during vppEndpointChanged()
169 if (!event.isBeforeConnected() && event.isAfterConnected()) {
170 // TODO reconciliation - diff between disconnected snapshot and current snapshot
174 if (event.isBeforeConnected()) {
175 // TODO we could do snapshot of VppEndpoints
176 // which can be used for reconciliation
182 private static Optional<ConfigCommand> createInterfaceWithoutBdCommand(@Nonnull VppEndpoint vppEp,
183 @Nonnull Operations operations) {
184 if (!hasNodeAndInterface(vppEp)) {
185 LOG.debug("Interface command is not created for {}", vppEp);
186 return Optional.absent();
188 VhostUserCommandBuilder builder = VhostUserCommand.builder();
189 builder.setName(vppEp.getVppInterfaceName());
190 InterfaceTypeChoice interfaceTypeChoice = vppEp.getInterfaceTypeChoice();
191 if (interfaceTypeChoice instanceof VhostUserCase) {
192 VhostUserCase vhostUserIface = (VhostUserCase) interfaceTypeChoice;
193 String socket = vhostUserIface.getSocket();
194 if (Strings.isNullOrEmpty(socket)) {
195 LOG.debug("Vhost user interface command is not created because socket is missing. {}", vppEp);
196 return Optional.absent();
198 builder.setSocket(socket);
199 builder.setRole(VhostUserRole.Client);
201 VhostUserCommand vhostUserCommand =
202 builder.setOperation(operations).setDescription(vppEp.getDescription()).build();
203 return Optional.of(vhostUserCommand);
207 * Adds bridge domain to an interface if the interface exist.<br>
208 * It rewrites bridge domain in case it already exist.<br>
209 * {@link VppEndpointLocationProvider#VPP_ENDPOINT_LOCATION_PROVIDER} will update location
210 * when the interface is created successfully.<br>
211 * If the interface does not exist or other problems occur {@link ListenableFuture} will fail
212 * as {@link Futures#immediateFailedFuture(Throwable)} with {@link Exception}
213 * containing message in {@link Exception#getMessage()}
215 * @param bridgeDomainName bridge domain
216 * @param addrEpWithLoc {@link AddressEndpointWithLocation} containing
217 * {@link ExternalLocationCase} where
218 * {@link ExternalLocationCase#getExternalNodeMountPoint()} MUST NOT be {@code null}
219 * and {@link ExternalLocationCase#getExternalNodeConnector()} MUST NOT be {@code null}
220 * @return {@link ListenableFuture}
222 public synchronized @Nonnull ListenableFuture<Void> addBridgeDomainToInterface(@Nonnull String bridgeDomainName,
223 @Nonnull AddressEndpointWithLocation addrEpWithLoc) {
224 ExternalLocationCase epLoc = resolveAndValidateLocation(addrEpWithLoc);
225 InstanceIdentifier<?> vppNodeIid = epLoc.getExternalNodeMountPoint();
226 String interfacePath = epLoc.getExternalNodeConnector();
228 Optional<InstanceIdentifier<Interface>> optInterfaceIid =
229 VppPathMapper.interfaceToInstanceIdentifier(interfacePath);
230 if (!optInterfaceIid.isPresent()) {
231 return Futures.immediateFailedFuture(
232 new Exception("Cannot resolve interface instance-identifier for interface path" + interfacePath));
234 InstanceIdentifier<Interface> interfaceIid = optInterfaceIid.get();
236 Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
237 if (!potentialVppDataProvider.isPresent()) {
238 return Futures.immediateFailedFuture(new Exception("Cannot get data broker for mount point " + vppNodeIid));
241 ReadWriteTransaction rwTx = potentialVppDataProvider.get().newReadWriteTransaction();
242 CheckedFuture<Optional<Interface>, ReadFailedException> futureIface =
243 rwTx.read(LogicalDatastoreType.CONFIGURATION, interfaceIid);
244 return Futures.transform(futureIface, new AsyncFunction<Optional<Interface>, Void>() {
247 public ListenableFuture<Void> apply(Optional<Interface> optIface) throws Exception {
248 if (!optIface.isPresent()) {
249 return Futures.immediateFailedFuture(new Exception("Iterface "
250 + interfaceIid.firstKeyOf(Interface.class) + " does not exist on node " + vppNodeIid));
253 String existingBridgeDomain = resolveBridgeDomain(optIface.get());
254 if (bridgeDomainName.equals(existingBridgeDomain)) {
255 LOG.debug("Bridge domain {} already exists on interface {}", bridgeDomainName, interfacePath);
256 String bridgeDomainPath = VppPathMapper.bridgeDomainToRestPath(bridgeDomainName);
257 if (!bridgeDomainPath.equals(epLoc.getExternalNode())) {
258 vppEndpointLocationProvider.replaceLocationForEndpoint(new ExternalLocationCaseBuilder()
259 .setExternalNode(bridgeDomainPath)
260 .setExternalNodeMountPoint(vppNodeIid)
261 .setExternalNodeConnector(interfacePath)
262 .build(), addrEpWithLoc.getKey());
264 return Futures.immediateFuture(null);
267 InstanceIdentifier<L2> l2Iid =
268 interfaceIid.builder().augmentation(VppInterfaceAugmentation.class).child(L2.class).build();
269 L2 l2 = new L2Builder()
270 .setInterconnection(new BridgeBasedBuilder().setBridgeDomain(bridgeDomainName).build()).build();
271 rwTx.merge(LogicalDatastoreType.CONFIGURATION, l2Iid, l2);
272 LOG.debug("Adding bridge domain {} to interface {}", bridgeDomainName, interfacePath);
273 return Futures.transform(rwTx.submit(), new Function<Void, Void>() {
276 public Void apply(Void input) {
277 String bridgeDomainPath = VppPathMapper.bridgeDomainToRestPath(bridgeDomainName);
278 vppEndpointLocationProvider.replaceLocationForEndpoint(new ExternalLocationCaseBuilder()
279 .setExternalNode(bridgeDomainPath)
280 .setExternalNodeMountPoint(vppNodeIid)
281 .setExternalNodeConnector(interfacePath)
282 .build(), addrEpWithLoc.getKey());
292 * Removes bridge domain (if exist) from an interface (if exist).<br>
293 * {@link VppEndpointLocationProvider#VPP_ENDPOINT_LOCATION_PROVIDER} will update endpoint
296 * If the interface does not exist or other problems occur {@link ListenableFuture} will fail
297 * as {@link Futures#immediateFailedFuture(Throwable)} with {@link Exception}
298 * containing message in {@link Exception#getMessage()}
300 * @param addrEpWithLoc {@link AddressEndpointWithLocation} containing
301 * {@link ExternalLocationCase} where
302 * {@link ExternalLocationCase#getExternalNodeMountPoint()} MUST NOT be {@code null}
303 * and {@link ExternalLocationCase#getExternalNodeConnector()} MUST NOT be {@code null}
304 * @return {@link ListenableFuture}
306 public synchronized @Nonnull ListenableFuture<Void> deleteBridgeDomainFromInterface(
307 @Nonnull AddressEndpointWithLocation addrEpWithLoc) {
308 ExternalLocationCase epLoc = resolveAndValidateLocation(addrEpWithLoc);
309 InstanceIdentifier<?> vppNodeIid = epLoc.getExternalNodeMountPoint();
310 String interfacePath = epLoc.getExternalNodeConnector();
312 Optional<InstanceIdentifier<Interface>> optInterfaceIid =
313 VppPathMapper.interfaceToInstanceIdentifier(interfacePath);
314 if (!optInterfaceIid.isPresent()) {
315 return Futures.immediateFailedFuture(
316 new Exception("Cannot resolve interface instance-identifier for interface path" + interfacePath));
318 InstanceIdentifier<Interface> interfaceIid = optInterfaceIid.get();
320 Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
321 if (!potentialVppDataProvider.isPresent()) {
322 return Futures.immediateFailedFuture(new Exception("Cannot get data broker for mount point " + vppNodeIid));
325 ReadWriteTransaction rwTx = potentialVppDataProvider.get().newReadWriteTransaction();
326 CheckedFuture<Optional<Interface>, ReadFailedException> futureIface =
327 rwTx.read(LogicalDatastoreType.CONFIGURATION, interfaceIid);
328 return Futures.transform(futureIface, new AsyncFunction<Optional<Interface>, Void>() {
331 public ListenableFuture<Void> apply(Optional<Interface> optIface) throws Exception {
332 if (!optIface.isPresent()) {
333 // interface does not exist so we consider job done
334 return Futures.immediateFuture(null);
337 String existingBridgeDomain = resolveBridgeDomain(optIface.get());
338 if (Strings.isNullOrEmpty(existingBridgeDomain)) {
339 LOG.debug("Bridge domain does not exist therefore it is cosidered as"
340 + "deleted for interface {}", interfacePath);
341 // bridge domain does not exist on interface so we consider job done
342 vppEndpointLocationProvider.replaceLocationForEndpoint(new ExternalLocationCaseBuilder()
343 .setExternalNode(null)
344 .setExternalNodeMountPoint(vppNodeIid)
345 .setExternalNodeConnector(interfacePath)
346 .build(), addrEpWithLoc.getKey());
347 return Futures.immediateFuture(null);
350 InstanceIdentifier<L2> l2Iid =
351 interfaceIid.builder().augmentation(VppInterfaceAugmentation.class).child(L2.class).build();
352 rwTx.delete(LogicalDatastoreType.CONFIGURATION, l2Iid);
353 LOG.debug("Deleting bridge domain from interface {}", interfacePath);
354 return Futures.transform(rwTx.submit(), new Function<Void, Void>() {
357 public Void apply(Void input) {
358 vppEndpointLocationProvider.replaceLocationForEndpoint(new ExternalLocationCaseBuilder()
359 .setExternalNode(null)
360 .setExternalNodeMountPoint(vppNodeIid)
361 .setExternalNodeConnector(interfacePath)
362 .build(), addrEpWithLoc.getKey());
370 private static ExternalLocationCase resolveAndValidateLocation(AddressEndpointWithLocation addrEpWithLoc) {
371 LocationType locationType = addrEpWithLoc.getAbsoluteLocation().getLocationType();
372 if (!(locationType instanceof ExternalLocationCase)) {
373 throw new IllegalArgumentException("Endpoint does not have external location " + addrEpWithLoc);
375 ExternalLocationCase result = (ExternalLocationCase) locationType;
376 if (result.getExternalNodeMountPoint() == null || result.getExternalNodeConnector() == null) {
377 throw new IllegalArgumentException(
378 "Endpoint does not have external-node-mount-point or external-node-connector " + addrEpWithLoc);
383 private static @Nullable String resolveBridgeDomain(@Nonnull Interface iface) {
384 VppInterfaceAugmentation vppInterfaceAugmentation = iface.getAugmentation(VppInterfaceAugmentation.class);
385 L2 existingL2 = vppInterfaceAugmentation.getL2();
386 if (existingL2 != null) {
387 Interconnection interconnection = existingL2.getInterconnection();
388 if (interconnection instanceof BridgeBased) {
389 return ((BridgeBased) interconnection).getBridgeDomain();
395 private static boolean hasNodeAndInterface(VppEndpoint vppEp) {
396 if (vppEp.getVppNodePath() == null) {
397 LOG.trace("vpp-node is missing. {}", vppEp);
400 if (Strings.isNullOrEmpty(vppEp.getVppInterfaceName())) {
401 LOG.trace("vpp-interface-name is missing. {}", vppEp);
408 public void close() throws Exception {
409 vppEndpointLocationProvider.close();