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.policy;
11 import java.util.Collection;
12 import java.util.Collections;
13 import java.util.concurrent.ExecutionException;
14 import java.util.concurrent.TimeoutException;
16 import javax.annotation.Nonnull;
17 import javax.annotation.Nullable;
19 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
22 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
23 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
24 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
25 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
26 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
29 import org.opendaylight.groupbasedpolicy.renderer.vpp.api.BridgeDomainManager;
30 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory;
31 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.bridge.domain.base.attributes.PhysicalLocationRef;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.GbpBridgeDomain;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.GbpBridgeDomainKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.VxlanVni;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.bridge.domains.BridgeDomain;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.vpp.state.bridge.domains.BridgeDomainKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.status.rev161005.BridgeDomainStatusAugmentation;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.status.rev161005.BridgeDomainStatusFields.BridgeDomainStatus;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.NodeVbridgeAugment;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TopologyTypesVbridgeAugment;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TopologyTypesVbridgeAugmentBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TopologyVbridgeAugment;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TopologyVbridgeAugmentBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.node.BridgeMember;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.topology.types.VbridgeTopologyBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vlan.rev160429.NodeVbridgeVlanAugment;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vlan.rev160429.NodeVbridgeVlanAugmentBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vlan.rev160429.TunnelTypeVlan;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vlan.rev160429.network.topology.topology.tunnel.parameters.VlanNetworkParametersBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vxlan.rev160429.TunnelTypeVxlan;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vxlan.rev160429.network.topology.topology.tunnel.parameters.VxlanTunnelParametersBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214._802dot1q;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
61 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
62 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
63 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
64 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.TopologyTypes;
65 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.TopologyTypesBuilder;
66 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNodeBuilder;
67 import org.opendaylight.yangtools.concepts.ListenerRegistration;
68 import org.opendaylight.yangtools.yang.binding.DataObject;
69 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
73 import com.google.common.base.Optional;
74 import com.google.common.base.Preconditions;
75 import com.google.common.util.concurrent.AsyncFunction;
76 import com.google.common.util.concurrent.CheckedFuture;
77 import com.google.common.util.concurrent.FutureCallback;
78 import com.google.common.util.concurrent.Futures;
79 import com.google.common.util.concurrent.ListenableFuture;
80 import com.google.common.util.concurrent.SettableFuture;
82 public class BridgeDomainManagerImpl implements BridgeDomainManager {
84 private static final Logger LOG = LoggerFactory.getLogger(BridgeDomainManagerImpl.class);
85 private static final TopologyId SUPPORTING_TOPOLOGY_NETCONF = new TopologyId("topology-netconf");
86 private static final TopologyTypes VBRIDGE_TOPOLOGY_TYPE = new TopologyTypesBuilder().addAugmentation(
87 TopologyTypesVbridgeAugment.class,
88 new TopologyTypesVbridgeAugmentBuilder().setVbridgeTopology(new VbridgeTopologyBuilder().build()).build())
90 private final DataBroker dataProvider;
92 private static final class ListenableFutureSetter<T extends DataObject>
93 implements ClusteredDataTreeChangeListener<T> {
95 private static final Logger LOG = LoggerFactory.getLogger(ListenableFutureSetter.class);
96 private final SettableFuture<Void> future;
97 private final ModificationType modificationForFutureSet;
98 private final DataTreeIdentifier<T> iid;
99 private final ListenerRegistration<ListenableFutureSetter<T>> registeredListener;
101 private ListenableFutureSetter(DataBroker dataProvider, SettableFuture<Void> future,
102 DataTreeIdentifier<T> iid, ModificationType modificationForFutureSet) {
103 this.future = Preconditions.checkNotNull(future);
104 Preconditions.checkArgument(!future.isDone());
105 this.modificationForFutureSet = Preconditions.checkNotNull(modificationForFutureSet);
106 this.iid = Preconditions.checkNotNull(iid);
107 registeredListener = dataProvider.registerDataTreeChangeListener(iid, this);
108 LOG.debug("Registered listener for path {}", iid.getRootIdentifier());
112 public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<T>> changes) {
113 changes.forEach(modification -> {
114 final DataObjectModification<T> rootNode = modification.getRootNode();
115 final ModificationType modificationType = rootNode.getModificationType();
116 if (modificationType == modificationForFutureSet) {
117 LOG.debug("{} in OPER DS: {}", modificationType.name(), iid.getRootIdentifier());
118 final T data = rootNode.getDataAfter();
119 // If waiting for bridge domain creation, do more specific check about BD status
120 if (data != null && data instanceof BridgeDomain) {
121 final BridgeDomain domain = (BridgeDomain) data;
122 final BridgeDomainStatusAugmentation statusAugment =
123 domain.getAugmentation(BridgeDomainStatusAugmentation.class);
124 final BridgeDomainStatus status = statusAugment.getBridgeDomainStatus();
127 LOG.debug("Bridge domain {} started", domain.getName());
128 unregister(future.set(null));
132 LOG.warn("Bridge domain {} failed to start", domain.getName());
133 unregister(future.set(null));
138 LOG.debug("Bridge domain {} status changed to {}", domain.getName(), status.getName());
143 unregister(future.set(null));
149 private void unregister(boolean _true) {
151 LOG.debug("Unregistering listener for path {}", iid.getRootIdentifier());
152 if (registeredListener != null) {
153 registeredListener.close();
159 public BridgeDomainManagerImpl(DataBroker dataProvider) {
160 this.dataProvider = Preconditions.checkNotNull(dataProvider);
163 private static NodeBuilder createBasicVppNodeBuilder(NodeId nodeId) {
164 return new NodeBuilder().setNodeId(nodeId).setSupportingNode(Collections.singletonList(
165 new SupportingNodeBuilder().setTopologyRef(SUPPORTING_TOPOLOGY_NETCONF).setNodeRef(nodeId).build()));
169 public ListenableFuture<Void> createVxlanBridgeDomainOnVppNode(@Nonnull final String bridgeDomainName,
170 @Nonnull final VxlanVni vni,
171 @Nonnull final NodeId vppNodeId) {
172 TopologyVbridgeAugment topologyAug = new TopologyVbridgeAugmentBuilder().setTunnelType(TunnelTypeVxlan.class)
173 .setArpTermination(false)
177 .setUnknownUnicastFlood(true)
178 .setTunnelParameters(new VxlanTunnelParametersBuilder().setVni(vni).build())
180 return createBridgeDomainOnVppNode(bridgeDomainName, topologyAug,
181 createBasicVppNodeBuilder(vppNodeId).build());
185 public ListenableFuture<Void> createVlanBridgeDomainOnVppNode(@Nonnull final String bridgeDomainName,
186 @Nonnull final VlanId vlanId,
187 @Nonnull final NodeId vppNodeId) {
188 TopologyVbridgeAugment topologyAug = new TopologyVbridgeAugmentBuilder().setTunnelType(TunnelTypeVlan.class)
189 .setArpTermination(false)
193 .setUnknownUnicastFlood(true)
194 .setTunnelParameters(
195 new VlanNetworkParametersBuilder().setVlanId(vlanId).setVlanType(_802dot1q.class).build())
197 InstanceIdentifier<GbpBridgeDomain> bridgeDomainConfigIid = InstanceIdentifier.builder(Config.class)
198 .child(GbpBridgeDomain.class, new GbpBridgeDomainKey(bridgeDomainName))
200 ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction();
201 CheckedFuture<Optional<GbpBridgeDomain>, ReadFailedException> futureTopology =
202 rTx.read(LogicalDatastoreType.CONFIGURATION, bridgeDomainConfigIid);
204 return Futures.transform(futureTopology, new AsyncFunction<Optional<GbpBridgeDomain>, Void>() {
207 public ListenableFuture<Void> apply(@Nonnull Optional<GbpBridgeDomain> optBridgeDomainConf) throws Exception {
208 if (optBridgeDomainConf.isPresent() && optBridgeDomainConf.get().getPhysicalLocationRef() != null) {
209 for (PhysicalLocationRef ref : optBridgeDomainConf.get().getPhysicalLocationRef()) {
210 if (!ref.getNodeId().equals(vppNodeId)) {
211 LOG.debug("Node {} is not referenced node, skipping", ref.getNodeId());
214 if (ref.getInterface() != null && ref.getInterface().size() > 0) {
215 NodeVbridgeVlanAugment vppNodeVlanAug = new NodeVbridgeVlanAugmentBuilder()
216 .setSuperInterface(ref.getInterface().get(0)).build();
217 Node vppNode = createBasicVppNodeBuilder(vppNodeId)
218 .addAugmentation(NodeVbridgeVlanAugment.class, vppNodeVlanAug).build();
219 return createBridgeDomainOnVppNode(bridgeDomainName, topologyAug, vppNode);
223 return Futures.immediateFailedFuture(
224 new Throwable("Failed to apply config for VLAN bridge domain " + bridgeDomainName));
230 * Method checks whether bridge domain already exists in topology under its {@link TopologyId}. If not, BD is
231 * written into CONF DS (request for VBD) and registers listener which awaits result from VBD. Result can be
232 * checked in OPER DS as a {@link BridgeDomainStatus}. If status is {@link BridgeDomainStatus#Started}, listener
233 * unregisters itself and bridge domain creation in VBD is considered successful.
235 * Next part creates request for {@link BridgeMember} in topology CONF DS and registers listener listening on
236 * topology OPER DS. If bridge member is created in VBD, listener is closed.
238 * This process has limited time, limit is defined in {@link ForwardingManager#WAIT_FOR_BD_PROCESSING} to prevent
239 * stuck if VBD processing fails in some point.
241 * @param bridgeDomainName serving as a topology-id
242 * @param vBridgeAug augmented data in BD
243 * @param vppNode transformed into bridge member
244 * @return composed future which serves as a marker for caller method that the computation is done. If future is
245 * not returned in time, {@link TimeoutException} will be thrown there.
247 private ListenableFuture<Void> createBridgeDomainOnVppNode(@Nonnull final String bridgeDomainName,
248 @Nonnull final TopologyVbridgeAugment vBridgeAug,
249 @Nonnull final Node vppNode) {
250 LOG.info("Creating bridge domain {} on VPP node {}", bridgeDomainName, vppNode);
251 final TopologyKey topologyKey = new TopologyKey(new TopologyId(bridgeDomainName));
252 final ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction();
253 final InstanceIdentifier<Topology> topologyIid = VppIidFactory.getTopologyIid(topologyKey);
254 final CheckedFuture<Optional<Topology>, ReadFailedException> optTopology =
255 rTx.read(LogicalDatastoreType.CONFIGURATION, topologyIid);
257 return Futures.transform(optTopology, new AsyncFunction<Optional<Topology>, Void>() {
259 public ListenableFuture<Void> apply(@Nonnull final Optional<Topology> optTopology)
260 throws InterruptedException, ExecutionException {
262 final SettableFuture<Void> topologyFuture = SettableFuture.create();
263 if (!optTopology.isPresent()) {
264 final WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
265 final Topology topology = new TopologyBuilder().setKey(topologyKey)
266 .setTopologyTypes(VBRIDGE_TOPOLOGY_TYPE)
267 .addAugmentation(TopologyVbridgeAugment.class, vBridgeAug)
269 wTx.put(LogicalDatastoreType.CONFIGURATION, topologyIid, topology, true);
270 Futures.addCallback(wTx.submit(), new FutureCallback<Void>() {
273 public void onSuccess(@Nullable final Void result) {
274 final InstanceIdentifier<BridgeDomain> bridgeDomainStateIid =
275 VppIidFactory.getBridgeDomainStateIid(new BridgeDomainKey(bridgeDomainName));
276 LOG.debug("Adding a listener on bridge domain state", bridgeDomainName);
277 final DataTreeIdentifier<BridgeDomain> bridgeDomainStateIidDTI = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
278 bridgeDomainStateIid);
279 new ListenableFutureSetter<>(dataProvider, topologyFuture, bridgeDomainStateIidDTI, ModificationType.WRITE);
283 public void onFailure(@Nonnull Throwable t) {
284 LOG.warn("Request create topology for VBD was not stored to CONF DS. {}", topologyIid, t);
285 topologyFuture.setException(new Exception("Cannot send request to VBD."));
289 topologyFuture.set(null);
290 LOG.info("Bridge domain {} already exists", optTopology.get().getTopologyId());
292 return Futures.transform(topologyFuture, new AsyncFunction<Void, Void>() {
294 public ListenableFuture<Void> apply(@Nonnull Void topologyInput) throws Exception {
296 final SettableFuture<Void> futureBridgeMember = SettableFuture.create();
297 final InstanceIdentifier<Node> nodeIid = VppIidFactory.getNodeIid(topologyKey, vppNode.getKey());
298 LOG.debug("Adding node {} to bridge domain {}", vppNode.getKey(), topologyKey.getTopologyId());
299 final WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
300 wTx.put(LogicalDatastoreType.CONFIGURATION, nodeIid, vppNode);
301 Futures.addCallback(wTx.submit(), new FutureCallback<Void>() {
304 public void onSuccess(@Nullable final Void _void) {
305 final DataTreeIdentifier<BridgeMember> bridgeMemberIid =
306 new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
307 nodeIid.augmentation(NodeVbridgeAugment.class).child(BridgeMember.class));
308 LOG.debug("Request create node in topology for VBD was stored to CONF DS. {}", nodeIid);
309 new ListenableFutureSetter<>(dataProvider, futureBridgeMember, bridgeMemberIid,
310 ModificationType.WRITE);
314 public void onFailure(@Nonnull final Throwable t) {
315 LOG.warn("Request create node in topology for VBD was not stored to CONF DS. {}", nodeIid, t);
316 futureBridgeMember.setException(new Exception("Cannot send request to VBD."));
319 return futureBridgeMember;
327 public ListenableFuture<Void> removeBridgeDomainFromVppNode(@Nonnull final String bridgeDomainName,
328 @Nonnull final NodeId vppNode) {
329 LOG.info("Removing bridge domain {} from VPP node {}", bridgeDomainName, vppNode);
330 InstanceIdentifier<Topology> topologyIid =
331 VppIidFactory.getTopologyIid(new TopologyKey(new TopologyId(bridgeDomainName)));
332 ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction();
333 Optional<Topology> topologyOpt =
334 DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, topologyIid, rTx);
335 WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
336 InstanceIdentifier<Node> nodeIid =
337 VppIidFactory.getNodeIid(new TopologyKey(new TopologyId(bridgeDomainName)), new NodeKey(vppNode));
338 wTx.delete(LogicalDatastoreType.CONFIGURATION, nodeIid);
339 if (topologyOpt.isPresent()) {
340 Topology topology = topologyOpt.get();
341 if(topology.getNode() == null || topology.getNode().size() == 1) {
342 wTx.delete(LogicalDatastoreType.CONFIGURATION, topologyIid);
345 SettableFuture<Void> future = SettableFuture.create();
346 Futures.addCallback(wTx.submit(), new FutureCallback<Void>() {
349 public void onSuccess(Void result) {
350 DataTreeIdentifier<BridgeMember> bridgeMemberIid =
351 new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
352 nodeIid.augmentation(NodeVbridgeAugment.class).child(BridgeMember.class));
353 LOG.debug("Request delete node in topology for VBD was stored to CONF DS. {}", nodeIid);
354 new ListenableFutureSetter<>(dataProvider, future, bridgeMemberIid, ModificationType.DELETE);
358 public void onFailure(@Nonnull Throwable t) {
359 LOG.warn("Request delete node in topology for VBD was not stored to CONF DS. {}", nodeIid, t);
360 future.setException(new Exception("Cannot send request to VBD."));