blob: 01ffd6ea3e302c1964a6ede8364e984ed80143e2 [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;
Saurav Das4e3224f2016-11-29 14:27:25 -080019import com.google.common.collect.Lists;
sangho20eff1d2015-04-13 15:15:58 -070020import com.google.common.collect.Maps;
21import com.google.common.collect.Sets;
sangho666cd6d2015-04-14 16:27:13 -070022import org.onlab.packet.Ip4Address;
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070023import org.onlab.packet.Ip4Prefix;
sanghob35a6192015-04-01 13:05:26 -070024import org.onlab.packet.IpPrefix;
Charles Chan93e71ba2016-04-29 14:38:22 -070025import org.onosproject.net.ConnectPoint;
sanghob35a6192015-04-01 13:05:26 -070026import org.onosproject.net.Device;
27import org.onosproject.net.DeviceId;
sangho20eff1d2015-04-13 15:15:58 -070028import org.onosproject.net.Link;
Charles Chan0b4e6182015-11-03 10:42:14 -080029import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
30import org.onosproject.segmentrouting.config.DeviceConfiguration;
sanghob35a6192015-04-01 13:05:26 -070031import org.slf4j.Logger;
32import org.slf4j.LoggerFactory;
33
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070034import static java.util.concurrent.Executors.newScheduledThreadPool;
35import static org.onlab.util.Tools.groupedThreads;
36
sanghob35a6192015-04-01 13:05:26 -070037import java.util.ArrayList;
38import java.util.HashMap;
39import java.util.HashSet;
40import java.util.Set;
Saurav Das59232cf2016-04-27 18:35:50 -070041import java.util.concurrent.ScheduledExecutorService;
42import java.util.concurrent.TimeUnit;
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +090043import java.util.concurrent.locks.Lock;
44import java.util.concurrent.locks.ReentrantLock;
sanghob35a6192015-04-01 13:05:26 -070045
46import static com.google.common.base.Preconditions.checkNotNull;
47
Charles Chane849c192016-01-11 18:28:54 -080048/**
49 * Default routing handler that is responsible for route computing and
50 * routing rule population.
51 */
sanghob35a6192015-04-01 13:05:26 -070052public class DefaultRoutingHandler {
Saurav Das25190812016-05-27 13:54:07 -070053 private static final int MAX_RETRY_ATTEMPTS = 25;
Charles Chan93e71ba2016-04-29 14:38:22 -070054 private static final String ECMPSPG_MISSING = "ECMP shortest path graph not found";
55 private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
sanghob35a6192015-04-01 13:05:26 -070056
57 private SegmentRoutingManager srManager;
58 private RoutingRulePopulator rulePopulator;
Shashikanth VH013a7bc2015-12-11 01:32:44 +053059 private HashMap<DeviceId, EcmpShortestPathGraph> currentEcmpSpgMap;
60 private HashMap<DeviceId, EcmpShortestPathGraph> updatedEcmpSpgMap;
sangho666cd6d2015-04-14 16:27:13 -070061 private DeviceConfiguration config;
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +090062 private final Lock statusLock = new ReentrantLock();
63 private volatile Status populationStatus;
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070064 private ScheduledExecutorService executorService
65 = newScheduledThreadPool(1, groupedThreads("RoutingHandler", "retry-%d", log));
sanghob35a6192015-04-01 13:05:26 -070066
67 /**
68 * Represents the default routing population status.
69 */
70 public enum Status {
71 // population process is not started yet.
72 IDLE,
73
74 // population process started.
75 STARTED,
76
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070077 // population process was aborted due to errors, mostly for groups not
78 // found.
sanghob35a6192015-04-01 13:05:26 -070079 ABORTED,
80
81 // population process was finished successfully.
82 SUCCEEDED
83 }
84
85 /**
86 * Creates a DefaultRoutingHandler object.
87 *
88 * @param srManager SegmentRoutingManager object
89 */
90 public DefaultRoutingHandler(SegmentRoutingManager srManager) {
91 this.srManager = srManager;
92 this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
sangho666cd6d2015-04-14 16:27:13 -070093 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070094 this.populationStatus = Status.IDLE;
sangho20eff1d2015-04-13 15:15:58 -070095 this.currentEcmpSpgMap = Maps.newHashMap();
sanghob35a6192015-04-01 13:05:26 -070096 }
97
98 /**
99 * Populates all routing rules to all connected routers, including default
100 * routing rules, adjacency rules, and policy rules if any.
101 *
102 * @return true if it succeeds in populating all rules, otherwise false
103 */
104 public boolean populateAllRoutingRules() {
105
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900106 statusLock.lock();
107 try {
108 populationStatus = Status.STARTED;
109 rulePopulator.resetCounter();
Saurav Dasa07f2032015-10-19 14:37:36 -0700110 log.info("Starting to populate segment-routing rules");
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900111 log.debug("populateAllRoutingRules: populationStatus is STARTED");
sanghob35a6192015-04-01 13:05:26 -0700112
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900113 for (Device sw : srManager.deviceService.getDevices()) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700114 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900115 log.debug("populateAllRoutingRules: skipping device {}...we are not master",
116 sw.id());
117 continue;
118 }
119
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530120 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(sw.id(), srManager);
Charles Chan93e71ba2016-04-29 14:38:22 -0700121 if (!populateEcmpRoutingRules(sw.id(), ecmpSpg, ImmutableSet.of())) {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900122 log.debug("populateAllRoutingRules: populationStatus is ABORTED");
123 populationStatus = Status.ABORTED;
124 log.debug("Abort routing rule population");
125 return false;
126 }
127 currentEcmpSpgMap.put(sw.id(), ecmpSpg);
128
129 // TODO: Set adjacency routing rule for all switches
sanghob35a6192015-04-01 13:05:26 -0700130 }
131
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900132 log.debug("populateAllRoutingRules: populationStatus is SUCCEEDED");
133 populationStatus = Status.SUCCEEDED;
Saurav Dasa07f2032015-10-19 14:37:36 -0700134 log.info("Completed routing rule population. Total # of rules pushed : {}",
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900135 rulePopulator.getCounter());
136 return true;
137 } finally {
138 statusLock.unlock();
sanghob35a6192015-04-01 13:05:26 -0700139 }
sanghob35a6192015-04-01 13:05:26 -0700140 }
141
sangho20eff1d2015-04-13 15:15:58 -0700142 /**
143 * Populates the routing rules according to the route changes due to the link
144 * failure or link add. It computes the routes changed due to the link changes and
Saurav Das4e3224f2016-11-29 14:27:25 -0800145 * repopulates the rules only for these routes. Note that when a switch goes
146 * away, all of its links fail as well, but this is handled as a single
147 * switch removal event.
sangho20eff1d2015-04-13 15:15:58 -0700148 *
Saurav Das4e3224f2016-11-29 14:27:25 -0800149 * @param failedLink the single failed link, or null for other conditions
150 * such as an added link or a removed switch
sangho20eff1d2015-04-13 15:15:58 -0700151 * @return true if it succeeds to populate all rules, false otherwise
152 */
Saurav Das4e3224f2016-11-29 14:27:25 -0800153 public boolean populateRoutingRulesForLinkStatusChange(Link failedLink) {
sangho20eff1d2015-04-13 15:15:58 -0700154
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900155 statusLock.lock();
156 try {
sangho20eff1d2015-04-13 15:15:58 -0700157
158 if (populationStatus == Status.STARTED) {
sangho52abe3a2015-05-05 14:13:34 -0700159 log.warn("Previous rule population is not finished.");
sangho20eff1d2015-04-13 15:15:58 -0700160 return true;
161 }
162
sangho45b009c2015-05-07 13:30:57 -0700163 // Take the snapshots of the links
164 updatedEcmpSpgMap = new HashMap<>();
165 for (Device sw : srManager.deviceService.getDevices()) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700166 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
sangho45b009c2015-05-07 13:30:57 -0700167 continue;
168 }
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530169 EcmpShortestPathGraph ecmpSpgUpdated =
170 new EcmpShortestPathGraph(sw.id(), srManager);
sangho45b009c2015-05-07 13:30:57 -0700171 updatedEcmpSpgMap.put(sw.id(), ecmpSpgUpdated);
172 }
173
sangho52abe3a2015-05-05 14:13:34 -0700174 log.info("Starts rule population from link change");
175
sangho20eff1d2015-04-13 15:15:58 -0700176 Set<ArrayList<DeviceId>> routeChanges;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700177 log.trace("populateRoutingRulesForLinkStatusChange: "
178 + "populationStatus is STARTED");
sangho20eff1d2015-04-13 15:15:58 -0700179 populationStatus = Status.STARTED;
Saurav Das4e3224f2016-11-29 14:27:25 -0800180 // try optimized re-routing
181 if (failedLink == null) {
182 // Compare all routes of existing ECMP SPG to new ECMP SPG
sangho20eff1d2015-04-13 15:15:58 -0700183 routeChanges = computeRouteChange();
184 } else {
185 // Compare existing ECMP SPG only with the link removed
Saurav Das4e3224f2016-11-29 14:27:25 -0800186 routeChanges = computeDamagedRoutes(failedLink);
sangho20eff1d2015-04-13 15:15:58 -0700187 }
188
Saurav Das4e3224f2016-11-29 14:27:25 -0800189 // do full re-routing if optimized routing returns null routeChanges
Saurav Dasb5c236e2016-06-07 10:08:06 -0700190 if (routeChanges == null) {
191 return populateAllRoutingRules();
192 }
193
sangho20eff1d2015-04-13 15:15:58 -0700194 if (routeChanges.isEmpty()) {
sangho52abe3a2015-05-05 14:13:34 -0700195 log.info("No route changes for the link status change");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700196 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
sangho20eff1d2015-04-13 15:15:58 -0700197 populationStatus = Status.SUCCEEDED;
198 return true;
199 }
200
201 if (repopulateRoutingRulesForRoutes(routeChanges)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700202 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
sangho20eff1d2015-04-13 15:15:58 -0700203 populationStatus = Status.SUCCEEDED;
204 log.info("Complete to repopulate the rules. # of rules populated : {}",
205 rulePopulator.getCounter());
206 return true;
207 } else {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700208 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is ABORTED");
sangho20eff1d2015-04-13 15:15:58 -0700209 populationStatus = Status.ABORTED;
210 log.warn("Failed to repopulate the rules.");
211 return false;
212 }
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900213 } finally {
214 statusLock.unlock();
sangho20eff1d2015-04-13 15:15:58 -0700215 }
216 }
217
218 private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
219 rulePopulator.resetCounter();
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700220 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> routesBydevice =
221 new HashMap<>();
sangho20eff1d2015-04-13 15:15:58 -0700222 for (ArrayList<DeviceId> link: routes) {
sangho834e4b02015-05-01 09:38:25 -0700223 // When only the source device is defined, reinstall routes to all other devices
sangho20eff1d2015-04-13 15:15:58 -0700224 if (link.size() == 1) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700225 log.trace("repopulateRoutingRulesForRoutes: running ECMP graph for device {}", link.get(0));
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530226 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(link.get(0), srManager);
Charles Chan93e71ba2016-04-29 14:38:22 -0700227 if (populateEcmpRoutingRules(link.get(0), ecmpSpg, ImmutableSet.of())) {
Saurav Das25190812016-05-27 13:54:07 -0700228 log.debug("Populating flow rules from all to dest:{} is successful",
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700229 link.get(0));
sangho20eff1d2015-04-13 15:15:58 -0700230 currentEcmpSpgMap.put(link.get(0), ecmpSpg);
sangho52abe3a2015-05-05 14:13:34 -0700231 } else {
Saurav Das25190812016-05-27 13:54:07 -0700232 log.warn("Failed to populate the flow rules from all to dest:{}", link.get(0));
sangho52abe3a2015-05-05 14:13:34 -0700233 return false;
sangho20eff1d2015-04-13 15:15:58 -0700234 }
sangho45b009c2015-05-07 13:30:57 -0700235 } else {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700236 ArrayList<ArrayList<DeviceId>> deviceRoutes =
237 routesBydevice.get(link.get(1));
238 if (deviceRoutes == null) {
239 deviceRoutes = new ArrayList<>();
240 routesBydevice.put(link.get(1), deviceRoutes);
241 }
242 deviceRoutes.add(link);
243 }
244 }
245
246 for (DeviceId impactedDevice : routesBydevice.keySet()) {
247 ArrayList<ArrayList<DeviceId>> deviceRoutes =
248 routesBydevice.get(impactedDevice);
249 for (ArrayList<DeviceId> link: deviceRoutes) {
250 log.debug("repopulate RoutingRules For Routes {} -> {}",
251 link.get(0), link.get(1));
sangho45b009c2015-05-07 13:30:57 -0700252 DeviceId src = link.get(0);
253 DeviceId dst = link.get(1);
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530254 EcmpShortestPathGraph ecmpSpg = updatedEcmpSpgMap.get(dst);
sangho45b009c2015-05-07 13:30:57 -0700255 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
256 ecmpSpg.getAllLearnedSwitchesAndVia();
257 for (Integer itrIdx : switchVia.keySet()) {
258 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
259 switchVia.get(itrIdx);
260 for (DeviceId targetSw : swViaMap.keySet()) {
261 if (!targetSw.equals(src)) {
262 continue;
263 }
264 Set<DeviceId> nextHops = new HashSet<>();
265 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
266 if (via.isEmpty()) {
267 nextHops.add(dst);
268 } else {
269 nextHops.add(via.get(0));
270 }
271 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700272 if (!populateEcmpRoutingRulePartial(targetSw, dst,
273 nextHops, ImmutableSet.of())) {
sangho45b009c2015-05-07 13:30:57 -0700274 return false;
sangho20eff1d2015-04-13 15:15:58 -0700275 }
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700276 log.debug("Populating flow rules from {} to {} is successful",
277 targetSw, dst);
sangho20eff1d2015-04-13 15:15:58 -0700278 }
sangho20eff1d2015-04-13 15:15:58 -0700279 }
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700280 //currentEcmpSpgMap.put(dst, ecmpSpg);
sangho20eff1d2015-04-13 15:15:58 -0700281 }
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700282 //Only if all the flows for all impacted routes to a
283 //specific target are pushed successfully, update the
Saurav Das4e3224f2016-11-29 14:27:25 -0800284 //ECMP graph for that target. Or else the next event
285 //would not see any changes in the ECMP graphs.
286 //In another case, the target switch has gone away, so
287 //routes can't be installed. In that case, the current map
288 //is updated here, without any flows being pushed.
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700289 currentEcmpSpgMap.put(impactedDevice,
290 updatedEcmpSpgMap.get(impactedDevice));
sangho20eff1d2015-04-13 15:15:58 -0700291 }
292 return true;
293 }
294
Saurav Dasb5c236e2016-06-07 10:08:06 -0700295 /**
Saurav Das4e3224f2016-11-29 14:27:25 -0800296 * Computes set of affected routes due to failed link. Assumes
Saurav Dasb5c236e2016-06-07 10:08:06 -0700297 * previous ecmp shortest-path graph exists for a switch in order to compute
298 * affected routes. If such a graph does not exist, the method returns null.
299 *
300 * @param linkFail the failed link
301 * @return the set of affected routes which may be empty if no routes were
302 * affected, or null if no previous ecmp spg was found for comparison
303 */
sangho20eff1d2015-04-13 15:15:58 -0700304 private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
305
306 Set<ArrayList<DeviceId>> routes = new HashSet<>();
307
308 for (Device sw : srManager.deviceService.getDevices()) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700309 log.debug("Computing the impacted routes for device {} due to link fail",
310 sw.id());
Charles Chanc42e84e2015-10-20 16:24:19 -0700311 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700312 log.debug("No mastership for {} .. skipping route optimization",
313 sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700314 continue;
315 }
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530316 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700317 if (ecmpSpg == null) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700318 log.warn("No existing ECMP graph for switch {}. Aborting optimized"
319 + " rerouting and opting for full-reroute", sw.id());
320 return null;
sangho20eff1d2015-04-13 15:15:58 -0700321 }
322 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
323 ecmpSpg.getAllLearnedSwitchesAndVia();
324 for (Integer itrIdx : switchVia.keySet()) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700325 log.trace("Iterindex# {}", itrIdx);
sangho20eff1d2015-04-13 15:15:58 -0700326 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
327 switchVia.get(itrIdx);
328 for (DeviceId targetSw : swViaMap.keySet()) {
Saurav Das4e3224f2016-11-29 14:27:25 -0800329 DeviceId rootSw = sw.id();
Saurav Dasb5c236e2016-06-07 10:08:06 -0700330 if (log.isTraceEnabled()) {
Saurav Das4e3224f2016-11-29 14:27:25 -0800331 log.trace("TargetSwitch {} --> RootSwitch {}", targetSw, rootSw);
Saurav Dasb5c236e2016-06-07 10:08:06 -0700332 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
333 log.trace(" Via:");
334 via.forEach(e -> { log.trace(" {}", e); });
335 }
336 }
sangho20eff1d2015-04-13 15:15:58 -0700337 Set<ArrayList<DeviceId>> subLinks =
Saurav Das4e3224f2016-11-29 14:27:25 -0800338 computeLinks(targetSw, rootSw, swViaMap);
sangho20eff1d2015-04-13 15:15:58 -0700339 for (ArrayList<DeviceId> alink: subLinks) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700340 if ((alink.get(0).equals(linkFail.src().deviceId()) &&
341 alink.get(1).equals(linkFail.dst().deviceId()))
342 ||
343 (alink.get(0).equals(linkFail.dst().deviceId()) &&
344 alink.get(1).equals(linkFail.src().deviceId()))) {
Saurav Das4e3224f2016-11-29 14:27:25 -0800345 log.debug("Impacted route:{}->{}", targetSw, rootSw);
sangho20eff1d2015-04-13 15:15:58 -0700346 ArrayList<DeviceId> aRoute = new ArrayList<>();
347 aRoute.add(targetSw);
Saurav Das4e3224f2016-11-29 14:27:25 -0800348 aRoute.add(rootSw);
sangho20eff1d2015-04-13 15:15:58 -0700349 routes.add(aRoute);
350 break;
351 }
352 }
353 }
354 }
sangho45b009c2015-05-07 13:30:57 -0700355
sangho20eff1d2015-04-13 15:15:58 -0700356 }
357
358 return routes;
359 }
360
Saurav Das4e3224f2016-11-29 14:27:25 -0800361 /**
362 * Computes set of affected routes due to new links or failed switches.
363 *
364 * @return the set of affected routes which may be empty if no routes were
365 * affected
366 */
sangho20eff1d2015-04-13 15:15:58 -0700367 private Set<ArrayList<DeviceId>> computeRouteChange() {
368
Saurav Das4e3224f2016-11-29 14:27:25 -0800369 ImmutableSet.Builder<ArrayList<DeviceId>> changedRoutesBuilder =
370 ImmutableSet.builder();
sangho20eff1d2015-04-13 15:15:58 -0700371
372 for (Device sw : srManager.deviceService.getDevices()) {
Saurav Das4e3224f2016-11-29 14:27:25 -0800373 DeviceId rootSw = sw.id();
374 log.debug("Computing the impacted routes for device {}", rootSw);
375 if (!srManager.mastershipService.isLocalMaster(rootSw)) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700376 log.debug("No mastership for {} ... skipping route optimization",
Saurav Das4e3224f2016-11-29 14:27:25 -0800377 rootSw);
sangho20eff1d2015-04-13 15:15:58 -0700378 continue;
379 }
Saurav Dasb5c236e2016-06-07 10:08:06 -0700380 if (log.isTraceEnabled()) {
Saurav Das4e3224f2016-11-29 14:27:25 -0800381 log.trace("link of {} - ", rootSw);
382 for (Link link: srManager.linkService.getDeviceLinks(rootSw)) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700383 log.trace("{} -> {} ", link.src().deviceId(), link.dst().deviceId());
384 }
sangho45b009c2015-05-07 13:30:57 -0700385 }
Saurav Das4e3224f2016-11-29 14:27:25 -0800386 EcmpShortestPathGraph currEcmpSpg = currentEcmpSpgMap.get(rootSw);
387 if (currEcmpSpg == null) {
388 log.debug("No existing ECMP graph for device {}", rootSw);
389 changedRoutesBuilder.add(Lists.newArrayList(rootSw));
sangho20eff1d2015-04-13 15:15:58 -0700390 continue;
391 }
Saurav Das4e3224f2016-11-29 14:27:25 -0800392 EcmpShortestPathGraph newEcmpSpg = updatedEcmpSpgMap.get(rootSw);
393 if (log.isTraceEnabled()) {
394 log.trace("Root switch: {}", rootSw);
395 log.trace(" Current/Existing SPG: {}", currEcmpSpg);
396 log.trace(" New/Updated SPG: {}", newEcmpSpg);
397 }
398 // first use the updated/new map to compare to current/existing map
399 // as new links may have come up
400 changedRoutesBuilder.addAll(compareGraphs(newEcmpSpg, currEcmpSpg, rootSw));
401 // then use the current/existing map to compare to updated/new map
402 // as switch may have been removed
403 changedRoutesBuilder.addAll(compareGraphs(currEcmpSpg, newEcmpSpg, rootSw));
404 }
sangho20eff1d2015-04-13 15:15:58 -0700405
Saurav Das4e3224f2016-11-29 14:27:25 -0800406 Set<ArrayList<DeviceId>> changedRoutes = changedRoutesBuilder.build();
407 for (ArrayList<DeviceId> route: changedRoutes) {
408 log.debug("Route changes Target -> Root");
409 if (route.size() == 1) {
410 log.debug(" : all -> {}", route.get(0));
411 } else {
412 log.debug(" : {} -> {}", route.get(0), route.get(1));
413 }
414 }
415 return changedRoutes;
416 }
417
418 /**
419 * For the root switch, searches all the target nodes reachable in the base
420 * graph, and compares paths to the ones in the comp graph.
421 *
422 * @param base the graph that is indexed for all reachable target nodes
423 * from the root node
424 * @param comp the graph that the base graph is compared to
425 * @param rootSw both ecmp graphs are calculated for the root node
426 * @return all the routes that have changed in the base graph
427 */
428 private Set<ArrayList<DeviceId>> compareGraphs(EcmpShortestPathGraph base,
429 EcmpShortestPathGraph comp,
430 DeviceId rootSw) {
431 ImmutableSet.Builder<ArrayList<DeviceId>> changedRoutesBuilder =
432 ImmutableSet.builder();
433 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> baseMap =
434 base.getAllLearnedSwitchesAndVia();
435 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> compMap =
436 comp.getAllLearnedSwitchesAndVia();
437 for (Integer itrIdx : baseMap.keySet()) {
438 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> baseViaMap =
439 baseMap.get(itrIdx);
440 for (DeviceId targetSw : baseViaMap.keySet()) {
441 ArrayList<ArrayList<DeviceId>> basePath = baseViaMap.get(targetSw);
442 ArrayList<ArrayList<DeviceId>> compPath = getVia(compMap, targetSw);
443 if ((compPath == null) || !basePath.equals(compPath)) {
444 log.debug("Impacted route:{} -> {}", targetSw, rootSw);
445 ArrayList<DeviceId> route = new ArrayList<>();
446 route.add(targetSw);
447 route.add(rootSw);
448 changedRoutesBuilder.add(route);
sangho20eff1d2015-04-13 15:15:58 -0700449 }
450 }
sangho45b009c2015-05-07 13:30:57 -0700451 }
Saurav Das4e3224f2016-11-29 14:27:25 -0800452 return changedRoutesBuilder.build();
sangho20eff1d2015-04-13 15:15:58 -0700453 }
454
455 private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
Saurav Das4e3224f2016-11-29 14:27:25 -0800456 ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId targetSw) {
sangho20eff1d2015-04-13 15:15:58 -0700457 for (Integer itrIdx : switchVia.keySet()) {
458 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
459 switchVia.get(itrIdx);
Saurav Das4e3224f2016-11-29 14:27:25 -0800460 if (swViaMap.get(targetSw) == null) {
sangho20eff1d2015-04-13 15:15:58 -0700461 continue;
462 } else {
Saurav Das4e3224f2016-11-29 14:27:25 -0800463 return swViaMap.get(targetSw);
sangho20eff1d2015-04-13 15:15:58 -0700464 }
465 }
466
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700467 return null;
sangho20eff1d2015-04-13 15:15:58 -0700468 }
469
470 private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
471 DeviceId dst,
472 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
473 Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
474 for (ArrayList<DeviceId> via : viaMap.get(src)) {
475 DeviceId linkSrc = src;
476 DeviceId linkDst = dst;
477 for (DeviceId viaDevice: via) {
478 ArrayList<DeviceId> link = new ArrayList<>();
479 linkDst = viaDevice;
480 link.add(linkSrc);
481 link.add(linkDst);
482 subLinks.add(link);
483 linkSrc = viaDevice;
484 }
485 ArrayList<DeviceId> link = new ArrayList<>();
486 link.add(linkSrc);
487 link.add(dst);
488 subLinks.add(link);
489 }
490
491 return subLinks;
492 }
493
Charles Chan93e71ba2016-04-29 14:38:22 -0700494 /**
495 * Populate ECMP rules for subnets from all switches to destination.
496 *
497 * @param destSw Device ID of destination switch
498 * @param ecmpSPG ECMP shortest path graph
499 * @param subnets Subnets to be populated. If empty, populate all configured subnets.
500 * @return true if succeed
501 */
sangho20eff1d2015-04-13 15:15:58 -0700502 private boolean populateEcmpRoutingRules(DeviceId destSw,
Charles Chan93e71ba2016-04-29 14:38:22 -0700503 EcmpShortestPathGraph ecmpSPG,
504 Set<Ip4Prefix> subnets) {
sanghob35a6192015-04-01 13:05:26 -0700505
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700506 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = ecmpSPG
507 .getAllLearnedSwitchesAndVia();
sanghob35a6192015-04-01 13:05:26 -0700508 for (Integer itrIdx : switchVia.keySet()) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700509 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap = switchVia
510 .get(itrIdx);
sanghob35a6192015-04-01 13:05:26 -0700511 for (DeviceId targetSw : swViaMap.keySet()) {
sanghob35a6192015-04-01 13:05:26 -0700512 Set<DeviceId> nextHops = new HashSet<>();
Saurav Dasa07f2032015-10-19 14:37:36 -0700513 log.debug("** Iter: {} root: {} target: {}", itrIdx, destSw, targetSw);
sanghob35a6192015-04-01 13:05:26 -0700514 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
515 if (via.isEmpty()) {
516 nextHops.add(destSw);
517 } else {
518 nextHops.add(via.get(0));
519 }
520 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700521 if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops, subnets)) {
sanghob35a6192015-04-01 13:05:26 -0700522 return false;
523 }
524 }
525 }
526
527 return true;
528 }
529
Charles Chan93e71ba2016-04-29 14:38:22 -0700530 /**
531 * Populate ECMP rules for subnets from target to destination via nexthops.
532 *
Saurav Das25190812016-05-27 13:54:07 -0700533 * @param targetSw Device ID of target switch in which rules will be programmed
534 * @param destSw Device ID of final destination switch to which the rules will forward
535 * @param nextHops List of next hops via which destSw will be reached
Charles Chan93e71ba2016-04-29 14:38:22 -0700536 * @param subnets Subnets to be populated. If empty, populate all configured subnets.
537 * @return true if succeed
538 */
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700539 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw,
540 DeviceId destSw,
Charles Chan93e71ba2016-04-29 14:38:22 -0700541 Set<DeviceId> nextHops,
542 Set<Ip4Prefix> subnets) {
sanghob35a6192015-04-01 13:05:26 -0700543 boolean result;
544
545 if (nextHops.isEmpty()) {
546 nextHops.add(destSw);
547 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700548 // If both target switch and dest switch are edge routers, then set IP
sangho52abe3a2015-05-05 14:13:34 -0700549 // rule for both subnet and router IP.
Charles Chan0b4e6182015-11-03 10:42:14 -0800550 boolean targetIsEdge;
551 boolean destIsEdge;
552 Ip4Address destRouterIp;
553
554 try {
555 targetIsEdge = config.isEdgeDevice(targetSw);
556 destIsEdge = config.isEdgeDevice(destSw);
557 destRouterIp = config.getRouterIp(destSw);
558 } catch (DeviceConfigNotFoundException e) {
559 log.warn(e.getMessage() + " Aborting populateEcmpRoutingRulePartial.");
560 return false;
561 }
562
563 if (targetIsEdge && destIsEdge) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700564 subnets = (subnets != null && !subnets.isEmpty()) ? subnets : config.getSubnets(destSw);
Saurav Dasa07f2032015-10-19 14:37:36 -0700565 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for subnets {}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800566 targetSw, destSw, subnets);
Charles Chan93e71ba2016-04-29 14:38:22 -0700567 result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets,
568 destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700569 if (!result) {
570 return false;
571 }
572
Charles Chan0b4e6182015-11-03 10:42:14 -0800573 Ip4Address routerIp = destRouterIp;
sangho666cd6d2015-04-14 16:27:13 -0700574 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
Saurav Dasa07f2032015-10-19 14:37:36 -0700575 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800576 targetSw, destSw, routerIpPrefix);
sangho666cd6d2015-04-14 16:27:13 -0700577 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700578 if (!result) {
579 return false;
580 }
581
Charles Chan0b4e6182015-11-03 10:42:14 -0800582 } else if (targetIsEdge) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800583 // If the target switch is an edge router, then set IP rules for the router IP.
Charles Chan0b4e6182015-11-03 10:42:14 -0800584 Ip4Address routerIp = destRouterIp;
sangho666cd6d2015-04-14 16:27:13 -0700585 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
Saurav Dasa07f2032015-10-19 14:37:36 -0700586 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800587 targetSw, destSw, routerIpPrefix);
sangho666cd6d2015-04-14 16:27:13 -0700588 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700589 if (!result) {
590 return false;
591 }
sangho52abe3a2015-05-05 14:13:34 -0700592 }
sangho52abe3a2015-05-05 14:13:34 -0700593 // Populates MPLS rules to all routers
Saurav Dasa07f2032015-10-19 14:37:36 -0700594 log.debug("* populateEcmpRoutingRulePartial in device{} towards {} for all MPLS rules",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700595 targetSw, destSw);
sangho52abe3a2015-05-05 14:13:34 -0700596 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
597 if (!result) {
598 return false;
sanghob35a6192015-04-01 13:05:26 -0700599 }
sanghob35a6192015-04-01 13:05:26 -0700600 return true;
601 }
602
603 /**
Saurav Das822c4e22015-10-23 10:51:11 -0700604 * Populates filtering rules for permitting Router DstMac and VLAN.
sanghob35a6192015-04-01 13:05:26 -0700605 *
606 * @param deviceId Switch ID to set the rules
607 */
Saurav Das822c4e22015-10-23 10:51:11 -0700608 public void populatePortAddressingRules(DeviceId deviceId) {
Saurav Das822c4e22015-10-23 10:51:11 -0700609 rulePopulator.populateRouterIpPunts(deviceId);
Saurav Das59232cf2016-04-27 18:35:50 -0700610
611 // Although device is added, sometimes device store does not have the
612 // ports for this device yet. It results in missing filtering rules in the
613 // switch. We will attempt it a few times. If it still does not work,
614 // user can manually repopulate using CLI command sr-reroute-network
615 boolean success = rulePopulator.populateRouterMacVlanFilters(deviceId);
616 if (!success) {
617 executorService.schedule(new RetryFilters(deviceId), 200, TimeUnit.MILLISECONDS);
618 }
sanghob35a6192015-04-01 13:05:26 -0700619 }
620
621 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700622 * Start the flow rule population process if it was never started. The
623 * process finishes successfully when all flow rules are set and stops with
624 * ABORTED status when any groups required for flows is not set yet.
sanghob35a6192015-04-01 13:05:26 -0700625 */
626 public void startPopulationProcess() {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900627 statusLock.lock();
628 try {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700629 if (populationStatus == Status.IDLE
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700630 || populationStatus == Status.SUCCEEDED
631 || populationStatus == Status.ABORTED) {
sanghob35a6192015-04-01 13:05:26 -0700632 populationStatus = Status.STARTED;
633 populateAllRoutingRules();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700634 } else {
635 log.warn("Not initiating startPopulationProcess as populationStatus is {}",
636 populationStatus);
sanghob35a6192015-04-01 13:05:26 -0700637 }
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900638 } finally {
639 statusLock.unlock();
sanghob35a6192015-04-01 13:05:26 -0700640 }
641 }
642
643 /**
644 * Resume the flow rule population process if it was aborted for any reason.
645 * Mostly the process is aborted when the groups required are not set yet.
Saurav Dasa07f2032015-10-19 14:37:36 -0700646 * XXX is this called?
647 *
sanghob35a6192015-04-01 13:05:26 -0700648 */
649 public void resumePopulationProcess() {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900650 statusLock.lock();
651 try {
sanghob35a6192015-04-01 13:05:26 -0700652 if (populationStatus == Status.ABORTED) {
653 populationStatus = Status.STARTED;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700654 // TODO: we need to restart from the point aborted instead of
655 // restarting.
sanghob35a6192015-04-01 13:05:26 -0700656 populateAllRoutingRules();
657 }
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900658 } finally {
659 statusLock.unlock();
sanghob35a6192015-04-01 13:05:26 -0700660 }
661 }
Saurav Das80980c72016-03-23 11:22:49 -0700662
Charles Chan93e71ba2016-04-29 14:38:22 -0700663 /**
664 * Populate rules of given subnet at given location.
665 *
666 * @param cp connect point of the subnet being added
667 * @param subnets subnet being added
668 * @return true if succeed
669 */
670 protected boolean populateSubnet(ConnectPoint cp, Set<Ip4Prefix> subnets) {
671 statusLock.lock();
672 try {
673 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(cp.deviceId());
674 if (ecmpSpg == null) {
675 log.warn("Fail to populating subnet {}: {}", subnets, ECMPSPG_MISSING);
676 return false;
677 }
678 return populateEcmpRoutingRules(cp.deviceId(), ecmpSpg, subnets);
679 } finally {
680 statusLock.unlock();
681 }
682 }
683
684 /**
685 * Revoke rules of given subnet at given location.
686 *
687 * @param subnets subnet being removed
688 * @return true if succeed
689 */
690 protected boolean revokeSubnet(Set<Ip4Prefix> subnets) {
691 statusLock.lock();
692 try {
693 return srManager.routingRulePopulator.revokeIpRuleForSubnet(subnets);
694 } finally {
695 statusLock.unlock();
696 }
697 }
698
699 protected void purgeEcmpGraph(DeviceId deviceId) {
Saurav Das80980c72016-03-23 11:22:49 -0700700 currentEcmpSpgMap.remove(deviceId);
Saurav Das7a1ffca2016-03-28 19:00:18 -0700701 if (updatedEcmpSpgMap != null) {
702 updatedEcmpSpgMap.remove(deviceId);
703 }
Pier Ventre2c515312016-09-13 21:33:40 -0700704 this.populateRoutingRulesForLinkStatusChange(null);
Saurav Das80980c72016-03-23 11:22:49 -0700705 }
Saurav Das59232cf2016-04-27 18:35:50 -0700706
Charles Chan93e71ba2016-04-29 14:38:22 -0700707 private final class RetryFilters implements Runnable {
Saurav Das59232cf2016-04-27 18:35:50 -0700708 int attempts = MAX_RETRY_ATTEMPTS;
709 DeviceId devId;
710
Charles Chan93e71ba2016-04-29 14:38:22 -0700711 private RetryFilters(DeviceId deviceId) {
Saurav Das59232cf2016-04-27 18:35:50 -0700712 devId = deviceId;
713 }
714
715 @Override
716 public void run() {
Saurav Das25190812016-05-27 13:54:07 -0700717 log.info("RETRY FILTER ATTEMPT# {} for dev:{}",
718 MAX_RETRY_ATTEMPTS - attempts, devId);
Saurav Das59232cf2016-04-27 18:35:50 -0700719 boolean success = rulePopulator.populateRouterMacVlanFilters(devId);
720 if (!success && --attempts > 0) {
721 executorService.schedule(this, 200, TimeUnit.MILLISECONDS);
722 } else if (attempts == 0) {
723 log.error("Unable to populate MacVlan filters in dev:{}", devId);
724 }
725 }
726
727 }
728
sanghob35a6192015-04-01 13:05:26 -0700729}