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