blob: 13247951bc3843e5858f82edeb471a79065a179a [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
33import java.util.ArrayList;
34import java.util.HashMap;
35import java.util.HashSet;
36import java.util.Set;
Saurav Das59232cf2016-04-27 18:35:50 -070037import java.util.concurrent.Executors;
38import java.util.concurrent.ScheduledExecutorService;
39import java.util.concurrent.TimeUnit;
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +090040import java.util.concurrent.locks.Lock;
41import java.util.concurrent.locks.ReentrantLock;
sanghob35a6192015-04-01 13:05:26 -070042
43import static com.google.common.base.Preconditions.checkNotNull;
44
Charles Chane849c192016-01-11 18:28:54 -080045/**
46 * Default routing handler that is responsible for route computing and
47 * routing rule population.
48 */
sanghob35a6192015-04-01 13:05:26 -070049public class DefaultRoutingHandler {
Charles Chan93e71ba2016-04-29 14:38:22 -070050 private static final int MAX_RETRY_ATTEMPTS = 5;
51 private static final String ECMPSPG_MISSING = "ECMP shortest path graph not found";
52 private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
sanghob35a6192015-04-01 13:05:26 -070053
54 private SegmentRoutingManager srManager;
55 private RoutingRulePopulator rulePopulator;
Shashikanth VH013a7bc2015-12-11 01:32:44 +053056 private HashMap<DeviceId, EcmpShortestPathGraph> currentEcmpSpgMap;
57 private HashMap<DeviceId, EcmpShortestPathGraph> updatedEcmpSpgMap;
sangho666cd6d2015-04-14 16:27:13 -070058 private DeviceConfiguration config;
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +090059 private final Lock statusLock = new ReentrantLock();
60 private volatile Status populationStatus;
Saurav Das59232cf2016-04-27 18:35:50 -070061 private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
sanghob35a6192015-04-01 13:05:26 -070062
63 /**
64 * Represents the default routing population status.
65 */
66 public enum Status {
67 // population process is not started yet.
68 IDLE,
69
70 // population process started.
71 STARTED,
72
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070073 // population process was aborted due to errors, mostly for groups not
74 // found.
sanghob35a6192015-04-01 13:05:26 -070075 ABORTED,
76
77 // population process was finished successfully.
78 SUCCEEDED
79 }
80
81 /**
82 * Creates a DefaultRoutingHandler object.
83 *
84 * @param srManager SegmentRoutingManager object
85 */
86 public DefaultRoutingHandler(SegmentRoutingManager srManager) {
87 this.srManager = srManager;
88 this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
sangho666cd6d2015-04-14 16:27:13 -070089 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070090 this.populationStatus = Status.IDLE;
sangho20eff1d2015-04-13 15:15:58 -070091 this.currentEcmpSpgMap = Maps.newHashMap();
sanghob35a6192015-04-01 13:05:26 -070092 }
93
94 /**
95 * Populates all routing rules to all connected routers, including default
96 * routing rules, adjacency rules, and policy rules if any.
97 *
98 * @return true if it succeeds in populating all rules, otherwise false
99 */
100 public boolean populateAllRoutingRules() {
101
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900102 statusLock.lock();
103 try {
104 populationStatus = Status.STARTED;
105 rulePopulator.resetCounter();
Saurav Dasa07f2032015-10-19 14:37:36 -0700106 log.info("Starting to populate segment-routing rules");
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900107 log.debug("populateAllRoutingRules: populationStatus is STARTED");
sanghob35a6192015-04-01 13:05:26 -0700108
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900109 for (Device sw : srManager.deviceService.getDevices()) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700110 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900111 log.debug("populateAllRoutingRules: skipping device {}...we are not master",
112 sw.id());
113 continue;
114 }
115
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530116 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(sw.id(), srManager);
Charles Chan93e71ba2016-04-29 14:38:22 -0700117 if (!populateEcmpRoutingRules(sw.id(), ecmpSpg, ImmutableSet.of())) {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900118 log.debug("populateAllRoutingRules: populationStatus is ABORTED");
119 populationStatus = Status.ABORTED;
120 log.debug("Abort routing rule population");
121 return false;
122 }
123 currentEcmpSpgMap.put(sw.id(), ecmpSpg);
124
125 // TODO: Set adjacency routing rule for all switches
sanghob35a6192015-04-01 13:05:26 -0700126 }
127
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900128 log.debug("populateAllRoutingRules: populationStatus is SUCCEEDED");
129 populationStatus = Status.SUCCEEDED;
Saurav Dasa07f2032015-10-19 14:37:36 -0700130 log.info("Completed routing rule population. Total # of rules pushed : {}",
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900131 rulePopulator.getCounter());
132 return true;
133 } finally {
134 statusLock.unlock();
sanghob35a6192015-04-01 13:05:26 -0700135 }
sanghob35a6192015-04-01 13:05:26 -0700136 }
137
sangho20eff1d2015-04-13 15:15:58 -0700138 /**
139 * Populates the routing rules according to the route changes due to the link
140 * failure or link add. It computes the routes changed due to the link changes and
141 * repopulates the rules only for the routes.
142 *
143 * @param linkFail link failed, null for link added
144 * @return true if it succeeds to populate all rules, false otherwise
145 */
146 public boolean populateRoutingRulesForLinkStatusChange(Link linkFail) {
147
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900148 statusLock.lock();
149 try {
sangho20eff1d2015-04-13 15:15:58 -0700150
151 if (populationStatus == Status.STARTED) {
sangho52abe3a2015-05-05 14:13:34 -0700152 log.warn("Previous rule population is not finished.");
sangho20eff1d2015-04-13 15:15:58 -0700153 return true;
154 }
155
sangho45b009c2015-05-07 13:30:57 -0700156 // Take the snapshots of the links
157 updatedEcmpSpgMap = new HashMap<>();
158 for (Device sw : srManager.deviceService.getDevices()) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700159 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
sangho45b009c2015-05-07 13:30:57 -0700160 continue;
161 }
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530162 EcmpShortestPathGraph ecmpSpgUpdated =
163 new EcmpShortestPathGraph(sw.id(), srManager);
sangho45b009c2015-05-07 13:30:57 -0700164 updatedEcmpSpgMap.put(sw.id(), ecmpSpgUpdated);
165 }
166
sangho52abe3a2015-05-05 14:13:34 -0700167 log.info("Starts rule population from link change");
168
sangho20eff1d2015-04-13 15:15:58 -0700169 Set<ArrayList<DeviceId>> routeChanges;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700170 log.trace("populateRoutingRulesForLinkStatusChange: "
171 + "populationStatus is STARTED");
sangho20eff1d2015-04-13 15:15:58 -0700172 populationStatus = Status.STARTED;
173 if (linkFail == null) {
174 // Compare all routes of existing ECMP SPG with the new ones
175 routeChanges = computeRouteChange();
176 } else {
177 // Compare existing ECMP SPG only with the link removed
178 routeChanges = computeDamagedRoutes(linkFail);
179 }
180
181 if (routeChanges.isEmpty()) {
sangho52abe3a2015-05-05 14:13:34 -0700182 log.info("No route changes for the link status change");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700183 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
sangho20eff1d2015-04-13 15:15:58 -0700184 populationStatus = Status.SUCCEEDED;
185 return true;
186 }
187
188 if (repopulateRoutingRulesForRoutes(routeChanges)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700189 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
sangho20eff1d2015-04-13 15:15:58 -0700190 populationStatus = Status.SUCCEEDED;
191 log.info("Complete to repopulate the rules. # of rules populated : {}",
192 rulePopulator.getCounter());
193 return true;
194 } else {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700195 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is ABORTED");
sangho20eff1d2015-04-13 15:15:58 -0700196 populationStatus = Status.ABORTED;
197 log.warn("Failed to repopulate the rules.");
198 return false;
199 }
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900200 } finally {
201 statusLock.unlock();
sangho20eff1d2015-04-13 15:15:58 -0700202 }
203 }
204
205 private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
206 rulePopulator.resetCounter();
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700207 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> routesBydevice =
208 new HashMap<>();
sangho20eff1d2015-04-13 15:15:58 -0700209 for (ArrayList<DeviceId> link: routes) {
sangho834e4b02015-05-01 09:38:25 -0700210 // When only the source device is defined, reinstall routes to all other devices
sangho20eff1d2015-04-13 15:15:58 -0700211 if (link.size() == 1) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700212 log.trace("repopulateRoutingRulesForRoutes: running ECMP graph for device {}", link.get(0));
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530213 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(link.get(0), srManager);
Charles Chan93e71ba2016-04-29 14:38:22 -0700214 if (populateEcmpRoutingRules(link.get(0), ecmpSpg, ImmutableSet.of())) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700215 log.debug("Populating flow rules from {} to all is successful",
216 link.get(0));
sangho20eff1d2015-04-13 15:15:58 -0700217 currentEcmpSpgMap.put(link.get(0), ecmpSpg);
sangho52abe3a2015-05-05 14:13:34 -0700218 } else {
sangho45b009c2015-05-07 13:30:57 -0700219 log.warn("Failed to populate the flow rules from {} to all", link.get(0));
sangho52abe3a2015-05-05 14:13:34 -0700220 return false;
sangho20eff1d2015-04-13 15:15:58 -0700221 }
sangho45b009c2015-05-07 13:30:57 -0700222 } else {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700223 ArrayList<ArrayList<DeviceId>> deviceRoutes =
224 routesBydevice.get(link.get(1));
225 if (deviceRoutes == null) {
226 deviceRoutes = new ArrayList<>();
227 routesBydevice.put(link.get(1), deviceRoutes);
228 }
229 deviceRoutes.add(link);
230 }
231 }
232
233 for (DeviceId impactedDevice : routesBydevice.keySet()) {
234 ArrayList<ArrayList<DeviceId>> deviceRoutes =
235 routesBydevice.get(impactedDevice);
236 for (ArrayList<DeviceId> link: deviceRoutes) {
237 log.debug("repopulate RoutingRules For Routes {} -> {}",
238 link.get(0), link.get(1));
sangho45b009c2015-05-07 13:30:57 -0700239 DeviceId src = link.get(0);
240 DeviceId dst = link.get(1);
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530241 EcmpShortestPathGraph ecmpSpg = updatedEcmpSpgMap.get(dst);
sangho45b009c2015-05-07 13:30:57 -0700242 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
243 ecmpSpg.getAllLearnedSwitchesAndVia();
244 for (Integer itrIdx : switchVia.keySet()) {
245 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
246 switchVia.get(itrIdx);
247 for (DeviceId targetSw : swViaMap.keySet()) {
248 if (!targetSw.equals(src)) {
249 continue;
250 }
251 Set<DeviceId> nextHops = new HashSet<>();
252 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
253 if (via.isEmpty()) {
254 nextHops.add(dst);
255 } else {
256 nextHops.add(via.get(0));
257 }
258 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700259 if (!populateEcmpRoutingRulePartial(targetSw, dst,
260 nextHops, ImmutableSet.of())) {
sangho45b009c2015-05-07 13:30:57 -0700261 return false;
sangho20eff1d2015-04-13 15:15:58 -0700262 }
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700263 log.debug("Populating flow rules from {} to {} is successful",
264 targetSw, dst);
sangho20eff1d2015-04-13 15:15:58 -0700265 }
sangho20eff1d2015-04-13 15:15:58 -0700266 }
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700267 //currentEcmpSpgMap.put(dst, ecmpSpg);
sangho20eff1d2015-04-13 15:15:58 -0700268 }
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700269 //Only if all the flows for all impacted routes to a
270 //specific target are pushed successfully, update the
271 //ECMP graph for that target. (Or else the next event
272 //would not see any changes in the ECMP graphs)
273 currentEcmpSpgMap.put(impactedDevice,
274 updatedEcmpSpgMap.get(impactedDevice));
sangho20eff1d2015-04-13 15:15:58 -0700275 }
276 return true;
277 }
278
279 private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
280
281 Set<ArrayList<DeviceId>> routes = new HashSet<>();
282
283 for (Device sw : srManager.deviceService.getDevices()) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700284 log.debug("Computing the impacted routes for device {} due to link fail",
285 sw.id());
Charles Chanc42e84e2015-10-20 16:24:19 -0700286 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
sangho20eff1d2015-04-13 15:15:58 -0700287 continue;
288 }
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530289 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700290 if (ecmpSpg == null) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700291 log.error("No existing ECMP graph for switch {}", sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700292 continue;
293 }
294 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
295 ecmpSpg.getAllLearnedSwitchesAndVia();
296 for (Integer itrIdx : switchVia.keySet()) {
297 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
298 switchVia.get(itrIdx);
299 for (DeviceId targetSw : swViaMap.keySet()) {
300 DeviceId destSw = sw.id();
301 Set<ArrayList<DeviceId>> subLinks =
302 computeLinks(targetSw, destSw, swViaMap);
303 for (ArrayList<DeviceId> alink: subLinks) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700304 if ((alink.get(0).equals(linkFail.src().deviceId()) &&
305 alink.get(1).equals(linkFail.dst().deviceId()))
306 ||
307 (alink.get(0).equals(linkFail.dst().deviceId()) &&
308 alink.get(1).equals(linkFail.src().deviceId()))) {
309 log.debug("Impacted route:{}->{}", targetSw, destSw);
sangho20eff1d2015-04-13 15:15:58 -0700310 ArrayList<DeviceId> aRoute = new ArrayList<>();
311 aRoute.add(targetSw);
312 aRoute.add(destSw);
313 routes.add(aRoute);
314 break;
315 }
316 }
317 }
318 }
sangho45b009c2015-05-07 13:30:57 -0700319
sangho20eff1d2015-04-13 15:15:58 -0700320 }
321
322 return routes;
323 }
324
325 private Set<ArrayList<DeviceId>> computeRouteChange() {
326
327 Set<ArrayList<DeviceId>> routes = new HashSet<>();
328
329 for (Device sw : srManager.deviceService.getDevices()) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700330 log.debug("Computing the impacted routes for device {}",
331 sw.id());
Charles Chanc42e84e2015-10-20 16:24:19 -0700332 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700333 log.debug("No mastership for {} and skip route optimization",
334 sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700335 continue;
336 }
sangho45b009c2015-05-07 13:30:57 -0700337
338 log.trace("link of {} - ", sw.id());
339 for (Link link: srManager.linkService.getDeviceLinks(sw.id())) {
340 log.trace("{} -> {} ", link.src().deviceId(), link.dst().deviceId());
341 }
342
343 log.debug("Checking route change for switch {}", sw.id());
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530344 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700345 if (ecmpSpg == null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700346 log.debug("No existing ECMP graph for device {}", sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700347 ArrayList<DeviceId> route = new ArrayList<>();
348 route.add(sw.id());
349 routes.add(route);
350 continue;
351 }
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530352 EcmpShortestPathGraph newEcmpSpg = updatedEcmpSpgMap.get(sw.id());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700353 //currentEcmpSpgMap.put(sw.id(), newEcmpSpg);
sangho20eff1d2015-04-13 15:15:58 -0700354 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
355 ecmpSpg.getAllLearnedSwitchesAndVia();
356 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchViaUpdated =
357 newEcmpSpg.getAllLearnedSwitchesAndVia();
358
sangho45b009c2015-05-07 13:30:57 -0700359 for (Integer itrIdx : switchViaUpdated.keySet()) {
360 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMapUpdated =
361 switchViaUpdated.get(itrIdx);
362 for (DeviceId srcSw : swViaMapUpdated.keySet()) {
363 ArrayList<ArrayList<DeviceId>> viaUpdated = swViaMapUpdated.get(srcSw);
364 ArrayList<ArrayList<DeviceId>> via = getVia(switchVia, srcSw);
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700365 if ((via == null) || !viaUpdated.equals(via)) {
366 log.debug("Impacted route:{}->{}", srcSw, sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700367 ArrayList<DeviceId> route = new ArrayList<>();
368 route.add(srcSw);
369 route.add(sw.id());
370 routes.add(route);
371 }
372 }
373 }
sangho45b009c2015-05-07 13:30:57 -0700374 }
sangho20eff1d2015-04-13 15:15:58 -0700375
sangho45b009c2015-05-07 13:30:57 -0700376 for (ArrayList<DeviceId> link: routes) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700377 log.trace("Route changes - ");
sangho45b009c2015-05-07 13:30:57 -0700378 if (link.size() == 1) {
379 log.trace(" : {} - all", link.get(0));
380 } else {
381 log.trace(" : {} - {}", link.get(0), link.get(1));
382 }
sangho20eff1d2015-04-13 15:15:58 -0700383 }
384
385 return routes;
386 }
387
388 private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
389 ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId srcSw) {
390 for (Integer itrIdx : switchVia.keySet()) {
391 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
392 switchVia.get(itrIdx);
393 if (swViaMap.get(srcSw) == null) {
394 continue;
395 } else {
396 return swViaMap.get(srcSw);
397 }
398 }
399
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700400 return null;
sangho20eff1d2015-04-13 15:15:58 -0700401 }
402
403 private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
404 DeviceId dst,
405 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
406 Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
407 for (ArrayList<DeviceId> via : viaMap.get(src)) {
408 DeviceId linkSrc = src;
409 DeviceId linkDst = dst;
410 for (DeviceId viaDevice: via) {
411 ArrayList<DeviceId> link = new ArrayList<>();
412 linkDst = viaDevice;
413 link.add(linkSrc);
414 link.add(linkDst);
415 subLinks.add(link);
416 linkSrc = viaDevice;
417 }
418 ArrayList<DeviceId> link = new ArrayList<>();
419 link.add(linkSrc);
420 link.add(dst);
421 subLinks.add(link);
422 }
423
424 return subLinks;
425 }
426
Charles Chan93e71ba2016-04-29 14:38:22 -0700427 /**
428 * Populate ECMP rules for subnets from all switches to destination.
429 *
430 * @param destSw Device ID of destination switch
431 * @param ecmpSPG ECMP shortest path graph
432 * @param subnets Subnets to be populated. If empty, populate all configured subnets.
433 * @return true if succeed
434 */
sangho20eff1d2015-04-13 15:15:58 -0700435 private boolean populateEcmpRoutingRules(DeviceId destSw,
Charles Chan93e71ba2016-04-29 14:38:22 -0700436 EcmpShortestPathGraph ecmpSPG,
437 Set<Ip4Prefix> subnets) {
sanghob35a6192015-04-01 13:05:26 -0700438
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700439 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = ecmpSPG
440 .getAllLearnedSwitchesAndVia();
sanghob35a6192015-04-01 13:05:26 -0700441 for (Integer itrIdx : switchVia.keySet()) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700442 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap = switchVia
443 .get(itrIdx);
sanghob35a6192015-04-01 13:05:26 -0700444 for (DeviceId targetSw : swViaMap.keySet()) {
sanghob35a6192015-04-01 13:05:26 -0700445 Set<DeviceId> nextHops = new HashSet<>();
Saurav Dasa07f2032015-10-19 14:37:36 -0700446 log.debug("** Iter: {} root: {} target: {}", itrIdx, destSw, targetSw);
sanghob35a6192015-04-01 13:05:26 -0700447 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
448 if (via.isEmpty()) {
449 nextHops.add(destSw);
450 } else {
451 nextHops.add(via.get(0));
452 }
453 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700454 if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops, subnets)) {
sanghob35a6192015-04-01 13:05:26 -0700455 return false;
456 }
457 }
458 }
459
460 return true;
461 }
462
Charles Chan93e71ba2016-04-29 14:38:22 -0700463 /**
464 * Populate ECMP rules for subnets from target to destination via nexthops.
465 *
466 * @param targetSw Device ID of target switch
467 * @param destSw Device ID of destination switch
468 * @param nextHops List of next hops
469 * @param subnets Subnets to be populated. If empty, populate all configured subnets.
470 * @return true if succeed
471 */
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700472 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw,
473 DeviceId destSw,
Charles Chan93e71ba2016-04-29 14:38:22 -0700474 Set<DeviceId> nextHops,
475 Set<Ip4Prefix> subnets) {
sanghob35a6192015-04-01 13:05:26 -0700476 boolean result;
477
478 if (nextHops.isEmpty()) {
479 nextHops.add(destSw);
480 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700481 // If both target switch and dest switch are edge routers, then set IP
sangho52abe3a2015-05-05 14:13:34 -0700482 // rule for both subnet and router IP.
Charles Chan0b4e6182015-11-03 10:42:14 -0800483 boolean targetIsEdge;
484 boolean destIsEdge;
485 Ip4Address destRouterIp;
486
487 try {
488 targetIsEdge = config.isEdgeDevice(targetSw);
489 destIsEdge = config.isEdgeDevice(destSw);
490 destRouterIp = config.getRouterIp(destSw);
491 } catch (DeviceConfigNotFoundException e) {
492 log.warn(e.getMessage() + " Aborting populateEcmpRoutingRulePartial.");
493 return false;
494 }
495
496 if (targetIsEdge && destIsEdge) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700497 subnets = (subnets != null && !subnets.isEmpty()) ? subnets : config.getSubnets(destSw);
Saurav Dasa07f2032015-10-19 14:37:36 -0700498 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for subnets {}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800499 targetSw, destSw, subnets);
Charles Chan93e71ba2016-04-29 14:38:22 -0700500 result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets,
501 destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700502 if (!result) {
503 return false;
504 }
505
Charles Chan0b4e6182015-11-03 10:42:14 -0800506 Ip4Address routerIp = destRouterIp;
sangho666cd6d2015-04-14 16:27:13 -0700507 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
Saurav Dasa07f2032015-10-19 14:37:36 -0700508 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800509 targetSw, destSw, routerIpPrefix);
sangho666cd6d2015-04-14 16:27:13 -0700510 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700511 if (!result) {
512 return false;
513 }
514
Charles Chan0b4e6182015-11-03 10:42:14 -0800515 } else if (targetIsEdge) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800516 // If the target switch is an edge router, then set IP rules for the router IP.
Charles Chan0b4e6182015-11-03 10:42:14 -0800517 Ip4Address routerIp = destRouterIp;
sangho666cd6d2015-04-14 16:27:13 -0700518 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
Saurav Dasa07f2032015-10-19 14:37:36 -0700519 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800520 targetSw, destSw, routerIpPrefix);
sangho666cd6d2015-04-14 16:27:13 -0700521 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700522 if (!result) {
523 return false;
524 }
sangho52abe3a2015-05-05 14:13:34 -0700525 }
sangho52abe3a2015-05-05 14:13:34 -0700526 // Populates MPLS rules to all routers
Saurav Dasa07f2032015-10-19 14:37:36 -0700527 log.debug("* populateEcmpRoutingRulePartial in device{} towards {} for all MPLS rules",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700528 targetSw, destSw);
sangho52abe3a2015-05-05 14:13:34 -0700529 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
530 if (!result) {
531 return false;
sanghob35a6192015-04-01 13:05:26 -0700532 }
sanghob35a6192015-04-01 13:05:26 -0700533 return true;
534 }
535
536 /**
Saurav Das822c4e22015-10-23 10:51:11 -0700537 * Populates filtering rules for permitting Router DstMac and VLAN.
sanghob35a6192015-04-01 13:05:26 -0700538 *
539 * @param deviceId Switch ID to set the rules
540 */
Saurav Das822c4e22015-10-23 10:51:11 -0700541 public void populatePortAddressingRules(DeviceId deviceId) {
Charles Chane849c192016-01-11 18:28:54 -0800542 rulePopulator.populateXConnectVlanFilters(deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -0700543 rulePopulator.populateRouterIpPunts(deviceId);
Saurav Das59232cf2016-04-27 18:35:50 -0700544
545 // Although device is added, sometimes device store does not have the
546 // ports for this device yet. It results in missing filtering rules in the
547 // switch. We will attempt it a few times. If it still does not work,
548 // user can manually repopulate using CLI command sr-reroute-network
549 boolean success = rulePopulator.populateRouterMacVlanFilters(deviceId);
550 if (!success) {
551 executorService.schedule(new RetryFilters(deviceId), 200, TimeUnit.MILLISECONDS);
552 }
sanghob35a6192015-04-01 13:05:26 -0700553 }
554
555 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700556 * Start the flow rule population process if it was never started. The
557 * process finishes successfully when all flow rules are set and stops with
558 * ABORTED status when any groups required for flows is not set yet.
sanghob35a6192015-04-01 13:05:26 -0700559 */
560 public void startPopulationProcess() {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900561 statusLock.lock();
562 try {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700563 if (populationStatus == Status.IDLE
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700564 || populationStatus == Status.SUCCEEDED
565 || populationStatus == Status.ABORTED) {
sanghob35a6192015-04-01 13:05:26 -0700566 populationStatus = Status.STARTED;
567 populateAllRoutingRules();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700568 } else {
569 log.warn("Not initiating startPopulationProcess as populationStatus is {}",
570 populationStatus);
sanghob35a6192015-04-01 13:05:26 -0700571 }
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900572 } finally {
573 statusLock.unlock();
sanghob35a6192015-04-01 13:05:26 -0700574 }
575 }
576
577 /**
578 * Resume the flow rule population process if it was aborted for any reason.
579 * Mostly the process is aborted when the groups required are not set yet.
Saurav Dasa07f2032015-10-19 14:37:36 -0700580 * XXX is this called?
581 *
sanghob35a6192015-04-01 13:05:26 -0700582 */
583 public void resumePopulationProcess() {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900584 statusLock.lock();
585 try {
sanghob35a6192015-04-01 13:05:26 -0700586 if (populationStatus == Status.ABORTED) {
587 populationStatus = Status.STARTED;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700588 // TODO: we need to restart from the point aborted instead of
589 // restarting.
sanghob35a6192015-04-01 13:05:26 -0700590 populateAllRoutingRules();
591 }
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900592 } finally {
593 statusLock.unlock();
sanghob35a6192015-04-01 13:05:26 -0700594 }
595 }
Saurav Das80980c72016-03-23 11:22:49 -0700596
Charles Chan93e71ba2016-04-29 14:38:22 -0700597 /**
598 * Populate rules of given subnet at given location.
599 *
600 * @param cp connect point of the subnet being added
601 * @param subnets subnet being added
602 * @return true if succeed
603 */
604 protected boolean populateSubnet(ConnectPoint cp, Set<Ip4Prefix> subnets) {
605 statusLock.lock();
606 try {
607 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(cp.deviceId());
608 if (ecmpSpg == null) {
609 log.warn("Fail to populating subnet {}: {}", subnets, ECMPSPG_MISSING);
610 return false;
611 }
612 return populateEcmpRoutingRules(cp.deviceId(), ecmpSpg, subnets);
613 } finally {
614 statusLock.unlock();
615 }
616 }
617
618 /**
619 * Revoke rules of given subnet at given location.
620 *
621 * @param subnets subnet being removed
622 * @return true if succeed
623 */
624 protected boolean revokeSubnet(Set<Ip4Prefix> subnets) {
625 statusLock.lock();
626 try {
627 return srManager.routingRulePopulator.revokeIpRuleForSubnet(subnets);
628 } finally {
629 statusLock.unlock();
630 }
631 }
632
633 protected void purgeEcmpGraph(DeviceId deviceId) {
Saurav Das80980c72016-03-23 11:22:49 -0700634 currentEcmpSpgMap.remove(deviceId);
Saurav Das7a1ffca2016-03-28 19:00:18 -0700635 if (updatedEcmpSpgMap != null) {
636 updatedEcmpSpgMap.remove(deviceId);
637 }
Saurav Das80980c72016-03-23 11:22:49 -0700638 }
Saurav Das59232cf2016-04-27 18:35:50 -0700639
Charles Chan93e71ba2016-04-29 14:38:22 -0700640 private final class RetryFilters implements Runnable {
Saurav Das59232cf2016-04-27 18:35:50 -0700641 int attempts = MAX_RETRY_ATTEMPTS;
642 DeviceId devId;
643
Charles Chan93e71ba2016-04-29 14:38:22 -0700644 private RetryFilters(DeviceId deviceId) {
Saurav Das59232cf2016-04-27 18:35:50 -0700645 devId = deviceId;
646 }
647
648 @Override
649 public void run() {
650 boolean success = rulePopulator.populateRouterMacVlanFilters(devId);
651 if (!success && --attempts > 0) {
652 executorService.schedule(this, 200, TimeUnit.MILLISECONDS);
653 } else if (attempts == 0) {
654 log.error("Unable to populate MacVlan filters in dev:{}", devId);
655 }
656 }
657
658 }
659
sanghob35a6192015-04-01 13:05:26 -0700660}