dd2133001d08204cec803d762d53a3304df2e008
[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.util.concurrent.ListenableFuture;
11 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
12 import java.util.Collections;
13 import java.util.List;
14 import java.util.Optional;
15 import java.util.concurrent.Callable;
16 import org.eclipse.jdt.annotation.NonNull;
17 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
18 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
19 import org.opendaylight.genius.itm.cache.DpnTepStateCache;
20 import org.opendaylight.genius.itm.cache.OvsBridgeEntryCache;
21 import org.opendaylight.genius.itm.globals.ITMConstants;
22 import org.opendaylight.genius.itm.itmdirecttunnels.renderer.ovs.utilities.DirectTunnelUtils;
23 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
24 import org.opendaylight.mdsal.binding.api.DataBroker;
25 import org.opendaylight.mdsal.binding.api.WriteTransaction;
26 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
27 import org.opendaylight.serviceutils.tools.listener.AbstractClusteredSyncDataTreeChangeListener;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfTunnel;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.bridge.tunnel.info.OvsBridgeEntry;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.bridge.tunnel.info.OvsBridgeEntryBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.bridge.tunnel.info.OvsBridgeEntryKey;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.bridge.tunnel.info.ovs.bridge.entry.OvsBridgeTunnelEntry;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.ovs.bridge.ref.info.OvsBridgeRefEntry;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.ovs.bridge.ref.info.OvsBridgeRefEntryBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.meta.rev171210.ovs.bridge.ref.info.OvsBridgeRefEntryKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeRef;
39 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
40 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
41 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
42 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
43 import org.opendaylight.yangtools.yang.common.Uint64;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 public class TunnelTopologyStateListener extends AbstractClusteredSyncDataTreeChangeListener<OvsdbBridgeAugmentation> {
48
49     private static final Logger LOG = LoggerFactory.getLogger(TunnelTopologyStateListener.class);
50     private static final Logger EVENT_LOGGER = LoggerFactory.getLogger("GeniusEventLogger");
51
52     private final JobCoordinator coordinator;
53     private final ManagedNewTransactionRunner txRunner;
54     private final DirectTunnelUtils directTunnelUtils;
55     private final OvsBridgeEntryCache ovsBridgeEntryCache;
56     protected final DpnTepStateCache dpnTepStateCache;
57
58     public TunnelTopologyStateListener(final DataBroker dataBroker,
59                                        final JobCoordinator coordinator,
60                                        final DirectTunnelUtils directTunnelUtils,
61                                        final DpnTepStateCache dpnTepStateCache,
62                                        final OvsBridgeEntryCache ovsBridgeEntryCache)  {
63         super(dataBroker, LogicalDatastoreType.OPERATIONAL,
64                 InstanceIdentifier.create(NetworkTopology.class).child(Topology.class).child(Node.class)
65                         .augmentation(OvsdbBridgeAugmentation.class));
66         this.coordinator = coordinator;
67         this.dpnTepStateCache = dpnTepStateCache;
68         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
69         this.directTunnelUtils = directTunnelUtils;
70         this.ovsBridgeEntryCache = ovsBridgeEntryCache;
71         super.register();
72     }
73
74     @Override
75     public void remove(@NonNull InstanceIdentifier<OvsdbBridgeAugmentation> identifier,
76                        @NonNull OvsdbBridgeAugmentation bridgeOld) {
77         EVENT_LOGGER.debug("ITM-TunnelTopologyState, REMOVE DTCN received");
78         if (directTunnelUtils.isEntityOwner()) {
79             LOG.debug("Received Remove DataChange Notification for identifier: {}, ovsdbBridgeAugmentation: {}",
80                     identifier, bridgeOld);
81             TunnelRendererStateRemoveWorker rendererStateRemoveWorker =
82                     new TunnelRendererStateRemoveWorker(identifier, bridgeOld);
83             coordinator.enqueueJob(bridgeOld.getBridgeName().getValue(), rendererStateRemoveWorker,
84                     ITMConstants.JOB_MAX_RETRIES);
85         }
86     }
87
88     @Override
89     public void update(@NonNull InstanceIdentifier<OvsdbBridgeAugmentation> identifier,
90                        @NonNull OvsdbBridgeAugmentation bridgeOld, @NonNull OvsdbBridgeAugmentation bridgeNew) {
91         EVENT_LOGGER.debug("ITM-TunnelTopologyState, UPDATE DTCN received");
92
93         if (!directTunnelUtils.isEntityOwner()) {
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             TunnelRendererStateAddWorker rendererStateAddWorker =
103                     new TunnelRendererStateAddWorker(identifier, bridgeNew);
104             coordinator.enqueueJob(bridgeNew.getBridgeName().getValue(), rendererStateAddWorker,
105                     ITMConstants.JOB_MAX_RETRIES);
106         } else if (oldDpid != null && !oldDpid.equals(newDpid)) {
107             TunnelRendererStateUpdateWorker rendererStateAddWorker =
108                     new TunnelRendererStateUpdateWorker(identifier, bridgeNew, bridgeOld);
109             coordinator.enqueueJob(bridgeNew.getBridgeName().getValue(), rendererStateAddWorker,
110                     ITMConstants.JOB_MAX_RETRIES);
111         }
112     }
113
114     @Override
115     public void add(@NonNull InstanceIdentifier<OvsdbBridgeAugmentation> identifier,
116                     @NonNull OvsdbBridgeAugmentation bridgeNew) {
117         EVENT_LOGGER.debug("ITM-TunnelTopologyState, ADD DTCN received");
118         if (directTunnelUtils.isEntityOwner()) {
119             LOG.debug("Received Add DataChange Notification for identifier: {}, ovsdbBridgeAugmentation: {}",
120                     identifier, bridgeNew);
121             TunnelRendererStateAddWorker rendererStateAddWorker =
122                     new TunnelRendererStateAddWorker(identifier, bridgeNew);
123             coordinator.enqueueJob(bridgeNew.getBridgeName().getValue(), rendererStateAddWorker,
124                     ITMConstants.JOB_MAX_RETRIES);
125         }
126     }
127
128     /*
129      * This code is used to handle only a dpnId change scenario for a particular change,
130      * which is not expected to happen in usual cases.
131      */
132     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
133             justification = "https://github.com/spotbugs/spotbugs/issues/811")
134     private List<ListenableFuture<Void>> updateOvsBridgeRefEntry(InstanceIdentifier<OvsdbBridgeAugmentation> bridgeIid,
135                                                                  OvsdbBridgeAugmentation bridgeNew,
136                                                                  OvsdbBridgeAugmentation bridgeOld) {
137
138         return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
139             Uint64 dpnIdNew = directTunnelUtils.getDpnId(bridgeNew.getDatapathId());
140             Uint64 dpnIdOld = directTunnelUtils.getDpnId(bridgeOld.getDatapathId());
141
142             LOG.debug("updating bridge references for bridge: {}, dpnNew: {}, dpnOld: {}", bridgeNew,
143                     dpnIdNew, dpnIdOld);
144             //delete bridge reference entry for the old dpn in interface meta operational DS
145             deleteOvsBridgeRefEntry(dpnIdOld, tx);
146
147             // create bridge reference entry in interface meta operational DS
148             createOvsBridgeRefEntry(dpnIdNew, bridgeIid, tx);
149
150             // handle pre-provisioning of tunnels for the newly connected dpn
151             Optional<OvsBridgeEntry> bridgeEntry = null;
152             bridgeEntry = ovsBridgeEntryCache.get(dpnIdNew);
153             if (bridgeEntry.isPresent()) {
154                 addAllPortsToBridge(bridgeEntry.get(), bridgeIid, bridgeNew);
155             }
156         }));
157     }
158
159     public List<ListenableFuture<Void>> removePortFromBridge(InstanceIdentifier<OvsdbBridgeAugmentation> bridgeIid,
160                                                              OvsdbBridgeAugmentation bridgeOld) {
161         Uint64 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             EVENT_LOGGER.debug("ITM-TunnelTopologyState, REMOVE {} completed", bridgeOld.getBridgeName().getValue());
169             //delete bridge reference entry in interface meta operational DS
170             deleteOvsBridgeRefEntry(dpnId, tx);
171
172             // the bridge reference is copied to dpn-tunnel interfaces map, so that whenever a northbound delete
173             // happens when bridge is not connected, we need the bridge reference to clean up the topology config DS
174             addBridgeRefToBridgeTunnelEntry(dpnId, new OvsdbBridgeRef(bridgeIid), tx);
175         }));
176     }
177
178     private void createOvsBridgeRefEntry(Uint64 dpnId, InstanceIdentifier<?> bridgeIid, 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().withKey(bridgeRefEntryKey).setDpid(dpnId)
186                         .setOvsBridgeReference(new OvsdbBridgeRef(bridgeIid));
187         tx.put(LogicalDatastoreType.OPERATIONAL, bridgeEntryId, tunnelDpnBridgeEntryBuilder.build(), true);
188     }
189
190     private void deleteOvsBridgeRefEntry(Uint64 dpnId, WriteTransaction tx) {
191         LOG.debug("Deleting bridge ref entry for dpn: {}",
192                 dpnId);
193         OvsBridgeRefEntryKey bridgeRefEntryKey = new OvsBridgeRefEntryKey(dpnId);
194         InstanceIdentifier<OvsBridgeRefEntry> bridgeEntryId =
195                 DirectTunnelUtils.getOvsBridgeRefEntryIdentifier(bridgeRefEntryKey);
196         tx.delete(LogicalDatastoreType.OPERATIONAL, bridgeEntryId);
197     }
198
199     private void addBridgeRefToBridgeTunnelEntry(Uint64 dpId, OvsdbBridgeRef ovsdbBridgeRef, WriteTransaction tx) {
200         OvsBridgeEntryKey bridgeEntryKey = new OvsBridgeEntryKey(dpId);
201         InstanceIdentifier<OvsBridgeEntry> bridgeEntryInstanceIdentifier =
202                 DirectTunnelUtils.getOvsBridgeEntryIdentifier(bridgeEntryKey);
203
204         OvsBridgeEntryBuilder bridgeEntryBuilder = new OvsBridgeEntryBuilder().withKey(bridgeEntryKey)
205                 .setOvsBridgeReference(ovsdbBridgeRef);
206         tx.merge(LogicalDatastoreType.CONFIGURATION, bridgeEntryInstanceIdentifier, bridgeEntryBuilder.build(), true);
207     }
208
209     /*
210      * Add all tunnels ports corresponding to the bridge to the topology config
211      * DS
212      */
213     private void addAllPortsToBridge(OvsBridgeEntry bridgeEntry, InstanceIdentifier<OvsdbBridgeAugmentation> bridgeIid,
214                                      OvsdbBridgeAugmentation bridgeNew) {
215         String bridgeName = bridgeNew.getBridgeName().getValue();
216         LOG.debug("adding all ports to bridge: {}", bridgeName);
217         List<OvsBridgeTunnelEntry> bridgeInterfaceEntries = bridgeEntry.getOvsBridgeTunnelEntry();
218         if (bridgeInterfaceEntries != null) {
219             for (OvsBridgeTunnelEntry bridgeInterfaceEntry : bridgeInterfaceEntries) {
220                 String portName = bridgeInterfaceEntry.getTunnelName();
221                 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
222                         .ietf.interfaces.rev140508.interfaces.Interface iface =
223                         dpnTepStateCache.getInterfaceFromCache(portName);
224                 if (iface != null) {
225                     IfTunnel ifTunnel = iface.augmentation(IfTunnel.class);
226                     if (ifTunnel != null) {
227                         directTunnelUtils.addTunnelPortToBridge(ifTunnel, bridgeIid, iface, portName);
228                     }
229                 } else {
230                     LOG.debug("Interface {} not found in config DS", portName);
231                 }
232             }
233             EVENT_LOGGER.debug("ITM-TunnelTopologyState, ADD port on {} completed", bridgeName);
234         }
235     }
236
237     private class TunnelRendererStateAddWorker implements Callable<List<? extends ListenableFuture<?>>> {
238         private final InstanceIdentifier<OvsdbBridgeAugmentation> bridgeIid;
239         private final OvsdbBridgeAugmentation bridgeNew;
240
241         TunnelRendererStateAddWorker(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             Uint64 dpnId = directTunnelUtils.getDpnId(bridgeNew.getDatapathId());
257             LOG.debug("adding bridge references for bridge: {}, dpn: {}", bridgeNew, dpnId);
258             EVENT_LOGGER.debug("TunnelTopologyState, ADD bridge {} for {}", bridgeNew.getBridgeName(), dpnId);
259
260             // create bridge reference entry in interface meta operational DS
261             return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
262                 createOvsBridgeRefEntry(dpnId, bridgeIid, tx);
263                 // handle pre-provisioning of tunnels for the newly connected dpn
264                 Optional<OvsBridgeEntry> bridgeEntry = ovsBridgeEntryCache.get(dpnId);
265                 if (!bridgeEntry.isPresent()) {
266                     LOG.debug("Bridge entry not found in config DS for dpn: {}", dpnId);
267                 } else {
268                     addAllPortsToBridge(bridgeEntry.get(), bridgeIid, bridgeNew);
269                 }
270             }));
271         }
272     }
273
274     private class TunnelRendererStateRemoveWorker implements Callable<List<? extends ListenableFuture<?>>> {
275         private final InstanceIdentifier<OvsdbBridgeAugmentation> instanceIdentifier;
276         private final OvsdbBridgeAugmentation bridgeNew;
277
278         TunnelRendererStateRemoveWorker(InstanceIdentifier<OvsdbBridgeAugmentation> instanceIdentifier,
279                                         OvsdbBridgeAugmentation bridgeNew) {
280             this.instanceIdentifier = instanceIdentifier;
281             this.bridgeNew = bridgeNew;
282         }
283
284         @Override
285         public List<ListenableFuture<Void>> call() throws Exception {
286             // If another renderer needs to be supported, check can be performed here
287             // to call the respective helpers.
288             return removePortFromBridge(instanceIdentifier, bridgeNew);
289         }
290     }
291
292     private class TunnelRendererStateUpdateWorker implements Callable<List<? extends ListenableFuture<?>>> {
293         private final InstanceIdentifier<OvsdbBridgeAugmentation> instanceIdentifier;
294         private final OvsdbBridgeAugmentation bridgeNew;
295         private final OvsdbBridgeAugmentation bridgeOld;
296
297         TunnelRendererStateUpdateWorker(InstanceIdentifier<OvsdbBridgeAugmentation> instanceIdentifier,
298                                         OvsdbBridgeAugmentation bridgeNew, OvsdbBridgeAugmentation bridgeOld) {
299             this.instanceIdentifier = instanceIdentifier;
300             this.bridgeNew = bridgeNew;
301             this.bridgeOld = bridgeOld;
302         }
303
304         @Override
305         public List<ListenableFuture<Void>> call() throws Exception {
306             // If another renderer(for eg : OVS) needs to be supported, check can be performed here
307             // to call the respective helpers.
308             return updateOvsBridgeRefEntry(instanceIdentifier, bridgeNew, bridgeOld);
309         }
310     }
311 }