blob: 48936845274b5f0ebbd833eb9697713636677785 [file] [log] [blame]
sangho80f11cb2015-04-01 13:05:26 -07001/*
Brian O'Connor43b53542016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
sangho80f11cb2015-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 Dasd1872b02016-12-02 15:43:47 -080018import com.google.common.base.MoreObjects;
Charles Chanc22cef32016-04-29 14:38:22 -070019import com.google.common.collect.ImmutableSet;
Saurav Das1b391d52016-11-29 14:27:25 -080020import com.google.common.collect.Lists;
sanghofb7c7292015-04-13 15:15:58 -070021import com.google.common.collect.Maps;
22import com.google.common.collect.Sets;
sangho9b169e32015-04-14 16:27:13 -070023import org.onlab.packet.Ip4Address;
Pier Ventreadb4ae62016-11-23 09:57:42 -080024import org.onlab.packet.Ip6Address;
sangho80f11cb2015-04-01 13:05:26 -070025import org.onlab.packet.IpPrefix;
Charles Chanc22cef32016-04-29 14:38:22 -070026import org.onosproject.net.ConnectPoint;
sangho80f11cb2015-04-01 13:05:26 -070027import org.onosproject.net.Device;
28import org.onosproject.net.DeviceId;
sanghofb7c7292015-04-13 15:15:58 -070029import org.onosproject.net.Link;
Charles Chan319d1a22015-11-03 10:42:14 -080030import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
31import org.onosproject.segmentrouting.config.DeviceConfiguration;
sangho80f11cb2015-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 Dasd1872b02016-12-02 15:43:47 -080038import java.util.Objects;
sangho80f11cb2015-04-01 13:05:26 -070039import java.util.Set;
Saurav Das07c74602016-04-27 18:35:50 -070040import java.util.concurrent.ScheduledExecutorService;
41import java.util.concurrent.TimeUnit;
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +090042import java.util.concurrent.locks.Lock;
43import java.util.concurrent.locks.ReentrantLock;
sangho80f11cb2015-04-01 13:05:26 -070044
Saurav Dasd1872b02016-12-02 15:43:47 -080045import static com.google.common.base.MoreObjects.toStringHelper;
Pier Ventreadb4ae62016-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;
sangho80f11cb2015-04-01 13:05:26 -070049
Charles Chanb7f75ac2016-01-11 18:28:54 -080050/**
51 * Default routing handler that is responsible for route computing and
52 * routing rule population.
53 */
sangho80f11cb2015-04-01 13:05:26 -070054public class DefaultRoutingHandler {
Saurav Dasd1872b02016-12-02 15:43:47 -080055 private static final int MAX_CONSTANT_RETRY_ATTEMPTS = 4;
56 private static final int RETRY_INTERVAL_MS = 500;
Charles Chanc22cef32016-04-29 14:38:22 -070057 private static final String ECMPSPG_MISSING = "ECMP shortest path graph not found";
58 private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
sangho80f11cb2015-04-01 13:05:26 -070059
60 private SegmentRoutingManager srManager;
61 private RoutingRulePopulator rulePopulator;
Shashikanth VH0637b162015-12-11 01:32:44 +053062 private HashMap<DeviceId, EcmpShortestPathGraph> currentEcmpSpgMap;
63 private HashMap<DeviceId, EcmpShortestPathGraph> updatedEcmpSpgMap;
sangho9b169e32015-04-14 16:27:13 -070064 private DeviceConfiguration config;
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +090065 private final Lock statusLock = new ReentrantLock();
66 private volatile Status populationStatus;
Yuta HIGUCHIebee2f12016-07-21 16:54:33 -070067 private ScheduledExecutorService executorService
Saurav Dasd1872b02016-12-02 15:43:47 -080068 = newScheduledThreadPool(1, groupedThreads("retryftr", "retry-%d", log));
sangho80f11cb2015-04-01 13:05:26 -070069
70 /**
71 * Represents the default routing population status.
72 */
73 public enum Status {
74 // population process is not started yet.
75 IDLE,
76
77 // population process started.
78 STARTED,
79
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070080 // population process was aborted due to errors, mostly for groups not
81 // found.
sangho80f11cb2015-04-01 13:05:26 -070082 ABORTED,
83
84 // population process was finished successfully.
85 SUCCEEDED
86 }
87
88 /**
89 * Creates a DefaultRoutingHandler object.
90 *
91 * @param srManager SegmentRoutingManager object
92 */
93 public DefaultRoutingHandler(SegmentRoutingManager srManager) {
94 this.srManager = srManager;
95 this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
sangho9b169e32015-04-14 16:27:13 -070096 this.config = checkNotNull(srManager.deviceConfiguration);
sangho80f11cb2015-04-01 13:05:26 -070097 this.populationStatus = Status.IDLE;
sanghofb7c7292015-04-13 15:15:58 -070098 this.currentEcmpSpgMap = Maps.newHashMap();
sangho80f11cb2015-04-01 13:05:26 -070099 }
100
101 /**
102 * Populates all routing rules to all connected routers, including default
103 * routing rules, adjacency rules, and policy rules if any.
104 *
105 * @return true if it succeeds in populating all rules, otherwise false
106 */
107 public boolean populateAllRoutingRules() {
108
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900109 statusLock.lock();
110 try {
111 populationStatus = Status.STARTED;
112 rulePopulator.resetCounter();
Saurav Das88979182015-10-19 14:37:36 -0700113 log.info("Starting to populate segment-routing rules");
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900114 log.debug("populateAllRoutingRules: populationStatus is STARTED");
sangho80f11cb2015-04-01 13:05:26 -0700115
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900116 for (Device sw : srManager.deviceService.getDevices()) {
Charles Chan77277672015-10-20 16:24:19 -0700117 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900118 log.debug("populateAllRoutingRules: skipping device {}...we are not master",
119 sw.id());
120 continue;
121 }
122
Shashikanth VH0637b162015-12-11 01:32:44 +0530123 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(sw.id(), srManager);
Charles Chanc22cef32016-04-29 14:38:22 -0700124 if (!populateEcmpRoutingRules(sw.id(), ecmpSpg, ImmutableSet.of())) {
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900125 log.debug("populateAllRoutingRules: populationStatus is ABORTED");
126 populationStatus = Status.ABORTED;
127 log.debug("Abort routing rule population");
128 return false;
129 }
130 currentEcmpSpgMap.put(sw.id(), ecmpSpg);
131
132 // TODO: Set adjacency routing rule for all switches
sangho80f11cb2015-04-01 13:05:26 -0700133 }
134
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900135 log.debug("populateAllRoutingRules: populationStatus is SUCCEEDED");
136 populationStatus = Status.SUCCEEDED;
Saurav Das88979182015-10-19 14:37:36 -0700137 log.info("Completed routing rule population. Total # of rules pushed : {}",
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900138 rulePopulator.getCounter());
139 return true;
140 } finally {
141 statusLock.unlock();
sangho80f11cb2015-04-01 13:05:26 -0700142 }
sangho80f11cb2015-04-01 13:05:26 -0700143 }
144
sanghofb7c7292015-04-13 15:15:58 -0700145 /**
146 * Populates the routing rules according to the route changes due to the link
147 * failure or link add. It computes the routes changed due to the link changes and
Saurav Das1b391d52016-11-29 14:27:25 -0800148 * repopulates the rules only for these routes. Note that when a switch goes
149 * away, all of its links fail as well, but this is handled as a single
150 * switch removal event.
sanghofb7c7292015-04-13 15:15:58 -0700151 *
Saurav Das1b391d52016-11-29 14:27:25 -0800152 * @param failedLink the single failed link, or null for other conditions
153 * such as an added link or a removed switch
sanghofb7c7292015-04-13 15:15:58 -0700154 * @return true if it succeeds to populate all rules, false otherwise
155 */
Saurav Das1b391d52016-11-29 14:27:25 -0800156 public boolean populateRoutingRulesForLinkStatusChange(Link failedLink) {
sanghofb7c7292015-04-13 15:15:58 -0700157
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900158 statusLock.lock();
159 try {
sanghofb7c7292015-04-13 15:15:58 -0700160
161 if (populationStatus == Status.STARTED) {
sanghodf0153f2015-05-05 14:13:34 -0700162 log.warn("Previous rule population is not finished.");
sanghofb7c7292015-04-13 15:15:58 -0700163 return true;
164 }
165
sangho28d0b6d2015-05-07 13:30:57 -0700166 // Take the snapshots of the links
167 updatedEcmpSpgMap = new HashMap<>();
168 for (Device sw : srManager.deviceService.getDevices()) {
Charles Chan77277672015-10-20 16:24:19 -0700169 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
sangho28d0b6d2015-05-07 13:30:57 -0700170 continue;
171 }
Shashikanth VH0637b162015-12-11 01:32:44 +0530172 EcmpShortestPathGraph ecmpSpgUpdated =
173 new EcmpShortestPathGraph(sw.id(), srManager);
sangho28d0b6d2015-05-07 13:30:57 -0700174 updatedEcmpSpgMap.put(sw.id(), ecmpSpgUpdated);
175 }
176
sanghodf0153f2015-05-05 14:13:34 -0700177 log.info("Starts rule population from link change");
178
sanghofb7c7292015-04-13 15:15:58 -0700179 Set<ArrayList<DeviceId>> routeChanges;
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700180 log.trace("populateRoutingRulesForLinkStatusChange: "
181 + "populationStatus is STARTED");
sanghofb7c7292015-04-13 15:15:58 -0700182 populationStatus = Status.STARTED;
Saurav Das1b391d52016-11-29 14:27:25 -0800183 // try optimized re-routing
184 if (failedLink == null) {
185 // Compare all routes of existing ECMP SPG to new ECMP SPG
sanghofb7c7292015-04-13 15:15:58 -0700186 routeChanges = computeRouteChange();
187 } else {
188 // Compare existing ECMP SPG only with the link removed
Saurav Das1b391d52016-11-29 14:27:25 -0800189 routeChanges = computeDamagedRoutes(failedLink);
sanghofb7c7292015-04-13 15:15:58 -0700190 }
191
Saurav Das1b391d52016-11-29 14:27:25 -0800192 // do full re-routing if optimized routing returns null routeChanges
Saurav Dasb149be12016-06-07 10:08:06 -0700193 if (routeChanges == null) {
194 return populateAllRoutingRules();
195 }
196
sanghofb7c7292015-04-13 15:15:58 -0700197 if (routeChanges.isEmpty()) {
sanghodf0153f2015-05-05 14:13:34 -0700198 log.info("No route changes for the link status change");
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700199 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
sanghofb7c7292015-04-13 15:15:58 -0700200 populationStatus = Status.SUCCEEDED;
201 return true;
202 }
203
204 if (repopulateRoutingRulesForRoutes(routeChanges)) {
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700205 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
sanghofb7c7292015-04-13 15:15:58 -0700206 populationStatus = Status.SUCCEEDED;
207 log.info("Complete to repopulate the rules. # of rules populated : {}",
208 rulePopulator.getCounter());
209 return true;
210 } else {
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700211 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is ABORTED");
sanghofb7c7292015-04-13 15:15:58 -0700212 populationStatus = Status.ABORTED;
213 log.warn("Failed to repopulate the rules.");
214 return false;
215 }
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900216 } finally {
217 statusLock.unlock();
sanghofb7c7292015-04-13 15:15:58 -0700218 }
219 }
220
221 private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
222 rulePopulator.resetCounter();
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700223 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> routesBydevice =
224 new HashMap<>();
sanghofb7c7292015-04-13 15:15:58 -0700225 for (ArrayList<DeviceId> link: routes) {
sangho2165d222015-05-01 09:38:25 -0700226 // When only the source device is defined, reinstall routes to all other devices
sanghofb7c7292015-04-13 15:15:58 -0700227 if (link.size() == 1) {
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700228 log.trace("repopulateRoutingRulesForRoutes: running ECMP graph for device {}", link.get(0));
Shashikanth VH0637b162015-12-11 01:32:44 +0530229 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(link.get(0), srManager);
Charles Chanc22cef32016-04-29 14:38:22 -0700230 if (populateEcmpRoutingRules(link.get(0), ecmpSpg, ImmutableSet.of())) {
Saurav Dase0237a32016-05-27 13:54:07 -0700231 log.debug("Populating flow rules from all to dest:{} is successful",
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700232 link.get(0));
sanghofb7c7292015-04-13 15:15:58 -0700233 currentEcmpSpgMap.put(link.get(0), ecmpSpg);
sanghodf0153f2015-05-05 14:13:34 -0700234 } else {
Saurav Dase0237a32016-05-27 13:54:07 -0700235 log.warn("Failed to populate the flow rules from all to dest:{}", link.get(0));
sanghodf0153f2015-05-05 14:13:34 -0700236 return false;
sanghofb7c7292015-04-13 15:15:58 -0700237 }
sangho28d0b6d2015-05-07 13:30:57 -0700238 } else {
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700239 ArrayList<ArrayList<DeviceId>> deviceRoutes =
240 routesBydevice.get(link.get(1));
241 if (deviceRoutes == null) {
242 deviceRoutes = new ArrayList<>();
243 routesBydevice.put(link.get(1), deviceRoutes);
244 }
245 deviceRoutes.add(link);
246 }
247 }
248
249 for (DeviceId impactedDevice : routesBydevice.keySet()) {
250 ArrayList<ArrayList<DeviceId>> deviceRoutes =
251 routesBydevice.get(impactedDevice);
252 for (ArrayList<DeviceId> link: deviceRoutes) {
253 log.debug("repopulate RoutingRules For Routes {} -> {}",
254 link.get(0), link.get(1));
sangho28d0b6d2015-05-07 13:30:57 -0700255 DeviceId src = link.get(0);
256 DeviceId dst = link.get(1);
Shashikanth VH0637b162015-12-11 01:32:44 +0530257 EcmpShortestPathGraph ecmpSpg = updatedEcmpSpgMap.get(dst);
sangho28d0b6d2015-05-07 13:30:57 -0700258 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
259 ecmpSpg.getAllLearnedSwitchesAndVia();
260 for (Integer itrIdx : switchVia.keySet()) {
261 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
262 switchVia.get(itrIdx);
263 for (DeviceId targetSw : swViaMap.keySet()) {
264 if (!targetSw.equals(src)) {
265 continue;
266 }
267 Set<DeviceId> nextHops = new HashSet<>();
268 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
269 if (via.isEmpty()) {
270 nextHops.add(dst);
271 } else {
272 nextHops.add(via.get(0));
273 }
274 }
Charles Chanc22cef32016-04-29 14:38:22 -0700275 if (!populateEcmpRoutingRulePartial(targetSw, dst,
276 nextHops, ImmutableSet.of())) {
sangho28d0b6d2015-05-07 13:30:57 -0700277 return false;
sanghofb7c7292015-04-13 15:15:58 -0700278 }
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700279 log.debug("Populating flow rules from {} to {} is successful",
280 targetSw, dst);
sanghofb7c7292015-04-13 15:15:58 -0700281 }
sanghofb7c7292015-04-13 15:15:58 -0700282 }
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700283 //currentEcmpSpgMap.put(dst, ecmpSpg);
sanghofb7c7292015-04-13 15:15:58 -0700284 }
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700285 //Only if all the flows for all impacted routes to a
286 //specific target are pushed successfully, update the
Saurav Das1b391d52016-11-29 14:27:25 -0800287 //ECMP graph for that target. Or else the next event
288 //would not see any changes in the ECMP graphs.
289 //In another case, the target switch has gone away, so
290 //routes can't be installed. In that case, the current map
291 //is updated here, without any flows being pushed.
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700292 currentEcmpSpgMap.put(impactedDevice,
293 updatedEcmpSpgMap.get(impactedDevice));
sanghofb7c7292015-04-13 15:15:58 -0700294 }
295 return true;
296 }
297
Saurav Dasb149be12016-06-07 10:08:06 -0700298 /**
Saurav Das1b391d52016-11-29 14:27:25 -0800299 * Computes set of affected routes due to failed link. Assumes
Saurav Dasb149be12016-06-07 10:08:06 -0700300 * previous ecmp shortest-path graph exists for a switch in order to compute
301 * affected routes. If such a graph does not exist, the method returns null.
302 *
303 * @param linkFail the failed link
304 * @return the set of affected routes which may be empty if no routes were
305 * affected, or null if no previous ecmp spg was found for comparison
306 */
sanghofb7c7292015-04-13 15:15:58 -0700307 private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
308
309 Set<ArrayList<DeviceId>> routes = new HashSet<>();
310
311 for (Device sw : srManager.deviceService.getDevices()) {
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700312 log.debug("Computing the impacted routes for device {} due to link fail",
313 sw.id());
Charles Chan77277672015-10-20 16:24:19 -0700314 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
Saurav Dasb149be12016-06-07 10:08:06 -0700315 log.debug("No mastership for {} .. skipping route optimization",
316 sw.id());
sanghofb7c7292015-04-13 15:15:58 -0700317 continue;
318 }
Shashikanth VH0637b162015-12-11 01:32:44 +0530319 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
sanghofb7c7292015-04-13 15:15:58 -0700320 if (ecmpSpg == null) {
Saurav Dasb149be12016-06-07 10:08:06 -0700321 log.warn("No existing ECMP graph for switch {}. Aborting optimized"
322 + " rerouting and opting for full-reroute", sw.id());
323 return null;
sanghofb7c7292015-04-13 15:15:58 -0700324 }
325 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
326 ecmpSpg.getAllLearnedSwitchesAndVia();
327 for (Integer itrIdx : switchVia.keySet()) {
Saurav Dasb149be12016-06-07 10:08:06 -0700328 log.trace("Iterindex# {}", itrIdx);
sanghofb7c7292015-04-13 15:15:58 -0700329 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
330 switchVia.get(itrIdx);
331 for (DeviceId targetSw : swViaMap.keySet()) {
Saurav Das1b391d52016-11-29 14:27:25 -0800332 DeviceId rootSw = sw.id();
Saurav Dasb149be12016-06-07 10:08:06 -0700333 if (log.isTraceEnabled()) {
Saurav Das1b391d52016-11-29 14:27:25 -0800334 log.trace("TargetSwitch {} --> RootSwitch {}", targetSw, rootSw);
Saurav Dasb149be12016-06-07 10:08:06 -0700335 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
336 log.trace(" Via:");
Pier Ventreadb4ae62016-11-23 09:57:42 -0800337 via.forEach(e -> log.trace(" {}", e));
Saurav Dasb149be12016-06-07 10:08:06 -0700338 }
339 }
sanghofb7c7292015-04-13 15:15:58 -0700340 Set<ArrayList<DeviceId>> subLinks =
Saurav Das1b391d52016-11-29 14:27:25 -0800341 computeLinks(targetSw, rootSw, swViaMap);
sanghofb7c7292015-04-13 15:15:58 -0700342 for (ArrayList<DeviceId> alink: subLinks) {
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700343 if ((alink.get(0).equals(linkFail.src().deviceId()) &&
344 alink.get(1).equals(linkFail.dst().deviceId()))
345 ||
346 (alink.get(0).equals(linkFail.dst().deviceId()) &&
347 alink.get(1).equals(linkFail.src().deviceId()))) {
Saurav Das1b391d52016-11-29 14:27:25 -0800348 log.debug("Impacted route:{}->{}", targetSw, rootSw);
sanghofb7c7292015-04-13 15:15:58 -0700349 ArrayList<DeviceId> aRoute = new ArrayList<>();
350 aRoute.add(targetSw);
Saurav Das1b391d52016-11-29 14:27:25 -0800351 aRoute.add(rootSw);
sanghofb7c7292015-04-13 15:15:58 -0700352 routes.add(aRoute);
353 break;
354 }
355 }
356 }
357 }
sangho28d0b6d2015-05-07 13:30:57 -0700358
sanghofb7c7292015-04-13 15:15:58 -0700359 }
360
361 return routes;
362 }
363
Saurav Das1b391d52016-11-29 14:27:25 -0800364 /**
365 * Computes set of affected routes due to new links or failed switches.
366 *
367 * @return the set of affected routes which may be empty if no routes were
368 * affected
369 */
sanghofb7c7292015-04-13 15:15:58 -0700370 private Set<ArrayList<DeviceId>> computeRouteChange() {
371
Saurav Das1b391d52016-11-29 14:27:25 -0800372 ImmutableSet.Builder<ArrayList<DeviceId>> changedRoutesBuilder =
373 ImmutableSet.builder();
sanghofb7c7292015-04-13 15:15:58 -0700374
375 for (Device sw : srManager.deviceService.getDevices()) {
Saurav Das1b391d52016-11-29 14:27:25 -0800376 DeviceId rootSw = sw.id();
377 log.debug("Computing the impacted routes for device {}", rootSw);
378 if (!srManager.mastershipService.isLocalMaster(rootSw)) {
Saurav Dasb149be12016-06-07 10:08:06 -0700379 log.debug("No mastership for {} ... skipping route optimization",
Saurav Das1b391d52016-11-29 14:27:25 -0800380 rootSw);
sanghofb7c7292015-04-13 15:15:58 -0700381 continue;
382 }
Saurav Dasb149be12016-06-07 10:08:06 -0700383 if (log.isTraceEnabled()) {
Saurav Das1b391d52016-11-29 14:27:25 -0800384 log.trace("link of {} - ", rootSw);
385 for (Link link: srManager.linkService.getDeviceLinks(rootSw)) {
Saurav Dasb149be12016-06-07 10:08:06 -0700386 log.trace("{} -> {} ", link.src().deviceId(), link.dst().deviceId());
387 }
sangho28d0b6d2015-05-07 13:30:57 -0700388 }
Saurav Das1b391d52016-11-29 14:27:25 -0800389 EcmpShortestPathGraph currEcmpSpg = currentEcmpSpgMap.get(rootSw);
390 if (currEcmpSpg == null) {
391 log.debug("No existing ECMP graph for device {}", rootSw);
392 changedRoutesBuilder.add(Lists.newArrayList(rootSw));
sanghofb7c7292015-04-13 15:15:58 -0700393 continue;
394 }
Saurav Das1b391d52016-11-29 14:27:25 -0800395 EcmpShortestPathGraph newEcmpSpg = updatedEcmpSpgMap.get(rootSw);
396 if (log.isTraceEnabled()) {
397 log.trace("Root switch: {}", rootSw);
398 log.trace(" Current/Existing SPG: {}", currEcmpSpg);
399 log.trace(" New/Updated SPG: {}", newEcmpSpg);
400 }
401 // first use the updated/new map to compare to current/existing map
402 // as new links may have come up
403 changedRoutesBuilder.addAll(compareGraphs(newEcmpSpg, currEcmpSpg, rootSw));
404 // then use the current/existing map to compare to updated/new map
405 // as switch may have been removed
406 changedRoutesBuilder.addAll(compareGraphs(currEcmpSpg, newEcmpSpg, rootSw));
407 }
sanghofb7c7292015-04-13 15:15:58 -0700408
Saurav Das1b391d52016-11-29 14:27:25 -0800409 Set<ArrayList<DeviceId>> changedRoutes = changedRoutesBuilder.build();
410 for (ArrayList<DeviceId> route: changedRoutes) {
411 log.debug("Route changes Target -> Root");
412 if (route.size() == 1) {
413 log.debug(" : all -> {}", route.get(0));
414 } else {
415 log.debug(" : {} -> {}", route.get(0), route.get(1));
416 }
417 }
418 return changedRoutes;
419 }
420
421 /**
422 * For the root switch, searches all the target nodes reachable in the base
423 * graph, and compares paths to the ones in the comp graph.
424 *
425 * @param base the graph that is indexed for all reachable target nodes
426 * from the root node
427 * @param comp the graph that the base graph is compared to
428 * @param rootSw both ecmp graphs are calculated for the root node
429 * @return all the routes that have changed in the base graph
430 */
431 private Set<ArrayList<DeviceId>> compareGraphs(EcmpShortestPathGraph base,
432 EcmpShortestPathGraph comp,
433 DeviceId rootSw) {
434 ImmutableSet.Builder<ArrayList<DeviceId>> changedRoutesBuilder =
435 ImmutableSet.builder();
436 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> baseMap =
437 base.getAllLearnedSwitchesAndVia();
438 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> compMap =
439 comp.getAllLearnedSwitchesAndVia();
440 for (Integer itrIdx : baseMap.keySet()) {
441 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> baseViaMap =
442 baseMap.get(itrIdx);
443 for (DeviceId targetSw : baseViaMap.keySet()) {
444 ArrayList<ArrayList<DeviceId>> basePath = baseViaMap.get(targetSw);
445 ArrayList<ArrayList<DeviceId>> compPath = getVia(compMap, targetSw);
446 if ((compPath == null) || !basePath.equals(compPath)) {
447 log.debug("Impacted route:{} -> {}", targetSw, rootSw);
448 ArrayList<DeviceId> route = new ArrayList<>();
449 route.add(targetSw);
450 route.add(rootSw);
451 changedRoutesBuilder.add(route);
sanghofb7c7292015-04-13 15:15:58 -0700452 }
453 }
sangho28d0b6d2015-05-07 13:30:57 -0700454 }
Saurav Das1b391d52016-11-29 14:27:25 -0800455 return changedRoutesBuilder.build();
sanghofb7c7292015-04-13 15:15:58 -0700456 }
457
458 private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
Saurav Das1b391d52016-11-29 14:27:25 -0800459 ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId targetSw) {
sanghofb7c7292015-04-13 15:15:58 -0700460 for (Integer itrIdx : switchVia.keySet()) {
461 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
462 switchVia.get(itrIdx);
Saurav Das1b391d52016-11-29 14:27:25 -0800463 if (swViaMap.get(targetSw) == null) {
sanghofb7c7292015-04-13 15:15:58 -0700464 continue;
465 } else {
Saurav Das1b391d52016-11-29 14:27:25 -0800466 return swViaMap.get(targetSw);
sanghofb7c7292015-04-13 15:15:58 -0700467 }
468 }
469
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700470 return null;
sanghofb7c7292015-04-13 15:15:58 -0700471 }
472
473 private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
474 DeviceId dst,
475 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
476 Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
477 for (ArrayList<DeviceId> via : viaMap.get(src)) {
478 DeviceId linkSrc = src;
479 DeviceId linkDst = dst;
480 for (DeviceId viaDevice: via) {
481 ArrayList<DeviceId> link = new ArrayList<>();
482 linkDst = viaDevice;
483 link.add(linkSrc);
484 link.add(linkDst);
485 subLinks.add(link);
486 linkSrc = viaDevice;
487 }
488 ArrayList<DeviceId> link = new ArrayList<>();
489 link.add(linkSrc);
490 link.add(dst);
491 subLinks.add(link);
492 }
493
494 return subLinks;
495 }
496
Charles Chanc22cef32016-04-29 14:38:22 -0700497 /**
498 * Populate ECMP rules for subnets from all switches to destination.
499 *
500 * @param destSw Device ID of destination switch
501 * @param ecmpSPG ECMP shortest path graph
502 * @param subnets Subnets to be populated. If empty, populate all configured subnets.
503 * @return true if succeed
504 */
sanghofb7c7292015-04-13 15:15:58 -0700505 private boolean populateEcmpRoutingRules(DeviceId destSw,
Charles Chanc22cef32016-04-29 14:38:22 -0700506 EcmpShortestPathGraph ecmpSPG,
Pier Ventreb6a7f342016-11-26 21:05:22 -0800507 Set<IpPrefix> subnets) {
sangho80f11cb2015-04-01 13:05:26 -0700508
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700509 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = ecmpSPG
510 .getAllLearnedSwitchesAndVia();
sangho80f11cb2015-04-01 13:05:26 -0700511 for (Integer itrIdx : switchVia.keySet()) {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700512 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap = switchVia
513 .get(itrIdx);
sangho80f11cb2015-04-01 13:05:26 -0700514 for (DeviceId targetSw : swViaMap.keySet()) {
sangho80f11cb2015-04-01 13:05:26 -0700515 Set<DeviceId> nextHops = new HashSet<>();
Saurav Das88979182015-10-19 14:37:36 -0700516 log.debug("** Iter: {} root: {} target: {}", itrIdx, destSw, targetSw);
sangho80f11cb2015-04-01 13:05:26 -0700517 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
518 if (via.isEmpty()) {
519 nextHops.add(destSw);
520 } else {
521 nextHops.add(via.get(0));
522 }
523 }
Charles Chanc22cef32016-04-29 14:38:22 -0700524 if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops, subnets)) {
sangho80f11cb2015-04-01 13:05:26 -0700525 return false;
526 }
527 }
528 }
529
530 return true;
531 }
532
Charles Chanc22cef32016-04-29 14:38:22 -0700533 /**
534 * Populate ECMP rules for subnets from target to destination via nexthops.
535 *
Saurav Dase0237a32016-05-27 13:54:07 -0700536 * @param targetSw Device ID of target switch in which rules will be programmed
537 * @param destSw Device ID of final destination switch to which the rules will forward
538 * @param nextHops List of next hops via which destSw will be reached
Charles Chanc22cef32016-04-29 14:38:22 -0700539 * @param subnets Subnets to be populated. If empty, populate all configured subnets.
540 * @return true if succeed
541 */
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700542 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw,
543 DeviceId destSw,
Charles Chanc22cef32016-04-29 14:38:22 -0700544 Set<DeviceId> nextHops,
Pier Ventreb6a7f342016-11-26 21:05:22 -0800545 Set<IpPrefix> subnets) {
sangho80f11cb2015-04-01 13:05:26 -0700546 boolean result;
547
548 if (nextHops.isEmpty()) {
549 nextHops.add(destSw);
550 }
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700551 // If both target switch and dest switch are edge routers, then set IP
sanghodf0153f2015-05-05 14:13:34 -0700552 // rule for both subnet and router IP.
Charles Chan319d1a22015-11-03 10:42:14 -0800553 boolean targetIsEdge;
554 boolean destIsEdge;
Pier Ventreadb4ae62016-11-23 09:57:42 -0800555 Ip4Address destRouterIpv4;
556 Ip6Address destRouterIpv6;
Charles Chan319d1a22015-11-03 10:42:14 -0800557
558 try {
559 targetIsEdge = config.isEdgeDevice(targetSw);
560 destIsEdge = config.isEdgeDevice(destSw);
Pier Ventreadb4ae62016-11-23 09:57:42 -0800561 destRouterIpv4 = config.getRouterIpv4(destSw);
562 destRouterIpv6 = config.getRouterIpv6(destSw);
Charles Chan319d1a22015-11-03 10:42:14 -0800563 } catch (DeviceConfigNotFoundException e) {
564 log.warn(e.getMessage() + " Aborting populateEcmpRoutingRulePartial.");
565 return false;
566 }
567
568 if (targetIsEdge && destIsEdge) {
Charles Chanc22cef32016-04-29 14:38:22 -0700569 subnets = (subnets != null && !subnets.isEmpty()) ? subnets : config.getSubnets(destSw);
Saurav Das88979182015-10-19 14:37:36 -0700570 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for subnets {}",
Saurav Das4c35fc42015-11-20 15:27:53 -0800571 targetSw, destSw, subnets);
Charles Chanc22cef32016-04-29 14:38:22 -0700572 result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets,
573 destSw, nextHops);
sangho80f11cb2015-04-01 13:05:26 -0700574 if (!result) {
575 return false;
576 }
577
Pier Ventreadb4ae62016-11-23 09:57:42 -0800578 IpPrefix routerIpPrefix = destRouterIpv4.toIpPrefix();
Saurav Das88979182015-10-19 14:37:36 -0700579 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
Saurav Das4c35fc42015-11-20 15:27:53 -0800580 targetSw, destSw, routerIpPrefix);
sangho9b169e32015-04-14 16:27:13 -0700581 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sangho80f11cb2015-04-01 13:05:26 -0700582 if (!result) {
583 return false;
584 }
Pier Ventreadb4ae62016-11-23 09:57:42 -0800585 /*
586 * If present we deal with IPv6 loopback.
587 */
588 if (destRouterIpv6 != null) {
589 routerIpPrefix = destRouterIpv6.toIpPrefix();
590 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for v6 router IP {}",
591 targetSw, destSw, routerIpPrefix);
592 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
593 if (!result) {
594 return false;
595 }
596 }
sangho80f11cb2015-04-01 13:05:26 -0700597
Charles Chan319d1a22015-11-03 10:42:14 -0800598 } else if (targetIsEdge) {
Saurav Das4c35fc42015-11-20 15:27:53 -0800599 // If the target switch is an edge router, then set IP rules for the router IP.
Pier Ventreadb4ae62016-11-23 09:57:42 -0800600 IpPrefix routerIpPrefix = destRouterIpv4.toIpPrefix();
Saurav Das88979182015-10-19 14:37:36 -0700601 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
Saurav Das4c35fc42015-11-20 15:27:53 -0800602 targetSw, destSw, routerIpPrefix);
sangho9b169e32015-04-14 16:27:13 -0700603 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sangho80f11cb2015-04-01 13:05:26 -0700604 if (!result) {
605 return false;
606 }
Pier Ventreadb4ae62016-11-23 09:57:42 -0800607 if (destRouterIpv6 != null) {
608 routerIpPrefix = destRouterIpv6.toIpPrefix();
609 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for v6 router IP {}",
610 targetSw, destSw, routerIpPrefix);
611 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
612 if (!result) {
613 return false;
614 }
615 }
sanghodf0153f2015-05-05 14:13:34 -0700616 }
sanghodf0153f2015-05-05 14:13:34 -0700617 // Populates MPLS rules to all routers
Saurav Das88979182015-10-19 14:37:36 -0700618 log.debug("* populateEcmpRoutingRulePartial in device{} towards {} for all MPLS rules",
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700619 targetSw, destSw);
Pier Ventreadb4ae62016-11-23 09:57:42 -0800620 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops, destRouterIpv4);
sanghodf0153f2015-05-05 14:13:34 -0700621 if (!result) {
622 return false;
sangho80f11cb2015-04-01 13:05:26 -0700623 }
Pier Ventreadb4ae62016-11-23 09:57:42 -0800624 /*
625 * If present we will populate the MPLS rules for the IPv6 sid.
626 */
627 if (destRouterIpv6 != null) {
628 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops, destRouterIpv6);
629 if (!result) {
630 return false;
631 }
632 }
sangho80f11cb2015-04-01 13:05:26 -0700633 return true;
634 }
635
636 /**
Saurav Das9f1c42e2015-10-23 10:51:11 -0700637 * Populates filtering rules for permitting Router DstMac and VLAN.
sangho80f11cb2015-04-01 13:05:26 -0700638 *
639 * @param deviceId Switch ID to set the rules
640 */
Saurav Das9f1c42e2015-10-23 10:51:11 -0700641 public void populatePortAddressingRules(DeviceId deviceId) {
Saurav Das9f1c42e2015-10-23 10:51:11 -0700642 rulePopulator.populateRouterIpPunts(deviceId);
Pier Luigib9632ba2017-01-12 18:14:58 -0800643 rulePopulator.populateArpNdpPunts(deviceId);
Saurav Das07c74602016-04-27 18:35:50 -0700644
645 // Although device is added, sometimes device store does not have the
646 // ports for this device yet. It results in missing filtering rules in the
647 // switch. We will attempt it a few times. If it still does not work,
648 // user can manually repopulate using CLI command sr-reroute-network
Saurav Dasd1872b02016-12-02 15:43:47 -0800649 PortFilterInfo firstRun = rulePopulator.populateRouterMacVlanFilters(deviceId);
650 if (firstRun == null) {
651 firstRun = new PortFilterInfo(0, 0, 0);
Saurav Das07c74602016-04-27 18:35:50 -0700652 }
Saurav Dasd1872b02016-12-02 15:43:47 -0800653 executorService.schedule(new RetryFilters(deviceId, firstRun),
654 RETRY_INTERVAL_MS, TimeUnit.MILLISECONDS);
sangho80f11cb2015-04-01 13:05:26 -0700655 }
656
657 /**
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700658 * Start the flow rule population process if it was never started. The
659 * process finishes successfully when all flow rules are set and stops with
660 * ABORTED status when any groups required for flows is not set yet.
sangho80f11cb2015-04-01 13:05:26 -0700661 */
662 public void startPopulationProcess() {
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900663 statusLock.lock();
664 try {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700665 if (populationStatus == Status.IDLE
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700666 || populationStatus == Status.SUCCEEDED
667 || populationStatus == Status.ABORTED) {
sangho80f11cb2015-04-01 13:05:26 -0700668 populationStatus = Status.STARTED;
669 populateAllRoutingRules();
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700670 } else {
671 log.warn("Not initiating startPopulationProcess as populationStatus is {}",
672 populationStatus);
sangho80f11cb2015-04-01 13:05:26 -0700673 }
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900674 } finally {
675 statusLock.unlock();
sangho80f11cb2015-04-01 13:05:26 -0700676 }
677 }
678
679 /**
680 * Resume the flow rule population process if it was aborted for any reason.
681 * Mostly the process is aborted when the groups required are not set yet.
Saurav Das88979182015-10-19 14:37:36 -0700682 * XXX is this called?
683 *
sangho80f11cb2015-04-01 13:05:26 -0700684 */
685 public void resumePopulationProcess() {
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900686 statusLock.lock();
687 try {
sangho80f11cb2015-04-01 13:05:26 -0700688 if (populationStatus == Status.ABORTED) {
689 populationStatus = Status.STARTED;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700690 // TODO: we need to restart from the point aborted instead of
691 // restarting.
sangho80f11cb2015-04-01 13:05:26 -0700692 populateAllRoutingRules();
693 }
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900694 } finally {
695 statusLock.unlock();
sangho80f11cb2015-04-01 13:05:26 -0700696 }
697 }
Saurav Dasc3604f12016-03-23 11:22:49 -0700698
Charles Chanc22cef32016-04-29 14:38:22 -0700699 /**
700 * Populate rules of given subnet at given location.
701 *
702 * @param cp connect point of the subnet being added
703 * @param subnets subnet being added
704 * @return true if succeed
705 */
Pier Ventreb6a7f342016-11-26 21:05:22 -0800706 protected boolean populateSubnet(ConnectPoint cp, Set<IpPrefix> subnets) {
Charles Chanc22cef32016-04-29 14:38:22 -0700707 statusLock.lock();
708 try {
709 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(cp.deviceId());
710 if (ecmpSpg == null) {
711 log.warn("Fail to populating subnet {}: {}", subnets, ECMPSPG_MISSING);
712 return false;
713 }
714 return populateEcmpRoutingRules(cp.deviceId(), ecmpSpg, subnets);
715 } finally {
716 statusLock.unlock();
717 }
718 }
719
720 /**
721 * Revoke rules of given subnet at given location.
722 *
723 * @param subnets subnet being removed
724 * @return true if succeed
725 */
Pier Ventreb6a7f342016-11-26 21:05:22 -0800726 protected boolean revokeSubnet(Set<IpPrefix> subnets) {
Charles Chanc22cef32016-04-29 14:38:22 -0700727 statusLock.lock();
728 try {
729 return srManager.routingRulePopulator.revokeIpRuleForSubnet(subnets);
730 } finally {
731 statusLock.unlock();
732 }
733 }
734
735 protected void purgeEcmpGraph(DeviceId deviceId) {
Saurav Dasc3604f12016-03-23 11:22:49 -0700736 currentEcmpSpgMap.remove(deviceId);
Saurav Das52d4ed72016-03-28 19:00:18 -0700737 if (updatedEcmpSpgMap != null) {
738 updatedEcmpSpgMap.remove(deviceId);
739 }
Pier Ventre6d593892016-09-13 21:33:40 -0700740 this.populateRoutingRulesForLinkStatusChange(null);
Saurav Dasc3604f12016-03-23 11:22:49 -0700741 }
Saurav Das07c74602016-04-27 18:35:50 -0700742
Saurav Dasd1872b02016-12-02 15:43:47 -0800743 /**
744 * Utility class used to temporarily store information about the ports on a
745 * device processed for filtering objectives.
746 *
747 */
748 public final class PortFilterInfo {
749 int disabledPorts = 0, suppressedPorts = 0, filteredPorts = 0;
Saurav Das07c74602016-04-27 18:35:50 -0700750
Saurav Dasd1872b02016-12-02 15:43:47 -0800751 public PortFilterInfo(int disabledPorts, int suppressedPorts,
752 int filteredPorts) {
753 this.disabledPorts = disabledPorts;
754 this.filteredPorts = filteredPorts;
755 this.suppressedPorts = suppressedPorts;
756 }
757
758 @Override
759 public int hashCode() {
760 return Objects.hash(disabledPorts, filteredPorts, suppressedPorts);
761 }
762
763 @Override
764 public boolean equals(Object obj) {
765 if (this == obj) {
766 return true;
767 }
768 if ((obj == null) || (!(obj instanceof PortFilterInfo))) {
769 return false;
770 }
771 PortFilterInfo other = (PortFilterInfo) obj;
772 return ((disabledPorts == other.disabledPorts) &&
773 (filteredPorts == other.filteredPorts) &&
774 (suppressedPorts == other.suppressedPorts));
775 }
776
777 @Override
778 public String toString() {
779 MoreObjects.ToStringHelper helper = toStringHelper(this)
780 .add("disabledPorts", disabledPorts)
781 .add("suppressedPorts", suppressedPorts)
782 .add("filteredPorts", filteredPorts);
783 return helper.toString();
784 }
785 }
786
787 /**
788 * RetryFilters populates filtering objectives for a device and keeps retrying
789 * till the number of ports filtered are constant for a predefined number
790 * of attempts.
791 */
792 protected final class RetryFilters implements Runnable {
793 int constantAttempts = MAX_CONSTANT_RETRY_ATTEMPTS;
794 DeviceId devId;
795 int counter;
796 PortFilterInfo prevRun;
797
798 private RetryFilters(DeviceId deviceId, PortFilterInfo previousRun) {
Saurav Das07c74602016-04-27 18:35:50 -0700799 devId = deviceId;
Saurav Dasd1872b02016-12-02 15:43:47 -0800800 prevRun = previousRun;
801 counter = 0;
Saurav Das07c74602016-04-27 18:35:50 -0700802 }
803
804 @Override
805 public void run() {
Saurav Dasd1872b02016-12-02 15:43:47 -0800806 log.info("RETRY FILTER ATTEMPT {} ** dev:{}", ++counter, devId);
807 PortFilterInfo thisRun = rulePopulator.populateRouterMacVlanFilters(devId);
808 boolean sameResult = prevRun.equals(thisRun);
809 log.debug("dev:{} prevRun:{} thisRun:{} sameResult:{}", devId, prevRun,
810 thisRun, sameResult);
811 if (thisRun == null || !sameResult || (sameResult && --constantAttempts > 0)) {
812 executorService.schedule(this, RETRY_INTERVAL_MS, TimeUnit.MILLISECONDS);
813 if (!sameResult) {
814 constantAttempts = MAX_CONSTANT_RETRY_ATTEMPTS; //reset
815 }
Saurav Das07c74602016-04-27 18:35:50 -0700816 }
Saurav Dasd1872b02016-12-02 15:43:47 -0800817 prevRun = (thisRun == null) ? prevRun : thisRun;
Saurav Das07c74602016-04-27 18:35:50 -0700818 }
Saurav Das07c74602016-04-27 18:35:50 -0700819 }
820
sangho80f11cb2015-04-01 13:05:26 -0700821}