Merge branch 'master' into topic/ietf_yang
[lispflowmapping.git] / mappingservice / implementation / src / main / java / org / opendaylight / lispflowmapping / implementation / mapcache / MultiTableMapCache.java
1 /*
2  * Copyright (c) 2014 Contextream, 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.implementation.mapcache;
10
11 import java.util.Date;
12 import java.util.Map;
13
14 import org.opendaylight.lispflowmapping.interfaces.dao.ILispDAO;
15 import org.opendaylight.lispflowmapping.interfaces.dao.IRowVisitor;
16 import org.opendaylight.lispflowmapping.interfaces.dao.MappingEntry;
17 import org.opendaylight.lispflowmapping.interfaces.dao.SubKeys;
18 import org.opendaylight.lispflowmapping.interfaces.mapcache.IMapCache;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.lispaddress.LispAddressContainer;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.lispaddress.lispaddresscontainer.address.LcafSourceDest;
21 import org.opendaylight.lispflowmapping.lisp.util.LcafSourceDestHelper;
22 import org.opendaylight.lispflowmapping.lisp.util.LispAFIConvertor;
23 import org.opendaylight.lispflowmapping.lisp.util.MaskUtil;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28  * Multi table map-cache that works with 'simple' and SourceDest LCAF addresses (see lisp-proto.yang). It can do longest
29  * prefix matching for IP and SourceDest LCAF addresses. In case of the latter, it uses two tables, one for dst and
30  * another for source, queried and populated in this exact order.
31  *
32  * @author Florin Coras
33  *
34  */
35 public class MultiTableMapCache implements IMapCache {
36     private static final Logger LOG = LoggerFactory.getLogger(MultiTableMapCache.class);
37     private ILispDAO dao;
38
39     public MultiTableMapCache(ILispDAO dao) {
40         this.dao = dao;
41     }
42
43     public void addMapping(LispAddressContainer key, Object value, boolean shouldOverwrite) {
44         LispAddressContainer eid = MaskUtil.normalize(key);
45         if (eid.getAddress() instanceof LcafSourceDest) {
46             LispAddressContainer srcKey = LcafSourceDestHelper.getSrc(eid);
47             ILispDAO srcDstDao = getOrInstantiateSDInnerDao(eid, dao);
48             srcDstDao.put(srcKey, new MappingEntry<>(SubKeys.REGDATE, new Date(System.currentTimeMillis())));
49             srcDstDao.put(srcKey, new MappingEntry<>(SubKeys.RECORD, value));
50         } else {
51             dao.put(eid, new MappingEntry<>(SubKeys.REGDATE, new Date(System.currentTimeMillis())));
52             dao.put(eid, new MappingEntry<>(SubKeys.RECORD, value));
53         }
54     }
55
56     // Method returns the DAO entry (hash) corresponding to either the longest prefix match of eid, if eid is maskable,
57     // or the exact match otherwise. eid must be a 'simple' address
58     private Map<String, ?> getDaoEntryBest(LispAddressContainer eid, ILispDAO dao) {
59         if (MaskUtil.isMaskable(eid)) {
60             LispAddressContainer key;
61             short mask = MaskUtil.getMaskForAddress(eid);
62             while (mask > 0) {
63                 key = MaskUtil.normalize(eid, mask);
64                 mask--;
65                 Map<String, ?> entry = dao.get(key);
66                 if (entry != null) {
67                     return entry;
68                 }
69             }
70             return null;
71         } else {
72             Map<String, ?> entry = dao.get(eid);
73             if (entry != null) {
74                 return dao.get(eid);
75             } else {
76                 return null;
77             }
78         }
79     }
80
81     private Object getMappingExactSD(LispAddressContainer srcEid, LispAddressContainer dstEid,
82             ILispDAO dao) {
83         Map<String, ?> daoEntry = dao.get(dstEid);
84         if (daoEntry != null) {
85             // try SrcDst eid lookup
86             ILispDAO srcDstDao = (ILispDAO) daoEntry.get(SubKeys.LCAF_SRCDST);
87             if (srcEid != null && srcDstDao != null) {
88                 return srcDstDao.getSpecific(srcEid, SubKeys.RECORD);
89             }
90             // if lookup fails, return whatever is found for dst eid
91             return daoEntry.get(SubKeys.RECORD);
92         }
93         return null;
94     }
95
96     // Returns the mapping corresponding to the longest prefix match for eid. eid must be a simple (maskable or not) address
97     private Object getMappingLpmEid(LispAddressContainer eid, ILispDAO dao) {
98         if (eid == null) {
99             return null;
100         }
101         Map<String, ?> daoEntry = getDaoEntryBest(eid, dao);
102         if (daoEntry != null) {
103             return daoEntry.get(SubKeys.RECORD);
104         } else {
105             return null;
106         }
107     }
108
109     // Returns a mapping corresponding to either the longest prefix match for both dstEid and srcEid,
110     // if a SourceDest mapping exists, or to dstEid
111     private Object getMappingLpmSD(LispAddressContainer srcEid, LispAddressContainer dstEid, ILispDAO dao) {
112         Map<String, ?> daoEntry = getDaoEntryBest(dstEid, dao);
113         if (daoEntry != null) {
114             // try SrcDst eid lookup
115             ILispDAO srcDstDao = (ILispDAO) daoEntry.get(SubKeys.LCAF_SRCDST);
116             if (srcDstDao != null) {
117                 Object mapping = getMappingLpmEid(srcEid, srcDstDao);
118                 if (mapping!= null) {
119                     return mapping;
120                 }
121             }
122
123             // if lookup fails, return whatever is found for dst eid
124             return daoEntry.get(SubKeys.RECORD);
125         }
126         return null;
127     }
128
129     public Object getMapping(LispAddressContainer srcEid, LispAddressContainer dstEid) {
130         if (dstEid == null) {
131             return null;
132         }
133
134         // a map-request for an actual SrcDst LCAF, ignore src eid
135         if (dstEid.getAddress() instanceof LcafSourceDest) {
136             LispAddressContainer srcAddr = LcafSourceDestHelper.getSrc(dstEid);
137             LispAddressContainer dstAddr = LcafSourceDestHelper.getDst(dstEid);
138             return getMappingLpmSD(srcAddr, dstAddr, dao);
139         }
140
141         // potential map-request for SrcDst LCAF from non SrcDst capable devices
142         return getMappingLpmSD(srcEid, dstEid, dao);
143     }
144
145     public void removeMapping(LispAddressContainer eid, boolean overwrite) {
146         eid = MaskUtil.normalize(eid);
147         if (eid.getAddress() instanceof LcafSourceDest) {
148             ILispDAO db = getSDInnerDao(eid, dao);
149             if (db != null) {
150                 db.removeSpecific(LcafSourceDestHelper.getSrc(eid),
151                         SubKeys.RECORD);
152             }
153         } else {
154             dao.removeSpecific(eid, SubKeys.RECORD);
155         }
156     }
157
158     public void addAuthenticationKey(LispAddressContainer eid, String key) {
159         eid = MaskUtil.normalize(eid);
160         if (eid.getAddress() instanceof LcafSourceDest) {
161             ILispDAO srcDstDao = getOrInstantiateSDInnerDao(eid, dao);
162             srcDstDao.put(LcafSourceDestHelper.getSrc(eid), new MappingEntry<String>(SubKeys.AUTH_KEY, key));
163         } else {
164             dao.put(eid, new MappingEntry<String>(SubKeys.AUTH_KEY, key));
165         }
166     }
167
168     private String getAuthKeyLpm(LispAddressContainer prefix, ILispDAO db) {
169         short maskLength = MaskUtil.getMaskForAddress(prefix);
170         while (maskLength >= 0) {
171             LispAddressContainer key = MaskUtil.normalize(prefix, maskLength);
172             Object password = db.getSpecific(key, SubKeys.AUTH_KEY);
173             if (password != null && password instanceof String) {
174                 return (String) password;
175             }
176             maskLength -= 1;
177         }
178         return null;
179     }
180
181     public String getAuthenticationKey(LispAddressContainer eid) {
182         if (MaskUtil.isMaskable(LispAFIConvertor.toAFI(eid))) {
183             return getAuthKeyLpm(eid, dao);
184         } else if (eid.getAddress() instanceof LcafSourceDest) {
185             // NOTE: this is an exact match, not a longest prefix match
186             ILispDAO srcDstDao = getSDInnerDao(eid, dao);
187             if (srcDstDao != null) {
188                 return getAuthKeyLpm(LcafSourceDestHelper.getSrc(eid), srcDstDao);
189             }
190             return null;
191         } else {
192             Object password = dao.getSpecific(eid, SubKeys.AUTH_KEY);
193             if (password != null && password instanceof String) {
194                 return (String) password;
195             } else {
196                 LOG.warn("Failed to find password!");
197                 return null;
198             }
199         }
200     }
201
202     public void removeAuthenticationKey(LispAddressContainer eid) {
203         eid = MaskUtil.normalize(eid);
204         if (eid.getAddress() instanceof LcafSourceDest) {
205             ILispDAO srcDstDao = getSDInnerDao(eid, dao);
206             if (srcDstDao != null) {
207                 srcDstDao.removeSpecific(eid, SubKeys.AUTH_KEY);
208             }
209         } else {
210             dao.removeSpecific(eid, SubKeys.AUTH_KEY);
211         }
212     }
213
214     // SrcDst LCAFs are stored in a 2-tier DAO with dst having priority over src.
215     // This method returns the DAO associated to a dst or creates it if it doesn't exist.
216     private ILispDAO getOrInstantiateSDInnerDao(LispAddressContainer address, ILispDAO dao) {
217         LispAddressContainer dstKey = LcafSourceDestHelper.getDst(address);
218         ILispDAO srcDstDao = (ILispDAO) dao.getSpecific(dstKey, SubKeys.LCAF_SRCDST);
219         if (srcDstDao == null) {
220             // inserts nested table for source
221             srcDstDao = dao.putNestedTable(dstKey, SubKeys.LCAF_SRCDST);
222         }
223         return srcDstDao;
224     }
225
226     // SrcDst LCAFs are stored in a 2-tier DAO with dst having priority over src.
227     // This method returns the DAO associated to dst or null if it doesn't exist.
228     private ILispDAO getSDInnerDao(LispAddressContainer address, ILispDAO dao) {
229         return (ILispDAO) dao.getSpecific(LcafSourceDestHelper.getDst(address), SubKeys.LCAF_SRCDST);
230     }
231
232     public String printMappings() {
233         final StringBuffer sb = new StringBuffer();
234         sb.append("Keys\tValues\n");
235         final IRowVisitor innerVisitor = (new IRowVisitor() {
236             String lastKey = "";
237
238             public void visitRow(Object keyId, String valueKey, Object value) {
239                 String key = keyId.getClass().getSimpleName() + "#" + keyId;
240                 if (!lastKey.equals(key)) {
241                     sb.append(key + "\t");
242                 }
243                 if (!(valueKey.equals(SubKeys.LCAF_SRCDST))) {
244                     sb.append(valueKey + "=" + value + "\t");
245                 }
246                 lastKey = key;
247             }
248         });
249         dao.getAll(new IRowVisitor() {
250             String lastKey = "";
251
252             public void visitRow(Object keyId, String valueKey, Object value) {
253                 String key = keyId.getClass().getSimpleName() + "#" + keyId;
254                 if (!lastKey.equals(key)) {
255                     sb.append("\n" + key + "\t");
256                 }
257                 if (valueKey.equals(SubKeys.LCAF_SRCDST)) {
258                     sb.append(valueKey + "= { ");
259                     ((ILispDAO)value).getAll(innerVisitor);
260                     sb.append("}\t");
261                 } else {
262                     sb.append(valueKey + "=" + value + "\t");
263                 }
264                 lastKey = key;
265             }
266         });
267         sb.append("\n");
268         return sb.toString();
269     }
270
271     @Override
272     public void updateMappingRegistration(LispAddressContainer key) {
273
274     }
275
276     @Override
277     public void addData(LispAddressContainer key, String subKey, Object data) {
278         key = MaskUtil.normalize(key);
279         if (key.getAddress() instanceof LcafSourceDest) {
280             ILispDAO srcDstDao = getOrInstantiateSDInnerDao(key, dao);
281             srcDstDao.put(LcafSourceDestHelper.getSrc(key), new MappingEntry<Object>(subKey, data));
282         } else {
283             dao.put(key, new MappingEntry<Object>(subKey, data));
284         }
285     }
286
287     @Override
288     public Object getData(LispAddressContainer eid, String subKey) {
289         if (eid.getAddress() instanceof LcafSourceDest) {
290             ILispDAO srcDstDao = getSDInnerDao(eid, dao);
291             return srcDstDao.getSpecific(LcafSourceDestHelper.getSrc(eid), subKey);
292         } else {
293             return dao.getSpecific(eid, subKey);
294         }
295     }
296
297     @Override
298     public void removeData(LispAddressContainer key, String subKey) {
299         key = MaskUtil.normalize(key);
300         if (key.getAddress() instanceof LcafSourceDest) {
301             ILispDAO db = getSDInnerDao(key, dao);
302             if (db != null) {
303                 db.removeSpecific(LcafSourceDestHelper.getSrc(key), subKey);
304             }
305         } else {
306             dao.removeSpecific(key, subKey);
307         }
308     }
309 }