Support for ITM direct tunnels in ITM workers
[genius.git] / itm / itm-impl / src / main / java / org / opendaylight / genius / itm / itmdirecttunnels / listeners / TunnelTopologyStateListener.java
1 /*
2  * Copyright (c) 2018 Ericsson India Global Services Pvt Ltd. 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 package org.opendaylight.genius.itm.itmdirecttunnels.listeners;
9
10 import com.google.common.base.Optional;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.math.BigInteger;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.concurrent.Callable;
16 import javax.annotation.Nonnull;
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
19 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
20 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
21 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
22 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
23 import org.opendaylight.genius.itm.cache.DPNTEPsInfoCache;
24 import org.opendaylight.genius.itm.cache.DpnTepStateCache;
25 import org.opendaylight.genius.itm.cache.OvsBridgeEntryCache;
26 import org.opendaylight.genius.itm.cache.UnprocessedNodeConnectorCache;
27 import org.opendaylight.genius.itm.globals.ITMConstants;
28 import org.opendaylight.genius.itm.itmdirecttunnels.renderer.ovs.utilities.DirectTunnelUtils;
29 import org.opendaylight.genius.utils.clustering.EntityOwnershipUtils;
30 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfTunnel;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.bridge.tunnel.info.OvsBridgeEntry;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.bridge.tunnel.info.OvsBridgeEntryBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.bridge.tunnel.info.OvsBridgeEntryKey;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.bridge.tunnel.info.ovs.bridge.entry.OvsBridgeTunnelEntry;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.ovs.bridge.ref.info.OvsBridgeRefEntry;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.ovs.bridge.ref.info.OvsBridgeRefEntryBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.ovs.bridge.ref.info.OvsBridgeRefEntryKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathId;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeRef;
42 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
45 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 public class TunnelTopologyStateListener extends AbstractTunnelListenerBase<OvsdbBridgeAugmentation> {
50
51     private static final Logger LOG = LoggerFactory.getLogger(TunnelTopologyStateListener.class);
52
53     private final JobCoordinator coordinator;
54     private final ManagedNewTransactionRunner txRunner;
55     private final DirectTunnelUtils directTunnelUtils;
56     private final OvsBridgeEntryCache ovsBridgeEntryCache;
57
58     public TunnelTopologyStateListener(final DataBroker dataBroker,
59                                        final JobCoordinator coordinator,
60                                        final EntityOwnershipUtils entityOwnershipUtils,
61                                        final DirectTunnelUtils directTunnelUtils,
62                                        final DpnTepStateCache dpnTepStateCache,
63                                        final DPNTEPsInfoCache dpntePsInfoCache,
64                                        final OvsBridgeEntryCache ovsBridgeEntryCache,
65                                        final UnprocessedNodeConnectorCache unprocessedNodeConnectorCache)  {
66         super(dataBroker, LogicalDatastoreType.OPERATIONAL,
67                 InstanceIdentifier.create(NetworkTopology.class).child(Topology.class).child(Node.class)
68                         .augmentation(OvsdbBridgeAugmentation.class), dpnTepStateCache, dpntePsInfoCache,
69                 unprocessedNodeConnectorCache, entityOwnershipUtils, directTunnelUtils);
70         this.coordinator = coordinator;
71         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
72         this.directTunnelUtils = directTunnelUtils;
73         this.ovsBridgeEntryCache = ovsBridgeEntryCache;
74         super.register();
75     }
76
77     @Override
78     public void remove(@Nonnull InstanceIdentifier<OvsdbBridgeAugmentation> identifier,
79                        @Nonnull OvsdbBridgeAugmentation bridgeOld) {
80         if (entityOwner()) {
81             LOG.debug("Received Remove DataChange Notification for identifier: {}, ovsdbBridgeAugmentation: {}",
82                     identifier, bridgeOld);
83             RendererStateRemoveWorker rendererStateRemoveWorker = new RendererStateRemoveWorker(identifier, bridgeOld);
84             coordinator.enqueueJob(bridgeOld.getBridgeName().getValue(), rendererStateRemoveWorker,
85                     ITMConstants.JOB_MAX_RETRIES);
86         }
87     }
88
89     @Override
90     public void update(@Nonnull InstanceIdentifier<OvsdbBridgeAugmentation> identifier,
91                        @Nonnull OvsdbBridgeAugmentation bridgeOld, @Nonnull OvsdbBridgeAugmentation bridgeNew) {
92
93         if (!entityOwner()) {
94             return;
95         }
96         LOG.debug("Received Update DataChange Notification for identifier: {}, + ovsdbBridgeAugmentation old: {},"
97                 + " new: {}.", identifier, bridgeOld, bridgeNew);
98
99         DatapathId oldDpid = bridgeOld.getDatapathId();
100         DatapathId newDpid = bridgeNew.getDatapathId();
101         if (oldDpid == null && newDpid != null) {
102             RendererStateAddWorker rendererStateAddWorker = new RendererStateAddWorker(identifier, bridgeNew);
103             coordinator.enqueueJob(bridgeNew.getBridgeName().getValue(), rendererStateAddWorker,
104                     ITMConstants.JOB_MAX_RETRIES);
105         } else if (oldDpid != null && !oldDpid.equals(newDpid)) {
106             RendererStateUpdateWorker rendererStateAddWorker =
107                     new RendererStateUpdateWorker(identifier, bridgeNew, bridgeOld);
108             coordinator.enqueueJob(bridgeNew.getBridgeName().getValue(), rendererStateAddWorker,
109                     ITMConstants.JOB_MAX_RETRIES);
110         }
111     }
112
113     @Override
114     public void add(@Nonnull InstanceIdentifier<OvsdbBridgeAugmentation> identifier,
115                     @Nonnull OvsdbBridgeAugmentation bridgeNew) {
116         if (entityOwner()) {
117             LOG.debug("Received Add DataChange Notification for identifier: {}, ovsdbBridgeAugmentation: {}",
118                     identifier, bridgeNew);
119             RendererStateAddWorker rendererStateAddWorker = new RendererStateAddWorker(identifier, bridgeNew);
120             coordinator.enqueueJob(bridgeNew.getBridgeName().getValue(), rendererStateAddWorker,
121                     ITMConstants.JOB_MAX_RETRIES);
122         }
123     }
124
125     /*
126      *  This code is used to handle only a dpnId change scenario for a particular change,
127      * which is not expected to happen in usual cases.
128      */
129     private List<ListenableFuture<Void>> updateOvsBridgeRefEntry(InstanceIdentifier<OvsdbBridgeAugmentation> bridgeIid,
130                                                                 OvsdbBridgeAugmentation bridgeNew,
131                                                                 OvsdbBridgeAugmentation bridgeOld) {
132
133         return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
134             BigInteger dpnIdNew = directTunnelUtils.getDpnId(bridgeNew.getDatapathId());
135             BigInteger dpnIdOld = directTunnelUtils.getDpnId(bridgeOld.getDatapathId());
136
137             LOG.debug("updating bridge references for bridge: {}, dpnNew: {}, dpnOld: {}", bridgeNew,
138                     dpnIdNew, dpnIdOld);
139             //delete bridge reference entry for the old dpn in interface meta operational DS
140             deleteOvsBridgeRefEntry(dpnIdOld, tx);
141
142             // create bridge reference entry in interface meta operational DS
143             createOvsBridgeRefEntry(dpnIdNew, bridgeIid, tx);
144
145             // handle pre-provisioning of tunnels for the newly connected dpn
146             Optional<OvsBridgeEntry> bridgeEntry = null;
147             try {
148                 bridgeEntry = ovsBridgeEntryCache.get(dpnIdNew);
149                 if (bridgeEntry.isPresent()) {
150                     addAllPortsToBridge(bridgeEntry.get(), bridgeIid, bridgeNew);
151                 }
152             } catch (ReadFailedException e) {
153                 LOG.debug("OVSDB Bridge is not present for DPN {}", dpnIdNew);
154             }
155         }));
156     }
157
158     public List<ListenableFuture<Void>> removePortFromBridge(InstanceIdentifier<OvsdbBridgeAugmentation>
159                                                                             bridgeIid,
160                                                                     OvsdbBridgeAugmentation bridgeOld) {
161         BigInteger dpnId = directTunnelUtils.getDpnId(bridgeOld.getDatapathId());
162         if (dpnId == null) {
163             LOG.warn("Got Null DPID for Bridge: {}", bridgeOld);
164             return Collections.emptyList();
165         }
166         return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
167             LOG.debug("removing bridge references for bridge: {}, dpn: {}", bridgeOld, dpnId);
168             //delete bridge reference entry in interface meta operational DS
169             deleteOvsBridgeRefEntry(dpnId, tx);
170
171             // the bridge reference is copied to dpn-tunnel interfaces map, so that whenever a northbound delete
172             // happens when bridge is not connected, we need the bridge reference to clean up the topology config DS
173             addBridgeRefToBridgeTunnelEntry(dpnId, new OvsdbBridgeRef(bridgeIid), tx);
174         }));
175     }
176
177     private void createOvsBridgeRefEntry(BigInteger dpnId, InstanceIdentifier<?> bridgeIid,
178                                                WriteTransaction tx) {
179         LOG.debug("Creating bridge ref entry for dpn: {} bridge: {}",
180                 dpnId, bridgeIid);
181         OvsBridgeRefEntryKey bridgeRefEntryKey = new OvsBridgeRefEntryKey(dpnId);
182         InstanceIdentifier<OvsBridgeRefEntry> bridgeEntryId =
183                 DirectTunnelUtils.getOvsBridgeRefEntryIdentifier(bridgeRefEntryKey);
184         OvsBridgeRefEntryBuilder tunnelDpnBridgeEntryBuilder =
185                 new OvsBridgeRefEntryBuilder().setKey(bridgeRefEntryKey).setDpid(dpnId)
186                         .setOvsBridgeReference(new OvsdbBridgeRef(bridgeIid));
187         tx.put(LogicalDatastoreType.OPERATIONAL, bridgeEntryId, tunnelDpnBridgeEntryBuilder.build(), true);
188     }
189
190     private void deleteOvsBridgeRefEntry(BigInteger dpnId,
191                                                WriteTransaction tx) {
192         LOG.debug("Deleting bridge ref entry for dpn: {}",
193                 dpnId);
194         OvsBridgeRefEntryKey bridgeRefEntryKey = new OvsBridgeRefEntryKey(dpnId);
195         InstanceIdentifier<OvsBridgeRefEntry> bridgeEntryId =
196                 DirectTunnelUtils.getOvsBridgeRefEntryIdentifier(bridgeRefEntryKey);
197         tx.delete(LogicalDatastoreType.OPERATIONAL, bridgeEntryId);
198     }
199
200     private void addBridgeRefToBridgeTunnelEntry(BigInteger dpId, OvsdbBridgeRef ovsdbBridgeRef, WriteTransaction tx) {
201         OvsBridgeEntryKey bridgeEntryKey = new OvsBridgeEntryKey(dpId);
202         InstanceIdentifier<OvsBridgeEntry> bridgeEntryInstanceIdentifier =
203                 DirectTunnelUtils.getOvsBridgeEntryIdentifier(bridgeEntryKey);
204
205         OvsBridgeEntryBuilder bridgeEntryBuilder = new OvsBridgeEntryBuilder().setKey(bridgeEntryKey)
206                 .setOvsBridgeReference(ovsdbBridgeRef);
207         tx.merge(LogicalDatastoreType.CONFIGURATION, bridgeEntryInstanceIdentifier, bridgeEntryBuilder.build(), true);
208     }
209
210     /*
211      * Add all tunnels ports corresponding to the bridge to the topology config
212      * DS
213      */
214     private void addAllPortsToBridge(OvsBridgeEntry bridgeEntry, InstanceIdentifier<OvsdbBridgeAugmentation> bridgeIid,
215                                      OvsdbBridgeAugmentation bridgeNew) {
216         String bridgeName = bridgeNew.getBridgeName().getValue();
217         LOG.debug("adding all ports to bridge: {}", bridgeName);
218         List<OvsBridgeTunnelEntry> bridgeInterfaceEntries = bridgeEntry.getOvsBridgeTunnelEntry();
219         if (bridgeInterfaceEntries != null) {
220             for (OvsBridgeTunnelEntry bridgeInterfaceEntry : bridgeInterfaceEntries) {
221                 String portName = bridgeInterfaceEntry.getTunnelName();
222                 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
223                         .ietf.interfaces.rev140508.interfaces.Interface iface =
224                         dpnTepStateCache.getInterfaceFromCache(portName);
225                 if (iface != null) {
226                     IfTunnel ifTunnel = iface.getAugmentation(IfTunnel.class);
227                     if (ifTunnel != null) {
228                         directTunnelUtils.addTunnelPortToBridge(ifTunnel, bridgeIid, iface, portName);
229                     }
230                 } else {
231                     LOG.debug("Interface {} not found in config DS", portName);
232                 }
233             }
234         }
235     }
236
237     private class RendererStateAddWorker implements Callable<List<ListenableFuture<Void>>> {
238         private final InstanceIdentifier<OvsdbBridgeAugmentation> bridgeIid;
239         private final OvsdbBridgeAugmentation bridgeNew;
240
241         RendererStateAddWorker(InstanceIdentifier<OvsdbBridgeAugmentation> bridgeIid,
242                                OvsdbBridgeAugmentation bridgeNew) {
243             this.bridgeIid = bridgeIid;
244             this.bridgeNew = bridgeNew;
245         }
246
247         @Override
248         public List<ListenableFuture<Void>> call() throws Exception {
249             // If another renderer(for eg : OVS) needs to be supported, check can be performed here
250             // to call the respective helpers.
251             if (bridgeNew.getDatapathId() == null) {
252                 LOG.info("DataPathId found as null for Bridge Augmentation: {}... returning...", bridgeNew);
253                 return Collections.emptyList();
254             }
255
256             BigInteger dpnId = directTunnelUtils.getDpnId(bridgeNew.getDatapathId());
257             LOG.debug("adding bridge references for bridge: {}, dpn: {}", bridgeNew, dpnId);
258
259             // create bridge reference entry in interface meta operational DS
260             return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
261                 createOvsBridgeRefEntry(dpnId, bridgeIid, tx);
262                 // handle pre-provisioning of tunnels for the newly connected dpn
263                 Optional<OvsBridgeEntry> bridgeEntry = ovsBridgeEntryCache.get(dpnId);
264                 if (!bridgeEntry.isPresent()) {
265                     LOG.debug("Bridge entry not found in config DS for dpn: {}", dpnId);
266                 } else {
267                     addAllPortsToBridge(bridgeEntry.get(), bridgeIid, bridgeNew);
268                 }
269             }));
270         }
271     }
272
273     private class RendererStateRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
274         private final InstanceIdentifier<OvsdbBridgeAugmentation> instanceIdentifier;
275         private final OvsdbBridgeAugmentation bridgeNew;
276
277         RendererStateRemoveWorker(InstanceIdentifier<OvsdbBridgeAugmentation> instanceIdentifier,
278                                   OvsdbBridgeAugmentation bridgeNew) {
279             this.instanceIdentifier = instanceIdentifier;
280             this.bridgeNew = bridgeNew;
281         }
282
283         @Override
284         public List<ListenableFuture<Void>> call() throws Exception {
285             // If another renderer needs to be supported, check can be performed here
286             // to call the respective helpers.
287             return removePortFromBridge(instanceIdentifier, bridgeNew);
288         }
289     }
290
291     private class RendererStateUpdateWorker implements Callable<List<ListenableFuture<Void>>> {
292         private final InstanceIdentifier<OvsdbBridgeAugmentation> instanceIdentifier;
293         private final OvsdbBridgeAugmentation bridgeNew;
294         private final OvsdbBridgeAugmentation bridgeOld;
295
296         RendererStateUpdateWorker(InstanceIdentifier<OvsdbBridgeAugmentation> instanceIdentifier,
297                                   OvsdbBridgeAugmentation bridgeNew, OvsdbBridgeAugmentation bridgeOld) {
298             this.instanceIdentifier = instanceIdentifier;
299             this.bridgeNew = bridgeNew;
300             this.bridgeOld = bridgeOld;
301         }
302
303         @Override
304         public List<ListenableFuture<Void>> call() throws Exception {
305             // If another renderer(for eg : OVS) needs to be supported, check can be performed here
306             // to call the respective helpers.
307             return updateOvsBridgeRefEntry(instanceIdentifier, bridgeNew, bridgeOld);
308         }
309     }
310 }