Bug 5005: Send Map-Notify to all affected xTRs
[lispflowmapping.git] / mappingservice / implementation / src / main / java / org / opendaylight / lispflowmapping / implementation / mapcache / SimpleMapCache.java
index 67d94feeb205382eb592c5497e641175306545e6..0755030cca97dd712f355a498c25ed8b06d53e7b 100644 (file)
@@ -9,15 +9,22 @@
 package org.opendaylight.lispflowmapping.implementation.mapcache;
 
 import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
+import org.opendaylight.lispflowmapping.implementation.config.ConfigIni;
+import org.opendaylight.lispflowmapping.implementation.util.MappingMergeUtil;
 import org.opendaylight.lispflowmapping.interfaces.dao.ILispDAO;
 import org.opendaylight.lispflowmapping.interfaces.dao.IRowVisitor;
 import org.opendaylight.lispflowmapping.interfaces.dao.MappingEntry;
 import org.opendaylight.lispflowmapping.interfaces.dao.SubKeys;
 import org.opendaylight.lispflowmapping.interfaces.mapcache.IMapCache;
 import org.opendaylight.lispflowmapping.lisp.util.MaskUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecord;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.mapping.authkey.container.MappingAuthkey;
@@ -29,6 +36,7 @@ import org.slf4j.LoggerFactory;
  * addresses.
  *
  * @author Florin Coras
+ * @author Lorand Jakab
  *
  */
 public class SimpleMapCache implements IMapCache {
@@ -75,19 +83,52 @@ public class SimpleMapCache implements IMapCache {
         return table;
     }
 
+    private void removeExpiredXtrIdTableEntries(ILispDAO xtrIdDao, List<byte[]> expiredMappings) {
+        for (byte[] xtrId : expiredMappings) {
+            xtrIdDao.removeSpecific(xtrId, SubKeys.RECORD);
+        }
+    }
+
     public void addMapping(Eid key, Object value, boolean shouldOverwrite) {
+        if (value == null) {
+            LOG.warn("addMapping() called with null 'value', ignoring");
+            return;
+        }
+
+        if (!(value instanceof MappingRecord)) {
+            LOG.warn("addMapping() called with a 'value' that is not a 'MappingRecord', ignoring");
+            return;
+        }
+
+        MappingRecord record = (MappingRecord) value;
+        if (record.getXtrId() == null && !shouldOverwrite && ConfigIni.getInstance().mappingMergeIsSet()) {
+            LOG.warn("addMapping() called will null xTR-ID in MappingRecord, while merge is set, ignoring");
+            return;
+        }
+
+        Date regdate = new Date(record.getTimestamp());     // The serializer always sets it
         Eid eid = MaskUtil.normalize(key);
         ILispDAO table = getOrInstantiateVniTable(key);
 
-        table.put(eid, new MappingEntry<>(SubKeys.REGDATE, new Date(System.currentTimeMillis())));
-        table.put(eid, new MappingEntry<>(SubKeys.RECORD, value));
+        ILispDAO xtrIdDao = null;
+        if (!shouldOverwrite) {
+            xtrIdDao = getOrInstantiateXtrIdTable(eid, table);
+            xtrIdDao.put(record.getXtrId(), new MappingEntry<>(SubKeys.RECORD, value));
+        }
 
-        if (!shouldOverwrite && value instanceof MappingRecord) {
-            MappingRecord record = (MappingRecord) value;
-            if (record.getXtrId() != null) {
-                ILispDAO xtrIdDao = getOrInstantiateXtrIdTable(eid, table);
-                xtrIdDao.put(record.getXtrId(), new MappingEntry<>(SubKeys.RECORD, value));
-            }
+        if (ConfigIni.getInstance().mappingMergeIsSet()) {
+            List<byte[]> expiredMappings = new ArrayList<byte[]>();
+            Set<IpAddress> sourceRlocs = new HashSet<IpAddress>();
+            MappingRecord mergedEntry = MappingMergeUtil.mergeXtrIdMappings(getXtrIdMappingList(xtrIdDao),
+                    expiredMappings, sourceRlocs);
+            removeExpiredXtrIdTableEntries(xtrIdDao, expiredMappings);
+            regdate = new Date(mergedEntry.getTimestamp());
+            table.put(eid, new MappingEntry<>(SubKeys.REGDATE, regdate));
+            table.put(eid, new MappingEntry<>(SubKeys.RECORD, mergedEntry));
+            table.put(eid, new MappingEntry<>(SubKeys.SRC_RLOCS, sourceRlocs));
+        } else {
+            table.put(eid, new MappingEntry<>(SubKeys.REGDATE, regdate));
+            table.put(eid, new MappingEntry<>(SubKeys.RECORD, value));
         }
     }
 
@@ -114,20 +155,21 @@ public class SimpleMapCache implements IMapCache {
 
     // Method returns the DAO entry (hash) corresponding to either the longest prefix match of eid, if eid is maskable,
     // or the exact match otherwise. eid must be a 'simple' address
-    private SimpleImmutableEntry<Eid, Map<String, ?>> getDaoPairEntryBest(Eid key, ILispDAO dao) {
-        if (MaskUtil.isMaskable(key.getAddress())) {
-            Eid lookupKey;
-            short mask = MaskUtil.getMaskForAddress(key.getAddress());
+    private SimpleImmutableEntry<Eid, Map<String, ?>> getDaoPairEntryBest(Eid eid, ILispDAO dao) {
+        Eid key;
+        if (MaskUtil.isMaskable(eid.getAddress())) {
+            short mask = MaskUtil.getMaskForAddress(eid.getAddress());
             while (mask > 0) {
-                lookupKey = MaskUtil.normalize(key, mask);
+                key = MaskUtil.normalize(eid, mask);
                 mask--;
-                Map<String, ?> entry = dao.get(lookupKey);
+                Map<String, ?> entry = dao.get(key);
                 if (entry != null) {
-                    return new SimpleImmutableEntry<Eid, Map<String, ?>>(lookupKey, entry);
+                    return new SimpleImmutableEntry<Eid, Map<String, ?>>(key, entry);
                 }
             }
             return null;
         } else {
+            key = MaskUtil.normalize(eid);
             Map<String, ?> entry = dao.get(key);
             if (entry != null) {
                 return new SimpleImmutableEntry<Eid, Map<String, ?>>(key, entry);
@@ -137,30 +179,91 @@ public class SimpleMapCache implements IMapCache {
         }
     }
 
-    // Returns the mapping corresponding to the longest prefix match for eid. eid must be a simple (maskable or not)
-    // address
-    private Object getMappingLpmEid(Eid eid, ILispDAO dao) {
-        Map<String, ?> daoEntry = getDaoEntryBest(eid, dao);
-        if (daoEntry != null) {
-            return daoEntry.get(SubKeys.RECORD);
-        } else {
-            return null;
+    // Returns the list of mappings stored in an xTR-ID DAO
+    private List<Object> getXtrIdMappingList(ILispDAO dao) {
+        if (dao != null) {
+            final List<Object> records = new ArrayList<Object>();
+            dao.getAll(new IRowVisitor() {
+                public void visitRow(Object keyId, String valueKey, Object value) {
+                    if (valueKey.equals(SubKeys.RECORD)) {
+                        records.add(value);
+                    }
+                }
+            });
+            return records;
         }
+        return null;
     }
-
-    // Returns the matched key and mapping corresponding to the longest prefix match for eid. eid must be a simple
-    // (maskable or not) address
-    private SimpleImmutableEntry<Eid, Object> getMappingPairLpmEid(Eid eid, ILispDAO dao) {
+/*
+    private SimpleImmutableEntry<byte[], Long> getXtrIdMinTimeStamp(ILispDAO dao) {
+        if (dao != null) {
+            // We don't actually need a list here, but the external variable we modify inside the IRowVisitor
+            // implementation has to be final. So we use a List, and it's methods, to modify contents
+            final List<SimpleImmutableEntry<byte[], Long>> entries =
+                    new ArrayList<SimpleImmutableEntry<byte[], Long>>();
+            entries.set(0, new SimpleImmutableEntry<byte[], Long>(null, System.currentTimeMillis()));
+
+            dao.getAll(new IRowVisitor() {
+                public void visitRow(Object keyId, String valueKey, Object value) {
+                    if (valueKey.equals(SubKeys.RECORD)) {
+                        byte[] currentXtrId = ((MappingRecord) value).getXtrId();
+                        long currentTimestamp = ((MappingRecord) value).getTimestamp();
+                        // If one of the timestamps is expired, we signal it to the caller by setting the returned
+                        // timestamp to null. The caller will then have to remove that xTR-ID, and do a full merge
+                        // (or decrement)
+                        if (MappingMergeUtil.timestampIsExpired(currentTimestamp)) {
+                            SimpleImmutableEntry<byte[], Long> entry =
+                                    new SimpleImmutableEntry<byte[], Long>(currentXtrId, null);
+                            entries.set(0, entry);
+                            return;
+                        }
+                        if (entries.get(0).getValue() > currentTimestamp) {
+                            SimpleImmutableEntry<byte[], Long> entry =
+                                    new SimpleImmutableEntry<byte[], Long>(currentXtrId, currentTimestamp);
+                            entries.set(0, entry);
+                        }
+                    }
+                }
+            });
+            return entries.get(0);
+        }
+        return null;
+    }
+*/
+    // Returns the mapping corresponding to the longest prefix match for eid. eid must be a simple (maskable or not)
+    // address
+    private Object getMappingLpmEid(Eid eid, byte[] xtrId, ILispDAO dao) {
         SimpleImmutableEntry<Eid, Map<String, ?>> daoEntry = getDaoPairEntryBest(eid, dao);
         if (daoEntry != null) {
-            return new SimpleImmutableEntry<Eid, Object>(daoEntry.getKey(), daoEntry.getValue().get(
-                    SubKeys.RECORD));
+            if (xtrId != null) {
+                ILispDAO xtrIdTable = getXtrIdTable(eid, (ILispDAO) daoEntry.getValue().get(SubKeys.XTRID_RECORDS));
+                if (xtrIdTable != null) {
+                    MappingRecord xtrIdRecord = (MappingRecord) xtrIdTable.getSpecific(xtrId, SubKeys.RECORD);
+                    if (xtrIdRecord.getTimestamp() != null &&
+                            MappingMergeUtil.timestampIsExpired(xtrIdRecord.getTimestamp())) {
+                        xtrIdTable.removeSpecific(xtrId, SubKeys.RECORD);
+                        return null;
+                    } else {
+                        return xtrIdRecord;
+                    }
+                } else {
+                    return null;
+                }
+            } else {
+                Date timestamp = (Date) daoEntry.getValue().get(SubKeys.REGDATE);
+                if (timestamp != null && MappingMergeUtil.timestampIsExpired(timestamp)) {
+                    dao.removeSpecific(daoEntry.getKey(), SubKeys.REGDATE);
+                    dao.removeSpecific(daoEntry.getKey(), SubKeys.RECORD);
+                    return null;
+                }
+                return daoEntry.getValue().get(SubKeys.RECORD);
+            }
         } else {
             return null;
         }
     }
 
-    public Object getMapping(Eid srcEid, Eid dstEid) {
+    public Object getMapping(Eid srcEid, Eid dstEid, byte[] xtrId) {
         if (dstEid == null) {
             return null;
         }
@@ -169,7 +272,22 @@ public class SimpleMapCache implements IMapCache {
         if (table == null) {
             return null;
         }
-        return getMappingLpmEid(dstEid, table);
+        return getMappingLpmEid(dstEid, xtrId, table);
+    }
+
+    public Object getMapping(Eid srcEid, Eid dstEid) {
+        return getMapping(srcEid, dstEid, null);
+    }
+
+    public List<Object> getAllXtrIdMappings(Eid eid) {
+        Map<String, ?> daoEntry = getDaoEntryBest(eid, dao);
+        if (daoEntry != null) {
+            ILispDAO xtrIdTable = getXtrIdTable(eid, (ILispDAO) daoEntry.get(SubKeys.XTRID_RECORDS));
+            if (xtrIdTable != null) {
+                return getXtrIdMappingList(xtrIdTable);
+            }
+        }
+        return null;
     }
 
     public void removeMapping(Eid eid, boolean overwrite) {