Bug 5005: Send Map-Notify to all affected xTRs
[lispflowmapping.git] / mappingservice / implementation / src / main / java / org / opendaylight / lispflowmapping / implementation / mapcache / SimpleMapCache.java
index 34f573435eae3120a529e4363d8aa9c8dc3f5a5f..0755030cca97dd712f355a498c25ed8b06d53e7b 100644 (file)
@@ -9,17 +9,25 @@
 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.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev150820.lispaddress.LispAddressContainer;
-import org.opendaylight.lispflowmapping.lisp.util.LispAFIConvertor;
 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;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -28,6 +36,7 @@ import org.slf4j.LoggerFactory;
  * addresses.
  *
  * @author Florin Coras
+ * @author Lorand Jakab
  *
  */
 public class SimpleMapCache implements IMapCache {
@@ -38,117 +47,297 @@ public class SimpleMapCache implements IMapCache {
         this.dao = dao;
     }
 
-    public void addMapping(LispAddressContainer key, Object value, boolean shouldOverwrite) {
-        LispAddressContainer eid = MaskUtil.normalize(key);
-        dao.put(eid, new MappingEntry<>(SubKeys.REGDATE, new Date(System.currentTimeMillis())));
-        dao.put(eid, new MappingEntry<>(SubKeys.RECORD, value));
+    private ILispDAO getVniTable(Eid eid) {
+        long vni = 0;
+        if (eid.getVirtualNetworkId() == null) {
+            vni = 0;
+        } else {
+            vni = eid.getVirtualNetworkId().getValue();
+        }
+        return (ILispDAO) dao.getSpecific(vni, SubKeys.VNI);
+    }
+
+    private ILispDAO getOrInstantiateVniTable(Eid eid) {
+        long vni = 0;
+        if (eid.getVirtualNetworkId() == null) {
+            vni = 0;
+        } else {
+            vni = eid.getVirtualNetworkId().getValue();
+        }
+        ILispDAO table = (ILispDAO) dao.getSpecific(vni, SubKeys.VNI);
+        if (table == null) {
+            table = dao.putNestedTable(vni, SubKeys.VNI);
+        }
+        return table;
+    }
+
+    private ILispDAO getXtrIdTable(Eid eid, ILispDAO dao) {
+        return (ILispDAO) dao.getSpecific(eid, SubKeys.XTRID_RECORDS);
+    }
+
+    private ILispDAO getOrInstantiateXtrIdTable(Eid eid, ILispDAO dao) {
+        ILispDAO table = (ILispDAO) dao.getSpecific(eid, SubKeys.XTRID_RECORDS);
+        if (table == null) {
+            table = dao.putNestedTable(eid, SubKeys.XTRID_RECORDS);
+        }
+        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);
+
+        ILispDAO xtrIdDao = null;
+        if (!shouldOverwrite) {
+            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));
+        }
     }
 
     // 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  Map<String, ?> getDaoEntryBest(LispAddressContainer key, ILispDAO dao) {
-        if (MaskUtil.isMaskable(key)) {
-            LispAddressContainer lookupKey;
-            short mask = MaskUtil.getMaskForAddress(key);
+    private  Map<String, Object> getDaoEntryBest(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, Object> entry = dao.get(key);
                 if (entry != null) {
                     return entry;
                 }
             }
             return null;
         } else {
+            key = MaskUtil.normalize(eid);
             return dao.get(key);
         }
     }
 
     // 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<LispAddressContainer, Map<String, ?>> getDaoPairEntryBest(LispAddressContainer key, ILispDAO dao) {
-        if (MaskUtil.isMaskable(key)) {
-            LispAddressContainer lookupKey;
-            short mask = MaskUtil.getMaskForAddress(key);
+    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<LispAddressContainer, 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<LispAddressContainer, Map<String, ?>>(key, entry);
+                return new SimpleImmutableEntry<Eid, Map<String, ?>>(key, entry);
             } 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;
+    }
+/*
+    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(LispAddressContainer eid, ILispDAO dao) {
-        Map<String, ?> daoEntry = getDaoEntryBest(eid, dao);
+    private Object getMappingLpmEid(Eid eid, byte[] xtrId, ILispDAO dao) {
+        SimpleImmutableEntry<Eid, Map<String, ?>> daoEntry = getDaoPairEntryBest(eid, dao);
         if (daoEntry != null) {
-            return daoEntry.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;
         }
     }
 
-    // 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<LispAddressContainer, Object> getMappingPairLpmEid(LispAddressContainer eid, ILispDAO dao) {
-        SimpleImmutableEntry<LispAddressContainer, Map<String, ?>> daoEntry = getDaoPairEntryBest(eid, dao);
-        if (daoEntry != null) {
-            return new SimpleImmutableEntry<LispAddressContainer, Object>(daoEntry.getKey(), daoEntry.getValue().get(
-                    SubKeys.RECORD));
-        } else {
+    public Object getMapping(Eid srcEid, Eid dstEid, byte[] xtrId) {
+        if (dstEid == null) {
             return null;
         }
-    }
 
-    public Object getMapping(LispAddressContainer srcEid, LispAddressContainer dstEid) {
-        if (dstEid == null) {
+        ILispDAO table = getVniTable(dstEid);
+        if (table == null) {
             return null;
         }
-        return getMappingLpmEid(dstEid, dao);
+        return getMappingLpmEid(dstEid, xtrId, table);
     }
 
-    public void removeMapping(LispAddressContainer eid, boolean overwrite) {
-        eid = MaskUtil.normalize(eid);
-        dao.removeSpecific(eid, SubKeys.RECORD);
+    public Object getMapping(Eid srcEid, Eid dstEid) {
+        return getMapping(srcEid, dstEid, null);
     }
 
-    public void addAuthenticationKey(LispAddressContainer eid, String key) {
-        eid = MaskUtil.normalize(eid);
-        dao.put(eid, new MappingEntry<String>(SubKeys.AUTH_KEY, key));
+    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;
     }
 
-    private String getAuthKeyLpm(LispAddressContainer prefix, ILispDAO db) {
-        short maskLength = MaskUtil.getMaskForAddress(prefix);
+    public void removeMapping(Eid eid, boolean overwrite) {
+        Eid key = MaskUtil.normalize(eid);
+        ILispDAO table = getVniTable(key);
+        if (table == null) {
+            return;
+        }
+
+        table.removeSpecific(key, SubKeys.RECORD);
+
+        if (!overwrite) {
+            ILispDAO xtrIdTable = getXtrIdTable(key, table);
+            if (xtrIdTable != null) {
+                xtrIdTable.removeSpecific(key, SubKeys.RECORD);
+            }
+        }
+    }
+
+    public void addAuthenticationKey(Eid eid, MappingAuthkey authKey) {
+        Eid key = MaskUtil.normalize(eid);
+        ILispDAO table = getOrInstantiateVniTable(key);
+        table.put(key, new MappingEntry<>(SubKeys.AUTH_KEY, authKey));
+    }
+
+    private MappingAuthkey getAuthKeyLpm(Eid prefix, ILispDAO db) {
+        short maskLength = MaskUtil.getMaskForAddress(prefix.getAddress());
         while (maskLength >= 0) {
-            LispAddressContainer key = MaskUtil.normalize(prefix, maskLength);
+            Eid key = MaskUtil.normalize(prefix, maskLength);
             Object password = db.getSpecific(key, SubKeys.AUTH_KEY);
-            if (password != null && password instanceof String) {
-                return (String) password;
+            if (password != null && password instanceof MappingAuthkey) {
+                return (MappingAuthkey) password;
             }
             maskLength -= 1;
         }
         return null;
     }
 
-    public String getAuthenticationKey(LispAddressContainer eid) {
-        if (MaskUtil.isMaskable(LispAFIConvertor.toAFI(eid))) {
-            return getAuthKeyLpm(eid, dao);
+    public MappingAuthkey getAuthenticationKey(Eid eid) {
+        ILispDAO table = getVniTable(eid);
+        if (table == null) {
+            return null;
+        }
+        if (MaskUtil.isMaskable(eid.getAddress())) {
+            return getAuthKeyLpm(eid, table);
         } else {
-            Object password = dao.getSpecific(eid, SubKeys.AUTH_KEY);
-            if (password != null && password instanceof String) {
-                return (String) password;
+            Eid key = MaskUtil.normalize(eid);
+            Object password = table.getSpecific(key, SubKeys.AUTH_KEY);
+            if (password != null && password instanceof MappingAuthkey) {
+                return (MappingAuthkey) password;
             } else {
                 LOG.warn("Failed to find password!");
                 return null;
@@ -156,15 +345,20 @@ public class SimpleMapCache implements IMapCache {
         }
     }
 
-    public void removeAuthenticationKey(LispAddressContainer eid) {
-        eid = MaskUtil.normalize(eid);
-        dao.removeSpecific(eid, SubKeys.AUTH_KEY);
+    public void removeAuthenticationKey(Eid eid) {
+        Eid key = MaskUtil.normalize(eid);
+        ILispDAO table = getVniTable(key);
+        if (table == null) {
+            return;
+        }
+        table.removeSpecific(key, SubKeys.AUTH_KEY);
     }
 
     public String printMappings() {
         final StringBuffer sb = new StringBuffer();
         sb.append("Keys\tValues\n");
-        dao.getAll(new IRowVisitor() {
+
+        final IRowVisitor innerVisitor = (new IRowVisitor() {
             String lastKey = "";
 
             public void visitRow(Object keyId, String valueKey, Object value) {
@@ -176,31 +370,65 @@ public class SimpleMapCache implements IMapCache {
                 lastKey = key;
             }
         });
+
+        dao.getAll(new IRowVisitor() {
+            String lastKey = "";
+
+            public void visitRow(Object keyId, String valueKey, Object value) {
+                String key = keyId.getClass().getSimpleName() + "#" + keyId;
+                if (!lastKey.equals(key)) {
+                    sb.append("\n" + key + "\t");
+                }
+                if (valueKey.equals(SubKeys.VNI)) {
+                    sb.append(valueKey + "= { ");
+                    ((ILispDAO)value).getAll(innerVisitor);
+                    sb.append("}\t");
+                } else {
+                    sb.append(valueKey + "=" + value + "\t");
+                }
+                lastKey = key;
+            }
+        });
         sb.append("\n");
         return sb.toString();
     }
 
     @Override
-    public void updateMappingRegistration(LispAddressContainer key) {
-        Map<String, ?> daoEntry = getDaoEntryBest(key, dao);
+    public void updateMappingRegistration(Eid eid) {
+        ILispDAO table = getVniTable(eid);
+        if (table == null) {
+            return;
+        }
+        Map<String, Object> daoEntry = getDaoEntryBest(eid, table);
         if (daoEntry != null) {
-            dao.put(key, new MappingEntry<>(SubKeys.REGDATE, new Date(System.currentTimeMillis())));
+            daoEntry.put(SubKeys.REGDATE, new Date(System.currentTimeMillis()));
         }
     }
 
     @Override
-    public void addData(LispAddressContainer key, String subKey, Object data) {
-        LispAddressContainer normKey = MaskUtil.normalize(key);
-        dao.put(normKey, new MappingEntry<>(subKey, data));
+    public void addData(Eid eid, String subKey, Object data) {
+        Eid key = MaskUtil.normalize(eid);
+        ILispDAO table = getOrInstantiateVniTable(key);
+        table.put(key, new MappingEntry<>(subKey, data));
     }
 
     @Override
-    public Object getData(LispAddressContainer key, String subKey) {
-        return dao.getSpecific(key, subKey);
+    public Object getData(Eid eid, String subKey) {
+        ILispDAO table = getOrInstantiateVniTable(eid);
+        if (table == null) {
+            return null;
+        }
+        Eid key = MaskUtil.normalize(eid);
+        return table.getSpecific(key, subKey);
     }
 
     @Override
-    public void removeData(LispAddressContainer key, String subKey) {
-        dao.removeSpecific(key, subKey);
+    public void removeData(Eid eid, String subKey) {
+        ILispDAO table = getOrInstantiateVniTable(eid);
+        if (table == null) {
+            return;
+        }
+        Eid key = MaskUtil.normalize(eid);
+        table.removeSpecific(key, subKey);
     }
 }