sangho
Committed by Jonathan Hart

ONOS-1438: Improved the routing rule population process for link add and failure…

…; computes the routes changed from the link changes and populates the rules only for the routes.

Change-Id: Id4dbd80da37b333f2c19bc97333472dc8031481b
...@@ -15,10 +15,13 @@ ...@@ -15,10 +15,13 @@
15 */ 15 */
16 package org.onosproject.segmentrouting; 16 package org.onosproject.segmentrouting;
17 17
18 +import com.google.common.collect.Maps;
19 +import com.google.common.collect.Sets;
18 import org.onlab.packet.Ip4Prefix; 20 import org.onlab.packet.Ip4Prefix;
19 import org.onlab.packet.IpPrefix; 21 import org.onlab.packet.IpPrefix;
20 import org.onosproject.net.Device; 22 import org.onosproject.net.Device;
21 import org.onosproject.net.DeviceId; 23 import org.onosproject.net.DeviceId;
24 +import org.onosproject.net.Link;
22 import org.onosproject.net.MastershipRole; 25 import org.onosproject.net.MastershipRole;
23 import org.onosproject.net.flow.FlowRule; 26 import org.onosproject.net.flow.FlowRule;
24 import org.slf4j.Logger; 27 import org.slf4j.Logger;
...@@ -39,6 +42,7 @@ public class DefaultRoutingHandler { ...@@ -39,6 +42,7 @@ public class DefaultRoutingHandler {
39 private SegmentRoutingManager srManager; 42 private SegmentRoutingManager srManager;
40 private RoutingRulePopulator rulePopulator; 43 private RoutingRulePopulator rulePopulator;
41 private NetworkConfigHandler config; 44 private NetworkConfigHandler config;
45 + private HashMap<DeviceId, ECMPShortestPathGraph> currentEcmpSpgMap;
42 private Status populationStatus; 46 private Status populationStatus;
43 47
44 /** 48 /**
...@@ -68,6 +72,7 @@ public class DefaultRoutingHandler { ...@@ -68,6 +72,7 @@ public class DefaultRoutingHandler {
68 this.rulePopulator = checkNotNull(srManager.routingRulePopulator); 72 this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
69 this.config = checkNotNull(srManager.networkConfigHandler); 73 this.config = checkNotNull(srManager.networkConfigHandler);
70 this.populationStatus = Status.IDLE; 74 this.populationStatus = Status.IDLE;
75 + this.currentEcmpSpgMap = Maps.newHashMap();
71 } 76 }
72 77
73 /** 78 /**
...@@ -79,6 +84,7 @@ public class DefaultRoutingHandler { ...@@ -79,6 +84,7 @@ public class DefaultRoutingHandler {
79 public boolean populateAllRoutingRules() { 84 public boolean populateAllRoutingRules() {
80 85
81 populationStatus = Status.STARTED; 86 populationStatus = Status.STARTED;
87 + rulePopulator.resetCounter();
82 log.info("Starts to populate routing rules"); 88 log.info("Starts to populate routing rules");
83 89
84 for (Device sw : srManager.deviceService.getDevices()) { 90 for (Device sw : srManager.deviceService.getDevices()) {
...@@ -87,22 +93,233 @@ public class DefaultRoutingHandler { ...@@ -87,22 +93,233 @@ public class DefaultRoutingHandler {
87 continue; 93 continue;
88 } 94 }
89 95
90 - ECMPShortestPathGraph ecmpSPG = new ECMPShortestPathGraph(sw.id(), srManager); 96 + ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(sw.id(), srManager);
91 - if (!populateEcmpRoutingRules(sw, ecmpSPG)) { 97 + if (!populateEcmpRoutingRules(sw.id(), ecmpSpg)) {
92 populationStatus = Status.ABORTED; 98 populationStatus = Status.ABORTED;
93 log.debug("Abort routing rule population"); 99 log.debug("Abort routing rule population");
94 return false; 100 return false;
95 } 101 }
102 + currentEcmpSpgMap.put(sw.id(), ecmpSpg);
96 103
97 // TODO: Set adjacency routing rule for all switches 104 // TODO: Set adjacency routing rule for all switches
98 } 105 }
99 106
100 populationStatus = Status.SUCCEEDED; 107 populationStatus = Status.SUCCEEDED;
101 - log.info("Completes routing rule population"); 108 + log.info("Completes routing rule population. Total # of rules pushed : {}",
109 + rulePopulator.getCounter());
102 return true; 110 return true;
103 } 111 }
104 112
105 - private boolean populateEcmpRoutingRules(Device sw, 113 + /**
114 + * Populates the routing rules according to the route changes due to the link
115 + * failure or link add. It computes the routes changed due to the link changes and
116 + * repopulates the rules only for the routes.
117 + *
118 + * @param linkFail link failed, null for link added
119 + * @return true if it succeeds to populate all rules, false otherwise
120 + */
121 + public boolean populateRoutingRulesForLinkStatusChange(Link linkFail) {
122 +
123 + synchronized (populationStatus) {
124 +
125 + if (populationStatus == Status.STARTED) {
126 + return true;
127 + }
128 +
129 + Set<ArrayList<DeviceId>> routeChanges;
130 + populationStatus = Status.STARTED;
131 + if (linkFail == null) {
132 + // Compare all routes of existing ECMP SPG with the new ones
133 + routeChanges = computeRouteChange();
134 + } else {
135 + // Compare existing ECMP SPG only with the link removed
136 + routeChanges = computeDamagedRoutes(linkFail);
137 + }
138 +
139 + if (routeChanges.isEmpty()) {
140 + log.debug("No route changes for the link status change");
141 + populationStatus = Status.SUCCEEDED;
142 + return true;
143 + }
144 +
145 + if (repopulateRoutingRulesForRoutes(routeChanges)) {
146 + populationStatus = Status.SUCCEEDED;
147 + log.info("Complete to repopulate the rules. # of rules populated : {}",
148 + rulePopulator.getCounter());
149 + return true;
150 + } else {
151 + populationStatus = Status.ABORTED;
152 + log.warn("Failed to repopulate the rules.");
153 + return false;
154 + }
155 + }
156 + }
157 +
158 + private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
159 + rulePopulator.resetCounter();
160 + for (ArrayList<DeviceId> link: routes) {
161 + if (link.size() == 1) {
162 + ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(link.get(0), srManager);
163 + if (populateEcmpRoutingRules(link.get(0), ecmpSpg)) {
164 + currentEcmpSpgMap.put(link.get(0), ecmpSpg);
165 + }
166 + continue;
167 + }
168 + DeviceId src = link.get(0);
169 + DeviceId dst = link.get(1);
170 + ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(dst, srManager);
171 +
172 + currentEcmpSpgMap.put(dst, ecmpSpg);
173 + HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
174 + ecmpSpg.getAllLearnedSwitchesAndVia();
175 + for (Integer itrIdx : switchVia.keySet()) {
176 + HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
177 + switchVia.get(itrIdx);
178 + for (DeviceId targetSw : swViaMap.keySet()) {
179 + if (!targetSw.equals(src)) {
180 + continue;
181 + }
182 + Set<DeviceId> nextHops = new HashSet<>();
183 + for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
184 + if (via.isEmpty()) {
185 + nextHops.add(dst);
186 + } else {
187 + nextHops.add(via.get(0));
188 + }
189 + }
190 + if (!populateEcmpRoutingRulePartial(targetSw, dst, nextHops)) {
191 + return false;
192 + }
193 + }
194 + }
195 + }
196 + return true;
197 + }
198 +
199 + private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
200 +
201 + Set<ArrayList<DeviceId>> routes = new HashSet<>();
202 +
203 + for (Device sw : srManager.deviceService.getDevices()) {
204 + if (srManager.mastershipService.
205 + getLocalRole(sw.id()) != MastershipRole.MASTER) {
206 + continue;
207 + }
208 + ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
209 + if (ecmpSpg == null) {
210 + log.error("No existing ECMP path for switch {}", sw.id());
211 + continue;
212 + }
213 + HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
214 + ecmpSpg.getAllLearnedSwitchesAndVia();
215 + for (Integer itrIdx : switchVia.keySet()) {
216 + HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
217 + switchVia.get(itrIdx);
218 + for (DeviceId targetSw : swViaMap.keySet()) {
219 + DeviceId destSw = sw.id();
220 + Set<ArrayList<DeviceId>> subLinks =
221 + computeLinks(targetSw, destSw, swViaMap);
222 + for (ArrayList<DeviceId> alink: subLinks) {
223 + if (alink.get(0).equals(linkFail.src().deviceId()) &&
224 + alink.get(1).equals(linkFail.dst().deviceId())) {
225 + ArrayList<DeviceId> aRoute = new ArrayList<>();
226 + aRoute.add(targetSw);
227 + aRoute.add(destSw);
228 + routes.add(aRoute);
229 + break;
230 + }
231 + }
232 + }
233 + }
234 + }
235 +
236 + return routes;
237 + }
238 +
239 + private Set<ArrayList<DeviceId>> computeRouteChange() {
240 +
241 + Set<ArrayList<DeviceId>> routes = new HashSet<>();
242 +
243 + for (Device sw : srManager.deviceService.getDevices()) {
244 + if (srManager.mastershipService.
245 + getLocalRole(sw.id()) != MastershipRole.MASTER) {
246 + continue;
247 + }
248 + ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
249 + if (ecmpSpg == null) {
250 + log.debug("No existing ECMP path for Switch {}", sw.id());
251 + ArrayList<DeviceId> route = new ArrayList<>();
252 + route.add(sw.id());
253 + routes.add(route);
254 + continue;
255 + }
256 + ECMPShortestPathGraph newEcmpSpg =
257 + new ECMPShortestPathGraph(sw.id(), srManager);
258 + HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
259 + ecmpSpg.getAllLearnedSwitchesAndVia();
260 + HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchViaUpdated =
261 + newEcmpSpg.getAllLearnedSwitchesAndVia();
262 +
263 + for (Integer itrIdx : switchVia.keySet()) {
264 + HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
265 + switchVia.get(itrIdx);
266 + for (DeviceId srcSw : swViaMap.keySet()) {
267 + ArrayList<ArrayList<DeviceId>> via1 = swViaMap.get(srcSw);
268 + ArrayList<ArrayList<DeviceId>> via2 = getVia(switchViaUpdated, srcSw);
269 + if (!via1.equals(via2)) {
270 + ArrayList<DeviceId> route = new ArrayList<>();
271 + route.add(srcSw);
272 + route.add(sw.id());
273 + routes.add(route);
274 + }
275 + }
276 + }
277 +
278 + }
279 +
280 + return routes;
281 + }
282 +
283 + private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
284 + ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId srcSw) {
285 + for (Integer itrIdx : switchVia.keySet()) {
286 + HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
287 + switchVia.get(itrIdx);
288 + if (swViaMap.get(srcSw) == null) {
289 + continue;
290 + } else {
291 + return swViaMap.get(srcSw);
292 + }
293 + }
294 +
295 + return new ArrayList<>();
296 + }
297 +
298 + private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
299 + DeviceId dst,
300 + HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
301 + Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
302 + for (ArrayList<DeviceId> via : viaMap.get(src)) {
303 + DeviceId linkSrc = src;
304 + DeviceId linkDst = dst;
305 + for (DeviceId viaDevice: via) {
306 + ArrayList<DeviceId> link = new ArrayList<>();
307 + linkDst = viaDevice;
308 + link.add(linkSrc);
309 + link.add(linkDst);
310 + subLinks.add(link);
311 + linkSrc = viaDevice;
312 + }
313 + ArrayList<DeviceId> link = new ArrayList<>();
314 + link.add(linkSrc);
315 + link.add(dst);
316 + subLinks.add(link);
317 + }
318 +
319 + return subLinks;
320 + }
321 +
322 + private boolean populateEcmpRoutingRules(DeviceId destSw,
106 ECMPShortestPathGraph ecmpSPG) { 323 ECMPShortestPathGraph ecmpSPG) {
107 324
108 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = 325 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
...@@ -111,7 +328,6 @@ public class DefaultRoutingHandler { ...@@ -111,7 +328,6 @@ public class DefaultRoutingHandler {
111 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap = 328 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
112 switchVia.get(itrIdx); 329 switchVia.get(itrIdx);
113 for (DeviceId targetSw : swViaMap.keySet()) { 330 for (DeviceId targetSw : swViaMap.keySet()) {
114 - DeviceId destSw = sw.id();
115 Set<DeviceId> nextHops = new HashSet<>(); 331 Set<DeviceId> nextHops = new HashSet<>();
116 332
117 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) { 333 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
......
...@@ -139,6 +139,7 @@ public class IpHandler { ...@@ -139,6 +139,7 @@ public class IpHandler {
139 OutboundPacket packet = new DefaultOutboundPacket(deviceId, 139 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
140 treatment, ByteBuffer.wrap(eth.serialize())); 140 treatment, ByteBuffer.wrap(eth.serialize()));
141 srManager.packetService.emit(packet); 141 srManager.packetService.emit(packet);
142 + ipPacketQueue.get(destIpAddress).remove(ipPacket);
142 } 143 }
143 } 144 }
144 } 145 }
......
...@@ -40,6 +40,7 @@ import java.util.ArrayList; ...@@ -40,6 +40,7 @@ import java.util.ArrayList;
40 import java.util.Collection; 40 import java.util.Collection;
41 import java.util.List; 41 import java.util.List;
42 import java.util.Set; 42 import java.util.Set;
43 +import java.util.concurrent.atomic.AtomicLong;
43 44
44 import static com.google.common.base.Preconditions.checkNotNull; 45 import static com.google.common.base.Preconditions.checkNotNull;
45 46
...@@ -47,8 +48,9 @@ public class RoutingRulePopulator { ...@@ -47,8 +48,9 @@ public class RoutingRulePopulator {
47 48
48 private static final Logger log = LoggerFactory.getLogger(RoutingRulePopulator.class); 49 private static final Logger log = LoggerFactory.getLogger(RoutingRulePopulator.class);
49 50
50 - private SegmentRoutingManager srManager; 51 + private final SegmentRoutingManager srManager;
51 - private NetworkConfigHandler config; 52 + private final NetworkConfigHandler config;
53 + private AtomicLong rulePopulationCounter;
52 54
53 /** 55 /**
54 * Creates a RoutingRulePopulator object. 56 * Creates a RoutingRulePopulator object.
...@@ -58,6 +60,21 @@ public class RoutingRulePopulator { ...@@ -58,6 +60,21 @@ public class RoutingRulePopulator {
58 public RoutingRulePopulator(SegmentRoutingManager srManager) { 60 public RoutingRulePopulator(SegmentRoutingManager srManager) {
59 this.srManager = srManager; 61 this.srManager = srManager;
60 this.config = checkNotNull(srManager.networkConfigHandler); 62 this.config = checkNotNull(srManager.networkConfigHandler);
63 + this.rulePopulationCounter = new AtomicLong(0);
64 + }
65 +
66 + /**
67 + * Resets the population counter.
68 + */
69 + public void resetCounter() {
70 + rulePopulationCounter.set(0);
71 + }
72 +
73 + /**
74 + * Returns the number of rules populated.
75 + */
76 + public long getCounter() {
77 + return rulePopulationCounter.get();
61 } 78 }
62 79
63 /** 80 /**
...@@ -87,6 +104,7 @@ public class RoutingRulePopulator { ...@@ -87,6 +104,7 @@ public class RoutingRulePopulator {
87 srManager.appId, 600, false, FlowRule.Type.IP); 104 srManager.appId, 600, false, FlowRule.Type.IP);
88 105
89 srManager.flowRuleService.applyFlowRules(f); 106 srManager.flowRuleService.applyFlowRules(f);
107 + rulePopulationCounter.incrementAndGet();
90 log.debug("Flow rule {} is set to switch {}", f, deviceId); 108 log.debug("Flow rule {} is set to switch {}", f, deviceId);
91 } 109 }
92 110
...@@ -162,6 +180,7 @@ public class RoutingRulePopulator { ...@@ -162,6 +180,7 @@ public class RoutingRulePopulator {
162 srManager.appId, 600, false, FlowRule.Type.IP); 180 srManager.appId, 600, false, FlowRule.Type.IP);
163 181
164 srManager.flowRuleService.applyFlowRules(f); 182 srManager.flowRuleService.applyFlowRules(f);
183 + rulePopulationCounter.incrementAndGet();
165 log.debug("IP flow rule {} is set to switch {}", f, deviceId); 184 log.debug("IP flow rule {} is set to switch {}", f, deviceId);
166 185
167 return true; 186 return true;
...@@ -216,6 +235,7 @@ public class RoutingRulePopulator { ...@@ -216,6 +235,7 @@ public class RoutingRulePopulator {
216 FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100, 235 FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100,
217 srManager.appId, 600, false, FlowRule.Type.MPLS); 236 srManager.appId, 600, false, FlowRule.Type.MPLS);
218 srManager.flowRuleService.applyFlowRules(f); 237 srManager.flowRuleService.applyFlowRules(f);
238 + rulePopulationCounter.incrementAndGet();
219 log.debug("MPLS rule {} is set to {}", f, deviceId); 239 log.debug("MPLS rule {} is set to {}", f, deviceId);
220 } 240 }
221 241
......
...@@ -151,8 +151,7 @@ public class SegmentRoutingManager { ...@@ -151,8 +151,7 @@ public class SegmentRoutingManager {
151 groupHandler.createGroups(); 151 groupHandler.createGroups();
152 groupHandlerMap.put(device.id(), groupHandler); 152 groupHandlerMap.put(device.id(), groupHandler);
153 log.debug("Initiating default group handling for {}", device.id()); 153 log.debug("Initiating default group handling for {}", device.id());
154 - 154 + defaultRoutingHandler.populateTtpRules(device.id());
155 - defaultRoutingHandler.startPopulationProcess();
156 } else { 155 } else {
157 log.debug("Activate: Local role {} " 156 log.debug("Activate: Local role {} "
158 + "is not MASTER for device {}", 157 + "is not MASTER for device {}",
...@@ -162,6 +161,8 @@ public class SegmentRoutingManager { ...@@ -162,6 +161,8 @@ public class SegmentRoutingManager {
162 } 161 }
163 } 162 }
164 163
164 + defaultRoutingHandler.startPopulationProcess();
165 +
165 log.info("Started"); 166 log.info("Started");
166 } 167 }
167 168
...@@ -239,6 +240,8 @@ public class SegmentRoutingManager { ...@@ -239,6 +240,8 @@ public class SegmentRoutingManager {
239 switch (event.type()) { 240 switch (event.type()) {
240 case DEVICE_ADDED: 241 case DEVICE_ADDED:
241 case PORT_REMOVED: 242 case PORT_REMOVED:
243 + case DEVICE_UPDATED:
244 + case DEVICE_AVAILABILITY_CHANGED:
242 scheduleEventHandlerIfNotScheduled(event); 245 scheduleEventHandlerIfNotScheduled(event);
243 break; 246 break;
244 default: 247 default:
...@@ -294,8 +297,12 @@ public class SegmentRoutingManager { ...@@ -294,8 +297,12 @@ public class SegmentRoutingManager {
294 processLinkRemoved((Link) event.subject()); 297 processLinkRemoved((Link) event.subject());
295 } else if (event.type() == GroupEvent.Type.GROUP_ADDED) { 298 } else if (event.type() == GroupEvent.Type.GROUP_ADDED) {
296 processGroupAdded((Group) event.subject()); 299 processGroupAdded((Group) event.subject());
297 - } else if (event.type() == DeviceEvent.Type.DEVICE_ADDED) { 300 + } else if (event.type() == DeviceEvent.Type.DEVICE_ADDED ||
298 - processDeviceAdded((Device) event.subject()); 301 + event.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
302 + event.type() == DeviceEvent.Type.DEVICE_UPDATED) {
303 + if (deviceService.isAvailable(((Device) event.subject()).id())) {
304 + processDeviceAdded((Device) event.subject());
305 + }
299 } else if (event.type() == DeviceEvent.Type.PORT_REMOVED) { 306 } else if (event.type() == DeviceEvent.Type.PORT_REMOVED) {
300 processPortRemoved((Device) event.subject(), 307 processPortRemoved((Device) event.subject(),
301 ((DeviceEvent) event).port()); 308 ((DeviceEvent) event).port());
...@@ -321,12 +328,12 @@ public class SegmentRoutingManager { ...@@ -321,12 +328,12 @@ public class SegmentRoutingManager {
321 groupHandler.linkUp(link); 328 groupHandler.linkUp(link);
322 } 329 }
323 } 330 }
324 - defaultRoutingHandler.startPopulationProcess(); 331 + defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(null);
325 } 332 }
326 333
327 private void processLinkRemoved(Link link) { 334 private void processLinkRemoved(Link link) {
328 log.debug("A link {} was removed", link.toString()); 335 log.debug("A link {} was removed", link.toString());
329 - defaultRoutingHandler.startPopulationProcess(); 336 + defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(link);
330 } 337 }
331 338
332 339
......