f7c587b5c6889c8fce96e8fbef850db95f81ce87
[groupbasedpolicy.git] / renderers / vpp / src / main / java / org / opendaylight / groupbasedpolicy / renderer / vpp / policy / BridgeDomainManagerImpl.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.vpp.policy;
10
11 import java.util.Collection;
12 import java.util.Collections;
13 import java.util.concurrent.ExecutionException;
14 import java.util.concurrent.TimeoutException;
15
16 import javax.annotation.Nonnull;
17 import javax.annotation.Nullable;
18
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.config.ConfigUtil;
31 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory;
32 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.Config;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.bridge.domain.base.attributes.PhysicalLocationRef;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.GbpBridgeDomain;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.GbpBridgeDomainKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev170315.VxlanVni;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev170315.bridge.domains.state.BridgeDomain;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev170315.bridge.domains.state.BridgeDomainKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.status.rev170327.BridgeDomainStatusAugmentation;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.status.rev170327.BridgeDomainStatusFields.BridgeDomainStatus;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.NodeVbridgeAugment;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TopologyTypesVbridgeAugment;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TopologyTypesVbridgeAugmentBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TopologyVbridgeAugment;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.TopologyVbridgeAugmentBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.node.BridgeMember;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.topology.rev160129.network.topology.topology.topology.types.VbridgeTopologyBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vlan.rev170327.NodeVbridgeVlanAugment;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vlan.rev170327.NodeVbridgeVlanAugmentBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vlan.rev170327.TunnelTypeVlan;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vlan.rev170327.network.topology.topology.tunnel.parameters.VlanNetworkParametersBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vxlan.rev170327.TunnelTypeVxlan;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vbridge.tunnel.vxlan.rev170327.network.topology.topology.tunnel.parameters.VxlanTunnelParametersBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev170315._802dot1q;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
61 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
62 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
63 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
64 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
65 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.TopologyTypes;
66 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.TopologyTypesBuilder;
67 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNodeBuilder;
68 import org.opendaylight.yangtools.concepts.ListenerRegistration;
69 import org.opendaylight.yangtools.yang.binding.DataObject;
70 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
73
74 import com.google.common.base.Optional;
75 import com.google.common.base.Preconditions;
76 import com.google.common.util.concurrent.AsyncFunction;
77 import com.google.common.util.concurrent.CheckedFuture;
78 import com.google.common.util.concurrent.FutureCallback;
79 import com.google.common.util.concurrent.Futures;
80 import com.google.common.util.concurrent.ListenableFuture;
81 import com.google.common.util.concurrent.SettableFuture;
82
83 public class BridgeDomainManagerImpl implements BridgeDomainManager {
84
85     private static final Logger LOG = LoggerFactory.getLogger(BridgeDomainManagerImpl.class);
86     private static final TopologyId SUPPORTING_TOPOLOGY_NETCONF = new TopologyId("topology-netconf");
87     private static final TopologyTypes VBRIDGE_TOPOLOGY_TYPE = new TopologyTypesBuilder().addAugmentation(
88             TopologyTypesVbridgeAugment.class,
89             new TopologyTypesVbridgeAugmentBuilder().setVbridgeTopology(new VbridgeTopologyBuilder().build()).build())
90             .build();
91     private final DataBroker dataProvider;
92
93     private static final class ListenableFutureSetter<T extends DataObject>
94             implements ClusteredDataTreeChangeListener<T> {
95
96         private static final Logger LOG = LoggerFactory.getLogger(ListenableFutureSetter.class);
97         private final SettableFuture<Void> future;
98         private final ModificationType modificationForFutureSet;
99         private final DataTreeIdentifier<T> iid;
100         private final ListenerRegistration<ListenableFutureSetter<T>> registeredListener;
101
102         private ListenableFutureSetter(DataBroker dataProvider, SettableFuture<Void> future,
103                                        DataTreeIdentifier<T> iid, ModificationType modificationForFutureSet) {
104             this.future = Preconditions.checkNotNull(future);
105             Preconditions.checkArgument(!future.isDone());
106             this.modificationForFutureSet = Preconditions.checkNotNull(modificationForFutureSet);
107             this.iid = Preconditions.checkNotNull(iid);
108             if(!ConfigUtil.getInstance().isL3FlatEnabled()) {
109                 registeredListener = dataProvider.registerDataTreeChangeListener(iid, this);
110                 LOG.debug("Registered listener for path {}", iid.getRootIdentifier());
111             } else {
112                 throw new IllegalStateException("L3 flat is enabled, BD manager should not even be registering now!");
113             }
114         }
115
116         @Override
117         public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<T>> changes) {
118             changes.forEach(modification -> {
119                 final DataObjectModification<T> rootNode = modification.getRootNode();
120                 final ModificationType modificationType = rootNode.getModificationType();
121                 if (modificationType == modificationForFutureSet) {
122                     LOG.debug("{} in OPER DS: {}", modificationType.name(), iid.getRootIdentifier());
123                     final T data = rootNode.getDataAfter();
124                     // If waiting for bridge domain creation, do more specific check about BD status
125                     if (data != null && data instanceof BridgeDomain) {
126                         final BridgeDomain domain = (BridgeDomain) data;
127                         final BridgeDomainStatusAugmentation statusAugment =
128                                 domain.getAugmentation(BridgeDomainStatusAugmentation.class);
129                         final BridgeDomainStatus status = statusAugment.getBridgeDomainStatus();
130                         switch (status) {
131                             case Started: {
132                                 LOG.debug("Bridge domain {} started", domain.getName());
133                                 unregister(future.set(null));
134                                 break;
135                             }
136                             case Failed: {
137                                 LOG.warn("Bridge domain {} failed to start", domain.getName());
138                                 unregister(future.set(null));
139                                 break;
140                             }
141                             case Starting:
142                             case Stopped: {
143                                 LOG.debug("Bridge domain {} status changed to {}", domain.getName(), status.getName());
144                                 break;
145                             }
146                         }
147                     } else {
148                         unregister(future.set(null));
149                     }
150                 }
151             });
152         }
153
154         private void unregister(boolean _true) {
155             if (_true) {
156                 LOG.debug("Unregistering listener for path {}", iid.getRootIdentifier());
157                 if (registeredListener != null) {
158                     registeredListener.close();
159                 }
160             }
161         }
162     }
163
164     public BridgeDomainManagerImpl(DataBroker dataProvider) {
165         this.dataProvider = Preconditions.checkNotNull(dataProvider);
166     }
167
168     private static NodeBuilder createBasicVppNodeBuilder(NodeId nodeId) {
169         return new NodeBuilder().setNodeId(nodeId).setSupportingNode(Collections.singletonList(
170                 new SupportingNodeBuilder().setTopologyRef(SUPPORTING_TOPOLOGY_NETCONF).setNodeRef(nodeId).build()));
171     }
172
173     @Override
174     public ListenableFuture<Void> createVxlanBridgeDomainOnVppNode(@Nonnull final String bridgeDomainName,
175                                                                    @Nonnull final VxlanVni vni,
176                                                                    @Nonnull final NodeId vppNodeId) {
177         TopologyVbridgeAugment topologyAug = new TopologyVbridgeAugmentBuilder().setTunnelType(TunnelTypeVxlan.class)
178                 .setArpTermination(false)
179                 .setFlood(true)
180                 .setForward(true)
181                 .setLearn(true)
182                 .setUnknownUnicastFlood(true)
183                 .setTunnelParameters(new VxlanTunnelParametersBuilder().setVni(vni).build())
184                 .build();
185         return createBridgeDomainOnVppNode(bridgeDomainName, topologyAug,
186                 createBasicVppNodeBuilder(vppNodeId).build());
187     }
188
189     @Override
190     public ListenableFuture<Void> createVlanBridgeDomainOnVppNode(@Nonnull final String bridgeDomainName,
191                                                                   @Nonnull final VlanId vlanId,
192                                                                   @Nonnull final NodeId vppNodeId) {
193         TopologyVbridgeAugment topologyAug = new TopologyVbridgeAugmentBuilder().setTunnelType(TunnelTypeVlan.class)
194                 .setArpTermination(false)
195                 .setFlood(true)
196                 .setForward(true)
197                 .setLearn(true)
198                 .setUnknownUnicastFlood(true)
199                 .setTunnelParameters(
200                         new VlanNetworkParametersBuilder().setVlanId(vlanId).setVlanType(_802dot1q.class).build())
201                 .build();
202         InstanceIdentifier<GbpBridgeDomain> bridgeDomainConfigIid = InstanceIdentifier.builder(Config.class)
203                 .child(GbpBridgeDomain.class, new GbpBridgeDomainKey(bridgeDomainName))
204                 .build();
205         ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction();
206         CheckedFuture<Optional<GbpBridgeDomain>, ReadFailedException> futureTopology =
207                 rTx.read(LogicalDatastoreType.CONFIGURATION, bridgeDomainConfigIid);
208         rTx.close();
209         return Futures.transform(futureTopology, new AsyncFunction<Optional<GbpBridgeDomain>, Void>() {
210
211             @Override
212             public ListenableFuture<Void> apply(@Nonnull Optional<GbpBridgeDomain> optBridgeDomainConf) throws Exception {
213                 if (optBridgeDomainConf.isPresent() && optBridgeDomainConf.get().getPhysicalLocationRef() != null) {
214                     for (PhysicalLocationRef ref : optBridgeDomainConf.get().getPhysicalLocationRef()) {
215                         if (!ref.getNodeId().equals(vppNodeId)) {
216                             LOG.debug("Node {} is not referenced node, skipping", ref.getNodeId());
217                             continue;
218                         }
219                         if (ref.getInterface() != null && ref.getInterface().size() > 0) {
220                             NodeVbridgeVlanAugment vppNodeVlanAug = new NodeVbridgeVlanAugmentBuilder()
221                                     .setSuperInterface(ref.getInterface().get(0)).build();
222                             Node vppNode = createBasicVppNodeBuilder(vppNodeId)
223                                     .addAugmentation(NodeVbridgeVlanAugment.class, vppNodeVlanAug).build();
224                             return createBridgeDomainOnVppNode(bridgeDomainName, topologyAug, vppNode);
225                         }
226                     }
227                 }
228                 return Futures.immediateFailedFuture(
229                         new Throwable("Failed to apply config for VLAN bridge domain " + bridgeDomainName));
230             }
231         });
232     }
233
234     /**
235      * Method checks whether bridge domain already exists in topology under its {@link TopologyId}. If not, BD is
236      * written into CONF DS (request for VBD) and registers listener which awaits result from VBD. Result can be
237      * checked in OPER DS as a {@link BridgeDomainStatus}. If status is {@link BridgeDomainStatus#Started}, listener
238      * unregisters itself and bridge domain creation in VBD is considered successful.
239      * <p>
240      * Next part creates request for {@link BridgeMember} in topology CONF DS and registers listener listening on
241      * topology OPER DS. If bridge member is created in VBD, listener is closed.
242      * <p>
243      * This process has limited time, limit is defined in {@link ForwardingManager#WAIT_FOR_BD_PROCESSING} to prevent
244      * stuck if VBD processing fails in some point.
245      *
246      * @param bridgeDomainName serving as a topology-id
247      * @param vBridgeAug       augmented data in BD
248      * @param vppNode          transformed into bridge member
249      * @return composed future which serves as a marker for caller method that the computation is done. If future is
250      * not returned in time, {@link TimeoutException} will be thrown there.
251      */
252     private ListenableFuture<Void> createBridgeDomainOnVppNode(@Nonnull final String bridgeDomainName,
253                                                                @Nonnull final TopologyVbridgeAugment vBridgeAug,
254                                                                @Nonnull final Node vppNode) {
255         LOG.info("Creating bridge domain {} on VPP node {}", bridgeDomainName, vppNode);
256         final TopologyKey topologyKey = new TopologyKey(new TopologyId(bridgeDomainName));
257         final ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction();
258         final InstanceIdentifier<Topology> topologyIid = VppIidFactory.getTopologyIid(topologyKey);
259         final CheckedFuture<Optional<Topology>, ReadFailedException> optTopology =
260                 rTx.read(LogicalDatastoreType.CONFIGURATION, topologyIid);
261         rTx.close();
262         return Futures.transform(optTopology, new AsyncFunction<Optional<Topology>, Void>() {
263             @Override
264             public ListenableFuture<Void> apply(@Nonnull final Optional<Topology> optTopology)
265                     throws InterruptedException, ExecutionException {
266                 // Topology
267                 final SettableFuture<Void> topologyFuture = SettableFuture.create();
268                 if (!optTopology.isPresent()) {
269                     final WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
270                     final Topology topology = new TopologyBuilder().setKey(topologyKey)
271                             .setTopologyTypes(VBRIDGE_TOPOLOGY_TYPE)
272                             .addAugmentation(TopologyVbridgeAugment.class, vBridgeAug)
273                             .build();
274                     wTx.put(LogicalDatastoreType.CONFIGURATION, topologyIid, topology, true);
275                     Futures.addCallback(wTx.submit(), new FutureCallback<Void>() {
276
277                         @Override
278                         public void onSuccess(@Nullable final Void result) {
279                             final InstanceIdentifier<BridgeDomain> bridgeDomainStateIid =
280                                     VppIidFactory.getBridgeDomainStateIid(new BridgeDomainKey(bridgeDomainName));
281                             LOG.debug("Adding a listener on bridge domain state", bridgeDomainName);
282                             final DataTreeIdentifier<BridgeDomain> bridgeDomainStateIidDTI = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
283                                     bridgeDomainStateIid);
284                             new ListenableFutureSetter<>(dataProvider, topologyFuture, bridgeDomainStateIidDTI, ModificationType.WRITE);
285                         }
286
287                         @Override
288                         public void onFailure(@Nonnull Throwable t) {
289                             LOG.warn("Request create topology for VBD was not stored to CONF DS. {}", topologyIid, t);
290                             topologyFuture.setException(new Exception("Cannot send request to VBD."));
291                         }
292                     });
293                 } else {
294                     topologyFuture.set(null);
295                     LOG.info("Bridge domain {} already exists", optTopology.get().getTopologyId());
296                 }
297                 return Futures.transform(topologyFuture, new AsyncFunction<Void, Void>() {
298                     @Override
299                     public ListenableFuture<Void> apply(@Nonnull Void topologyInput) throws Exception {
300                         // Bridge member
301                         final SettableFuture<Void> futureBridgeMember = SettableFuture.create();
302                         final InstanceIdentifier<Node> nodeIid = VppIidFactory.getNodeIid(topologyKey, vppNode.getKey());
303                         LOG.debug("Adding node {} to bridge domain {}", vppNode.getKey(), topologyKey.getTopologyId());
304                         final WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
305                         wTx.put(LogicalDatastoreType.CONFIGURATION, nodeIid, vppNode);
306                         Futures.addCallback(wTx.submit(), new FutureCallback<Void>() {
307
308                             @Override
309                             public void onSuccess(@Nullable final Void _void) {
310                                 final DataTreeIdentifier<BridgeMember> bridgeMemberIid =
311                                         new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
312                                                 nodeIid.augmentation(NodeVbridgeAugment.class).child(BridgeMember.class));
313                                 LOG.debug("Request create node in topology for VBD was stored to CONF DS. {}", nodeIid);
314                                 new ListenableFutureSetter<>(dataProvider, futureBridgeMember, bridgeMemberIid,
315                                         ModificationType.WRITE);
316                             }
317
318                             @Override
319                             public void onFailure(@Nonnull final Throwable t) {
320                                 LOG.warn("Request create node in topology for VBD was not stored to CONF DS. {}", nodeIid, t);
321                                 futureBridgeMember.setException(new Exception("Cannot send request to VBD."));
322                             }
323                         });
324                         return futureBridgeMember;
325                     }
326                 });
327             }
328         });
329     }
330
331     @Override
332     public ListenableFuture<Void> removeBridgeDomainFromVppNode(@Nonnull final String bridgeDomainName,
333                                                                 @Nonnull final NodeId vppNode) {
334         LOG.info("Removing bridge domain {} from VPP node {}", bridgeDomainName, vppNode);
335         InstanceIdentifier<Topology> topologyIid =
336                 VppIidFactory.getTopologyIid(new TopologyKey(new TopologyId(bridgeDomainName)));
337         ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction();
338         Optional<Topology> topologyOpt =
339                 DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, topologyIid, rTx);
340         WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
341         InstanceIdentifier<Node> nodeIid =
342                 VppIidFactory.getNodeIid(new TopologyKey(new TopologyId(bridgeDomainName)), new NodeKey(vppNode));
343         wTx.delete(LogicalDatastoreType.CONFIGURATION, nodeIid);
344         if (topologyOpt.isPresent()) {
345             Topology topology = topologyOpt.get();
346             if(topology.getNode() == null || topology.getNode().size() == 1) {
347                 wTx.delete(LogicalDatastoreType.CONFIGURATION, topologyIid);
348             }
349         }
350         SettableFuture<Void> future = SettableFuture.create();
351         Futures.addCallback(wTx.submit(), new FutureCallback<Void>() {
352
353             @Override
354             public void onSuccess(Void result) {
355                 DataTreeIdentifier<BridgeMember> bridgeMemberIid =
356                         new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
357                                 nodeIid.augmentation(NodeVbridgeAugment.class).child(BridgeMember.class));
358                 LOG.debug("Request delete node in topology for VBD was stored to CONF DS. {}", nodeIid);
359                 new ListenableFutureSetter<>(dataProvider, future, bridgeMemberIid, ModificationType.DELETE);
360             }
361
362             @Override
363             public void onFailure(@Nonnull Throwable t) {
364                 LOG.warn("Request delete node in topology for VBD was not stored to CONF DS. {}", nodeIid, t);
365                 future.setException(new Exception("Cannot send request to VBD."));
366             }
367         });
368         return future;
369     }
370 }