Fix radix trie lookup exact
[lispflowmapping.git] / mappingservice / mapcache / src / main / java / org / opendaylight / lispflowmapping / mapcache / MappingMergeUtil.java
1 /*
2  * Copyright (c) 2016 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 package org.opendaylight.lispflowmapping.mapcache;
9
10 import com.google.common.base.Preconditions;
11 import java.util.ArrayList;
12 import java.util.Date;
13 import java.util.LinkedHashMap;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Set;
17 import org.opendaylight.lispflowmapping.lisp.util.LispAddressUtil;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.inet.binary.types.rev160303.IpAddressBinary;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.XtrId;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.locatorrecords.LocatorRecord;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecord;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecordBuilder;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.rloc.container.Rloc;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28  * Utility class to implement merging of locator sets.
29  *
30  * @author Lorand Jakab
31  *
32  */
33 public final class MappingMergeUtil {
34     protected static final Logger LOG = LoggerFactory.getLogger(MappingMergeUtil.class);
35
36     // SB Map Register validity period in milliseconds. Default is 3.3 minutes.
37     // TODO Added temporarily, until we get a proper configuration infra
38     public static final long MIN_REGISTRATION_VALIDITY_SB = 200000L;
39
40     // Utility class, should not be instantiated
41     private MappingMergeUtil() {
42     }
43
44     private static void mergeCommonMappingRecordFields(MappingRecordBuilder mrb, MappingRecord record) {
45         // Set xTR-ID and site-ID from the current mapping, it help with determining the timestamp
46         mrb.setXtrId(record.getXtrId());
47         mrb.setSiteId(record.getSiteId());
48         // For the TTL value we take the minimum of all records
49         mrb.setRecordTtl(Math.min(mrb.getRecordTtl(), record.getRecordTtl()));
50         if (!mrb.getAction().equals(record.getAction())) {
51             LOG.warn("Mapping merge operation: actions are different, which one is used is undefined");
52         }
53         if (mrb.isAuthoritative() != record.isAuthoritative()) {
54             LOG.warn("Mapping merge operation: authoritative status is different, which one is used is undefined");
55         }
56         if (!mrb.getEid().equals(record.getEid())) {
57             LOG.warn("Mapping merge operation: EID records are different, which one is used is undefined");
58         }
59     }
60
61     private static LocatorRecord mergeLocators(LocatorRecord existingLocator, LocatorRecord newLocator) {
62         if (existingLocator.isLocalLocator()) {
63             return existingLocator;
64         }
65         return newLocator;
66     }
67
68     private static int compareLocators(LocatorRecord one, LocatorRecord two) {
69         byte[] oneIp = LispAddressUtil.ipAddressToByteArray(one.getRloc().getAddress());
70         byte[] twoIp = LispAddressUtil.ipAddressToByteArray(two.getRloc().getAddress());
71         return LispAddressUtil.compareIpAddressByteArrays(oneIp, twoIp);
72     }
73
74     private static void mergeLocatorRecords(MappingRecordBuilder mrb, MappingRecord newRecord) {
75         List<LocatorRecord> locators = mrb.getLocatorRecord();
76
77         // We assume locators are unique and sorted and don't show up several times (with different or identical
78         // p/w/mp/mw), so we create a LinkedHashMap (which preserves order) of the locators from the existing merged
79         // record, keyed by the Rloc
80         Map<Rloc, LocatorRecord> locatorMap = new LinkedHashMap<Rloc, LocatorRecord>();
81
82         // All locators to be added to the merge set are first stored in this list
83         List<LocatorRecord> newLocatorList = new ArrayList<LocatorRecord>();
84
85         for (LocatorRecord locator : locators) {
86             locatorMap.put(locator.getRloc(), locator);
87         }
88         for (LocatorRecord newLocator : newRecord.getLocatorRecord()) {
89             Rloc newRloc = newLocator.getRloc();
90             if (locatorMap.containsKey(newRloc)) {
91                 // overlapping locator
92                 if (locatorMap.get(newRloc).equals(newLocator)) {
93                     continue;
94                 } else {
95                     LocatorRecord mergedLocator = mergeLocators(locatorMap.get(newRloc), newLocator);
96                     newLocatorList.add(mergedLocator);
97                 }
98             } else {
99                 // new locator
100                 newLocatorList.add(newLocator);
101             }
102         }
103
104         // Build new merged and sorted locator set if need be
105         if (newLocatorList.size() != 0) {
106             List<LocatorRecord> mergedLocators = new ArrayList<LocatorRecord>();
107
108             int mlocIt = 0;
109             int locIt = 0;
110             while (mlocIt < newLocatorList.size() && locIt < locators.size()) {
111                 int cmp = compareLocators(locators.get(locIt), newLocatorList.get(mlocIt));
112                 if (cmp < 0) {
113                     mergedLocators.add(locators.get(locIt));
114                     locIt++;
115                 } else if (cmp > 0) {
116                     mergedLocators.add(newLocatorList.get(mlocIt));
117                     mlocIt++;
118                 } else {
119                     // when a locator appears in both lists, keep the new (merged) one and skip the old
120                     mergedLocators.add(newLocatorList.get(mlocIt));
121                     mlocIt++;
122                     locIt++;
123                 }
124             }
125             while (locIt < locators.size()) {
126                 mergedLocators.add(locators.get(locIt));
127                 locIt++;
128             }
129             while (mlocIt < newLocatorList.size()) {
130                 mergedLocators.add(newLocatorList.get(mlocIt));
131                 mlocIt++;
132             }
133             mrb.setLocatorRecord(mergedLocators);
134         }
135     }
136
137     public static MappingRecord mergeMappings(MappingRecord currentMergedMapping, MappingRecord newMapping,
138             XtrId xtrId, Date regdate) {
139         if (currentMergedMapping == null) {
140             return newMapping;
141         }
142
143         MappingRecordBuilder mrb = new MappingRecordBuilder(currentMergedMapping);
144         mergeCommonMappingRecordFields(mrb, newMapping);
145         mergeLocatorRecords(mrb, newMapping);
146
147         if (xtrId != null) {
148             mrb.setXtrId(xtrId);
149             mrb.setTimestamp(regdate.getTime());
150         }
151
152         return mrb.build();
153     }
154
155     public static ExtendedMappingRecord mergeXtrIdMappings(List<Object> extendedRecords, List<XtrId> expiredMappings,
156             Set<IpAddressBinary> sourceRlocs) {
157         MappingRecordBuilder mrb = null;
158         XtrId xtrId = null;
159         Long timestamp = Long.MAX_VALUE;
160
161         for (int i = 0; i < extendedRecords.size(); i++) {
162             ExtendedMappingRecord extendedRecord = (ExtendedMappingRecord) extendedRecords.get(i);
163             MappingRecord record = extendedRecord.getRecord();
164
165             // Skip expired mappings and add them to a list to be returned to the caller
166             if (timestampIsExpired(extendedRecord.getTimestamp())) {
167                 expiredMappings.add(record.getXtrId());
168                 continue;
169             }
170
171             if (mrb == null) {
172                 mrb = new MappingRecordBuilder(record);
173             }
174
175             // Save the oldest valid timestamp
176             if (extendedRecord.getTimestamp().getTime() < timestamp) {
177                 timestamp = extendedRecord.getTimestamp().getTime();
178                 xtrId = record.getXtrId();
179             }
180
181             // Merge record fields and locators
182             mergeCommonMappingRecordFields(mrb, record);
183             mergeLocatorRecords(mrb, record);
184
185             // Save source locator for use in Map-Notify
186             sourceRlocs.add(record.getSourceRloc());
187         }
188
189         if (mrb == null) {
190             LOG.warn("All mappings expired when merging! Unexpected!");
191             return null;
192         }
193         mrb.setXtrId(xtrId);
194
195         return new ExtendedMappingRecord(mrb.build(), new Date(timestamp));
196     }
197
198     public static boolean mappingIsExpired(ExtendedMappingRecord mapping) {
199         Preconditions.checkNotNull(mapping, "mapping should not be null!");
200         if (mapping.getTimestamp() != null) {
201             return timestampIsExpired(mapping.getTimestamp());
202         }
203         return false;
204     }
205
206     public static boolean timestampIsExpired(Date timestamp) {
207         Preconditions.checkNotNull(timestamp, "timestamp should not be null!");
208         return timestampIsExpired(timestamp.getTime());
209     }
210
211     public static boolean timestampIsExpired(Long timestamp) {
212         Preconditions.checkNotNull(timestamp, "timestamp should not be null!");
213         if ((System.currentTimeMillis() - timestamp) > MIN_REGISTRATION_VALIDITY_SB ) {
214             return true;
215         }
216         return false;
217     }
218 }