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 LOG.debug("Bridge domain {} already exists on interface {}", bridgeDomainName, interfacePath);
255 String nodePath = VppPathMapper.bridgeDomainToRestPath(bridgeDomainName);
256 if (!nodePath.equals(epLoc.getExternalNode())) {
257 vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(nodePath,
258 epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
260 return Futures.immediateFuture(null);
263 InstanceIdentifier<L2> l2Iid =
264 interfaceIid.builder().augmentation(VppInterfaceAugmentation.class).child(L2.class).build();
265 L2 l2 = new L2Builder()
266 .setInterconnection(new BridgeBasedBuilder().setBridgeDomain(bridgeDomainName).build()).build();
267 rwTx.merge(LogicalDatastoreType.CONFIGURATION, l2Iid, l2);
268 LOG.debug("Adding bridge domain {} to interface {}", bridgeDomainName, interfacePath);
269 return Futures.transform(rwTx.submit(), new Function<Void, Void>() {
272 public Void apply(Void input) {
273 String nodePath = VppPathMapper.bridgeDomainToRestPath(bridgeDomainName);
274 vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(nodePath,
275 epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
285 * Removes bridge domain (if exist) from an interface (if exist).<br>
286 * {@link VppEndpointLocationProvider#VPP_ENDPOINT_LOCATION_PROVIDER} will update endpoint
289 * If the interface does not exist or other problems occur {@link ListenableFuture} will fail
290 * as {@link Futures#immediateFailedFuture(Throwable)} with {@link Exception}
291 * containing message in {@link Exception#getMessage()}
293 * @param addrEpWithLoc {@link AddressEndpointWithLocation} containing
294 * {@link ExternalLocationCase} where
295 * {@link ExternalLocationCase#getExternalNodeMountPoint()} MUST NOT be {@code null}
296 * and {@link ExternalLocationCase#getExternalNodeConnector()} MUST NOT be {@code null}
297 * @return {@link ListenableFuture}
299 public synchronized @Nonnull ListenableFuture<Void> deleteBridgeDomainFromInterface(
300 @Nonnull AddressEndpointWithLocation addrEpWithLoc) {
301 ExternalLocationCase epLoc = resolveAndValidateLocation(addrEpWithLoc);
302 InstanceIdentifier<?> vppNodeIid = epLoc.getExternalNodeMountPoint();
303 String interfacePath = epLoc.getExternalNodeConnector();
305 Optional<InstanceIdentifier<Interface>> optInterfaceIid =
306 VppPathMapper.interfaceToInstanceIdentifier(interfacePath);
307 if (!optInterfaceIid.isPresent()) {
308 return Futures.immediateFailedFuture(
309 new Exception("Cannot resolve interface instance-identifier for interface path" + interfacePath));
311 InstanceIdentifier<Interface> interfaceIid = optInterfaceIid.get();
313 Optional<DataBroker> potentialVppDataProvider = mountDataProvider.getDataBrokerForMountPoint(vppNodeIid);
314 if (!potentialVppDataProvider.isPresent()) {
315 return Futures.immediateFailedFuture(new Exception("Cannot get data broker for mount point " + vppNodeIid));
318 ReadWriteTransaction rwTx = potentialVppDataProvider.get().newReadWriteTransaction();
319 CheckedFuture<Optional<Interface>, ReadFailedException> futureIface =
320 rwTx.read(LogicalDatastoreType.CONFIGURATION, interfaceIid);
321 return Futures.transform(futureIface, new AsyncFunction<Optional<Interface>, Void>() {
324 public ListenableFuture<Void> apply(Optional<Interface> optIface) throws Exception {
325 if (!optIface.isPresent()) {
326 // interface does not exist so we consider job done
327 return Futures.immediateFuture(null);
330 String existingBridgeDomain = resolveBridgeDomain(optIface.get());
331 if (Strings.isNullOrEmpty(existingBridgeDomain)) {
332 LOG.debug("Bridge domain does not exist therefore it is cosidered as"
333 + "deleted for interface {}", interfacePath);
334 // bridge domain does not exist on interface so we consider job done
335 vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(null,
336 epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
337 return Futures.immediateFuture(null);
340 InstanceIdentifier<L2> l2Iid =
341 interfaceIid.builder().augmentation(VppInterfaceAugmentation.class).child(L2.class).build();
342 rwTx.delete(LogicalDatastoreType.CONFIGURATION, l2Iid);
343 LOG.debug("Deleting bridge domain from interface {}", interfacePath);
344 return Futures.transform(rwTx.submit(), new Function<Void, Void>() {
347 public Void apply(Void input) {
348 vppEndpointLocationProvider.updateExternalNodeLocationForEndpoint(null,
349 epLoc.getExternalNodeMountPoint(), addrEpWithLoc.getKey());
357 private static ExternalLocationCase resolveAndValidateLocation(AddressEndpointWithLocation addrEpWithLoc) {
358 LocationType locationType = addrEpWithLoc.getAbsoluteLocation().getLocationType();
359 if (!(locationType instanceof ExternalLocationCase)) {
360 throw new IllegalArgumentException("Endpoint does not have external location " + addrEpWithLoc);
362 ExternalLocationCase result = (ExternalLocationCase) locationType;
363 if (result.getExternalNodeMountPoint() == null || result.getExternalNodeConnector() == null) {
364 throw new IllegalArgumentException(
365 "Endpoint does not have external-node-mount-point or external-node-connector " + addrEpWithLoc);
370 private static @Nullable String resolveBridgeDomain(@Nonnull Interface iface) {
371 VppInterfaceAugmentation vppInterfaceAugmentation = iface.getAugmentation(VppInterfaceAugmentation.class);
372 L2 existingL2 = vppInterfaceAugmentation.getL2();
373 if (existingL2 != null) {
374 Interconnection interconnection = existingL2.getInterconnection();
375 if (interconnection instanceof BridgeBased) {
376 return ((BridgeBased) interconnection).getBridgeDomain();
382 private static boolean hasNodeAndInterface(VppEndpoint vppEp) {
383 if (vppEp.getVppNodePath() == null) {
384 LOG.trace("vpp-node is missing. {}", vppEp);
387 if (Strings.isNullOrEmpty(vppEp.getVppInterfaceName())) {
388 LOG.trace("vpp-interface-name is missing. {}", vppEp);
395 public void close() throws Exception {
396 vppEndpointLocationProvider.close();