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