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