Enhance the use of cache in HwvtepDeviceInfo
[ovsdb.git] / hwvtepsouthbound / hwvtepsouthbound-impl / src / main / java / org / opendaylight / ovsdb / hwvtepsouthbound / HwvtepDeviceInfo.java
1 /*
2  * Copyright (c) 2016, 2017 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
9 package org.opendaylight.ovsdb.hwvtepsouthbound;
10
11 import com.google.common.collect.Sets;
12
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.Map;
17 import java.util.Set;
18
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.atomic.AtomicInteger;
21
22 import org.opendaylight.ovsdb.hwvtepsouthbound.transact.DependencyQueue;
23 import org.opendaylight.ovsdb.hwvtepsouthbound.transact.DependentJob;
24 import org.opendaylight.ovsdb.hwvtepsouthbound.transact.TransactCommand;
25 import org.opendaylight.ovsdb.lib.notation.UUID;
26 import org.opendaylight.ovsdb.schema.hardwarevtep.LogicalSwitch;
27 import org.opendaylight.ovsdb.schema.hardwarevtep.PhysicalLocator;
28 import org.opendaylight.ovsdb.schema.hardwarevtep.PhysicalSwitch;
29 import org.opendaylight.ovsdb.utils.mdsal.utils.TransactionHistory;
30 import org.opendaylight.ovsdb.utils.mdsal.utils.TransactionType;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteMcastMacs;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.RemoteUcastMacs;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
35 import org.opendaylight.yangtools.yang.binding.Identifiable;
36 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /*
41  * HwvtepDeviceInfo is used to store some of the table entries received
42  * in updates from a Hwvtep device. There will be one instance of this per
43  * Hwvtep device connected. Table entries are stored in a map keyed by
44  * uuids of respective rows.
45  *
46  * Purpose of this class is to provide data present in tables which
47  * were updated in a previous transaction and are not available in
48  * current updatedRows. This allows us to handle updates for Tables
49  * which reference other tables and need information in those tables
50  * to add data to Operational data store.
51  *
52  * e.g. Mac-entries in data store use logical-switch-ref as one of the
53  * keys. Mac-entry updates from switch rarely contain Logical_Switch
54  * table entries. To add mac-entries we need table entries from
55  * Logical_Switch table which were created in an earlier update.
56  *
57  */
58 public class HwvtepDeviceInfo {
59
60     private static final Logger LOG = LoggerFactory.getLogger(HwvtepDeviceInfo.class);
61
62     private Map<Class<? extends Identifiable>, Map<InstanceIdentifier, Boolean>> availableInOperDs =
63             new ConcurrentHashMap<>();
64
65     public void markAvailableInOperDs(Class<? extends Identifiable> cls, InstanceIdentifier key) {
66         availableInOperDs.putIfAbsent(cls, new ConcurrentHashMap<>());
67         availableInOperDs.get(cls).put(key, Boolean.TRUE);
68     }
69
70     public Boolean isAvailableInOperDs(Class<? extends Identifiable> cls, InstanceIdentifier key) {
71         availableInOperDs.putIfAbsent(cls, new ConcurrentHashMap<>());
72         return availableInOperDs.get(cls).getOrDefault(key, Boolean.FALSE);
73     }
74
75     public Boolean clearOperDsAvailability(Class<? extends Identifiable> cls, InstanceIdentifier key) {
76         availableInOperDs.putIfAbsent(cls, new ConcurrentHashMap<>());
77         return availableInOperDs.get(cls).remove(key);
78     }
79
80     public enum DeviceDataStatus {
81         IN_TRANSIT,
82         UNAVAILABLE,
83         AVAILABLE
84     }
85
86     public static class DeviceData {
87         private final InstanceIdentifier key;
88         private final UUID uuid;
89         private final Object data;
90         private final DeviceDataStatus status;
91         private long intransitTimeStamp;
92
93         DeviceData(InstanceIdentifier key, UUID uuid, Object data, DeviceDataStatus status) {
94             this.data = data;
95             this.key = key;
96             this.status = status;
97             this.uuid = uuid;
98             if (status == DeviceDataStatus.IN_TRANSIT) {
99                 intransitTimeStamp = System.currentTimeMillis();
100             }
101         }
102
103         public Object getData() {
104             return data;
105         }
106
107         public DeviceDataStatus getStatus() {
108             return status;
109         }
110
111         public UUID getUuid() {
112             return uuid;
113         }
114
115         public InstanceIdentifier getKey() {
116             return key;
117         }
118
119         public boolean isIntransitTimeExpired() {
120             return System.currentTimeMillis()
121                     > intransitTimeStamp + HwvtepSouthboundConstants.IN_TRANSIT_STATE_EXPIRY_TIME_MILLIS;
122         }
123
124         public boolean isInTransitState() {
125             return status == DeviceDataStatus.IN_TRANSIT;
126         }
127
128         public boolean isAvailableInOperDs() {
129             return false;
130         }
131
132         @Override
133         public String toString() {
134             return key + " uuid:" + uuid + " data:" + data + " status:" + status;
135         }
136     }
137
138     private static AtomicInteger ZERO = new AtomicInteger(0);
139     private final Map<InstanceIdentifier<?>, Set<InstanceIdentifier>> tepIdReferences = new ConcurrentHashMap<>();
140     private final Map<InstanceIdentifier<LogicalSwitches>, Map<InstanceIdentifier<RemoteUcastMacs>, RemoteUcastMacs>>
141             logicalSwitchVsUcasts = new ConcurrentHashMap<>();
142     private final Map<InstanceIdentifier<LogicalSwitches>, Map<InstanceIdentifier<RemoteMcastMacs>, RemoteMcastMacs>>
143             logicalSwitchVsMcasts = new ConcurrentHashMap<>();
144     private final Map<UUID, PhysicalSwitch> physicalSwitches = new ConcurrentHashMap<>();
145     private final Map<UUID, UUID> mapTunnelToPhysicalSwitch = new ConcurrentHashMap<>();
146
147     private final HwvtepConnectionInstance connectionInstance;
148
149     private Map<Class<? extends Identifiable>, Map<InstanceIdentifier, DeviceData>> configKeyVsData =
150             new ConcurrentHashMap<>();
151     private final Map<Class<? extends Identifiable>, Map<InstanceIdentifier, DeviceData>> opKeyVsData =
152             new ConcurrentHashMap<>();
153     private final Map<Class<? extends Identifiable>, Map<UUID, DeviceData>> uuidVsData = new ConcurrentHashMap<>();
154     private final DependencyQueue dependencyQueue;
155     private TransactionHistory controllerTxHistory;
156     private TransactionHistory deviceUpdateHistory;
157
158
159     public HwvtepDeviceInfo(HwvtepConnectionInstance hwvtepConnectionInstance) {
160         this.connectionInstance = hwvtepConnectionInstance;
161         this.dependencyQueue = new DependencyQueue(this);
162     }
163
164     public LogicalSwitch getLogicalSwitch(UUID uuid) {
165         DeviceData deviceData = getDeviceOperData(LogicalSwitches.class, uuid);
166         if (deviceData != null && deviceData.getData() != null) {
167             return (LogicalSwitch) deviceData.getData();
168         }
169         return null;
170     }
171
172     public Map<UUID, LogicalSwitch> getLogicalSwitches() {
173         Map<UUID, DeviceData> switches = uuidVsData.get(LogicalSwitches.class);
174         Map<UUID, LogicalSwitch> result = new HashMap<>();
175         if (switches != null) {
176             for (Map.Entry<UUID, DeviceData> entry : switches.entrySet()) {
177                 result.put(entry.getKey(), (LogicalSwitch) entry.getValue().getData());
178             }
179         }
180         return result;
181     }
182
183     public void putPhysicalSwitch(UUID uuid, PhysicalSwitch physicalSwitch) {
184         physicalSwitches.put(uuid, physicalSwitch);
185     }
186
187     public PhysicalSwitch getPhysicalSwitch(UUID uuid) {
188         return physicalSwitches.get(uuid);
189     }
190
191     public PhysicalSwitch removePhysicalSwitch(UUID uuid) {
192         return physicalSwitches.remove(uuid);
193     }
194
195     public Map<UUID, PhysicalSwitch> getPhysicalSwitches() {
196         return physicalSwitches;
197     }
198
199     public PhysicalLocator getPhysicalLocator(UUID uuid) {
200         DeviceData deviceData = getDeviceOperData(TerminationPoint.class, uuid);
201         if (deviceData != null && deviceData.getData() != null) {
202             return (PhysicalLocator) deviceData.getData();
203         }
204         return null;
205     }
206
207     public Map<UUID, PhysicalLocator> getPhysicalLocators() {
208         Map<UUID, DeviceData> locators = uuidVsData.get(TerminationPoint.class);
209         Map<UUID, PhysicalLocator> result = new HashMap<>();
210         if (locators != null) {
211             for (Map.Entry<UUID, DeviceData> entry : locators.entrySet()) {
212                 result.put(entry.getKey(), (PhysicalLocator) entry.getValue().getData());
213             }
214         }
215         return result;
216     }
217
218     public void putPhysicalSwitchForTunnel(UUID uuid, UUID psUUID) {
219         mapTunnelToPhysicalSwitch.put(uuid, psUUID);
220     }
221
222     public PhysicalSwitch getPhysicalSwitchForTunnel(UUID uuid) {
223         return physicalSwitches.get(mapTunnelToPhysicalSwitch.get(uuid));
224     }
225
226     public void removePhysicalSwitchForTunnel(UUID uuid) {
227         mapTunnelToPhysicalSwitch.remove(uuid);
228     }
229
230     public Map<UUID, UUID> getPhysicalSwitchesForTunnels() {
231         return mapTunnelToPhysicalSwitch;
232     }
233
234     public boolean isKeyInTransit(Class<? extends Identifiable> cls, InstanceIdentifier key) {
235         DeviceData deviceData = HwvtepSouthboundUtil.getData(opKeyVsData, cls, key);
236         return deviceData != null && DeviceDataStatus.IN_TRANSIT == deviceData.status;
237     }
238
239     public boolean isConfigDataAvailable(Class<? extends Identifiable> cls, InstanceIdentifier key) {
240         return HwvtepSouthboundUtil.getData(configKeyVsData, cls, key) != null;
241     }
242
243     public void updateConfigData(Class<? extends Identifiable> cls, InstanceIdentifier key, Object data) {
244         HwvtepSouthboundUtil.updateData(configKeyVsData, cls, key,
245                 new DeviceData(key, null, data, DeviceDataStatus.AVAILABLE));
246     }
247
248     public DeviceData getConfigData(Class<? extends Identifiable> cls, InstanceIdentifier key) {
249         return HwvtepSouthboundUtil.getData(configKeyVsData, cls, key);
250     }
251
252     public Map<Class<? extends Identifiable>, Map<InstanceIdentifier, DeviceData>> getConfigData() {
253         return Collections.unmodifiableMap(configKeyVsData);
254     }
255
256     public void clearConfigData(Class<? extends Identifiable> cls, InstanceIdentifier key) {
257         HwvtepSouthboundUtil.clearData(configKeyVsData, cls, key);
258     }
259
260     public void markKeyAsInTransit(Class<? extends Identifiable> cls, InstanceIdentifier key) {
261         LOG.debug("Marking device data as intransit {}", key);
262         DeviceData deviceData = getDeviceOperData(cls, key);
263         UUID uuid = null;
264         Object data = null;
265         if (deviceData != null) {
266             uuid = deviceData.getUuid();
267             data = deviceData.getData();
268         }
269         HwvtepSouthboundUtil.updateData(opKeyVsData, cls, key,
270                 new DeviceData(key, uuid, data, DeviceDataStatus.IN_TRANSIT));
271     }
272
273     public void updateDeviceOperData(Class<? extends Identifiable> cls, InstanceIdentifier key,
274             UUID uuid, Object data) {
275         LOG.debug("Updating device data {}", key);
276         DeviceData deviceData = new DeviceData(key, uuid, data, DeviceDataStatus.AVAILABLE);
277         HwvtepSouthboundUtil.updateData(opKeyVsData, cls, key, deviceData);
278         HwvtepSouthboundUtil.updateData(uuidVsData, cls, uuid, deviceData);
279     }
280
281     public void clearDeviceOperData(Class<? extends Identifiable> cls, InstanceIdentifier key) {
282         DeviceData deviceData = HwvtepSouthboundUtil.getData(opKeyVsData, cls, key);
283         if (deviceData != null && deviceData.uuid != null) {
284             HwvtepSouthboundUtil.clearData(uuidVsData, cls, deviceData.uuid);
285         }
286         HwvtepSouthboundUtil.clearData(opKeyVsData, cls, key);
287     }
288
289     public void clearDeviceOperData(Class<? extends Identifiable> cls) {
290         Map<InstanceIdentifier, DeviceData> iids = opKeyVsData.get(cls);
291         if (iids != null && !iids.isEmpty()) {
292             Iterator<Map.Entry<InstanceIdentifier, DeviceData>> it = iids.entrySet().iterator();
293             while (it.hasNext()) {
294                 Map.Entry<InstanceIdentifier, DeviceData> entry = it.next();
295                 DeviceData deviceData = entry.getValue();
296                 if (deviceData != null && deviceData.getStatus() != DeviceDataStatus.IN_TRANSIT) {
297                     it.remove();
298                 }
299             }
300         }
301     }
302
303     public void clearDeviceOperUUID(Class<? extends Identifiable> cls, InstanceIdentifier key, UUID uuid) {
304         LOG.debug("Clearing device data {}", key);
305         if (uuidVsData.containsKey(cls) && uuidVsData.get(cls).containsKey(uuid)) {
306             LOG.debug("Remove {} {} from device data.", connectionInstance.getNodeId().getValue(), cls.getSimpleName());
307         }
308         HwvtepSouthboundUtil.clearData(uuidVsData, cls, uuid);
309         HwvtepSouthboundUtil.clearData(opKeyVsData, cls, key);
310     }
311
312     public DeviceData getDeviceOperData(Class<? extends Identifiable> cls, UUID uuid) {
313         return HwvtepSouthboundUtil.getData(uuidVsData, cls, uuid);
314     }
315
316     public DeviceData getDeviceOperData(Class<? extends Identifiable> cls, InstanceIdentifier key) {
317         return HwvtepSouthboundUtil.getData(opKeyVsData, cls, key);
318     }
319
320     public Map<InstanceIdentifier, DeviceData> getDeviceOperData(Class<? extends Identifiable> cls) {
321         return opKeyVsData.get(cls);
322     }
323
324     public InstanceIdentifier getDeviceOperKey(final Class<? extends Identifiable> cls, final UUID uuid) {
325         DeviceData deviceData = HwvtepSouthboundUtil.getData(uuidVsData, cls, uuid);
326         if (deviceData != null) {
327             return deviceData.getKey();
328         }
329         return null;
330     }
331
332     public UUID getUUID(Class<? extends Identifiable> cls, InstanceIdentifier key) {
333         DeviceData data = HwvtepSouthboundUtil.getData(opKeyVsData, cls, key);
334         if (data != null) {
335             return data.uuid;
336         }
337         return null;
338     }
339
340     public <T extends Identifiable> void addJobToQueue(DependentJob<T> job) {
341         dependencyQueue.addToQueue(job);
342     }
343
344     public void onConfigDataAvailable() {
345         dependencyQueue.processReadyJobsFromConfigQueue(connectionInstance);
346     }
347
348     public synchronized void onOperDataAvailable() {
349         dependencyQueue.processReadyJobsFromOpQueue(connectionInstance);
350     }
351
352     public void scheduleTransaction(final TransactCommand transactCommand) {
353         dependencyQueue.submit(() -> connectionInstance.transact(transactCommand));
354     }
355
356     public void clearInTransit(Class<? extends Identifiable> cls, InstanceIdentifier key) {
357         DeviceData deviceData = getDeviceOperData(cls, key);
358         if (deviceData != null && deviceData.isInTransitState()) {
359             if (deviceData.getData() != null) {
360                 HwvtepSouthboundUtil.updateData(opKeyVsData, cls, key,
361                         new DeviceData(key, deviceData.getUuid(), deviceData.getData(), DeviceDataStatus.AVAILABLE));
362             } else {
363                 clearDeviceOperData(cls, key);
364             }
365         }
366     }
367
368     public void incRefCount(InstanceIdentifier reference, InstanceIdentifier tep) {
369         if (reference == null || tep == null) {
370             return;
371         }
372         tepIdReferences.computeIfAbsent(tep, (tepId) -> Sets.newConcurrentHashSet());
373         tepIdReferences.get(tep).add(reference);
374     }
375
376     public int getRefCount(InstanceIdentifier tep) {
377         return tepIdReferences.containsKey(tep) ? tepIdReferences.get(tep).size() : 0;
378     }
379
380     public Set<InstanceIdentifier> getRefCounts(InstanceIdentifier tep) {
381         return tepIdReferences.get(tep);
382     }
383
384     public void decRefCount(InstanceIdentifier reference, InstanceIdentifier tep) {
385         if (reference == null || tep == null || !tepIdReferences.containsKey(tep)) {
386             return;
387         }
388         //synchronize to make sure that no two parallel deletes puts the key in transit state twice
389         synchronized (this) {
390             boolean removed = tepIdReferences.get(tep).remove(reference);
391             if (removed && tepIdReferences.get(tep).isEmpty()) {
392                 LOG.debug("Marking the termination point as in transit ref count zero {} ", tep);
393                 markKeyAsInTransit(TerminationPoint.class, tep);
394             }
395         }
396     }
397
398     public void clearLogicalSwitchRefs(InstanceIdentifier<LogicalSwitches> logicalSwitchKey) {
399         Map<InstanceIdentifier<RemoteMcastMacs>, RemoteMcastMacs> mcasts = logicalSwitchVsMcasts.get(logicalSwitchKey);
400         if (mcasts != null) {
401             mcasts.entrySet().forEach((entry) -> removeRemoteMcast(logicalSwitchKey, entry.getKey()));
402         }
403         Map<InstanceIdentifier<RemoteUcastMacs>, RemoteUcastMacs> ucasts = logicalSwitchVsUcasts.get(logicalSwitchKey);
404         if (ucasts != null) {
405             ucasts.entrySet().forEach((entry) -> removeRemoteUcast(logicalSwitchKey, entry.getKey()));
406         }
407         markKeyAsInTransit(LogicalSwitches.class, logicalSwitchKey);
408     }
409
410     public  void updateRemoteMcast(InstanceIdentifier<LogicalSwitches> lsIid,
411                                    InstanceIdentifier<RemoteMcastMacs> mcastIid,
412                                    RemoteMcastMacs mac) {
413         logicalSwitchVsMcasts.computeIfAbsent(lsIid, (lsKey) -> new ConcurrentHashMap<>());
414         logicalSwitchVsMcasts.get(lsIid).put(mcastIid, mac);
415         if (mac.getLocatorSet() != null) {
416             mac.getLocatorSet().forEach((iid) -> incRefCount(mcastIid, iid.getLocatorRef().getValue()));
417         }
418     }
419
420     public  void updateRemoteUcast(InstanceIdentifier<LogicalSwitches> lsIid,
421                                    InstanceIdentifier<RemoteUcastMacs> ucastIid,
422                                    RemoteUcastMacs mac) {
423         logicalSwitchVsUcasts.computeIfAbsent(lsIid, (lsKey) -> new ConcurrentHashMap<>());
424         logicalSwitchVsUcasts.get(lsIid).put(ucastIid, mac);
425         incRefCount(ucastIid, mac.getLocatorRef().getValue());
426     }
427
428     public void removeRemoteMcast(InstanceIdentifier<LogicalSwitches> lsIid,
429             InstanceIdentifier<RemoteMcastMacs> mcastIid) {
430         if (!logicalSwitchVsMcasts.containsKey(lsIid)) {
431             return;
432         }
433         RemoteMcastMacs mac = logicalSwitchVsMcasts.get(lsIid).remove(mcastIid);
434         if (mac != null && mac.getLocatorSet() != null) {
435             mac.getLocatorSet().forEach((iid) -> decRefCount(mcastIid, iid.getLocatorRef().getValue()));
436         }
437         markKeyAsInTransit(RemoteMcastMacs.class, mcastIid);
438     }
439
440     public void removeRemoteUcast(InstanceIdentifier<LogicalSwitches> lsIid,
441                                    InstanceIdentifier<RemoteUcastMacs> ucastIid) {
442         if (!logicalSwitchVsUcasts.containsKey(lsIid)) {
443             return;
444         }
445         RemoteUcastMacs mac = logicalSwitchVsUcasts.get(lsIid).remove(ucastIid);
446         if (mac != null) {
447             decRefCount(ucastIid, mac.getLocatorRef().getValue());
448         }
449         markKeyAsInTransit(RemoteUcastMacs.class, ucastIid);
450     }
451
452     public HwvtepConnectionInstance getConnectionInstance() {
453         return connectionInstance;
454     }
455
456     public void setConfigKeyVsData(Map<Class<? extends Identifiable>, Map<InstanceIdentifier,
457             DeviceData>> configKeyVsData) {
458         this.configKeyVsData = configKeyVsData;
459     }
460
461     public void setControllerTxHistory(TransactionHistory controllerTxHistory) {
462         this.controllerTxHistory = controllerTxHistory;
463     }
464
465     public void setDeviceUpdateHistory(TransactionHistory deviceUpdateHistory) {
466         this.deviceUpdateHistory = deviceUpdateHistory;
467     }
468
469     public void addToControllerTx(TransactionType transactionType, Object object) {
470         controllerTxHistory.addToHistory(transactionType, object);
471     }
472
473     public void addToDeviceUpdate(TransactionType transactionType, Object object) {
474         deviceUpdateHistory.addToHistory(transactionType, object);
475     }
476
477     public Map<Class<? extends Identifiable>, Map<InstanceIdentifier, DeviceData>> getOperData() {
478         return Collections.unmodifiableMap(opKeyVsData);
479     }
480
481     public Map<Class<? extends Identifiable>, Map<UUID, DeviceData>> getUuidData() {
482         return Collections.unmodifiableMap(uuidVsData);
483     }
484
485 }