69aec2927a7ec8f0bc24e8de64e7a8dc02fd0a98
[lispflowmapping.git] / mappingservice / mapcache / src / main / java / org / opendaylight / lispflowmapping / mapcache / SimpleMapCache.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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.lispflowmapping.mapcache;
10
11 import java.util.AbstractMap.SimpleImmutableEntry;
12 import java.util.ArrayList;
13 import java.util.Date;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18
19 import org.opendaylight.lispflowmapping.interfaces.dao.ILispDAO;
20 import org.opendaylight.lispflowmapping.interfaces.dao.IRowVisitor;
21 import org.opendaylight.lispflowmapping.interfaces.dao.MappingEntry;
22 import org.opendaylight.lispflowmapping.interfaces.dao.SubKeys;
23 import org.opendaylight.lispflowmapping.interfaces.mapcache.IMapCache;
24 import org.opendaylight.lispflowmapping.lisp.util.MaskUtil;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.SourceDestKey;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.inet.binary.types.rev160303.IpAddressBinary;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.XtrId;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecord;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.mapping.authkey.container.MappingAuthkey;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * Simple map-cache that works with 'simple' addresses (see lisp-proto.yang). It can do longest prefix matching for IP
36  * addresses.
37  *
38  * @author Florin Coras
39  * @author Lorand Jakab
40  *
41  */
42 public class SimpleMapCache implements IMapCache {
43     private static final Logger LOG = LoggerFactory.getLogger(SimpleMapCache.class);
44     private ILispDAO dao;
45
46     public SimpleMapCache(ILispDAO dao) {
47         this.dao = dao;
48     }
49
50     private ILispDAO getVniTable(Eid eid) {
51         long vni = 0;
52         if (eid.getVirtualNetworkId() == null) {
53             vni = 0;
54         } else {
55             vni = eid.getVirtualNetworkId().getValue();
56         }
57         return (ILispDAO) dao.getSpecific(vni, SubKeys.VNI);
58     }
59
60     private ILispDAO getOrInstantiateVniTable(Eid eid) {
61         long vni = 0;
62         if (eid.getVirtualNetworkId() == null) {
63             vni = 0;
64         } else {
65             vni = eid.getVirtualNetworkId().getValue();
66         }
67         ILispDAO table = (ILispDAO) dao.getSpecific(vni, SubKeys.VNI);
68         if (table == null) {
69             table = dao.putNestedTable(vni, SubKeys.VNI);
70         }
71         return table;
72     }
73
74     private ILispDAO getXtrIdTable(Eid eid, ILispDAO dao) {
75         return (ILispDAO) dao.getSpecific(eid, SubKeys.XTRID_RECORDS);
76     }
77
78     private ILispDAO getOrInstantiateXtrIdTable(Eid eid, ILispDAO dao) {
79         ILispDAO table = (ILispDAO) dao.getSpecific(eid, SubKeys.XTRID_RECORDS);
80         if (table == null) {
81             table = dao.putNestedTable(eid, SubKeys.XTRID_RECORDS);
82         }
83         return table;
84     }
85
86     private void removeExpiredXtrIdTableEntries(ILispDAO xtrIdDao, List<XtrId> expiredMappings) {
87         for (XtrId xtrId : expiredMappings) {
88             xtrIdDao.removeSpecific(xtrId, SubKeys.RECORD);
89         }
90     }
91
92     public void addMapping(Eid key, Object value, boolean shouldOverwrite, boolean shouldMerge) {
93         if (value == null) {
94             LOG.warn("addMapping() called with null 'value', ignoring");
95             return;
96         }
97
98         if (!(value instanceof MappingRecord)) {
99             LOG.warn("addMapping() called with a 'value' that is not a 'MappingRecord', ignoring");
100             return;
101         }
102
103         MappingRecord record = (MappingRecord) value;
104         if (record.getXtrId() == null && !shouldOverwrite && shouldMerge) {
105             LOG.warn("addMapping() called will null xTR-ID in MappingRecord, while merge is set, ignoring");
106             return;
107         }
108
109         Date regdate = new Date(record.getTimestamp());     // The serializer always sets it
110         Eid eid = MaskUtil.normalize(key);
111         ILispDAO table = getOrInstantiateVniTable(key);
112
113         ILispDAO xtrIdDao = null;
114         if (!shouldOverwrite) {
115             xtrIdDao = getOrInstantiateXtrIdTable(eid, table);
116             xtrIdDao.put(record.getXtrId(), new MappingEntry<>(SubKeys.RECORD, value));
117         }
118
119         if (shouldMerge) {
120             List<XtrId> expiredMappings = new ArrayList<XtrId>();
121             Set<IpAddressBinary> sourceRlocs = new HashSet<IpAddressBinary>();
122             MappingRecord mergedEntry = MappingMergeUtil.mergeXtrIdMappings(getXtrIdMappingList(xtrIdDao),
123                     expiredMappings, sourceRlocs);
124             removeExpiredXtrIdTableEntries(xtrIdDao, expiredMappings);
125             if (mergedEntry == null) {
126                 return;
127             }
128             regdate = new Date(mergedEntry.getTimestamp());
129             table.put(eid, new MappingEntry<>(SubKeys.REGDATE, regdate));
130             table.put(eid, new MappingEntry<>(SubKeys.RECORD, mergedEntry));
131             table.put(eid, new MappingEntry<>(SubKeys.SRC_RLOCS, sourceRlocs));
132         } else {
133             table.put(eid, new MappingEntry<>(SubKeys.REGDATE, regdate));
134             table.put(eid, new MappingEntry<>(SubKeys.RECORD, value));
135         }
136     }
137
138     // Returns the list of mappings stored in an xTR-ID DAO
139     private List<Object> getXtrIdMappingList(ILispDAO dao) {
140         if (dao != null) {
141             final List<Object> records = new ArrayList<Object>();
142             dao.getAll(new IRowVisitor() {
143                 public void visitRow(Object keyId, String valueKey, Object value) {
144                     if (valueKey.equals(SubKeys.RECORD)) {
145                         records.add(value);
146                     }
147                 }
148             });
149             return records;
150         }
151         return null;
152     }
153 /*
154     private SimpleImmutableEntry<byte[], Long> getXtrIdMinTimeStamp(ILispDAO dao) {
155         if (dao != null) {
156             // We don't actually need a list here, but the external variable we modify inside the IRowVisitor
157             // implementation has to be final. So we use a List, and it's methods, to modify contents
158             final List<SimpleImmutableEntry<byte[], Long>> entries =
159                     new ArrayList<SimpleImmutableEntry<byte[], Long>>();
160             entries.set(0, new SimpleImmutableEntry<byte[], Long>(null, System.currentTimeMillis()));
161
162             dao.getAll(new IRowVisitor() {
163                 public void visitRow(Object keyId, String valueKey, Object value) {
164                     if (valueKey.equals(SubKeys.RECORD)) {
165                         byte[] currentXtrId = ((MappingRecord) value).getXtrId();
166                         long currentTimestamp = ((MappingRecord) value).getTimestamp();
167                         // If one of the timestamps is expired, we signal it to the caller by setting the returned
168                         // timestamp to null. The caller will then have to remove that xTR-ID, and do a full merge
169                         // (or decrement)
170                         if (MappingMergeUtil.timestampIsExpired(currentTimestamp)) {
171                             SimpleImmutableEntry<byte[], Long> entry =
172                                     new SimpleImmutableEntry<byte[], Long>(currentXtrId, null);
173                             entries.set(0, entry);
174                             return;
175                         }
176                         if (entries.get(0).getValue() > currentTimestamp) {
177                             SimpleImmutableEntry<byte[], Long> entry =
178                                     new SimpleImmutableEntry<byte[], Long>(currentXtrId, currentTimestamp);
179                             entries.set(0, entry);
180                         }
181                     }
182                 }
183             });
184             return entries.get(0);
185         }
186         return null;
187     }
188 */
189     // Returns the mapping corresponding to the longest prefix match for eid. eid must be a simple (maskable or not)
190     // address
191     private Object getMappingLpmEid(Eid eid, byte[] xtrId, ILispDAO dao) {
192         SimpleImmutableEntry<Eid, Map<String, ?>> daoEntry = dao.getBestPair(MaskUtil.normalize(eid));
193         if (daoEntry != null) {
194             if (xtrId != null) {
195                 ILispDAO xtrIdTable = getXtrIdTable(eid, (ILispDAO) daoEntry.getValue().get(SubKeys.XTRID_RECORDS));
196                 if (xtrIdTable != null) {
197                     MappingRecord xtrIdRecord = (MappingRecord) xtrIdTable.getSpecific(xtrId, SubKeys.RECORD);
198                     if (xtrIdRecord.getTimestamp() != null &&
199                             MappingMergeUtil.timestampIsExpired(xtrIdRecord.getTimestamp())) {
200                         xtrIdTable.removeSpecific(xtrId, SubKeys.RECORD);
201                         return null;
202                     } else {
203                         return xtrIdRecord;
204                     }
205                 } else {
206                     return null;
207                 }
208             } else {
209                 Date timestamp = (Date) daoEntry.getValue().get(SubKeys.REGDATE);
210                 if (timestamp != null && MappingMergeUtil.timestampIsExpired(timestamp)) {
211                     dao.removeSpecific(daoEntry.getKey(), SubKeys.REGDATE);
212                     dao.removeSpecific(daoEntry.getKey(), SubKeys.RECORD);
213                 }
214                 return daoEntry.getValue().get(SubKeys.RECORD);
215             }
216         } else {
217             return null;
218         }
219     }
220
221     public Object getMapping(Eid srcEid, Eid dstEid, byte[] xtrId) {
222         if (dstEid == null) {
223             return null;
224         }
225
226         ILispDAO table = getVniTable(dstEid);
227         if (table == null) {
228             return null;
229         }
230         return getMappingLpmEid(dstEid, xtrId, table);
231     }
232
233     public Object getMapping(Eid srcEid, Eid dstEid) {
234         return getMapping(srcEid, dstEid, null);
235     }
236
237     public List<Object> getAllXtrIdMappings(Eid eid) {
238         Map<String, ?> daoEntry = dao.getBest(MaskUtil.normalize(eid));
239         if (daoEntry != null) {
240             ILispDAO xtrIdTable = getXtrIdTable(eid, (ILispDAO) daoEntry.get(SubKeys.XTRID_RECORDS));
241             if (xtrIdTable != null) {
242                 return getXtrIdMappingList(xtrIdTable);
243             }
244         }
245         return null;
246     }
247
248     public Eid getWidestNegativeMapping(Eid key) {
249         return dao.getWidestNegativePrefix(MaskUtil.normalize(key));
250     }
251
252     public void removeMapping(Eid eid, boolean overwrite) {
253         Eid key = MaskUtil.normalize(eid);
254         ILispDAO table = getVniTable(key);
255         if (table == null) {
256             return;
257         }
258
259         table.removeSpecific(key, SubKeys.RECORD);
260
261         if (!overwrite) {
262             ILispDAO xtrIdTable = getXtrIdTable(key, table);
263             if (xtrIdTable != null) {
264                 xtrIdTable.removeSpecific(key, SubKeys.RECORD);
265             }
266         }
267     }
268
269     public void addAuthenticationKey(Eid eid, MappingAuthkey authKey) {
270         Eid key = MaskUtil.normalize(eid);
271         ILispDAO table = getOrInstantiateVniTable(key);
272         table.put(key, new MappingEntry<>(SubKeys.AUTH_KEY, authKey));
273     }
274
275     private MappingAuthkey getAuthKeyLpm(Eid prefix, ILispDAO db) {
276         short maskLength = MaskUtil.getMaskForAddress(prefix.getAddress());
277         while (maskLength >= 0) {
278             Eid key = MaskUtil.normalize(prefix, maskLength);
279             Object password = db.getSpecific(key, SubKeys.AUTH_KEY);
280             if (password != null && password instanceof MappingAuthkey) {
281                 return (MappingAuthkey) password;
282             }
283             maskLength -= 1;
284         }
285         return null;
286     }
287
288     public MappingAuthkey getAuthenticationKey(Eid eid) {
289         ILispDAO table = getVniTable(eid);
290         if (table == null) {
291             return null;
292         }
293         if (MaskUtil.isMaskable(eid.getAddress()) && !(eid.getAddress() instanceof SourceDestKey)) {
294             return getAuthKeyLpm(eid, table);
295         } else {
296             Eid key = MaskUtil.normalize(eid);
297             Object password = table.getSpecific(key, SubKeys.AUTH_KEY);
298             if (password != null && password instanceof MappingAuthkey) {
299                 return (MappingAuthkey) password;
300             } else {
301                 LOG.warn("Failed to find password!");
302                 return null;
303             }
304         }
305     }
306
307     public void removeAuthenticationKey(Eid eid) {
308         Eid key = MaskUtil.normalize(eid);
309         ILispDAO table = getVniTable(key);
310         if (table == null) {
311             return;
312         }
313         table.removeSpecific(key, SubKeys.AUTH_KEY);
314     }
315
316     public String printMappings() {
317         final StringBuffer sb = new StringBuffer();
318         sb.append("Keys\tValues\n");
319
320         final IRowVisitor innerVisitor = (new IRowVisitor() {
321             String lastKey = "";
322
323             public void visitRow(Object keyId, String valueKey, Object value) {
324                 String key = keyId.getClass().getSimpleName() + "#" + keyId;
325                 if (!lastKey.equals(key)) {
326                     sb.append("\n" + key + "\t");
327                 }
328                 sb.append(valueKey + "=" + value + "\t");
329                 lastKey = key;
330             }
331         });
332
333         dao.getAll(new IRowVisitor() {
334             String lastKey = "";
335
336             public void visitRow(Object keyId, String valueKey, Object value) {
337                 String key = keyId.getClass().getSimpleName() + "#" + keyId;
338                 if (!lastKey.equals(key)) {
339                     sb.append("\n" + key + "\t");
340                 }
341                 if (valueKey.equals(SubKeys.VNI)) {
342                     sb.append(valueKey + "= { ");
343                     ((ILispDAO)value).getAll(innerVisitor);
344                     sb.append("}\t");
345                 } else {
346                     sb.append(valueKey + "=" + value + "\t");
347                 }
348                 lastKey = key;
349             }
350         });
351         sb.append("\n");
352         return sb.toString();
353     }
354
355     @Override
356     public void updateMappingRegistration(Eid eid, Long timestamp) {
357         ILispDAO table = getVniTable(eid);
358         if (table == null) {
359             return;
360         }
361         if (timestamp == null) {
362             timestamp = System.currentTimeMillis();
363         }
364         Map<String, Object> daoEntry = table.getBest(MaskUtil.normalize(eid));
365         if (daoEntry != null) {
366             daoEntry.put(SubKeys.REGDATE, new Date(timestamp));
367         }
368     }
369
370     @Override
371     public void addData(Eid eid, String subKey, Object data) {
372         Eid key = MaskUtil.normalize(eid);
373         ILispDAO table = getOrInstantiateVniTable(key);
374         table.put(key, new MappingEntry<>(subKey, data));
375     }
376
377     @Override
378     public Object getData(Eid eid, String subKey) {
379         ILispDAO table = getOrInstantiateVniTable(eid);
380         if (table == null) {
381             return null;
382         }
383         Eid key = MaskUtil.normalize(eid);
384         return table.getSpecific(key, subKey);
385     }
386
387     @Override
388     public void removeData(Eid eid, String subKey) {
389         ILispDAO table = getOrInstantiateVniTable(eid);
390         if (table == null) {
391             return;
392         }
393         Eid key = MaskUtil.normalize(eid);
394         table.removeSpecific(key, subKey);
395     }
396 }