Bug 7818: xTR-ID timestamp updated only when merging
[lispflowmapping.git] / mappingservice / implementation / src / main / java / org / opendaylight / lispflowmapping / implementation / MappingSystem.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc.  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;
10
11 import java.util.ArrayList;
12 import java.util.Date;
13 import java.util.EnumMap;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Set;
17
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.lispflowmapping.config.ConfigIni;
20 import org.opendaylight.lispflowmapping.dsbackend.DataStoreBackEnd;
21 import org.opendaylight.lispflowmapping.implementation.timebucket.implementation.TimeBucketMappingTimeoutService;
22 import org.opendaylight.lispflowmapping.implementation.timebucket.interfaces.ISouthBoundMappingTimeoutService;
23 import org.opendaylight.lispflowmapping.implementation.util.DSBEInputUtil;
24 import org.opendaylight.lispflowmapping.implementation.util.MappingMergeUtil;
25 import org.opendaylight.lispflowmapping.interfaces.dao.ILispDAO;
26 import org.opendaylight.lispflowmapping.interfaces.dao.SubKeys;
27 import org.opendaylight.lispflowmapping.interfaces.mapcache.IAuthKeyDb;
28 import org.opendaylight.lispflowmapping.interfaces.mapcache.ILispMapCache;
29 import org.opendaylight.lispflowmapping.interfaces.mapcache.IMapCache;
30 import org.opendaylight.lispflowmapping.interfaces.mapcache.IMappingSystem;
31 import org.opendaylight.lispflowmapping.interfaces.mappingservice.IMappingService;
32 import org.opendaylight.lispflowmapping.lisp.type.MappingData;
33 import org.opendaylight.lispflowmapping.lisp.util.LispAddressStringifier;
34 import org.opendaylight.lispflowmapping.lisp.util.LispAddressUtil;
35 import org.opendaylight.lispflowmapping.mapcache.AuthKeyDb;
36 import org.opendaylight.lispflowmapping.mapcache.MultiTableMapCache;
37 import org.opendaylight.lispflowmapping.mapcache.SimpleMapCache;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.SimpleAddress;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.ExplicitLocatorPath;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.Ipv4;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.Ipv6;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.ServicePath;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.explicit.locator.path.explicit.locator.path.Hop;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.inet.binary.types.rev160303.IpAddressBinary;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.XtrId;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.locatorrecords.LocatorRecord;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.locatorrecords.LocatorRecordBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.authkey.container.MappingAuthkey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecordBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.rloc.container.Rloc;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingOrigin;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.db.instance.AuthenticationKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.db.instance.Mapping;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 /**
59  * The Mapping System coordinates caching of md-sal stored mappings and if so configured enables longest prefix match
60  * mapping lookups.
61  *
62  * @author Florin Coras
63  *
64  */
65 public class MappingSystem implements IMappingSystem {
66     private static final Logger LOG = LoggerFactory.getLogger(MappingSystem.class);
67     private static final String AUTH_KEY_TABLE = "authentication";
68     private boolean notificationService;
69     private boolean mappingMerge;
70     private ILispDAO dao;
71     private ILispDAO sdao;
72     private ILispMapCache smc;
73     private IMapCache pmc;
74     private IAuthKeyDb akdb;
75     private final EnumMap<MappingOrigin, IMapCache> tableMap = new EnumMap<>(MappingOrigin.class);
76     private DataStoreBackEnd dsbe;
77     private boolean isMaster = false;
78
79     private ISouthBoundMappingTimeoutService sbMappingTimeoutService;
80
81     public MappingSystem(ILispDAO dao, boolean iterateMask, boolean notifications, boolean mappingMerge) {
82         this.dao = dao;
83         this.notificationService = notifications;
84         this.mappingMerge = mappingMerge;
85         buildMapCaches();
86
87         sbMappingTimeoutService = new TimeBucketMappingTimeoutService(ConfigIni.getInstance()
88                 .getNumberOfBucketsInTimeBucketWheel(), ConfigIni.getInstance().getRegistrationValiditySb(),
89                 this);
90     }
91
92     public void setDataStoreBackEnd(DataStoreBackEnd dsbe) {
93         this.dsbe = dsbe;
94     }
95
96     @Override
97     public void setMappingMerge(boolean mappingMerge) {
98         this.mappingMerge = mappingMerge;
99     }
100
101     @Override
102     public void setIterateMask(boolean iterate) {
103         LOG.error("Non-longest prefix match lookups are not properly supported, variable is set to true");
104     }
105
106     public void initialize() {
107         restoreDaoFromDatastore();
108     }
109
110     private void buildMapCaches() {
111         /*
112          * There exists a direct relationship between MappingOrigins and the tables that are part of the MappingSystem.
113          * Therefore, if a new origin is added, probably a new table should be instantiated here as well. Here we
114          * instantiate a SimpleMapCache for southbound originated LISP mappings and a MultiTableMapCache for northbound
115          * originated mappings. Use of FlatMapCache would be possible when no longest prefix match is needed at all,
116          * but that option is no longer supported in the code, since it was never tested and may lead to unexpected
117          * results.
118          */
119         sdao = dao.putTable(MappingOrigin.Southbound.toString());
120         pmc = new MultiTableMapCache(dao.putTable(MappingOrigin.Northbound.toString()));
121         smc = new SimpleMapCache(sdao);
122         akdb = new AuthKeyDb(dao.putTable(AUTH_KEY_TABLE));
123         tableMap.put(MappingOrigin.Northbound, pmc);
124         tableMap.put(MappingOrigin.Southbound, smc);
125     }
126
127     public void addMapping(MappingOrigin origin, Eid key, MappingData mappingData) {
128
129         sbMappingTimeoutService.removeExpiredMappings();
130
131         if (mappingData == null) {
132             LOG.warn("addMapping() called with null mapping, ignoring");
133             return;
134         }
135
136         if (origin == MappingOrigin.Southbound) {
137             XtrId xtrId = mappingData.getXtrId();
138             if (xtrId == null && mappingMerge && mappingData.isMergeEnabled()) {
139                 LOG.warn("addMapping() called will null xTR-ID in MappingRecord, while merge is set, ignoring");
140                 return;
141             }
142             if (xtrId != null && mappingMerge) {
143                 if (mappingData.isMergeEnabled()) {
144                     smc.addMapping(key, xtrId, mappingData);
145                     handleMergedMapping(key);
146                     return;
147                 } else {
148                     clearPresentXtrIdMappings(key);
149                     smc.addMapping(key, xtrId, mappingData);
150                 }
151             }
152             addOrRefreshMappingInTimeoutService(key, mappingData);
153         }
154
155         tableMap.get(origin).addMapping(key, mappingData);
156     }
157
158     private void clearPresentXtrIdMappings(Eid key) {
159         List<MappingData> allXtrMappingList = (List<MappingData>) (List<?>) smc.getAllXtrIdMappings(key);
160
161         if (((MappingData) smc.getMapping(key, (XtrId) null)).isMergeEnabled()) {
162             LOG.trace("Different xTRs have different merge configuration!");
163         }
164
165         for (MappingData mappingData : allXtrMappingList) {
166             removeSbXtrIdSpecificMapping(key, mappingData.getXtrId(), mappingData);
167         }
168     }
169
170     private void addOrRefreshMappingInTimeoutService(Eid key, MappingData mappingData) {
171         Integer oldBucketId = (Integer) smc.getData(key, SubKeys.TIME_BUCKET_ID);
172         Integer updatedBucketId;
173
174         if (oldBucketId != null) {
175             //refresh mapping
176             updatedBucketId = sbMappingTimeoutService.refreshMapping(key, mappingData, oldBucketId);
177         } else {
178             updatedBucketId = sbMappingTimeoutService.addMapping(key, mappingData);
179         }
180
181         smc.addData(key, SubKeys.TIME_BUCKET_ID, updatedBucketId);
182     }
183
184     /*
185      * Since this method is only called when there is a hit in the southbound Map-Register cache, and that cache is
186      * not used when merge is on, it's OK to ignore the effects of timestamp changes on merging for now.
187      */
188     public void refreshMappingRegistration(Eid key, XtrId xtrId, Long timestamp) {
189
190         sbMappingTimeoutService.removeExpiredMappings();
191
192         if (timestamp == null) {
193             timestamp = System.currentTimeMillis();
194         }
195         MappingData mappingData = (MappingData) smc.getMapping(null, key);
196         if (mappingData != null) {
197             mappingData.setTimestamp(new Date(timestamp));
198             addOrRefreshMappingInTimeoutService(key, mappingData);
199         } else {
200             LOG.warn("Could not update timestamp for EID {}, no mapping found", LispAddressStringifier.getString(key));
201         }
202         if (mappingMerge && xtrId != null) {
203             MappingData xtrIdMappingData = (MappingData) smc.getMapping(key, xtrId);
204             if (xtrIdMappingData != null) {
205                 xtrIdMappingData.setTimestamp(new Date(timestamp));
206             } else {
207                 LOG.warn("Could not update timestamp for EID {} xTR-ID {}, no mapping found",
208                         LispAddressStringifier.getString(key), LispAddressStringifier.getString(xtrId));
209             }
210         }
211     }
212
213     private MappingData updateServicePathMappingRecord(MappingData mappingData, Eid eid) {
214         // keep properties of original record
215         MappingRecordBuilder recordBuilder = new MappingRecordBuilder(mappingData.getRecord());
216         recordBuilder.setLocatorRecord(new ArrayList<LocatorRecord>());
217
218         // there should only be one locator record
219         if (mappingData.getRecord().getLocatorRecord().size() != 1) {
220             LOG.warn("MappingRecord associated to ServicePath EID has more than one locator!");
221             return mappingData;
222         }
223
224         LocatorRecord locatorRecord = mappingData.getRecord().getLocatorRecord().get(0);
225         long serviceIndex = ((ServicePath) eid.getAddress()).getServicePath().getServiceIndex();
226         int index = LispAddressUtil.STARTING_SERVICE_INDEX - (int) serviceIndex;
227         Rloc rloc = locatorRecord.getRloc();
228         if (rloc.getAddress() instanceof Ipv4 || rloc.getAddress() instanceof Ipv6) {
229             if (index != 0) {
230                 LOG.warn("Service Index should be 255 for simple IP RLOCs!");
231             }
232             return mappingData;
233         } else if (rloc.getAddress() instanceof ExplicitLocatorPath) {
234             ExplicitLocatorPath elp = (ExplicitLocatorPath) rloc.getAddress();
235             List<Hop> hops = elp.getExplicitLocatorPath().getHop();
236
237             if (index < 0 || index > hops.size())  {
238                 LOG.warn("Service Index out of bounds!");
239                 return mappingData;
240             }
241
242             SimpleAddress nextHop = hops.get(index).getAddress();
243             LocatorRecordBuilder lrb = new LocatorRecordBuilder(locatorRecord);
244             lrb.setRloc(LispAddressUtil.toRloc(nextHop));
245             recordBuilder.getLocatorRecord().add(lrb.build());
246             return new MappingData(recordBuilder.build());
247         } else {
248             LOG.warn("Nothing to do with ServicePath mapping record");
249             return mappingData;
250         }
251     }
252
253     private MappingData handleMergedMapping(Eid key) {
254         List<MappingData> expiredMappingDataList = new ArrayList<>();
255         Set<IpAddressBinary> sourceRlocs = new HashSet<>();
256
257         MappingData mergedMappingData = MappingMergeUtil.mergeXtrIdMappings(smc.getAllXtrIdMappings(key),
258                 expiredMappingDataList, sourceRlocs);
259
260         for (MappingData mappingData : expiredMappingDataList) {
261             removeSbXtrIdSpecificMapping(key, mappingData.getXtrId(), mappingData);
262         }
263
264         if (mergedMappingData != null) {
265             smc.addMapping(key, mergedMappingData, sourceRlocs);
266             dsbe.addMapping(DSBEInputUtil.toMapping(MappingOrigin.Southbound, key, mergedMappingData));
267             addOrRefreshMappingInTimeoutService(key, mergedMappingData);
268         } else {
269             removeSbMapping(key, mergedMappingData);
270         }
271         return mergedMappingData;
272     }
273
274     @Override
275     public MappingData getMapping(Eid src, Eid dst) {
276         // NOTE: Currently we have two lookup algorithms implemented, which are configurable
277
278         if (ConfigIni.getInstance().getLookupPolicy() == IMappingService.LookupPolicy.NB_AND_SB) {
279             return getMappingNbSbIntersection(src, dst);
280         } else {
281             return getMappingNbFirst(src, dst);
282         }
283     }
284
285     @Override
286     public MappingData getMapping(Eid dst) {
287         return getMapping((Eid) null, dst);
288     }
289
290     @Override
291     public MappingData getMapping(Eid src, Eid dst, XtrId xtrId) {
292         // Note: If xtrId is null, we need to go through regular policy checking else Policy doesn't matter
293
294         if (xtrId == null) {
295             return getMapping(src, dst);
296         }
297
298         return getSbMappingWithExpiration(src, dst, xtrId);
299     }
300
301     @Override
302     public MappingData getMapping(MappingOrigin origin, Eid key) {
303         if (origin.equals(MappingOrigin.Southbound)) {
304             return getSbMappingWithExpiration(null, key, null);
305         }
306         return (MappingData) tableMap.get(origin).getMapping(null, key);
307     }
308
309     private MappingData getMappingNbFirst(Eid src, Eid dst) {
310
311         // Default lookup policy is northboundFirst
312         //lookupPolicy == NB_FIRST
313
314         MappingData nbMappingData = (MappingData) pmc.getMapping(src, dst);
315
316         if (nbMappingData == null) {
317             return getSbMappingWithExpiration(src, dst, null);
318         }
319         if (dst.getAddress() instanceof ServicePath) {
320             return updateServicePathMappingRecord(nbMappingData, dst);
321         }
322         return nbMappingData;
323     }
324
325     private MappingData getMappingNbSbIntersection(Eid src, Eid dst) {
326         //lookupPolicy == NB_AND_SB, we return intersection
327         //of NB and SB mappings, or NB mapping if intersection is empty.
328
329         MappingData nbMappingData = (MappingData) pmc.getMapping(src, dst);
330         if (nbMappingData == null) {
331             return nbMappingData;
332         }
333         // no intersection for Service Path mappings
334         if (dst.getAddress() instanceof ServicePath) {
335             return updateServicePathMappingRecord(nbMappingData, dst);
336         }
337         MappingData sbMappingData = getSbMappingWithExpiration(src, dst, null);
338         if (sbMappingData == null) {
339             return nbMappingData;
340         }
341         // both NB and SB mappings exist. Compute intersection of the mappings
342         return MappingMergeUtil.computeNbSbIntersection(nbMappingData, sbMappingData);
343     }
344
345     private MappingData getSbMappingWithExpiration(Eid src, Eid dst, XtrId xtrId) {
346         MappingData mappingData = (MappingData) smc.getMapping(dst, xtrId);
347         if (mappingData != null && MappingMergeUtil.mappingIsExpired(mappingData)) {
348             return handleSbExpiredMapping(dst, xtrId, mappingData);
349         } else {
350             return mappingData;
351         }
352     }
353
354     public MappingData handleSbExpiredMapping(Eid key, XtrId xtrId, MappingData mappingData) {
355         if (mappingMerge && mappingData.isMergeEnabled()) {
356             return handleMergedMapping(key);
357         }
358
359         if (xtrId != null) {
360             removeSbXtrIdSpecificMapping(key, xtrId, mappingData);
361         } else {
362             removeSbMapping(key, mappingData);
363         }
364         return null;
365     }
366
367     private void removeSbXtrIdSpecificMapping(Eid key, XtrId xtrId, MappingData mappingData) {
368         smc.removeMapping(key, xtrId);
369         dsbe.removeXtrIdMapping(DSBEInputUtil.toXtrIdMapping(mappingData));
370     }
371
372     private void removeSbMapping(Eid key, MappingData mappingData) {
373         if (mappingData != null && mappingData.getXtrId() != null) {
374             removeSbXtrIdSpecificMapping(key, mappingData.getXtrId(), mappingData);
375         }
376         removeFromSbTimeoutService(key);
377         smc.removeMapping(key);
378         dsbe.removeMapping(DSBEInputUtil.toMapping(MappingOrigin.Southbound, key, mappingData));
379     }
380
381     private void removeFromSbTimeoutService(Eid key) {
382         Integer bucketId = (Integer) smc.getData(key, SubKeys.TIME_BUCKET_ID);
383         if (bucketId != null) {
384             sbMappingTimeoutService.removeMappingFromTimeoutService(key, bucketId);
385         }
386     }
387
388     @Override
389     public Eid getWidestNegativePrefix(Eid key) {
390         Eid nbPrefix = pmc.getWidestNegativeMapping(key);
391         if (nbPrefix == null) {
392             return null;
393         }
394
395         Eid sbPrefix = smc.getWidestNegativeMapping(key);
396         if (sbPrefix == null) {
397             return null;
398         }
399
400         // since prefixes overlap, just return the more specific (larger mask)
401         if (LispAddressUtil.getIpPrefixMask(nbPrefix) < LispAddressUtil.getIpPrefixMask(sbPrefix)) {
402             return sbPrefix;
403         } else {
404             return nbPrefix;
405         }
406     }
407
408     @Override
409     public void removeMapping(MappingOrigin origin, Eid key) {
410         if (origin == MappingOrigin.Southbound) {
411             removeFromSbTimeoutService(key);
412         }
413         tableMap.get(origin).removeMapping(key);
414         if (notificationService) {
415             // TODO
416         }
417     }
418
419     @Override
420     public void addAuthenticationKey(Eid key, MappingAuthkey authKey) {
421         LOG.debug("Adding authentication key '{}' with key-ID {} for {}", authKey.getKeyString(), authKey.getKeyType(),
422                 LispAddressStringifier.getString(key));
423         akdb.addAuthenticationKey(key, authKey);
424     }
425
426     @Override
427     public MappingAuthkey getAuthenticationKey(Eid key) {
428         if (LOG.isDebugEnabled()) {
429             LOG.debug("Retrieving authentication key for {}", LispAddressStringifier.getString(key));
430         }
431         return akdb.getAuthenticationKey(key);
432     }
433
434     @Override
435     public void removeAuthenticationKey(Eid key) {
436         if (LOG.isDebugEnabled()) {
437             LOG.debug("Removing authentication key for {}", LispAddressStringifier.getString(key));
438         }
439         akdb.removeAuthenticationKey(key);
440     }
441
442     @Override
443     public void addData(MappingOrigin origin, Eid key, String subKey, Object data) {
444         if (LOG.isDebugEnabled()) {
445             LOG.debug("Add data of class {} for key {} and subkey {}", data.getClass(),
446                     LispAddressStringifier.getString(key), subKey);
447         }
448         tableMap.get(origin).addData(key, subKey, data);
449     }
450
451     @Override
452     public Object getData(MappingOrigin origin, Eid key, String subKey) {
453         if (LOG.isDebugEnabled()) {
454             LOG.debug("Retrieving data for key {} and subkey {}", LispAddressStringifier.getString(key), subKey);
455         }
456         return tableMap.get(origin).getData(key, subKey);
457     }
458
459     @Override
460     public void removeData(MappingOrigin origin, Eid key, String subKey) {
461         if (LOG.isDebugEnabled()) {
462             LOG.debug("Removing data for key {} and subkey {}", LispAddressStringifier.getString(key), subKey);
463         }
464         tableMap.get(origin).removeData(key, subKey);
465     }
466
467     @Override
468     public Eid getParentPrefix(Eid key) {
469         return smc.getParentPrefix(key);
470     }
471
472
473     /**
474      * Restore all mappings and keys from mdsal datastore.
475      */
476     private void restoreDaoFromDatastore() {
477         List<AuthenticationKey> authKeys = dsbe.getAllAuthenticationKeys();
478         List<Mapping> mappings = dsbe.getAllMappings(LogicalDatastoreType.CONFIGURATION);
479
480         /*
481          * XXX By default, the operational datastore is not persisted to disk, either at run-time, or on shutdown,
482          * so the following will have no effect (getLastUpdateTimestamp() will fail, since it's reading from
483          * the operational datastore, and even if it didn't getAllMappings() will fail anyway). According to rovarga it
484          * should be possible to turn on persistence for the operational datastore editing
485          * etc/opendaylight/karaf/05-clustering.xml, by setting <persistence>true</persistence>. At the time of writing
486          * the below code block that didn't seem to work though.
487          */
488         Long lastUpdateTimestamp = dsbe.getLastUpdateTimestamp();
489         if (lastUpdateTimestamp != null && System.currentTimeMillis() - lastUpdateTimestamp
490                 > ConfigIni.getInstance().getRegistrationValiditySb()) {
491             LOG.warn("Restore threshold passed, not restoring operational datastore into DAO");
492         } else {
493             mappings.addAll(dsbe.getAllMappings(LogicalDatastoreType.OPERATIONAL));
494         }
495         dsbe.removeLastUpdateTimestamp();
496
497         LOG.info("Restoring {} mappings and {} keys from datastore into DAO", mappings.size(), authKeys.size());
498
499         for (Mapping mapping : mappings) {
500             addMapping(mapping.getOrigin(), mapping.getMappingRecord().getEid(),
501                     new MappingData(mapping.getMappingRecord()));
502         }
503
504         for (AuthenticationKey authKey : authKeys) {
505             addAuthenticationKey(authKey.getEid(), authKey.getMappingAuthkey());
506         }
507     }
508
509     public void destroy() {
510         LOG.info("Mapping System is being destroyed!");
511         dsbe.saveLastUpdateTimestamp();
512     }
513
514     @Override
515     public String printMappings() {
516         final StringBuffer sb = new StringBuffer();
517         sb.append("PolicyMapCache\n--------------\n");
518         sb.append(pmc.printMappings());
519         sb.append("SbMapCache\n----------\n");
520         sb.append(smc.printMappings());
521         return sb.toString();
522     }
523
524     @Override
525     public String printKeys() {
526         return akdb.printKeys();
527     }
528
529     public void cleanCaches() {
530         dao.removeAll();
531         buildMapCaches();
532     }
533
534     /*
535      * XXX  Mappings and keys should be separated for this to work properly, as is it will remove northbound originated
536      * authentication keys too, since they are currently stored in smc.
537      */
538     public void cleanSBMappings() {
539         smc = new SimpleMapCache(sdao);
540     }
541
542     @Override
543     public void setIsMaster(boolean isMaster) {
544         this.isMaster = isMaster;
545     }
546
547     @Override
548     public boolean isMaster() {
549         return isMaster;
550     }
551 }