blob: 870ea06892d2dbeeef7ba30766352f17b9f4f96f [file] [log] [blame]
sanghob35a6192015-04-01 13:05:26 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
sanghob35a6192015-04-01 13:05:26 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.segmentrouting;
17
Charles Chan93e71ba2016-04-29 14:38:22 -070018import com.google.common.collect.ImmutableSet;
sangho20eff1d2015-04-13 15:15:58 -070019import com.google.common.collect.Maps;
20import com.google.common.collect.Sets;
sangho666cd6d2015-04-14 16:27:13 -070021import org.onlab.packet.Ip4Address;
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070022import org.onlab.packet.Ip4Prefix;
sanghob35a6192015-04-01 13:05:26 -070023import org.onlab.packet.IpPrefix;
Charles Chan93e71ba2016-04-29 14:38:22 -070024import org.onosproject.net.ConnectPoint;
sanghob35a6192015-04-01 13:05:26 -070025import org.onosproject.net.Device;
26import org.onosproject.net.DeviceId;
sangho20eff1d2015-04-13 15:15:58 -070027import org.onosproject.net.Link;
Charles Chan0b4e6182015-11-03 10:42:14 -080028import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
29import org.onosproject.segmentrouting.config.DeviceConfiguration;
sanghob35a6192015-04-01 13:05:26 -070030import org.slf4j.Logger;
31import org.slf4j.LoggerFactory;
32
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070033import static java.util.concurrent.Executors.newScheduledThreadPool;
34import static org.onlab.util.Tools.groupedThreads;
35
sanghob35a6192015-04-01 13:05:26 -070036import java.util.ArrayList;
37import java.util.HashMap;
38import java.util.HashSet;
39import java.util.Set;
Saurav Das59232cf2016-04-27 18:35:50 -070040import java.util.concurrent.ScheduledExecutorService;
41import java.util.concurrent.TimeUnit;
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +090042import java.util.concurrent.locks.Lock;
43import java.util.concurrent.locks.ReentrantLock;
sanghob35a6192015-04-01 13:05:26 -070044
45import static com.google.common.base.Preconditions.checkNotNull;
46
Charles Chane849c192016-01-11 18:28:54 -080047/**
48 * Default routing handler that is responsible for route computing and
49 * routing rule population.
50 */
sanghob35a6192015-04-01 13:05:26 -070051public class DefaultRoutingHandler {
Saurav Das25190812016-05-27 13:54:07 -070052 private static final int MAX_RETRY_ATTEMPTS = 25;
Charles Chan93e71ba2016-04-29 14:38:22 -070053 private static final String ECMPSPG_MISSING = "ECMP shortest path graph not found";
54 private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
sanghob35a6192015-04-01 13:05:26 -070055
56 private SegmentRoutingManager srManager;
57 private RoutingRulePopulator rulePopulator;
Shashikanth VH013a7bc2015-12-11 01:32:44 +053058 private HashMap<DeviceId, EcmpShortestPathGraph> currentEcmpSpgMap;
59 private HashMap<DeviceId, EcmpShortestPathGraph> updatedEcmpSpgMap;
sangho666cd6d2015-04-14 16:27:13 -070060 private DeviceConfiguration config;
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +090061 private final Lock statusLock = new ReentrantLock();
62 private volatile Status populationStatus;
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070063 private ScheduledExecutorService executorService
64 = newScheduledThreadPool(1, groupedThreads("RoutingHandler", "retry-%d", log));
sanghob35a6192015-04-01 13:05:26 -070065
66 /**
67 * Represents the default routing population status.
68 */
69 public enum Status {
70 // population process is not started yet.
71 IDLE,
72
73 // population process started.
74 STARTED,
75
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070076 // population process was aborted due to errors, mostly for groups not
77 // found.
sanghob35a6192015-04-01 13:05:26 -070078 ABORTED,
79
80 // population process was finished successfully.
81 SUCCEEDED
82 }
83
84 /**
85 * Creates a DefaultRoutingHandler object.
86 *
87 * @param srManager SegmentRoutingManager object
88 */
89 public DefaultRoutingHandler(SegmentRoutingManager srManager) {
90 this.srManager = srManager;
91 this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
sangho666cd6d2015-04-14 16:27:13 -070092 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070093 this.populationStatus = Status.IDLE;
sangho20eff1d2015-04-13 15:15:58 -070094 this.currentEcmpSpgMap = Maps.newHashMap();
sanghob35a6192015-04-01 13:05:26 -070095 }
96
97 /**
98 * Populates all routing rules to all connected routers, including default
99 * routing rules, adjacency rules, and policy rules if any.
100 *
101 * @return true if it succeeds in populating all rules, otherwise false
102 */
103 public boolean populateAllRoutingRules() {
104
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900105 statusLock.lock();
106 try {
107 populationStatus = Status.STARTED;
108 rulePopulator.resetCounter();
Saurav Dasa07f2032015-10-19 14:37:36 -0700109 log.info("Starting to populate segment-routing rules");
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900110 log.debug("populateAllRoutingRules: populationStatus is STARTED");
sanghob35a6192015-04-01 13:05:26 -0700111
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900112 for (Device sw : srManager.deviceService.getDevices()) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700113 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900114 log.debug("populateAllRoutingRules: skipping device {}...we are not master",
115 sw.id());
116 continue;
117 }
118
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530119 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(sw.id(), srManager);
Charles Chan93e71ba2016-04-29 14:38:22 -0700120 if (!populateEcmpRoutingRules(sw.id(), ecmpSpg, ImmutableSet.of())) {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900121 log.debug("populateAllRoutingRules: populationStatus is ABORTED");
122 populationStatus = Status.ABORTED;
123 log.debug("Abort routing rule population");
124 return false;
125 }
126 currentEcmpSpgMap.put(sw.id(), ecmpSpg);
127
128 // TODO: Set adjacency routing rule for all switches
sanghob35a6192015-04-01 13:05:26 -0700129 }
130
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900131 log.debug("populateAllRoutingRules: populationStatus is SUCCEEDED");
132 populationStatus = Status.SUCCEEDED;
Saurav Dasa07f2032015-10-19 14:37:36 -0700133 log.info("Completed routing rule population. Total # of rules pushed : {}",
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900134 rulePopulator.getCounter());
135 return true;
136 } finally {
137 statusLock.unlock();
sanghob35a6192015-04-01 13:05:26 -0700138 }
sanghob35a6192015-04-01 13:05:26 -0700139 }
140
sangho20eff1d2015-04-13 15:15:58 -0700141 /**
142 * Populates the routing rules according to the route changes due to the link
143 * failure or link add. It computes the routes changed due to the link changes and
144 * repopulates the rules only for the routes.
145 *
146 * @param linkFail link failed, null for link added
147 * @return true if it succeeds to populate all rules, false otherwise
148 */
149 public boolean populateRoutingRulesForLinkStatusChange(Link linkFail) {
150
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900151 statusLock.lock();
152 try {
sangho20eff1d2015-04-13 15:15:58 -0700153
154 if (populationStatus == Status.STARTED) {
sangho52abe3a2015-05-05 14:13:34 -0700155 log.warn("Previous rule population is not finished.");
sangho20eff1d2015-04-13 15:15:58 -0700156 return true;
157 }
158
sangho45b009c2015-05-07 13:30:57 -0700159 // Take the snapshots of the links
160 updatedEcmpSpgMap = new HashMap<>();
161 for (Device sw : srManager.deviceService.getDevices()) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700162 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
sangho45b009c2015-05-07 13:30:57 -0700163 continue;
164 }
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530165 EcmpShortestPathGraph ecmpSpgUpdated =
166 new EcmpShortestPathGraph(sw.id(), srManager);
sangho45b009c2015-05-07 13:30:57 -0700167 updatedEcmpSpgMap.put(sw.id(), ecmpSpgUpdated);
168 }
169
sangho52abe3a2015-05-05 14:13:34 -0700170 log.info("Starts rule population from link change");
171
sangho20eff1d2015-04-13 15:15:58 -0700172 Set<ArrayList<DeviceId>> routeChanges;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700173 log.trace("populateRoutingRulesForLinkStatusChange: "
174 + "populationStatus is STARTED");
sangho20eff1d2015-04-13 15:15:58 -0700175 populationStatus = Status.STARTED;
Saurav Dasb5c236e2016-06-07 10:08:06 -0700176 // optimized re-routing
sangho20eff1d2015-04-13 15:15:58 -0700177 if (linkFail == null) {
178 // Compare all routes of existing ECMP SPG with the new ones
179 routeChanges = computeRouteChange();
180 } else {
181 // Compare existing ECMP SPG only with the link removed
182 routeChanges = computeDamagedRoutes(linkFail);
183 }
184
Saurav Dasb5c236e2016-06-07 10:08:06 -0700185 // null routeChanges indicates that full re-routing is required
186 if (routeChanges == null) {
187 return populateAllRoutingRules();
188 }
189
sangho20eff1d2015-04-13 15:15:58 -0700190 if (routeChanges.isEmpty()) {
sangho52abe3a2015-05-05 14:13:34 -0700191 log.info("No route changes for the link status change");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700192 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
sangho20eff1d2015-04-13 15:15:58 -0700193 populationStatus = Status.SUCCEEDED;
194 return true;
195 }
196
197 if (repopulateRoutingRulesForRoutes(routeChanges)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700198 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
sangho20eff1d2015-04-13 15:15:58 -0700199 populationStatus = Status.SUCCEEDED;
200 log.info("Complete to repopulate the rules. # of rules populated : {}",
201 rulePopulator.getCounter());
202 return true;
203 } else {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700204 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is ABORTED");
sangho20eff1d2015-04-13 15:15:58 -0700205 populationStatus = Status.ABORTED;
206 log.warn("Failed to repopulate the rules.");
207 return false;
208 }
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900209 } finally {
210 statusLock.unlock();
sangho20eff1d2015-04-13 15:15:58 -0700211 }
212 }
213
214 private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
215 rulePopulator.resetCounter();
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700216 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> routesBydevice =
217 new HashMap<>();
sangho20eff1d2015-04-13 15:15:58 -0700218 for (ArrayList<DeviceId> link: routes) {
sangho834e4b02015-05-01 09:38:25 -0700219 // When only the source device is defined, reinstall routes to all other devices
sangho20eff1d2015-04-13 15:15:58 -0700220 if (link.size() == 1) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700221 log.trace("repopulateRoutingRulesForRoutes: running ECMP graph for device {}", link.get(0));
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530222 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(link.get(0), srManager);
Charles Chan93e71ba2016-04-29 14:38:22 -0700223 if (populateEcmpRoutingRules(link.get(0), ecmpSpg, ImmutableSet.of())) {
Saurav Das25190812016-05-27 13:54:07 -0700224 log.debug("Populating flow rules from all to dest:{} is successful",
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700225 link.get(0));
sangho20eff1d2015-04-13 15:15:58 -0700226 currentEcmpSpgMap.put(link.get(0), ecmpSpg);
sangho52abe3a2015-05-05 14:13:34 -0700227 } else {
Saurav Das25190812016-05-27 13:54:07 -0700228 log.warn("Failed to populate the flow rules from all to dest:{}", link.get(0));
sangho52abe3a2015-05-05 14:13:34 -0700229 return false;
sangho20eff1d2015-04-13 15:15:58 -0700230 }
sangho45b009c2015-05-07 13:30:57 -0700231 } else {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700232 ArrayList<ArrayList<DeviceId>> deviceRoutes =
233 routesBydevice.get(link.get(1));
234 if (deviceRoutes == null) {
235 deviceRoutes = new ArrayList<>();
236 routesBydevice.put(link.get(1), deviceRoutes);
237 }
238 deviceRoutes.add(link);
239 }
240 }
241
242 for (DeviceId impactedDevice : routesBydevice.keySet()) {
243 ArrayList<ArrayList<DeviceId>> deviceRoutes =
244 routesBydevice.get(impactedDevice);
245 for (ArrayList<DeviceId> link: deviceRoutes) {
246 log.debug("repopulate RoutingRules For Routes {} -> {}",
247 link.get(0), link.get(1));
sangho45b009c2015-05-07 13:30:57 -0700248 DeviceId src = link.get(0);
249 DeviceId dst = link.get(1);
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530250 EcmpShortestPathGraph ecmpSpg = updatedEcmpSpgMap.get(dst);
sangho45b009c2015-05-07 13:30:57 -0700251 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
252 ecmpSpg.getAllLearnedSwitchesAndVia();
253 for (Integer itrIdx : switchVia.keySet()) {
254 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
255 switchVia.get(itrIdx);
256 for (DeviceId targetSw : swViaMap.keySet()) {
257 if (!targetSw.equals(src)) {
258 continue;
259 }
260 Set<DeviceId> nextHops = new HashSet<>();
261 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
262 if (via.isEmpty()) {
263 nextHops.add(dst);
264 } else {
265 nextHops.add(via.get(0));
266 }
267 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700268 if (!populateEcmpRoutingRulePartial(targetSw, dst,
269 nextHops, ImmutableSet.of())) {
sangho45b009c2015-05-07 13:30:57 -0700270 return false;
sangho20eff1d2015-04-13 15:15:58 -0700271 }
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700272 log.debug("Populating flow rules from {} to {} is successful",
273 targetSw, dst);
sangho20eff1d2015-04-13 15:15:58 -0700274 }
sangho20eff1d2015-04-13 15:15:58 -0700275 }
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700276 //currentEcmpSpgMap.put(dst, ecmpSpg);
sangho20eff1d2015-04-13 15:15:58 -0700277 }
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700278 //Only if all the flows for all impacted routes to a
279 //specific target are pushed successfully, update the
280 //ECMP graph for that target. (Or else the next event
281 //would not see any changes in the ECMP graphs)
282 currentEcmpSpgMap.put(impactedDevice,
283 updatedEcmpSpgMap.get(impactedDevice));
sangho20eff1d2015-04-13 15:15:58 -0700284 }
285 return true;
286 }
287
Saurav Dasb5c236e2016-06-07 10:08:06 -0700288 /**
289 * Computes set of affected ECMP routes due to failed link. Assumes
290 * previous ecmp shortest-path graph exists for a switch in order to compute
291 * affected routes. If such a graph does not exist, the method returns null.
292 *
293 * @param linkFail the failed link
294 * @return the set of affected routes which may be empty if no routes were
295 * affected, or null if no previous ecmp spg was found for comparison
296 */
sangho20eff1d2015-04-13 15:15:58 -0700297 private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
298
299 Set<ArrayList<DeviceId>> routes = new HashSet<>();
300
301 for (Device sw : srManager.deviceService.getDevices()) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700302 log.debug("Computing the impacted routes for device {} due to link fail",
303 sw.id());
Charles Chanc42e84e2015-10-20 16:24:19 -0700304 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700305 log.debug("No mastership for {} .. skipping route optimization",
306 sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700307 continue;
308 }
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530309 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700310 if (ecmpSpg == null) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700311 log.warn("No existing ECMP graph for switch {}. Aborting optimized"
312 + " rerouting and opting for full-reroute", sw.id());
313 return null;
sangho20eff1d2015-04-13 15:15:58 -0700314 }
315 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
316 ecmpSpg.getAllLearnedSwitchesAndVia();
317 for (Integer itrIdx : switchVia.keySet()) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700318 log.trace("Iterindex# {}", itrIdx);
sangho20eff1d2015-04-13 15:15:58 -0700319 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
320 switchVia.get(itrIdx);
321 for (DeviceId targetSw : swViaMap.keySet()) {
322 DeviceId destSw = sw.id();
Saurav Dasb5c236e2016-06-07 10:08:06 -0700323 if (log.isTraceEnabled()) {
324 log.trace("TargetSwitch {} --> RootSwitch {}", targetSw, destSw);
325 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
326 log.trace(" Via:");
327 via.forEach(e -> { log.trace(" {}", e); });
328 }
329 }
sangho20eff1d2015-04-13 15:15:58 -0700330 Set<ArrayList<DeviceId>> subLinks =
331 computeLinks(targetSw, destSw, swViaMap);
332 for (ArrayList<DeviceId> alink: subLinks) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700333 if ((alink.get(0).equals(linkFail.src().deviceId()) &&
334 alink.get(1).equals(linkFail.dst().deviceId()))
335 ||
336 (alink.get(0).equals(linkFail.dst().deviceId()) &&
337 alink.get(1).equals(linkFail.src().deviceId()))) {
338 log.debug("Impacted route:{}->{}", targetSw, destSw);
sangho20eff1d2015-04-13 15:15:58 -0700339 ArrayList<DeviceId> aRoute = new ArrayList<>();
340 aRoute.add(targetSw);
341 aRoute.add(destSw);
342 routes.add(aRoute);
343 break;
344 }
345 }
346 }
347 }
sangho45b009c2015-05-07 13:30:57 -0700348
sangho20eff1d2015-04-13 15:15:58 -0700349 }
350
351 return routes;
352 }
353
354 private Set<ArrayList<DeviceId>> computeRouteChange() {
355
356 Set<ArrayList<DeviceId>> routes = new HashSet<>();
357
358 for (Device sw : srManager.deviceService.getDevices()) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700359 log.debug("Computing the impacted routes for device {}", sw.id());
Charles Chanc42e84e2015-10-20 16:24:19 -0700360 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700361 log.debug("No mastership for {} ... skipping route optimization",
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700362 sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700363 continue;
364 }
Saurav Dasb5c236e2016-06-07 10:08:06 -0700365 if (log.isTraceEnabled()) {
366 log.trace("link of {} - ", sw.id());
367 for (Link link: srManager.linkService.getDeviceLinks(sw.id())) {
368 log.trace("{} -> {} ", link.src().deviceId(), link.dst().deviceId());
369 }
sangho45b009c2015-05-07 13:30:57 -0700370 }
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530371 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700372 if (ecmpSpg == null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700373 log.debug("No existing ECMP graph for device {}", sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700374 ArrayList<DeviceId> route = new ArrayList<>();
375 route.add(sw.id());
376 routes.add(route);
377 continue;
378 }
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530379 EcmpShortestPathGraph newEcmpSpg = updatedEcmpSpgMap.get(sw.id());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700380 //currentEcmpSpgMap.put(sw.id(), newEcmpSpg);
sangho20eff1d2015-04-13 15:15:58 -0700381 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
382 ecmpSpg.getAllLearnedSwitchesAndVia();
383 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchViaUpdated =
384 newEcmpSpg.getAllLearnedSwitchesAndVia();
385
sangho45b009c2015-05-07 13:30:57 -0700386 for (Integer itrIdx : switchViaUpdated.keySet()) {
387 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMapUpdated =
388 switchViaUpdated.get(itrIdx);
389 for (DeviceId srcSw : swViaMapUpdated.keySet()) {
390 ArrayList<ArrayList<DeviceId>> viaUpdated = swViaMapUpdated.get(srcSw);
391 ArrayList<ArrayList<DeviceId>> via = getVia(switchVia, srcSw);
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700392 if ((via == null) || !viaUpdated.equals(via)) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700393 log.debug("Impacted route:{} -> {}", srcSw, sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700394 ArrayList<DeviceId> route = new ArrayList<>();
395 route.add(srcSw);
396 route.add(sw.id());
397 routes.add(route);
398 }
399 }
400 }
sangho45b009c2015-05-07 13:30:57 -0700401 }
sangho20eff1d2015-04-13 15:15:58 -0700402
Saurav Dasb5c236e2016-06-07 10:08:06 -0700403 if (log.isTraceEnabled()) {
404 for (ArrayList<DeviceId> link: routes) {
405 log.trace("Route changes - ");
406 if (link.size() == 1) {
407 log.trace(" : all -> {}", link.get(0));
408 } else {
409 log.trace(" : {} -> {}", link.get(0), link.get(1));
410 }
sangho45b009c2015-05-07 13:30:57 -0700411 }
sangho20eff1d2015-04-13 15:15:58 -0700412 }
sangho20eff1d2015-04-13 15:15:58 -0700413 return routes;
414 }
415
416 private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
417 ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId srcSw) {
418 for (Integer itrIdx : switchVia.keySet()) {
419 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
420 switchVia.get(itrIdx);
421 if (swViaMap.get(srcSw) == null) {
422 continue;
423 } else {
424 return swViaMap.get(srcSw);
425 }
426 }
427
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700428 return null;
sangho20eff1d2015-04-13 15:15:58 -0700429 }
430
431 private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
432 DeviceId dst,
433 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
434 Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
435 for (ArrayList<DeviceId> via : viaMap.get(src)) {
436 DeviceId linkSrc = src;
437 DeviceId linkDst = dst;
438 for (DeviceId viaDevice: via) {
439 ArrayList<DeviceId> link = new ArrayList<>();
440 linkDst = viaDevice;
441 link.add(linkSrc);
442 link.add(linkDst);
443 subLinks.add(link);
444 linkSrc = viaDevice;
445 }
446 ArrayList<DeviceId> link = new ArrayList<>();
447 link.add(linkSrc);
448 link.add(dst);
449 subLinks.add(link);
450 }
451
452 return subLinks;
453 }
454
Charles Chan93e71ba2016-04-29 14:38:22 -0700455 /**
456 * Populate ECMP rules for subnets from all switches to destination.
457 *
458 * @param destSw Device ID of destination switch
459 * @param ecmpSPG ECMP shortest path graph
460 * @param subnets Subnets to be populated. If empty, populate all configured subnets.
461 * @return true if succeed
462 */
sangho20eff1d2015-04-13 15:15:58 -0700463 private boolean populateEcmpRoutingRules(DeviceId destSw,
Charles Chan93e71ba2016-04-29 14:38:22 -0700464 EcmpShortestPathGraph ecmpSPG,
465 Set<Ip4Prefix> subnets) {
sanghob35a6192015-04-01 13:05:26 -0700466
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700467 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = ecmpSPG
468 .getAllLearnedSwitchesAndVia();
sanghob35a6192015-04-01 13:05:26 -0700469 for (Integer itrIdx : switchVia.keySet()) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700470 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap = switchVia
471 .get(itrIdx);
sanghob35a6192015-04-01 13:05:26 -0700472 for (DeviceId targetSw : swViaMap.keySet()) {
sanghob35a6192015-04-01 13:05:26 -0700473 Set<DeviceId> nextHops = new HashSet<>();
Saurav Dasa07f2032015-10-19 14:37:36 -0700474 log.debug("** Iter: {} root: {} target: {}", itrIdx, destSw, targetSw);
sanghob35a6192015-04-01 13:05:26 -0700475 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
476 if (via.isEmpty()) {
477 nextHops.add(destSw);
478 } else {
479 nextHops.add(via.get(0));
480 }
481 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700482 if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops, subnets)) {
sanghob35a6192015-04-01 13:05:26 -0700483 return false;
484 }
485 }
486 }
487
488 return true;
489 }
490
Charles Chan93e71ba2016-04-29 14:38:22 -0700491 /**
492 * Populate ECMP rules for subnets from target to destination via nexthops.
493 *
Saurav Das25190812016-05-27 13:54:07 -0700494 * @param targetSw Device ID of target switch in which rules will be programmed
495 * @param destSw Device ID of final destination switch to which the rules will forward
496 * @param nextHops List of next hops via which destSw will be reached
Charles Chan93e71ba2016-04-29 14:38:22 -0700497 * @param subnets Subnets to be populated. If empty, populate all configured subnets.
498 * @return true if succeed
499 */
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700500 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw,
501 DeviceId destSw,
Charles Chan93e71ba2016-04-29 14:38:22 -0700502 Set<DeviceId> nextHops,
503 Set<Ip4Prefix> subnets) {
sanghob35a6192015-04-01 13:05:26 -0700504 boolean result;
505
506 if (nextHops.isEmpty()) {
507 nextHops.add(destSw);
508 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700509 // If both target switch and dest switch are edge routers, then set IP
sangho52abe3a2015-05-05 14:13:34 -0700510 // rule for both subnet and router IP.
Charles Chan0b4e6182015-11-03 10:42:14 -0800511 boolean targetIsEdge;
512 boolean destIsEdge;
513 Ip4Address destRouterIp;
514
515 try {
516 targetIsEdge = config.isEdgeDevice(targetSw);
517 destIsEdge = config.isEdgeDevice(destSw);
518 destRouterIp = config.getRouterIp(destSw);
519 } catch (DeviceConfigNotFoundException e) {
520 log.warn(e.getMessage() + " Aborting populateEcmpRoutingRulePartial.");
521 return false;
522 }
523
524 if (targetIsEdge && destIsEdge) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700525 subnets = (subnets != null && !subnets.isEmpty()) ? subnets : config.getSubnets(destSw);
Saurav Dasa07f2032015-10-19 14:37:36 -0700526 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for subnets {}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800527 targetSw, destSw, subnets);
Charles Chan93e71ba2016-04-29 14:38:22 -0700528 result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets,
529 destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700530 if (!result) {
531 return false;
532 }
533
Charles Chan0b4e6182015-11-03 10:42:14 -0800534 Ip4Address routerIp = destRouterIp;
sangho666cd6d2015-04-14 16:27:13 -0700535 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
Saurav Dasa07f2032015-10-19 14:37:36 -0700536 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800537 targetSw, destSw, routerIpPrefix);
sangho666cd6d2015-04-14 16:27:13 -0700538 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700539 if (!result) {
540 return false;
541 }
542
Charles Chan0b4e6182015-11-03 10:42:14 -0800543 } else if (targetIsEdge) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800544 // If the target switch is an edge router, then set IP rules for the router IP.
Charles Chan0b4e6182015-11-03 10:42:14 -0800545 Ip4Address routerIp = destRouterIp;
sangho666cd6d2015-04-14 16:27:13 -0700546 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
Saurav Dasa07f2032015-10-19 14:37:36 -0700547 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800548 targetSw, destSw, routerIpPrefix);
sangho666cd6d2015-04-14 16:27:13 -0700549 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700550 if (!result) {
551 return false;
552 }
sangho52abe3a2015-05-05 14:13:34 -0700553 }
sangho52abe3a2015-05-05 14:13:34 -0700554 // Populates MPLS rules to all routers
Saurav Dasa07f2032015-10-19 14:37:36 -0700555 log.debug("* populateEcmpRoutingRulePartial in device{} towards {} for all MPLS rules",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700556 targetSw, destSw);
sangho52abe3a2015-05-05 14:13:34 -0700557 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
558 if (!result) {
559 return false;
sanghob35a6192015-04-01 13:05:26 -0700560 }
sanghob35a6192015-04-01 13:05:26 -0700561 return true;
562 }
563
564 /**
Saurav Das822c4e22015-10-23 10:51:11 -0700565 * Populates filtering rules for permitting Router DstMac and VLAN.
sanghob35a6192015-04-01 13:05:26 -0700566 *
567 * @param deviceId Switch ID to set the rules
568 */
Saurav Das822c4e22015-10-23 10:51:11 -0700569 public void populatePortAddressingRules(DeviceId deviceId) {
Saurav Das822c4e22015-10-23 10:51:11 -0700570 rulePopulator.populateRouterIpPunts(deviceId);
Saurav Das59232cf2016-04-27 18:35:50 -0700571
572 // Although device is added, sometimes device store does not have the
573 // ports for this device yet. It results in missing filtering rules in the
574 // switch. We will attempt it a few times. If it still does not work,
575 // user can manually repopulate using CLI command sr-reroute-network
576 boolean success = rulePopulator.populateRouterMacVlanFilters(deviceId);
577 if (!success) {
578 executorService.schedule(new RetryFilters(deviceId), 200, TimeUnit.MILLISECONDS);
579 }
sanghob35a6192015-04-01 13:05:26 -0700580 }
581
582 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700583 * Start the flow rule population process if it was never started. The
584 * process finishes successfully when all flow rules are set and stops with
585 * ABORTED status when any groups required for flows is not set yet.
sanghob35a6192015-04-01 13:05:26 -0700586 */
587 public void startPopulationProcess() {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900588 statusLock.lock();
589 try {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700590 if (populationStatus == Status.IDLE
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700591 || populationStatus == Status.SUCCEEDED
592 || populationStatus == Status.ABORTED) {
sanghob35a6192015-04-01 13:05:26 -0700593 populationStatus = Status.STARTED;
594 populateAllRoutingRules();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700595 } else {
596 log.warn("Not initiating startPopulationProcess as populationStatus is {}",
597 populationStatus);
sanghob35a6192015-04-01 13:05:26 -0700598 }
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900599 } finally {
600 statusLock.unlock();
sanghob35a6192015-04-01 13:05:26 -0700601 }
602 }
603
604 /**
605 * Resume the flow rule population process if it was aborted for any reason.
606 * Mostly the process is aborted when the groups required are not set yet.
Saurav Dasa07f2032015-10-19 14:37:36 -0700607 * XXX is this called?
608 *
sanghob35a6192015-04-01 13:05:26 -0700609 */
610 public void resumePopulationProcess() {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900611 statusLock.lock();
612 try {
sanghob35a6192015-04-01 13:05:26 -0700613 if (populationStatus == Status.ABORTED) {
614 populationStatus = Status.STARTED;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700615 // TODO: we need to restart from the point aborted instead of
616 // restarting.
sanghob35a6192015-04-01 13:05:26 -0700617 populateAllRoutingRules();
618 }
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900619 } finally {
620 statusLock.unlock();
sanghob35a6192015-04-01 13:05:26 -0700621 }
622 }
Saurav Das80980c72016-03-23 11:22:49 -0700623
Charles Chan93e71ba2016-04-29 14:38:22 -0700624 /**
625 * Populate rules of given subnet at given location.
626 *
627 * @param cp connect point of the subnet being added
628 * @param subnets subnet being added
629 * @return true if succeed
630 */
631 protected boolean populateSubnet(ConnectPoint cp, Set<Ip4Prefix> subnets) {
632 statusLock.lock();
633 try {
634 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(cp.deviceId());
635 if (ecmpSpg == null) {
636 log.warn("Fail to populating subnet {}: {}", subnets, ECMPSPG_MISSING);
637 return false;
638 }
639 return populateEcmpRoutingRules(cp.deviceId(), ecmpSpg, subnets);
640 } finally {
641 statusLock.unlock();
642 }
643 }
644
645 /**
646 * Revoke rules of given subnet at given location.
647 *
648 * @param subnets subnet being removed
649 * @return true if succeed
650 */
651 protected boolean revokeSubnet(Set<Ip4Prefix> subnets) {
652 statusLock.lock();
653 try {
654 return srManager.routingRulePopulator.revokeIpRuleForSubnet(subnets);
655 } finally {
656 statusLock.unlock();
657 }
658 }
659
660 protected void purgeEcmpGraph(DeviceId deviceId) {
Saurav Das80980c72016-03-23 11:22:49 -0700661 currentEcmpSpgMap.remove(deviceId);
Saurav Das7a1ffca2016-03-28 19:00:18 -0700662 if (updatedEcmpSpgMap != null) {
663 updatedEcmpSpgMap.remove(deviceId);
664 }
Saurav Das80980c72016-03-23 11:22:49 -0700665 }
Saurav Das59232cf2016-04-27 18:35:50 -0700666
Charles Chan93e71ba2016-04-29 14:38:22 -0700667 private final class RetryFilters implements Runnable {
Saurav Das59232cf2016-04-27 18:35:50 -0700668 int attempts = MAX_RETRY_ATTEMPTS;
669 DeviceId devId;
670
Charles Chan93e71ba2016-04-29 14:38:22 -0700671 private RetryFilters(DeviceId deviceId) {
Saurav Das59232cf2016-04-27 18:35:50 -0700672 devId = deviceId;
673 }
674
675 @Override
676 public void run() {
Saurav Das25190812016-05-27 13:54:07 -0700677 log.info("RETRY FILTER ATTEMPT# {} for dev:{}",
678 MAX_RETRY_ATTEMPTS - attempts, devId);
Saurav Das59232cf2016-04-27 18:35:50 -0700679 boolean success = rulePopulator.populateRouterMacVlanFilters(devId);
680 if (!success && --attempts > 0) {
681 executorService.schedule(this, 200, TimeUnit.MILLISECONDS);
682 } else if (attempts == 0) {
683 log.error("Unable to populate MacVlan filters in dev:{}", devId);
684 }
685 }
686
687 }
688
sanghob35a6192015-04-01 13:05:26 -0700689}