blob: 678d8a0c31f2180b9ac057b480770239ec962f65 [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
Saurav Dasd2fded02016-12-02 15:43:47 -080018import com.google.common.base.MoreObjects;
Charles Chan93e71ba2016-04-29 14:38:22 -070019import com.google.common.collect.ImmutableSet;
Saurav Das4e3224f2016-11-29 14:27:25 -080020import com.google.common.collect.Lists;
sangho20eff1d2015-04-13 15:15:58 -070021import com.google.common.collect.Maps;
22import com.google.common.collect.Sets;
sangho666cd6d2015-04-14 16:27:13 -070023import org.onlab.packet.Ip4Address;
Pier Ventree0ae7a32016-11-23 09:57:42 -080024import org.onlab.packet.Ip6Address;
sanghob35a6192015-04-01 13:05:26 -070025import org.onlab.packet.IpPrefix;
Charles Chan93e71ba2016-04-29 14:38:22 -070026import org.onosproject.net.ConnectPoint;
sanghob35a6192015-04-01 13:05:26 -070027import org.onosproject.net.Device;
28import org.onosproject.net.DeviceId;
sangho20eff1d2015-04-13 15:15:58 -070029import org.onosproject.net.Link;
Charles Chan0b4e6182015-11-03 10:42:14 -080030import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
31import org.onosproject.segmentrouting.config.DeviceConfiguration;
sanghob35a6192015-04-01 13:05:26 -070032import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
34
35import java.util.ArrayList;
36import java.util.HashMap;
37import java.util.HashSet;
Saurav Dasd2fded02016-12-02 15:43:47 -080038import java.util.Objects;
sanghob35a6192015-04-01 13:05:26 -070039import 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
Saurav Dasd2fded02016-12-02 15:43:47 -080045import static com.google.common.base.MoreObjects.toStringHelper;
Pier Ventree0ae7a32016-11-23 09:57:42 -080046import static com.google.common.base.Preconditions.checkNotNull;
47import static java.util.concurrent.Executors.newScheduledThreadPool;
48import static org.onlab.util.Tools.groupedThreads;
sanghob35a6192015-04-01 13:05:26 -070049
Charles Chane849c192016-01-11 18:28:54 -080050/**
51 * Default routing handler that is responsible for route computing and
52 * routing rule population.
53 */
sanghob35a6192015-04-01 13:05:26 -070054public class DefaultRoutingHandler {
Saurav Das018605f2017-02-18 14:05:44 -080055 private static final int MAX_CONSTANT_RETRY_ATTEMPTS = 5;
56 private static final int RETRY_INTERVAL_MS = 250;
57 private static final int RETRY_INTERVAL_SCALE = 1;
Charles Chan93e71ba2016-04-29 14:38:22 -070058 private static final String ECMPSPG_MISSING = "ECMP shortest path graph not found";
59 private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
sanghob35a6192015-04-01 13:05:26 -070060
61 private SegmentRoutingManager srManager;
62 private RoutingRulePopulator rulePopulator;
Shashikanth VH013a7bc2015-12-11 01:32:44 +053063 private HashMap<DeviceId, EcmpShortestPathGraph> currentEcmpSpgMap;
64 private HashMap<DeviceId, EcmpShortestPathGraph> updatedEcmpSpgMap;
sangho666cd6d2015-04-14 16:27:13 -070065 private DeviceConfiguration config;
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +090066 private final Lock statusLock = new ReentrantLock();
67 private volatile Status populationStatus;
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070068 private ScheduledExecutorService executorService
Saurav Dasd2fded02016-12-02 15:43:47 -080069 = newScheduledThreadPool(1, groupedThreads("retryftr", "retry-%d", log));
sanghob35a6192015-04-01 13:05:26 -070070
71 /**
72 * Represents the default routing population status.
73 */
74 public enum Status {
75 // population process is not started yet.
76 IDLE,
77
78 // population process started.
79 STARTED,
80
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070081 // population process was aborted due to errors, mostly for groups not
82 // found.
sanghob35a6192015-04-01 13:05:26 -070083 ABORTED,
84
85 // population process was finished successfully.
86 SUCCEEDED
87 }
88
89 /**
90 * Creates a DefaultRoutingHandler object.
91 *
92 * @param srManager SegmentRoutingManager object
93 */
94 public DefaultRoutingHandler(SegmentRoutingManager srManager) {
95 this.srManager = srManager;
96 this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
sangho666cd6d2015-04-14 16:27:13 -070097 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070098 this.populationStatus = Status.IDLE;
sangho20eff1d2015-04-13 15:15:58 -070099 this.currentEcmpSpgMap = Maps.newHashMap();
sanghob35a6192015-04-01 13:05:26 -0700100 }
101
102 /**
103 * Populates all routing rules to all connected routers, including default
104 * routing rules, adjacency rules, and policy rules if any.
105 *
106 * @return true if it succeeds in populating all rules, otherwise false
107 */
108 public boolean populateAllRoutingRules() {
109
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900110 statusLock.lock();
111 try {
112 populationStatus = Status.STARTED;
113 rulePopulator.resetCounter();
Saurav Dasa07f2032015-10-19 14:37:36 -0700114 log.info("Starting to populate segment-routing rules");
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900115 log.debug("populateAllRoutingRules: populationStatus is STARTED");
sanghob35a6192015-04-01 13:05:26 -0700116
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900117 for (Device sw : srManager.deviceService.getDevices()) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700118 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900119 log.debug("populateAllRoutingRules: skipping device {}...we are not master",
120 sw.id());
121 continue;
122 }
123
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530124 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(sw.id(), srManager);
Charles Chan93e71ba2016-04-29 14:38:22 -0700125 if (!populateEcmpRoutingRules(sw.id(), ecmpSpg, ImmutableSet.of())) {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900126 log.debug("populateAllRoutingRules: populationStatus is ABORTED");
127 populationStatus = Status.ABORTED;
128 log.debug("Abort routing rule population");
129 return false;
130 }
131 currentEcmpSpgMap.put(sw.id(), ecmpSpg);
132
133 // TODO: Set adjacency routing rule for all switches
sanghob35a6192015-04-01 13:05:26 -0700134 }
135
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900136 log.debug("populateAllRoutingRules: populationStatus is SUCCEEDED");
137 populationStatus = Status.SUCCEEDED;
Saurav Dasa07f2032015-10-19 14:37:36 -0700138 log.info("Completed routing rule population. Total # of rules pushed : {}",
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900139 rulePopulator.getCounter());
140 return true;
141 } finally {
142 statusLock.unlock();
sanghob35a6192015-04-01 13:05:26 -0700143 }
sanghob35a6192015-04-01 13:05:26 -0700144 }
145
sangho20eff1d2015-04-13 15:15:58 -0700146 /**
147 * Populates the routing rules according to the route changes due to the link
148 * failure or link add. It computes the routes changed due to the link changes and
Saurav Das4e3224f2016-11-29 14:27:25 -0800149 * repopulates the rules only for these routes. Note that when a switch goes
150 * away, all of its links fail as well, but this is handled as a single
151 * switch removal event.
sangho20eff1d2015-04-13 15:15:58 -0700152 *
Saurav Das4e3224f2016-11-29 14:27:25 -0800153 * @param failedLink the single failed link, or null for other conditions
154 * such as an added link or a removed switch
sangho20eff1d2015-04-13 15:15:58 -0700155 * @return true if it succeeds to populate all rules, false otherwise
156 */
Saurav Das4e3224f2016-11-29 14:27:25 -0800157 public boolean populateRoutingRulesForLinkStatusChange(Link failedLink) {
sangho20eff1d2015-04-13 15:15:58 -0700158
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900159 statusLock.lock();
160 try {
sangho20eff1d2015-04-13 15:15:58 -0700161
162 if (populationStatus == Status.STARTED) {
sangho52abe3a2015-05-05 14:13:34 -0700163 log.warn("Previous rule population is not finished.");
sangho20eff1d2015-04-13 15:15:58 -0700164 return true;
165 }
166
sangho45b009c2015-05-07 13:30:57 -0700167 // Take the snapshots of the links
168 updatedEcmpSpgMap = new HashMap<>();
169 for (Device sw : srManager.deviceService.getDevices()) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700170 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
sangho45b009c2015-05-07 13:30:57 -0700171 continue;
172 }
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530173 EcmpShortestPathGraph ecmpSpgUpdated =
174 new EcmpShortestPathGraph(sw.id(), srManager);
sangho45b009c2015-05-07 13:30:57 -0700175 updatedEcmpSpgMap.put(sw.id(), ecmpSpgUpdated);
176 }
177
sangho52abe3a2015-05-05 14:13:34 -0700178 log.info("Starts rule population from link change");
179
sangho20eff1d2015-04-13 15:15:58 -0700180 Set<ArrayList<DeviceId>> routeChanges;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700181 log.trace("populateRoutingRulesForLinkStatusChange: "
182 + "populationStatus is STARTED");
sangho20eff1d2015-04-13 15:15:58 -0700183 populationStatus = Status.STARTED;
Saurav Das4e3224f2016-11-29 14:27:25 -0800184 // try optimized re-routing
185 if (failedLink == null) {
186 // Compare all routes of existing ECMP SPG to new ECMP SPG
sangho20eff1d2015-04-13 15:15:58 -0700187 routeChanges = computeRouteChange();
188 } else {
189 // Compare existing ECMP SPG only with the link removed
Saurav Das4e3224f2016-11-29 14:27:25 -0800190 routeChanges = computeDamagedRoutes(failedLink);
sangho20eff1d2015-04-13 15:15:58 -0700191 }
192
Saurav Das4e3224f2016-11-29 14:27:25 -0800193 // do full re-routing if optimized routing returns null routeChanges
Saurav Dasb5c236e2016-06-07 10:08:06 -0700194 if (routeChanges == null) {
195 return populateAllRoutingRules();
196 }
197
sangho20eff1d2015-04-13 15:15:58 -0700198 if (routeChanges.isEmpty()) {
sangho52abe3a2015-05-05 14:13:34 -0700199 log.info("No route changes for the link status change");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700200 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
sangho20eff1d2015-04-13 15:15:58 -0700201 populationStatus = Status.SUCCEEDED;
202 return true;
203 }
204
205 if (repopulateRoutingRulesForRoutes(routeChanges)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700206 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
sangho20eff1d2015-04-13 15:15:58 -0700207 populationStatus = Status.SUCCEEDED;
208 log.info("Complete to repopulate the rules. # of rules populated : {}",
209 rulePopulator.getCounter());
210 return true;
211 } else {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700212 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is ABORTED");
sangho20eff1d2015-04-13 15:15:58 -0700213 populationStatus = Status.ABORTED;
214 log.warn("Failed to repopulate the rules.");
215 return false;
216 }
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900217 } finally {
218 statusLock.unlock();
sangho20eff1d2015-04-13 15:15:58 -0700219 }
220 }
221
222 private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
223 rulePopulator.resetCounter();
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700224 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> routesBydevice =
225 new HashMap<>();
sangho20eff1d2015-04-13 15:15:58 -0700226 for (ArrayList<DeviceId> link: routes) {
sangho834e4b02015-05-01 09:38:25 -0700227 // When only the source device is defined, reinstall routes to all other devices
sangho20eff1d2015-04-13 15:15:58 -0700228 if (link.size() == 1) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700229 log.trace("repopulateRoutingRulesForRoutes: running ECMP graph for device {}", link.get(0));
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530230 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(link.get(0), srManager);
Charles Chan93e71ba2016-04-29 14:38:22 -0700231 if (populateEcmpRoutingRules(link.get(0), ecmpSpg, ImmutableSet.of())) {
Saurav Das25190812016-05-27 13:54:07 -0700232 log.debug("Populating flow rules from all to dest:{} is successful",
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700233 link.get(0));
sangho20eff1d2015-04-13 15:15:58 -0700234 currentEcmpSpgMap.put(link.get(0), ecmpSpg);
sangho52abe3a2015-05-05 14:13:34 -0700235 } else {
Saurav Das25190812016-05-27 13:54:07 -0700236 log.warn("Failed to populate the flow rules from all to dest:{}", link.get(0));
sangho52abe3a2015-05-05 14:13:34 -0700237 return false;
sangho20eff1d2015-04-13 15:15:58 -0700238 }
sangho45b009c2015-05-07 13:30:57 -0700239 } else {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700240 ArrayList<ArrayList<DeviceId>> deviceRoutes =
241 routesBydevice.get(link.get(1));
242 if (deviceRoutes == null) {
243 deviceRoutes = new ArrayList<>();
244 routesBydevice.put(link.get(1), deviceRoutes);
245 }
246 deviceRoutes.add(link);
247 }
248 }
249
250 for (DeviceId impactedDevice : routesBydevice.keySet()) {
251 ArrayList<ArrayList<DeviceId>> deviceRoutes =
252 routesBydevice.get(impactedDevice);
253 for (ArrayList<DeviceId> link: deviceRoutes) {
254 log.debug("repopulate RoutingRules For Routes {} -> {}",
255 link.get(0), link.get(1));
sangho45b009c2015-05-07 13:30:57 -0700256 DeviceId src = link.get(0);
257 DeviceId dst = link.get(1);
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530258 EcmpShortestPathGraph ecmpSpg = updatedEcmpSpgMap.get(dst);
sangho45b009c2015-05-07 13:30:57 -0700259 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
260 ecmpSpg.getAllLearnedSwitchesAndVia();
261 for (Integer itrIdx : switchVia.keySet()) {
262 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
263 switchVia.get(itrIdx);
264 for (DeviceId targetSw : swViaMap.keySet()) {
265 if (!targetSw.equals(src)) {
266 continue;
267 }
268 Set<DeviceId> nextHops = new HashSet<>();
269 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
270 if (via.isEmpty()) {
271 nextHops.add(dst);
272 } else {
273 nextHops.add(via.get(0));
274 }
275 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700276 if (!populateEcmpRoutingRulePartial(targetSw, dst,
277 nextHops, ImmutableSet.of())) {
sangho45b009c2015-05-07 13:30:57 -0700278 return false;
sangho20eff1d2015-04-13 15:15:58 -0700279 }
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700280 log.debug("Populating flow rules from {} to {} is successful",
281 targetSw, dst);
sangho20eff1d2015-04-13 15:15:58 -0700282 }
sangho20eff1d2015-04-13 15:15:58 -0700283 }
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700284 //currentEcmpSpgMap.put(dst, ecmpSpg);
sangho20eff1d2015-04-13 15:15:58 -0700285 }
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700286 //Only if all the flows for all impacted routes to a
287 //specific target are pushed successfully, update the
Saurav Das4e3224f2016-11-29 14:27:25 -0800288 //ECMP graph for that target. Or else the next event
289 //would not see any changes in the ECMP graphs.
290 //In another case, the target switch has gone away, so
291 //routes can't be installed. In that case, the current map
292 //is updated here, without any flows being pushed.
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700293 currentEcmpSpgMap.put(impactedDevice,
294 updatedEcmpSpgMap.get(impactedDevice));
sangho20eff1d2015-04-13 15:15:58 -0700295 }
296 return true;
297 }
298
Saurav Dasb5c236e2016-06-07 10:08:06 -0700299 /**
Saurav Das4e3224f2016-11-29 14:27:25 -0800300 * Computes set of affected routes due to failed link. Assumes
Saurav Dasb5c236e2016-06-07 10:08:06 -0700301 * previous ecmp shortest-path graph exists for a switch in order to compute
302 * affected routes. If such a graph does not exist, the method returns null.
303 *
304 * @param linkFail the failed link
305 * @return the set of affected routes which may be empty if no routes were
306 * affected, or null if no previous ecmp spg was found for comparison
307 */
sangho20eff1d2015-04-13 15:15:58 -0700308 private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
309
310 Set<ArrayList<DeviceId>> routes = new HashSet<>();
311
312 for (Device sw : srManager.deviceService.getDevices()) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700313 log.debug("Computing the impacted routes for device {} due to link fail",
314 sw.id());
Charles Chanc42e84e2015-10-20 16:24:19 -0700315 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700316 log.debug("No mastership for {} .. skipping route optimization",
317 sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700318 continue;
319 }
Shashikanth VH013a7bc2015-12-11 01:32:44 +0530320 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
sangho20eff1d2015-04-13 15:15:58 -0700321 if (ecmpSpg == null) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700322 log.warn("No existing ECMP graph for switch {}. Aborting optimized"
323 + " rerouting and opting for full-reroute", sw.id());
324 return null;
sangho20eff1d2015-04-13 15:15:58 -0700325 }
326 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
327 ecmpSpg.getAllLearnedSwitchesAndVia();
328 for (Integer itrIdx : switchVia.keySet()) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700329 log.trace("Iterindex# {}", itrIdx);
sangho20eff1d2015-04-13 15:15:58 -0700330 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
331 switchVia.get(itrIdx);
332 for (DeviceId targetSw : swViaMap.keySet()) {
Saurav Das4e3224f2016-11-29 14:27:25 -0800333 DeviceId rootSw = sw.id();
Saurav Dasb5c236e2016-06-07 10:08:06 -0700334 if (log.isTraceEnabled()) {
Saurav Das4e3224f2016-11-29 14:27:25 -0800335 log.trace("TargetSwitch {} --> RootSwitch {}", targetSw, rootSw);
Saurav Dasb5c236e2016-06-07 10:08:06 -0700336 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
337 log.trace(" Via:");
Pier Ventree0ae7a32016-11-23 09:57:42 -0800338 via.forEach(e -> log.trace(" {}", e));
Saurav Dasb5c236e2016-06-07 10:08:06 -0700339 }
340 }
sangho20eff1d2015-04-13 15:15:58 -0700341 Set<ArrayList<DeviceId>> subLinks =
Saurav Das4e3224f2016-11-29 14:27:25 -0800342 computeLinks(targetSw, rootSw, swViaMap);
sangho20eff1d2015-04-13 15:15:58 -0700343 for (ArrayList<DeviceId> alink: subLinks) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700344 if ((alink.get(0).equals(linkFail.src().deviceId()) &&
345 alink.get(1).equals(linkFail.dst().deviceId()))
346 ||
347 (alink.get(0).equals(linkFail.dst().deviceId()) &&
348 alink.get(1).equals(linkFail.src().deviceId()))) {
Saurav Das4e3224f2016-11-29 14:27:25 -0800349 log.debug("Impacted route:{}->{}", targetSw, rootSw);
sangho20eff1d2015-04-13 15:15:58 -0700350 ArrayList<DeviceId> aRoute = new ArrayList<>();
351 aRoute.add(targetSw);
Saurav Das4e3224f2016-11-29 14:27:25 -0800352 aRoute.add(rootSw);
sangho20eff1d2015-04-13 15:15:58 -0700353 routes.add(aRoute);
354 break;
355 }
356 }
357 }
358 }
sangho45b009c2015-05-07 13:30:57 -0700359
sangho20eff1d2015-04-13 15:15:58 -0700360 }
361
362 return routes;
363 }
364
Saurav Das4e3224f2016-11-29 14:27:25 -0800365 /**
366 * Computes set of affected routes due to new links or failed switches.
367 *
368 * @return the set of affected routes which may be empty if no routes were
369 * affected
370 */
sangho20eff1d2015-04-13 15:15:58 -0700371 private Set<ArrayList<DeviceId>> computeRouteChange() {
372
Saurav Das4e3224f2016-11-29 14:27:25 -0800373 ImmutableSet.Builder<ArrayList<DeviceId>> changedRoutesBuilder =
374 ImmutableSet.builder();
sangho20eff1d2015-04-13 15:15:58 -0700375
376 for (Device sw : srManager.deviceService.getDevices()) {
Saurav Das4e3224f2016-11-29 14:27:25 -0800377 DeviceId rootSw = sw.id();
378 log.debug("Computing the impacted routes for device {}", rootSw);
379 if (!srManager.mastershipService.isLocalMaster(rootSw)) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700380 log.debug("No mastership for {} ... skipping route optimization",
Saurav Das4e3224f2016-11-29 14:27:25 -0800381 rootSw);
sangho20eff1d2015-04-13 15:15:58 -0700382 continue;
383 }
Saurav Dasb5c236e2016-06-07 10:08:06 -0700384 if (log.isTraceEnabled()) {
Saurav Das4e3224f2016-11-29 14:27:25 -0800385 log.trace("link of {} - ", rootSw);
386 for (Link link: srManager.linkService.getDeviceLinks(rootSw)) {
Saurav Dasb5c236e2016-06-07 10:08:06 -0700387 log.trace("{} -> {} ", link.src().deviceId(), link.dst().deviceId());
388 }
sangho45b009c2015-05-07 13:30:57 -0700389 }
Saurav Das4e3224f2016-11-29 14:27:25 -0800390 EcmpShortestPathGraph currEcmpSpg = currentEcmpSpgMap.get(rootSw);
391 if (currEcmpSpg == null) {
392 log.debug("No existing ECMP graph for device {}", rootSw);
393 changedRoutesBuilder.add(Lists.newArrayList(rootSw));
sangho20eff1d2015-04-13 15:15:58 -0700394 continue;
395 }
Saurav Das4e3224f2016-11-29 14:27:25 -0800396 EcmpShortestPathGraph newEcmpSpg = updatedEcmpSpgMap.get(rootSw);
397 if (log.isTraceEnabled()) {
398 log.trace("Root switch: {}", rootSw);
399 log.trace(" Current/Existing SPG: {}", currEcmpSpg);
400 log.trace(" New/Updated SPG: {}", newEcmpSpg);
401 }
402 // first use the updated/new map to compare to current/existing map
403 // as new links may have come up
404 changedRoutesBuilder.addAll(compareGraphs(newEcmpSpg, currEcmpSpg, rootSw));
405 // then use the current/existing map to compare to updated/new map
406 // as switch may have been removed
407 changedRoutesBuilder.addAll(compareGraphs(currEcmpSpg, newEcmpSpg, rootSw));
408 }
sangho20eff1d2015-04-13 15:15:58 -0700409
Saurav Das4e3224f2016-11-29 14:27:25 -0800410 Set<ArrayList<DeviceId>> changedRoutes = changedRoutesBuilder.build();
411 for (ArrayList<DeviceId> route: changedRoutes) {
412 log.debug("Route changes Target -> Root");
413 if (route.size() == 1) {
414 log.debug(" : all -> {}", route.get(0));
415 } else {
416 log.debug(" : {} -> {}", route.get(0), route.get(1));
417 }
418 }
419 return changedRoutes;
420 }
421
422 /**
423 * For the root switch, searches all the target nodes reachable in the base
424 * graph, and compares paths to the ones in the comp graph.
425 *
426 * @param base the graph that is indexed for all reachable target nodes
427 * from the root node
428 * @param comp the graph that the base graph is compared to
429 * @param rootSw both ecmp graphs are calculated for the root node
430 * @return all the routes that have changed in the base graph
431 */
432 private Set<ArrayList<DeviceId>> compareGraphs(EcmpShortestPathGraph base,
433 EcmpShortestPathGraph comp,
434 DeviceId rootSw) {
435 ImmutableSet.Builder<ArrayList<DeviceId>> changedRoutesBuilder =
436 ImmutableSet.builder();
437 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> baseMap =
438 base.getAllLearnedSwitchesAndVia();
439 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> compMap =
440 comp.getAllLearnedSwitchesAndVia();
441 for (Integer itrIdx : baseMap.keySet()) {
442 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> baseViaMap =
443 baseMap.get(itrIdx);
444 for (DeviceId targetSw : baseViaMap.keySet()) {
445 ArrayList<ArrayList<DeviceId>> basePath = baseViaMap.get(targetSw);
446 ArrayList<ArrayList<DeviceId>> compPath = getVia(compMap, targetSw);
447 if ((compPath == null) || !basePath.equals(compPath)) {
448 log.debug("Impacted route:{} -> {}", targetSw, rootSw);
449 ArrayList<DeviceId> route = new ArrayList<>();
450 route.add(targetSw);
451 route.add(rootSw);
452 changedRoutesBuilder.add(route);
sangho20eff1d2015-04-13 15:15:58 -0700453 }
454 }
sangho45b009c2015-05-07 13:30:57 -0700455 }
Saurav Das4e3224f2016-11-29 14:27:25 -0800456 return changedRoutesBuilder.build();
sangho20eff1d2015-04-13 15:15:58 -0700457 }
458
459 private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
Saurav Das4e3224f2016-11-29 14:27:25 -0800460 ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId targetSw) {
sangho20eff1d2015-04-13 15:15:58 -0700461 for (Integer itrIdx : switchVia.keySet()) {
462 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
463 switchVia.get(itrIdx);
Saurav Das4e3224f2016-11-29 14:27:25 -0800464 if (swViaMap.get(targetSw) == null) {
sangho20eff1d2015-04-13 15:15:58 -0700465 continue;
466 } else {
Saurav Das4e3224f2016-11-29 14:27:25 -0800467 return swViaMap.get(targetSw);
sangho20eff1d2015-04-13 15:15:58 -0700468 }
469 }
470
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700471 return null;
sangho20eff1d2015-04-13 15:15:58 -0700472 }
473
474 private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
475 DeviceId dst,
476 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
477 Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
478 for (ArrayList<DeviceId> via : viaMap.get(src)) {
479 DeviceId linkSrc = src;
480 DeviceId linkDst = dst;
481 for (DeviceId viaDevice: via) {
482 ArrayList<DeviceId> link = new ArrayList<>();
483 linkDst = viaDevice;
484 link.add(linkSrc);
485 link.add(linkDst);
486 subLinks.add(link);
487 linkSrc = viaDevice;
488 }
489 ArrayList<DeviceId> link = new ArrayList<>();
490 link.add(linkSrc);
491 link.add(dst);
492 subLinks.add(link);
493 }
494
495 return subLinks;
496 }
497
Charles Chan93e71ba2016-04-29 14:38:22 -0700498 /**
499 * Populate ECMP rules for subnets from all switches to destination.
500 *
501 * @param destSw Device ID of destination switch
502 * @param ecmpSPG ECMP shortest path graph
503 * @param subnets Subnets to be populated. If empty, populate all configured subnets.
504 * @return true if succeed
505 */
sangho20eff1d2015-04-13 15:15:58 -0700506 private boolean populateEcmpRoutingRules(DeviceId destSw,
Charles Chan93e71ba2016-04-29 14:38:22 -0700507 EcmpShortestPathGraph ecmpSPG,
Pier Ventre10bd8d12016-11-26 21:05:22 -0800508 Set<IpPrefix> subnets) {
sanghob35a6192015-04-01 13:05:26 -0700509
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700510 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = ecmpSPG
511 .getAllLearnedSwitchesAndVia();
sanghob35a6192015-04-01 13:05:26 -0700512 for (Integer itrIdx : switchVia.keySet()) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700513 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap = switchVia
514 .get(itrIdx);
sanghob35a6192015-04-01 13:05:26 -0700515 for (DeviceId targetSw : swViaMap.keySet()) {
sanghob35a6192015-04-01 13:05:26 -0700516 Set<DeviceId> nextHops = new HashSet<>();
Saurav Dasa07f2032015-10-19 14:37:36 -0700517 log.debug("** Iter: {} root: {} target: {}", itrIdx, destSw, targetSw);
sanghob35a6192015-04-01 13:05:26 -0700518 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
519 if (via.isEmpty()) {
520 nextHops.add(destSw);
521 } else {
522 nextHops.add(via.get(0));
523 }
524 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700525 if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops, subnets)) {
sanghob35a6192015-04-01 13:05:26 -0700526 return false;
527 }
528 }
529 }
530
531 return true;
532 }
533
Charles Chan93e71ba2016-04-29 14:38:22 -0700534 /**
535 * Populate ECMP rules for subnets from target to destination via nexthops.
536 *
Saurav Das25190812016-05-27 13:54:07 -0700537 * @param targetSw Device ID of target switch in which rules will be programmed
538 * @param destSw Device ID of final destination switch to which the rules will forward
539 * @param nextHops List of next hops via which destSw will be reached
Charles Chan93e71ba2016-04-29 14:38:22 -0700540 * @param subnets Subnets to be populated. If empty, populate all configured subnets.
541 * @return true if succeed
542 */
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700543 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw,
544 DeviceId destSw,
Charles Chan93e71ba2016-04-29 14:38:22 -0700545 Set<DeviceId> nextHops,
Pier Ventre10bd8d12016-11-26 21:05:22 -0800546 Set<IpPrefix> subnets) {
sanghob35a6192015-04-01 13:05:26 -0700547 boolean result;
548
549 if (nextHops.isEmpty()) {
550 nextHops.add(destSw);
551 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700552 // If both target switch and dest switch are edge routers, then set IP
sangho52abe3a2015-05-05 14:13:34 -0700553 // rule for both subnet and router IP.
Charles Chan0b4e6182015-11-03 10:42:14 -0800554 boolean targetIsEdge;
555 boolean destIsEdge;
Pier Ventree0ae7a32016-11-23 09:57:42 -0800556 Ip4Address destRouterIpv4;
557 Ip6Address destRouterIpv6;
Charles Chan0b4e6182015-11-03 10:42:14 -0800558
559 try {
560 targetIsEdge = config.isEdgeDevice(targetSw);
561 destIsEdge = config.isEdgeDevice(destSw);
Pier Ventree0ae7a32016-11-23 09:57:42 -0800562 destRouterIpv4 = config.getRouterIpv4(destSw);
563 destRouterIpv6 = config.getRouterIpv6(destSw);
Charles Chan0b4e6182015-11-03 10:42:14 -0800564 } catch (DeviceConfigNotFoundException e) {
565 log.warn(e.getMessage() + " Aborting populateEcmpRoutingRulePartial.");
566 return false;
567 }
568
569 if (targetIsEdge && destIsEdge) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700570 subnets = (subnets != null && !subnets.isEmpty()) ? subnets : config.getSubnets(destSw);
Saurav Dasa07f2032015-10-19 14:37:36 -0700571 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for subnets {}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800572 targetSw, destSw, subnets);
Charles Chan93e71ba2016-04-29 14:38:22 -0700573 result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets,
574 destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700575 if (!result) {
576 return false;
577 }
578
Pier Ventree0ae7a32016-11-23 09:57:42 -0800579 IpPrefix routerIpPrefix = destRouterIpv4.toIpPrefix();
Saurav Dasa07f2032015-10-19 14:37:36 -0700580 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800581 targetSw, destSw, routerIpPrefix);
sangho666cd6d2015-04-14 16:27:13 -0700582 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700583 if (!result) {
584 return false;
585 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800586 /*
587 * If present we deal with IPv6 loopback.
588 */
589 if (destRouterIpv6 != null) {
590 routerIpPrefix = destRouterIpv6.toIpPrefix();
591 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for v6 router IP {}",
592 targetSw, destSw, routerIpPrefix);
593 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
594 if (!result) {
595 return false;
596 }
597 }
sanghob35a6192015-04-01 13:05:26 -0700598
Charles Chan0b4e6182015-11-03 10:42:14 -0800599 } else if (targetIsEdge) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800600 // If the target switch is an edge router, then set IP rules for the router IP.
Pier Ventree0ae7a32016-11-23 09:57:42 -0800601 IpPrefix routerIpPrefix = destRouterIpv4.toIpPrefix();
Saurav Dasa07f2032015-10-19 14:37:36 -0700602 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800603 targetSw, destSw, routerIpPrefix);
sangho666cd6d2015-04-14 16:27:13 -0700604 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sanghob35a6192015-04-01 13:05:26 -0700605 if (!result) {
606 return false;
607 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800608 if (destRouterIpv6 != null) {
609 routerIpPrefix = destRouterIpv6.toIpPrefix();
610 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for v6 router IP {}",
611 targetSw, destSw, routerIpPrefix);
612 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
613 if (!result) {
614 return false;
615 }
616 }
sangho52abe3a2015-05-05 14:13:34 -0700617 }
sangho52abe3a2015-05-05 14:13:34 -0700618 // Populates MPLS rules to all routers
Saurav Dasa07f2032015-10-19 14:37:36 -0700619 log.debug("* populateEcmpRoutingRulePartial in device{} towards {} for all MPLS rules",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700620 targetSw, destSw);
Pier Ventree0ae7a32016-11-23 09:57:42 -0800621 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops, destRouterIpv4);
sangho52abe3a2015-05-05 14:13:34 -0700622 if (!result) {
623 return false;
sanghob35a6192015-04-01 13:05:26 -0700624 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800625 /*
626 * If present we will populate the MPLS rules for the IPv6 sid.
627 */
628 if (destRouterIpv6 != null) {
629 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops, destRouterIpv6);
630 if (!result) {
631 return false;
632 }
633 }
sanghob35a6192015-04-01 13:05:26 -0700634 return true;
635 }
636
637 /**
Saurav Das018605f2017-02-18 14:05:44 -0800638 * Populates filtering rules for port, and punting rules
639 * for gateway IPs, loopback IPs and arp/ndp traffic.
640 * Should only be called by the master instance for this device/port.
sanghob35a6192015-04-01 13:05:26 -0700641 *
642 * @param deviceId Switch ID to set the rules
643 */
Saurav Das822c4e22015-10-23 10:51:11 -0700644 public void populatePortAddressingRules(DeviceId deviceId) {
Charles Chanf6ec1532017-02-08 16:10:40 -0800645 rulePopulator.populateIpPunts(deviceId);
Pier Luigi9e5c5ca2017-01-12 18:14:58 -0800646 rulePopulator.populateArpNdpPunts(deviceId);
Saurav Das59232cf2016-04-27 18:35:50 -0700647
648 // Although device is added, sometimes device store does not have the
649 // ports for this device yet. It results in missing filtering rules in the
650 // switch. We will attempt it a few times. If it still does not work,
651 // user can manually repopulate using CLI command sr-reroute-network
Charles Chanf6ec1532017-02-08 16:10:40 -0800652 PortFilterInfo firstRun = rulePopulator.populateVlanMacFilters(deviceId);
Saurav Dasd2fded02016-12-02 15:43:47 -0800653 if (firstRun == null) {
654 firstRun = new PortFilterInfo(0, 0, 0);
Saurav Das59232cf2016-04-27 18:35:50 -0700655 }
Saurav Dasd2fded02016-12-02 15:43:47 -0800656 executorService.schedule(new RetryFilters(deviceId, firstRun),
657 RETRY_INTERVAL_MS, TimeUnit.MILLISECONDS);
sanghob35a6192015-04-01 13:05:26 -0700658 }
659
660 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700661 * Start the flow rule population process if it was never started. The
662 * process finishes successfully when all flow rules are set and stops with
663 * ABORTED status when any groups required for flows is not set yet.
sanghob35a6192015-04-01 13:05:26 -0700664 */
665 public void startPopulationProcess() {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900666 statusLock.lock();
667 try {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700668 if (populationStatus == Status.IDLE
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700669 || populationStatus == Status.SUCCEEDED
670 || populationStatus == Status.ABORTED) {
sanghob35a6192015-04-01 13:05:26 -0700671 populationStatus = Status.STARTED;
672 populateAllRoutingRules();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700673 } else {
674 log.warn("Not initiating startPopulationProcess as populationStatus is {}",
675 populationStatus);
sanghob35a6192015-04-01 13:05:26 -0700676 }
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900677 } finally {
678 statusLock.unlock();
sanghob35a6192015-04-01 13:05:26 -0700679 }
680 }
681
682 /**
683 * Resume the flow rule population process if it was aborted for any reason.
684 * Mostly the process is aborted when the groups required are not set yet.
Saurav Dasa07f2032015-10-19 14:37:36 -0700685 * XXX is this called?
686 *
sanghob35a6192015-04-01 13:05:26 -0700687 */
688 public void resumePopulationProcess() {
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900689 statusLock.lock();
690 try {
sanghob35a6192015-04-01 13:05:26 -0700691 if (populationStatus == Status.ABORTED) {
692 populationStatus = Status.STARTED;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700693 // TODO: we need to restart from the point aborted instead of
694 // restarting.
sanghob35a6192015-04-01 13:05:26 -0700695 populateAllRoutingRules();
696 }
HIGUCHI Yuta84a25fc2015-09-08 16:16:31 +0900697 } finally {
698 statusLock.unlock();
sanghob35a6192015-04-01 13:05:26 -0700699 }
700 }
Saurav Das80980c72016-03-23 11:22:49 -0700701
Charles Chan93e71ba2016-04-29 14:38:22 -0700702 /**
703 * Populate rules of given subnet at given location.
704 *
705 * @param cp connect point of the subnet being added
706 * @param subnets subnet being added
707 * @return true if succeed
708 */
Pier Ventre10bd8d12016-11-26 21:05:22 -0800709 protected boolean populateSubnet(ConnectPoint cp, Set<IpPrefix> subnets) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700710 statusLock.lock();
711 try {
712 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(cp.deviceId());
713 if (ecmpSpg == null) {
714 log.warn("Fail to populating subnet {}: {}", subnets, ECMPSPG_MISSING);
715 return false;
716 }
717 return populateEcmpRoutingRules(cp.deviceId(), ecmpSpg, subnets);
718 } finally {
719 statusLock.unlock();
720 }
721 }
722
723 /**
724 * Revoke rules of given subnet at given location.
725 *
726 * @param subnets subnet being removed
727 * @return true if succeed
728 */
Pier Ventre10bd8d12016-11-26 21:05:22 -0800729 protected boolean revokeSubnet(Set<IpPrefix> subnets) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700730 statusLock.lock();
731 try {
732 return srManager.routingRulePopulator.revokeIpRuleForSubnet(subnets);
733 } finally {
734 statusLock.unlock();
735 }
736 }
737
738 protected void purgeEcmpGraph(DeviceId deviceId) {
Saurav Das80980c72016-03-23 11:22:49 -0700739 currentEcmpSpgMap.remove(deviceId);
Saurav Das7a1ffca2016-03-28 19:00:18 -0700740 if (updatedEcmpSpgMap != null) {
741 updatedEcmpSpgMap.remove(deviceId);
742 }
Pier Ventre2c515312016-09-13 21:33:40 -0700743 this.populateRoutingRulesForLinkStatusChange(null);
Saurav Das80980c72016-03-23 11:22:49 -0700744 }
Saurav Das59232cf2016-04-27 18:35:50 -0700745
Saurav Dasd2fded02016-12-02 15:43:47 -0800746 /**
747 * Utility class used to temporarily store information about the ports on a
748 * device processed for filtering objectives.
Saurav Dasd2fded02016-12-02 15:43:47 -0800749 */
750 public final class PortFilterInfo {
Saurav Das018605f2017-02-18 14:05:44 -0800751 int disabledPorts = 0, errorPorts = 0, filteredPorts = 0;
Saurav Das59232cf2016-04-27 18:35:50 -0700752
Saurav Das018605f2017-02-18 14:05:44 -0800753 public PortFilterInfo(int disabledPorts, int errorPorts,
Saurav Dasd2fded02016-12-02 15:43:47 -0800754 int filteredPorts) {
755 this.disabledPorts = disabledPorts;
756 this.filteredPorts = filteredPorts;
Saurav Das018605f2017-02-18 14:05:44 -0800757 this.errorPorts = errorPorts;
Saurav Dasd2fded02016-12-02 15:43:47 -0800758 }
759
760 @Override
761 public int hashCode() {
Saurav Das018605f2017-02-18 14:05:44 -0800762 return Objects.hash(disabledPorts, filteredPorts, errorPorts);
Saurav Dasd2fded02016-12-02 15:43:47 -0800763 }
764
765 @Override
766 public boolean equals(Object obj) {
767 if (this == obj) {
768 return true;
769 }
770 if ((obj == null) || (!(obj instanceof PortFilterInfo))) {
771 return false;
772 }
773 PortFilterInfo other = (PortFilterInfo) obj;
774 return ((disabledPorts == other.disabledPorts) &&
775 (filteredPorts == other.filteredPorts) &&
Saurav Das018605f2017-02-18 14:05:44 -0800776 (errorPorts == other.errorPorts));
Saurav Dasd2fded02016-12-02 15:43:47 -0800777 }
778
779 @Override
780 public String toString() {
781 MoreObjects.ToStringHelper helper = toStringHelper(this)
782 .add("disabledPorts", disabledPorts)
Saurav Das018605f2017-02-18 14:05:44 -0800783 .add("errorPorts", errorPorts)
Saurav Dasd2fded02016-12-02 15:43:47 -0800784 .add("filteredPorts", filteredPorts);
785 return helper.toString();
786 }
787 }
788
789 /**
790 * RetryFilters populates filtering objectives for a device and keeps retrying
791 * till the number of ports filtered are constant for a predefined number
792 * of attempts.
793 */
794 protected final class RetryFilters implements Runnable {
795 int constantAttempts = MAX_CONSTANT_RETRY_ATTEMPTS;
796 DeviceId devId;
797 int counter;
798 PortFilterInfo prevRun;
799
800 private RetryFilters(DeviceId deviceId, PortFilterInfo previousRun) {
Saurav Das59232cf2016-04-27 18:35:50 -0700801 devId = deviceId;
Saurav Dasd2fded02016-12-02 15:43:47 -0800802 prevRun = previousRun;
803 counter = 0;
Saurav Das59232cf2016-04-27 18:35:50 -0700804 }
805
806 @Override
807 public void run() {
Saurav Dasd2fded02016-12-02 15:43:47 -0800808 log.info("RETRY FILTER ATTEMPT {} ** dev:{}", ++counter, devId);
Charles Chanf6ec1532017-02-08 16:10:40 -0800809 PortFilterInfo thisRun = rulePopulator.populateVlanMacFilters(devId);
Saurav Dasd2fded02016-12-02 15:43:47 -0800810 boolean sameResult = prevRun.equals(thisRun);
811 log.debug("dev:{} prevRun:{} thisRun:{} sameResult:{}", devId, prevRun,
812 thisRun, sameResult);
813 if (thisRun == null || !sameResult || (sameResult && --constantAttempts > 0)) {
Saurav Das018605f2017-02-18 14:05:44 -0800814 // exponentially increasing intervals for retries
815 executorService.schedule(this,
816 RETRY_INTERVAL_MS * (int) Math.pow(counter, RETRY_INTERVAL_SCALE),
817 TimeUnit.MILLISECONDS);
Saurav Dasd2fded02016-12-02 15:43:47 -0800818 if (!sameResult) {
819 constantAttempts = MAX_CONSTANT_RETRY_ATTEMPTS; //reset
820 }
Saurav Das59232cf2016-04-27 18:35:50 -0700821 }
Saurav Dasd2fded02016-12-02 15:43:47 -0800822 prevRun = (thisRun == null) ? prevRun : thisRun;
Saurav Das59232cf2016-04-27 18:35:50 -0700823 }
Saurav Das59232cf2016-04-27 18:35:50 -0700824 }
825
sanghob35a6192015-04-01 13:05:26 -0700826}