Bug 4822: Fix mapping record merging
[lispflowmapping.git] / mappingservice / implementation / src / main / java / org / opendaylight / lispflowmapping / implementation / util / 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.implementation.util;
9
10 import java.util.ArrayList;
11 import java.util.Date;
12 import java.util.LinkedHashMap;
13 import java.util.List;
14 import java.util.Map;
15
16 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.locatorrecords.LocatorRecord;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecord;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecordBuilder;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.rloc.container.Rloc;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 /**
24  * Utility class to implement merging of locator sets
25  *
26  * @author Lorand Jakab
27  *
28  */
29 public final class MappingMergeUtil {
30     protected static final Logger LOG = LoggerFactory.getLogger(MappingMergeUtil.class);
31
32     // Utility class, should not be instantiated
33     private MappingMergeUtil() {
34     }
35
36     private static void mergeCommonMappingRecordFields(MappingRecordBuilder mrb, MappingRecord record) {
37         // Set xTR-ID and site-ID from the current mapping, it help with determining the timestamp
38         mrb.setXtrId(record.getXtrId());
39         mrb.setSiteId(record.getSiteId());
40         // For the TTL value we take the minimum of all records
41         mrb.setRecordTtl(Math.min(mrb.getRecordTtl(), record.getRecordTtl()));
42         if  (!mrb.getAction().equals(record.getAction())) {
43             LOG.warn("Mapping merge operation: actions are different, which one is used is undefined");
44         }
45         if  (mrb.isAuthoritative() != record.isAuthoritative()) {
46             LOG.warn("Mapping merge operation: authoritative status is different, which one is used is undefined");
47         }
48         if  (!mrb.getEid().equals(record.getEid())) {
49             LOG.warn("Mapping merge operation: EID records are different, which one is used is undefined");
50         }
51     }
52
53     private static LocatorRecord mergeLocators(LocatorRecord existingLocator, LocatorRecord newLocator) {
54         if (existingLocator.isLocalLocator()) {
55             return existingLocator;
56         }
57         return newLocator;
58     }
59
60     private static void mergeLocatorRecords(MappingRecordBuilder mrb, MappingRecord newRecord) {
61         List<LocatorRecord> locators = mrb.getLocatorRecord();
62
63         // Optimization: unless we had to merge any of the locators due to differences in weights, etc, we return
64         // the original 'locators' List with any new locators added
65         boolean mergeHappened = false;
66
67         // We assume locators are unique and don't show up several times (with different or identical p/w/mp/mw),
68         // so we create a LinkedHashMap (which preserves order) of the locators from the existing merged record,
69         // keyed by the Rloc
70         Map<Rloc, LocatorRecord> locatorMap = new LinkedHashMap<Rloc, LocatorRecord>();
71         for (LocatorRecord locator : locators) {
72             locatorMap.put(locator.getRloc(), locator);
73         }
74         for (LocatorRecord newLocator : newRecord.getLocatorRecord()) {
75             Rloc newRloc = newLocator.getRloc();
76             if (locatorMap.containsKey(newRloc)) {
77                 // XXX  LocatorRecord YANG generated class doesn't override equals() so I'm not sure of the behavior
78                 // here, need to verify if it works as expected
79                 if (locatorMap.get(newRloc).equals(newLocator)) {
80                     continue;
81                 } else {
82                     LocatorRecord mergedLocator = mergeLocators(locatorMap.get(newRloc), newLocator);
83                     locatorMap.put(newRloc, mergedLocator);
84                     mergeHappened = true;
85                 }
86             } else {
87                 // We add both the LinkedHanshMap and the List, in case we can return the original list plus new
88                 // elements and need not generate a new list with merged locators (which should be the most common
89                 // scenario).
90                 locatorMap.put(newRloc, newLocator);
91                 locators.add(newLocator);
92             }
93         }
94
95         if (mergeHappened) {
96             List<LocatorRecord> mergedLocators = new ArrayList<LocatorRecord>();
97             for (Map.Entry<Rloc, LocatorRecord> entry : locatorMap.entrySet()) {
98                 mergedLocators.add(entry.getValue());
99             }
100             mrb.setLocatorRecord(mergedLocators);
101         } else {
102             // TODO  Check if this is necessary after and .add() was called on locators
103             mrb.setLocatorRecord(locators);
104         }
105     }
106
107     public static MappingRecord mergeMappings(MappingRecord currentMergedMapping, MappingRecord newMapping,
108             byte[] xtrId, Date regdate) {
109         if (currentMergedMapping == null) {
110             return newMapping;
111         }
112
113         MappingRecordBuilder mrb = new MappingRecordBuilder(currentMergedMapping);
114         mergeCommonMappingRecordFields(mrb, newMapping);
115         mergeLocatorRecords(mrb, newMapping);
116
117         if (xtrId != null) {
118             mrb.setXtrId(xtrId);
119             mrb.setTimestamp(regdate.getTime());
120         }
121
122         return mrb.build();
123     }
124
125     public static MappingRecord mergeXtrIdMappings(List<Object> records) {
126         MappingRecordBuilder mrb = new MappingRecordBuilder((MappingRecord) records.get(0));
127         for (int i = 1; i < records.size(); i++) {
128             MappingRecord record = (MappingRecord) records.get(i);
129             mergeCommonMappingRecordFields(mrb, record);
130             mergeLocatorRecords(mrb, record);
131         }
132         return mrb.build();
133     }
134 }