blob: 1873530f61b3895ef9c0973b244e0bbd6f4e7c09 [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;
Saurav Dasf9332192017-02-18 14:05:44 -080030import org.onosproject.net.PortNumber;
Charles Chan319d1a22015-11-03 10:42:14 -080031import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
32import org.onosproject.segmentrouting.config.DeviceConfiguration;
sangho80f11cb2015-04-01 13:05:26 -070033import org.slf4j.Logger;
34import org.slf4j.LoggerFactory;
35
36import java.util.ArrayList;
37import java.util.HashMap;
38import java.util.HashSet;
Saurav Dasd1872b02016-12-02 15:43:47 -080039import java.util.Objects;
sangho80f11cb2015-04-01 13:05:26 -070040import java.util.Set;
Saurav Das07c74602016-04-27 18:35:50 -070041import java.util.concurrent.ScheduledExecutorService;
42import java.util.concurrent.TimeUnit;
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +090043import java.util.concurrent.locks.Lock;
44import java.util.concurrent.locks.ReentrantLock;
sangho80f11cb2015-04-01 13:05:26 -070045
Saurav Dasd1872b02016-12-02 15:43:47 -080046import static com.google.common.base.MoreObjects.toStringHelper;
Pier Ventreadb4ae62016-11-23 09:57:42 -080047import static com.google.common.base.Preconditions.checkNotNull;
48import static java.util.concurrent.Executors.newScheduledThreadPool;
49import static org.onlab.util.Tools.groupedThreads;
sangho80f11cb2015-04-01 13:05:26 -070050
Charles Chanb7f75ac2016-01-11 18:28:54 -080051/**
52 * Default routing handler that is responsible for route computing and
53 * routing rule population.
54 */
sangho80f11cb2015-04-01 13:05:26 -070055public class DefaultRoutingHandler {
Saurav Dasf9332192017-02-18 14:05:44 -080056 private static final int MAX_CONSTANT_RETRY_ATTEMPTS = 5;
57 private static final int RETRY_INTERVAL_MS = 250;
58 private static final int RETRY_INTERVAL_SCALE = 1;
Charles Chanc22cef32016-04-29 14:38:22 -070059 private static final String ECMPSPG_MISSING = "ECMP shortest path graph not found";
60 private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
sangho80f11cb2015-04-01 13:05:26 -070061
62 private SegmentRoutingManager srManager;
63 private RoutingRulePopulator rulePopulator;
Shashikanth VH0637b162015-12-11 01:32:44 +053064 private HashMap<DeviceId, EcmpShortestPathGraph> currentEcmpSpgMap;
65 private HashMap<DeviceId, EcmpShortestPathGraph> updatedEcmpSpgMap;
sangho9b169e32015-04-14 16:27:13 -070066 private DeviceConfiguration config;
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +090067 private final Lock statusLock = new ReentrantLock();
68 private volatile Status populationStatus;
Yuta HIGUCHIebee2f12016-07-21 16:54:33 -070069 private ScheduledExecutorService executorService
Saurav Dasd1872b02016-12-02 15:43:47 -080070 = newScheduledThreadPool(1, groupedThreads("retryftr", "retry-%d", log));
sangho80f11cb2015-04-01 13:05:26 -070071
72 /**
73 * Represents the default routing population status.
74 */
75 public enum Status {
76 // population process is not started yet.
77 IDLE,
78
79 // population process started.
80 STARTED,
81
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070082 // population process was aborted due to errors, mostly for groups not
83 // found.
sangho80f11cb2015-04-01 13:05:26 -070084 ABORTED,
85
86 // population process was finished successfully.
87 SUCCEEDED
88 }
89
90 /**
91 * Creates a DefaultRoutingHandler object.
92 *
93 * @param srManager SegmentRoutingManager object
94 */
95 public DefaultRoutingHandler(SegmentRoutingManager srManager) {
96 this.srManager = srManager;
97 this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
sangho9b169e32015-04-14 16:27:13 -070098 this.config = checkNotNull(srManager.deviceConfiguration);
sangho80f11cb2015-04-01 13:05:26 -070099 this.populationStatus = Status.IDLE;
sanghofb7c7292015-04-13 15:15:58 -0700100 this.currentEcmpSpgMap = Maps.newHashMap();
sangho80f11cb2015-04-01 13:05:26 -0700101 }
102
103 /**
104 * Populates all routing rules to all connected routers, including default
105 * routing rules, adjacency rules, and policy rules if any.
106 *
107 * @return true if it succeeds in populating all rules, otherwise false
108 */
109 public boolean populateAllRoutingRules() {
110
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900111 statusLock.lock();
112 try {
113 populationStatus = Status.STARTED;
114 rulePopulator.resetCounter();
Saurav Das88979182015-10-19 14:37:36 -0700115 log.info("Starting to populate segment-routing rules");
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900116 log.debug("populateAllRoutingRules: populationStatus is STARTED");
sangho80f11cb2015-04-01 13:05:26 -0700117
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900118 for (Device sw : srManager.deviceService.getDevices()) {
Charles Chan77277672015-10-20 16:24:19 -0700119 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900120 log.debug("populateAllRoutingRules: skipping device {}...we are not master",
121 sw.id());
122 continue;
123 }
124
Shashikanth VH0637b162015-12-11 01:32:44 +0530125 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(sw.id(), srManager);
Charles Chanc22cef32016-04-29 14:38:22 -0700126 if (!populateEcmpRoutingRules(sw.id(), ecmpSpg, ImmutableSet.of())) {
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900127 log.debug("populateAllRoutingRules: populationStatus is ABORTED");
128 populationStatus = Status.ABORTED;
129 log.debug("Abort routing rule population");
130 return false;
131 }
132 currentEcmpSpgMap.put(sw.id(), ecmpSpg);
133
134 // TODO: Set adjacency routing rule for all switches
sangho80f11cb2015-04-01 13:05:26 -0700135 }
136
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900137 log.debug("populateAllRoutingRules: populationStatus is SUCCEEDED");
138 populationStatus = Status.SUCCEEDED;
Saurav Das88979182015-10-19 14:37:36 -0700139 log.info("Completed routing rule population. Total # of rules pushed : {}",
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900140 rulePopulator.getCounter());
141 return true;
142 } finally {
143 statusLock.unlock();
sangho80f11cb2015-04-01 13:05:26 -0700144 }
sangho80f11cb2015-04-01 13:05:26 -0700145 }
146
sanghofb7c7292015-04-13 15:15:58 -0700147 /**
148 * Populates the routing rules according to the route changes due to the link
149 * failure or link add. It computes the routes changed due to the link changes and
Saurav Das1b391d52016-11-29 14:27:25 -0800150 * repopulates the rules only for these routes. Note that when a switch goes
151 * away, all of its links fail as well, but this is handled as a single
152 * switch removal event.
sanghofb7c7292015-04-13 15:15:58 -0700153 *
Saurav Das1b391d52016-11-29 14:27:25 -0800154 * @param failedLink the single failed link, or null for other conditions
155 * such as an added link or a removed switch
sanghofb7c7292015-04-13 15:15:58 -0700156 * @return true if it succeeds to populate all rules, false otherwise
157 */
Saurav Das1b391d52016-11-29 14:27:25 -0800158 public boolean populateRoutingRulesForLinkStatusChange(Link failedLink) {
sanghofb7c7292015-04-13 15:15:58 -0700159
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900160 statusLock.lock();
161 try {
sanghofb7c7292015-04-13 15:15:58 -0700162
163 if (populationStatus == Status.STARTED) {
sanghodf0153f2015-05-05 14:13:34 -0700164 log.warn("Previous rule population is not finished.");
sanghofb7c7292015-04-13 15:15:58 -0700165 return true;
166 }
167
sangho28d0b6d2015-05-07 13:30:57 -0700168 // Take the snapshots of the links
169 updatedEcmpSpgMap = new HashMap<>();
170 for (Device sw : srManager.deviceService.getDevices()) {
Charles Chan77277672015-10-20 16:24:19 -0700171 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
sangho28d0b6d2015-05-07 13:30:57 -0700172 continue;
173 }
Shashikanth VH0637b162015-12-11 01:32:44 +0530174 EcmpShortestPathGraph ecmpSpgUpdated =
175 new EcmpShortestPathGraph(sw.id(), srManager);
sangho28d0b6d2015-05-07 13:30:57 -0700176 updatedEcmpSpgMap.put(sw.id(), ecmpSpgUpdated);
177 }
178
sanghodf0153f2015-05-05 14:13:34 -0700179 log.info("Starts rule population from link change");
180
sanghofb7c7292015-04-13 15:15:58 -0700181 Set<ArrayList<DeviceId>> routeChanges;
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700182 log.trace("populateRoutingRulesForLinkStatusChange: "
183 + "populationStatus is STARTED");
sanghofb7c7292015-04-13 15:15:58 -0700184 populationStatus = Status.STARTED;
Saurav Das1b391d52016-11-29 14:27:25 -0800185 // try optimized re-routing
186 if (failedLink == null) {
187 // Compare all routes of existing ECMP SPG to new ECMP SPG
sanghofb7c7292015-04-13 15:15:58 -0700188 routeChanges = computeRouteChange();
189 } else {
190 // Compare existing ECMP SPG only with the link removed
Saurav Das1b391d52016-11-29 14:27:25 -0800191 routeChanges = computeDamagedRoutes(failedLink);
sanghofb7c7292015-04-13 15:15:58 -0700192 }
193
Saurav Das1b391d52016-11-29 14:27:25 -0800194 // do full re-routing if optimized routing returns null routeChanges
Saurav Dasb149be12016-06-07 10:08:06 -0700195 if (routeChanges == null) {
196 return populateAllRoutingRules();
197 }
198
sanghofb7c7292015-04-13 15:15:58 -0700199 if (routeChanges.isEmpty()) {
sanghodf0153f2015-05-05 14:13:34 -0700200 log.info("No route changes for the link status change");
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700201 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
sanghofb7c7292015-04-13 15:15:58 -0700202 populationStatus = Status.SUCCEEDED;
203 return true;
204 }
205
206 if (repopulateRoutingRulesForRoutes(routeChanges)) {
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700207 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
sanghofb7c7292015-04-13 15:15:58 -0700208 populationStatus = Status.SUCCEEDED;
209 log.info("Complete to repopulate the rules. # of rules populated : {}",
210 rulePopulator.getCounter());
211 return true;
212 } else {
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700213 log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is ABORTED");
sanghofb7c7292015-04-13 15:15:58 -0700214 populationStatus = Status.ABORTED;
215 log.warn("Failed to repopulate the rules.");
216 return false;
217 }
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900218 } finally {
219 statusLock.unlock();
sanghofb7c7292015-04-13 15:15:58 -0700220 }
221 }
222
223 private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
224 rulePopulator.resetCounter();
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700225 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> routesBydevice =
226 new HashMap<>();
sanghofb7c7292015-04-13 15:15:58 -0700227 for (ArrayList<DeviceId> link: routes) {
sangho2165d222015-05-01 09:38:25 -0700228 // When only the source device is defined, reinstall routes to all other devices
sanghofb7c7292015-04-13 15:15:58 -0700229 if (link.size() == 1) {
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700230 log.trace("repopulateRoutingRulesForRoutes: running ECMP graph for device {}", link.get(0));
Shashikanth VH0637b162015-12-11 01:32:44 +0530231 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(link.get(0), srManager);
Charles Chanc22cef32016-04-29 14:38:22 -0700232 if (populateEcmpRoutingRules(link.get(0), ecmpSpg, ImmutableSet.of())) {
Saurav Dase0237a32016-05-27 13:54:07 -0700233 log.debug("Populating flow rules from all to dest:{} is successful",
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700234 link.get(0));
sanghofb7c7292015-04-13 15:15:58 -0700235 currentEcmpSpgMap.put(link.get(0), ecmpSpg);
sanghodf0153f2015-05-05 14:13:34 -0700236 } else {
Saurav Dase0237a32016-05-27 13:54:07 -0700237 log.warn("Failed to populate the flow rules from all to dest:{}", link.get(0));
sanghodf0153f2015-05-05 14:13:34 -0700238 return false;
sanghofb7c7292015-04-13 15:15:58 -0700239 }
sangho28d0b6d2015-05-07 13:30:57 -0700240 } else {
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700241 ArrayList<ArrayList<DeviceId>> deviceRoutes =
242 routesBydevice.get(link.get(1));
243 if (deviceRoutes == null) {
244 deviceRoutes = new ArrayList<>();
245 routesBydevice.put(link.get(1), deviceRoutes);
246 }
247 deviceRoutes.add(link);
248 }
249 }
250
251 for (DeviceId impactedDevice : routesBydevice.keySet()) {
252 ArrayList<ArrayList<DeviceId>> deviceRoutes =
253 routesBydevice.get(impactedDevice);
254 for (ArrayList<DeviceId> link: deviceRoutes) {
255 log.debug("repopulate RoutingRules For Routes {} -> {}",
256 link.get(0), link.get(1));
sangho28d0b6d2015-05-07 13:30:57 -0700257 DeviceId src = link.get(0);
258 DeviceId dst = link.get(1);
Shashikanth VH0637b162015-12-11 01:32:44 +0530259 EcmpShortestPathGraph ecmpSpg = updatedEcmpSpgMap.get(dst);
sangho28d0b6d2015-05-07 13:30:57 -0700260 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
261 ecmpSpg.getAllLearnedSwitchesAndVia();
262 for (Integer itrIdx : switchVia.keySet()) {
263 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
264 switchVia.get(itrIdx);
265 for (DeviceId targetSw : swViaMap.keySet()) {
266 if (!targetSw.equals(src)) {
267 continue;
268 }
269 Set<DeviceId> nextHops = new HashSet<>();
270 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
271 if (via.isEmpty()) {
272 nextHops.add(dst);
273 } else {
274 nextHops.add(via.get(0));
275 }
276 }
Charles Chanc22cef32016-04-29 14:38:22 -0700277 if (!populateEcmpRoutingRulePartial(targetSw, dst,
278 nextHops, ImmutableSet.of())) {
sangho28d0b6d2015-05-07 13:30:57 -0700279 return false;
sanghofb7c7292015-04-13 15:15:58 -0700280 }
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700281 log.debug("Populating flow rules from {} to {} is successful",
282 targetSw, dst);
sanghofb7c7292015-04-13 15:15:58 -0700283 }
sanghofb7c7292015-04-13 15:15:58 -0700284 }
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700285 //currentEcmpSpgMap.put(dst, ecmpSpg);
sanghofb7c7292015-04-13 15:15:58 -0700286 }
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700287 //Only if all the flows for all impacted routes to a
288 //specific target are pushed successfully, update the
Saurav Das1b391d52016-11-29 14:27:25 -0800289 //ECMP graph for that target. Or else the next event
290 //would not see any changes in the ECMP graphs.
291 //In another case, the target switch has gone away, so
292 //routes can't be installed. In that case, the current map
293 //is updated here, without any flows being pushed.
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700294 currentEcmpSpgMap.put(impactedDevice,
295 updatedEcmpSpgMap.get(impactedDevice));
sanghofb7c7292015-04-13 15:15:58 -0700296 }
297 return true;
298 }
299
Saurav Dasb149be12016-06-07 10:08:06 -0700300 /**
Saurav Das1b391d52016-11-29 14:27:25 -0800301 * Computes set of affected routes due to failed link. Assumes
Saurav Dasb149be12016-06-07 10:08:06 -0700302 * previous ecmp shortest-path graph exists for a switch in order to compute
303 * affected routes. If such a graph does not exist, the method returns null.
304 *
305 * @param linkFail the failed link
306 * @return the set of affected routes which may be empty if no routes were
307 * affected, or null if no previous ecmp spg was found for comparison
308 */
sanghofb7c7292015-04-13 15:15:58 -0700309 private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
310
311 Set<ArrayList<DeviceId>> routes = new HashSet<>();
312
313 for (Device sw : srManager.deviceService.getDevices()) {
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700314 log.debug("Computing the impacted routes for device {} due to link fail",
315 sw.id());
Charles Chan77277672015-10-20 16:24:19 -0700316 if (!srManager.mastershipService.isLocalMaster(sw.id())) {
Saurav Dasb149be12016-06-07 10:08:06 -0700317 log.debug("No mastership for {} .. skipping route optimization",
318 sw.id());
sanghofb7c7292015-04-13 15:15:58 -0700319 continue;
320 }
Shashikanth VH0637b162015-12-11 01:32:44 +0530321 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
sanghofb7c7292015-04-13 15:15:58 -0700322 if (ecmpSpg == null) {
Saurav Dasb149be12016-06-07 10:08:06 -0700323 log.warn("No existing ECMP graph for switch {}. Aborting optimized"
324 + " rerouting and opting for full-reroute", sw.id());
325 return null;
sanghofb7c7292015-04-13 15:15:58 -0700326 }
327 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
328 ecmpSpg.getAllLearnedSwitchesAndVia();
329 for (Integer itrIdx : switchVia.keySet()) {
Saurav Dasb149be12016-06-07 10:08:06 -0700330 log.trace("Iterindex# {}", itrIdx);
sanghofb7c7292015-04-13 15:15:58 -0700331 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
332 switchVia.get(itrIdx);
333 for (DeviceId targetSw : swViaMap.keySet()) {
Saurav Das1b391d52016-11-29 14:27:25 -0800334 DeviceId rootSw = sw.id();
Saurav Dasb149be12016-06-07 10:08:06 -0700335 if (log.isTraceEnabled()) {
Saurav Das1b391d52016-11-29 14:27:25 -0800336 log.trace("TargetSwitch {} --> RootSwitch {}", targetSw, rootSw);
Saurav Dasb149be12016-06-07 10:08:06 -0700337 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
338 log.trace(" Via:");
Pier Ventreadb4ae62016-11-23 09:57:42 -0800339 via.forEach(e -> log.trace(" {}", e));
Saurav Dasb149be12016-06-07 10:08:06 -0700340 }
341 }
sanghofb7c7292015-04-13 15:15:58 -0700342 Set<ArrayList<DeviceId>> subLinks =
Saurav Das1b391d52016-11-29 14:27:25 -0800343 computeLinks(targetSw, rootSw, swViaMap);
sanghofb7c7292015-04-13 15:15:58 -0700344 for (ArrayList<DeviceId> alink: subLinks) {
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700345 if ((alink.get(0).equals(linkFail.src().deviceId()) &&
346 alink.get(1).equals(linkFail.dst().deviceId()))
347 ||
348 (alink.get(0).equals(linkFail.dst().deviceId()) &&
349 alink.get(1).equals(linkFail.src().deviceId()))) {
Saurav Das1b391d52016-11-29 14:27:25 -0800350 log.debug("Impacted route:{}->{}", targetSw, rootSw);
sanghofb7c7292015-04-13 15:15:58 -0700351 ArrayList<DeviceId> aRoute = new ArrayList<>();
352 aRoute.add(targetSw);
Saurav Das1b391d52016-11-29 14:27:25 -0800353 aRoute.add(rootSw);
sanghofb7c7292015-04-13 15:15:58 -0700354 routes.add(aRoute);
355 break;
356 }
357 }
358 }
359 }
sangho28d0b6d2015-05-07 13:30:57 -0700360
sanghofb7c7292015-04-13 15:15:58 -0700361 }
362
363 return routes;
364 }
365
Saurav Das1b391d52016-11-29 14:27:25 -0800366 /**
367 * Computes set of affected routes due to new links or failed switches.
368 *
369 * @return the set of affected routes which may be empty if no routes were
370 * affected
371 */
sanghofb7c7292015-04-13 15:15:58 -0700372 private Set<ArrayList<DeviceId>> computeRouteChange() {
373
Saurav Das1b391d52016-11-29 14:27:25 -0800374 ImmutableSet.Builder<ArrayList<DeviceId>> changedRoutesBuilder =
375 ImmutableSet.builder();
sanghofb7c7292015-04-13 15:15:58 -0700376
377 for (Device sw : srManager.deviceService.getDevices()) {
Saurav Das1b391d52016-11-29 14:27:25 -0800378 DeviceId rootSw = sw.id();
379 log.debug("Computing the impacted routes for device {}", rootSw);
380 if (!srManager.mastershipService.isLocalMaster(rootSw)) {
Saurav Dasb149be12016-06-07 10:08:06 -0700381 log.debug("No mastership for {} ... skipping route optimization",
Saurav Das1b391d52016-11-29 14:27:25 -0800382 rootSw);
sanghofb7c7292015-04-13 15:15:58 -0700383 continue;
384 }
Saurav Dasb149be12016-06-07 10:08:06 -0700385 if (log.isTraceEnabled()) {
Saurav Das1b391d52016-11-29 14:27:25 -0800386 log.trace("link of {} - ", rootSw);
387 for (Link link: srManager.linkService.getDeviceLinks(rootSw)) {
Saurav Dasb149be12016-06-07 10:08:06 -0700388 log.trace("{} -> {} ", link.src().deviceId(), link.dst().deviceId());
389 }
sangho28d0b6d2015-05-07 13:30:57 -0700390 }
Saurav Das1b391d52016-11-29 14:27:25 -0800391 EcmpShortestPathGraph currEcmpSpg = currentEcmpSpgMap.get(rootSw);
392 if (currEcmpSpg == null) {
393 log.debug("No existing ECMP graph for device {}", rootSw);
394 changedRoutesBuilder.add(Lists.newArrayList(rootSw));
sanghofb7c7292015-04-13 15:15:58 -0700395 continue;
396 }
Saurav Das1b391d52016-11-29 14:27:25 -0800397 EcmpShortestPathGraph newEcmpSpg = updatedEcmpSpgMap.get(rootSw);
398 if (log.isTraceEnabled()) {
399 log.trace("Root switch: {}", rootSw);
400 log.trace(" Current/Existing SPG: {}", currEcmpSpg);
401 log.trace(" New/Updated SPG: {}", newEcmpSpg);
402 }
403 // first use the updated/new map to compare to current/existing map
404 // as new links may have come up
405 changedRoutesBuilder.addAll(compareGraphs(newEcmpSpg, currEcmpSpg, rootSw));
406 // then use the current/existing map to compare to updated/new map
407 // as switch may have been removed
408 changedRoutesBuilder.addAll(compareGraphs(currEcmpSpg, newEcmpSpg, rootSw));
409 }
sanghofb7c7292015-04-13 15:15:58 -0700410
Saurav Das1b391d52016-11-29 14:27:25 -0800411 Set<ArrayList<DeviceId>> changedRoutes = changedRoutesBuilder.build();
412 for (ArrayList<DeviceId> route: changedRoutes) {
413 log.debug("Route changes Target -> Root");
414 if (route.size() == 1) {
415 log.debug(" : all -> {}", route.get(0));
416 } else {
417 log.debug(" : {} -> {}", route.get(0), route.get(1));
418 }
419 }
420 return changedRoutes;
421 }
422
423 /**
424 * For the root switch, searches all the target nodes reachable in the base
425 * graph, and compares paths to the ones in the comp graph.
426 *
427 * @param base the graph that is indexed for all reachable target nodes
428 * from the root node
429 * @param comp the graph that the base graph is compared to
430 * @param rootSw both ecmp graphs are calculated for the root node
431 * @return all the routes that have changed in the base graph
432 */
433 private Set<ArrayList<DeviceId>> compareGraphs(EcmpShortestPathGraph base,
434 EcmpShortestPathGraph comp,
435 DeviceId rootSw) {
436 ImmutableSet.Builder<ArrayList<DeviceId>> changedRoutesBuilder =
437 ImmutableSet.builder();
438 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> baseMap =
439 base.getAllLearnedSwitchesAndVia();
440 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> compMap =
441 comp.getAllLearnedSwitchesAndVia();
442 for (Integer itrIdx : baseMap.keySet()) {
443 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> baseViaMap =
444 baseMap.get(itrIdx);
445 for (DeviceId targetSw : baseViaMap.keySet()) {
446 ArrayList<ArrayList<DeviceId>> basePath = baseViaMap.get(targetSw);
447 ArrayList<ArrayList<DeviceId>> compPath = getVia(compMap, targetSw);
448 if ((compPath == null) || !basePath.equals(compPath)) {
449 log.debug("Impacted route:{} -> {}", targetSw, rootSw);
450 ArrayList<DeviceId> route = new ArrayList<>();
451 route.add(targetSw);
452 route.add(rootSw);
453 changedRoutesBuilder.add(route);
sanghofb7c7292015-04-13 15:15:58 -0700454 }
455 }
sangho28d0b6d2015-05-07 13:30:57 -0700456 }
Saurav Das1b391d52016-11-29 14:27:25 -0800457 return changedRoutesBuilder.build();
sanghofb7c7292015-04-13 15:15:58 -0700458 }
459
460 private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
Saurav Das1b391d52016-11-29 14:27:25 -0800461 ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId targetSw) {
sanghofb7c7292015-04-13 15:15:58 -0700462 for (Integer itrIdx : switchVia.keySet()) {
463 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
464 switchVia.get(itrIdx);
Saurav Das1b391d52016-11-29 14:27:25 -0800465 if (swViaMap.get(targetSw) == null) {
sanghofb7c7292015-04-13 15:15:58 -0700466 continue;
467 } else {
Saurav Das1b391d52016-11-29 14:27:25 -0800468 return swViaMap.get(targetSw);
sanghofb7c7292015-04-13 15:15:58 -0700469 }
470 }
471
Srikanth Vavilapalli64d96c12015-05-14 20:22:47 -0700472 return null;
sanghofb7c7292015-04-13 15:15:58 -0700473 }
474
475 private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
476 DeviceId dst,
477 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
478 Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
479 for (ArrayList<DeviceId> via : viaMap.get(src)) {
480 DeviceId linkSrc = src;
481 DeviceId linkDst = dst;
482 for (DeviceId viaDevice: via) {
483 ArrayList<DeviceId> link = new ArrayList<>();
484 linkDst = viaDevice;
485 link.add(linkSrc);
486 link.add(linkDst);
487 subLinks.add(link);
488 linkSrc = viaDevice;
489 }
490 ArrayList<DeviceId> link = new ArrayList<>();
491 link.add(linkSrc);
492 link.add(dst);
493 subLinks.add(link);
494 }
495
496 return subLinks;
497 }
498
Charles Chanc22cef32016-04-29 14:38:22 -0700499 /**
500 * Populate ECMP rules for subnets from all switches to destination.
501 *
502 * @param destSw Device ID of destination switch
503 * @param ecmpSPG ECMP shortest path graph
504 * @param subnets Subnets to be populated. If empty, populate all configured subnets.
505 * @return true if succeed
506 */
sanghofb7c7292015-04-13 15:15:58 -0700507 private boolean populateEcmpRoutingRules(DeviceId destSw,
Charles Chanc22cef32016-04-29 14:38:22 -0700508 EcmpShortestPathGraph ecmpSPG,
Pier Ventreb6a7f342016-11-26 21:05:22 -0800509 Set<IpPrefix> subnets) {
sangho80f11cb2015-04-01 13:05:26 -0700510
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700511 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = ecmpSPG
512 .getAllLearnedSwitchesAndVia();
sangho80f11cb2015-04-01 13:05:26 -0700513 for (Integer itrIdx : switchVia.keySet()) {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700514 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap = switchVia
515 .get(itrIdx);
sangho80f11cb2015-04-01 13:05:26 -0700516 for (DeviceId targetSw : swViaMap.keySet()) {
sangho80f11cb2015-04-01 13:05:26 -0700517 Set<DeviceId> nextHops = new HashSet<>();
Saurav Das88979182015-10-19 14:37:36 -0700518 log.debug("** Iter: {} root: {} target: {}", itrIdx, destSw, targetSw);
sangho80f11cb2015-04-01 13:05:26 -0700519 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
520 if (via.isEmpty()) {
521 nextHops.add(destSw);
522 } else {
523 nextHops.add(via.get(0));
524 }
525 }
Charles Chanc22cef32016-04-29 14:38:22 -0700526 if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops, subnets)) {
sangho80f11cb2015-04-01 13:05:26 -0700527 return false;
528 }
529 }
530 }
531
532 return true;
533 }
534
Charles Chanc22cef32016-04-29 14:38:22 -0700535 /**
536 * Populate ECMP rules for subnets from target to destination via nexthops.
537 *
Saurav Dase0237a32016-05-27 13:54:07 -0700538 * @param targetSw Device ID of target switch in which rules will be programmed
539 * @param destSw Device ID of final destination switch to which the rules will forward
540 * @param nextHops List of next hops via which destSw will be reached
Charles Chanc22cef32016-04-29 14:38:22 -0700541 * @param subnets Subnets to be populated. If empty, populate all configured subnets.
542 * @return true if succeed
543 */
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700544 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw,
545 DeviceId destSw,
Charles Chanc22cef32016-04-29 14:38:22 -0700546 Set<DeviceId> nextHops,
Pier Ventreb6a7f342016-11-26 21:05:22 -0800547 Set<IpPrefix> subnets) {
sangho80f11cb2015-04-01 13:05:26 -0700548 boolean result;
549
550 if (nextHops.isEmpty()) {
551 nextHops.add(destSw);
552 }
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700553 // If both target switch and dest switch are edge routers, then set IP
sanghodf0153f2015-05-05 14:13:34 -0700554 // rule for both subnet and router IP.
Charles Chan319d1a22015-11-03 10:42:14 -0800555 boolean targetIsEdge;
556 boolean destIsEdge;
Pier Ventreadb4ae62016-11-23 09:57:42 -0800557 Ip4Address destRouterIpv4;
558 Ip6Address destRouterIpv6;
Charles Chan319d1a22015-11-03 10:42:14 -0800559
560 try {
561 targetIsEdge = config.isEdgeDevice(targetSw);
562 destIsEdge = config.isEdgeDevice(destSw);
Pier Ventreadb4ae62016-11-23 09:57:42 -0800563 destRouterIpv4 = config.getRouterIpv4(destSw);
564 destRouterIpv6 = config.getRouterIpv6(destSw);
Charles Chan319d1a22015-11-03 10:42:14 -0800565 } catch (DeviceConfigNotFoundException e) {
566 log.warn(e.getMessage() + " Aborting populateEcmpRoutingRulePartial.");
567 return false;
568 }
569
570 if (targetIsEdge && destIsEdge) {
Charles Chanc22cef32016-04-29 14:38:22 -0700571 subnets = (subnets != null && !subnets.isEmpty()) ? subnets : config.getSubnets(destSw);
Saurav Das88979182015-10-19 14:37:36 -0700572 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for subnets {}",
Saurav Das4c35fc42015-11-20 15:27:53 -0800573 targetSw, destSw, subnets);
Charles Chanc22cef32016-04-29 14:38:22 -0700574 result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets,
575 destSw, nextHops);
sangho80f11cb2015-04-01 13:05:26 -0700576 if (!result) {
577 return false;
578 }
579
Pier Ventreadb4ae62016-11-23 09:57:42 -0800580 IpPrefix routerIpPrefix = destRouterIpv4.toIpPrefix();
Saurav Das88979182015-10-19 14:37:36 -0700581 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
Saurav Das4c35fc42015-11-20 15:27:53 -0800582 targetSw, destSw, routerIpPrefix);
sangho9b169e32015-04-14 16:27:13 -0700583 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sangho80f11cb2015-04-01 13:05:26 -0700584 if (!result) {
585 return false;
586 }
Pier Ventreadb4ae62016-11-23 09:57:42 -0800587 /*
588 * If present we deal with IPv6 loopback.
589 */
590 if (destRouterIpv6 != null) {
591 routerIpPrefix = destRouterIpv6.toIpPrefix();
592 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for v6 router IP {}",
593 targetSw, destSw, routerIpPrefix);
594 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
595 if (!result) {
596 return false;
597 }
598 }
sangho80f11cb2015-04-01 13:05:26 -0700599
Charles Chan319d1a22015-11-03 10:42:14 -0800600 } else if (targetIsEdge) {
Saurav Das4c35fc42015-11-20 15:27:53 -0800601 // If the target switch is an edge router, then set IP rules for the router IP.
Pier Ventreadb4ae62016-11-23 09:57:42 -0800602 IpPrefix routerIpPrefix = destRouterIpv4.toIpPrefix();
Saurav Das88979182015-10-19 14:37:36 -0700603 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
Saurav Das4c35fc42015-11-20 15:27:53 -0800604 targetSw, destSw, routerIpPrefix);
sangho9b169e32015-04-14 16:27:13 -0700605 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
sangho80f11cb2015-04-01 13:05:26 -0700606 if (!result) {
607 return false;
608 }
Pier Ventreadb4ae62016-11-23 09:57:42 -0800609 if (destRouterIpv6 != null) {
610 routerIpPrefix = destRouterIpv6.toIpPrefix();
611 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for v6 router IP {}",
612 targetSw, destSw, routerIpPrefix);
613 result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
614 if (!result) {
615 return false;
616 }
617 }
sanghodf0153f2015-05-05 14:13:34 -0700618 }
sanghodf0153f2015-05-05 14:13:34 -0700619 // Populates MPLS rules to all routers
Saurav Das88979182015-10-19 14:37:36 -0700620 log.debug("* populateEcmpRoutingRulePartial in device{} towards {} for all MPLS rules",
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700621 targetSw, destSw);
Pier Ventreadb4ae62016-11-23 09:57:42 -0800622 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops, destRouterIpv4);
sanghodf0153f2015-05-05 14:13:34 -0700623 if (!result) {
624 return false;
sangho80f11cb2015-04-01 13:05:26 -0700625 }
Pier Ventreadb4ae62016-11-23 09:57:42 -0800626 /*
627 * If present we will populate the MPLS rules for the IPv6 sid.
628 */
629 if (destRouterIpv6 != null) {
630 result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops, destRouterIpv6);
631 if (!result) {
632 return false;
633 }
634 }
sangho80f11cb2015-04-01 13:05:26 -0700635 return true;
636 }
637
638 /**
Saurav Dasf9332192017-02-18 14:05:44 -0800639 * Populates filtering rules for port, and punting rules
640 * for gateway IPs, loopback IPs and arp/ndp traffic.
641 * Should only be called by the master instance for this device/port.
sangho80f11cb2015-04-01 13:05:26 -0700642 *
643 * @param deviceId Switch ID to set the rules
644 */
Saurav Das9f1c42e2015-10-23 10:51:11 -0700645 public void populatePortAddressingRules(DeviceId deviceId) {
Charles Chan18fa4252017-02-08 16:10:40 -0800646 rulePopulator.populateIpPunts(deviceId);
Pier Luigib9632ba2017-01-12 18:14:58 -0800647 rulePopulator.populateArpNdpPunts(deviceId);
Saurav Das07c74602016-04-27 18:35:50 -0700648
649 // Although device is added, sometimes device store does not have the
650 // ports for this device yet. It results in missing filtering rules in the
651 // switch. We will attempt it a few times. If it still does not work,
652 // user can manually repopulate using CLI command sr-reroute-network
Charles Chan18fa4252017-02-08 16:10:40 -0800653 PortFilterInfo firstRun = rulePopulator.populateVlanMacFilters(deviceId);
Saurav Dasd1872b02016-12-02 15:43:47 -0800654 if (firstRun == null) {
655 firstRun = new PortFilterInfo(0, 0, 0);
Saurav Das07c74602016-04-27 18:35:50 -0700656 }
Saurav Dasd1872b02016-12-02 15:43:47 -0800657 executorService.schedule(new RetryFilters(deviceId, firstRun),
658 RETRY_INTERVAL_MS, TimeUnit.MILLISECONDS);
sangho80f11cb2015-04-01 13:05:26 -0700659 }
660
661 /**
Saurav Dasf9332192017-02-18 14:05:44 -0800662 * Populates filtering rules for a port that has been enabled.
663 * Should only be called by the master instance for this device/port.
664 *
665 * @param devId device identifier
666 * @param portnum port identifier
667 */
668 public void populateSinglePortFilteringRules(DeviceId devId, PortNumber portnum) {
669 rulePopulator.populateSinglePortFilters(devId, portnum);
670 }
671
672 /**
673 * Revokes filtering rules for a port that has been disabled.
674 * Should only be called by the master instance for this device/port.
675 *
676 * @param devId device identifier
677 * @param portnum port identifier
678 */
679 public void revokeSinglePortFilteringRules(DeviceId devId, PortNumber portnum) {
680 rulePopulator.revokeSinglePortFilters(devId, portnum);
681 }
682
683 /**
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700684 * Start the flow rule population process if it was never started. The
685 * process finishes successfully when all flow rules are set and stops with
686 * ABORTED status when any groups required for flows is not set yet.
sangho80f11cb2015-04-01 13:05:26 -0700687 */
688 public void startPopulationProcess() {
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900689 statusLock.lock();
690 try {
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700691 if (populationStatus == Status.IDLE
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700692 || populationStatus == Status.SUCCEEDED
693 || populationStatus == Status.ABORTED) {
sangho80f11cb2015-04-01 13:05:26 -0700694 populationStatus = Status.STARTED;
695 populateAllRoutingRules();
Srikanth Vavilapalli7cd16712015-05-04 09:48:09 -0700696 } else {
697 log.warn("Not initiating startPopulationProcess as populationStatus is {}",
698 populationStatus);
sangho80f11cb2015-04-01 13:05:26 -0700699 }
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900700 } finally {
701 statusLock.unlock();
sangho80f11cb2015-04-01 13:05:26 -0700702 }
703 }
704
705 /**
706 * Resume the flow rule population process if it was aborted for any reason.
707 * Mostly the process is aborted when the groups required are not set yet.
Saurav Das88979182015-10-19 14:37:36 -0700708 * XXX is this called?
709 *
sangho80f11cb2015-04-01 13:05:26 -0700710 */
711 public void resumePopulationProcess() {
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900712 statusLock.lock();
713 try {
sangho80f11cb2015-04-01 13:05:26 -0700714 if (populationStatus == Status.ABORTED) {
715 populationStatus = Status.STARTED;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700716 // TODO: we need to restart from the point aborted instead of
717 // restarting.
sangho80f11cb2015-04-01 13:05:26 -0700718 populateAllRoutingRules();
719 }
HIGUCHI Yuta16d8fd52015-09-08 16:16:31 +0900720 } finally {
721 statusLock.unlock();
sangho80f11cb2015-04-01 13:05:26 -0700722 }
723 }
Saurav Dasc3604f12016-03-23 11:22:49 -0700724
Charles Chanc22cef32016-04-29 14:38:22 -0700725 /**
726 * Populate rules of given subnet at given location.
727 *
728 * @param cp connect point of the subnet being added
729 * @param subnets subnet being added
730 * @return true if succeed
731 */
Pier Ventreb6a7f342016-11-26 21:05:22 -0800732 protected boolean populateSubnet(ConnectPoint cp, Set<IpPrefix> subnets) {
Charles Chanc22cef32016-04-29 14:38:22 -0700733 statusLock.lock();
734 try {
735 EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(cp.deviceId());
736 if (ecmpSpg == null) {
737 log.warn("Fail to populating subnet {}: {}", subnets, ECMPSPG_MISSING);
738 return false;
739 }
740 return populateEcmpRoutingRules(cp.deviceId(), ecmpSpg, subnets);
741 } finally {
742 statusLock.unlock();
743 }
744 }
745
746 /**
747 * Revoke rules of given subnet at given location.
748 *
749 * @param subnets subnet being removed
750 * @return true if succeed
751 */
Pier Ventreb6a7f342016-11-26 21:05:22 -0800752 protected boolean revokeSubnet(Set<IpPrefix> subnets) {
Charles Chanc22cef32016-04-29 14:38:22 -0700753 statusLock.lock();
754 try {
755 return srManager.routingRulePopulator.revokeIpRuleForSubnet(subnets);
756 } finally {
757 statusLock.unlock();
758 }
759 }
760
761 protected void purgeEcmpGraph(DeviceId deviceId) {
Saurav Dasc3604f12016-03-23 11:22:49 -0700762 currentEcmpSpgMap.remove(deviceId);
Saurav Das52d4ed72016-03-28 19:00:18 -0700763 if (updatedEcmpSpgMap != null) {
764 updatedEcmpSpgMap.remove(deviceId);
765 }
Pier Ventre6d593892016-09-13 21:33:40 -0700766 this.populateRoutingRulesForLinkStatusChange(null);
Saurav Dasc3604f12016-03-23 11:22:49 -0700767 }
Saurav Das07c74602016-04-27 18:35:50 -0700768
Saurav Dasd1872b02016-12-02 15:43:47 -0800769 /**
770 * Utility class used to temporarily store information about the ports on a
771 * device processed for filtering objectives.
Saurav Dasd1872b02016-12-02 15:43:47 -0800772 */
773 public final class PortFilterInfo {
Saurav Dasf9332192017-02-18 14:05:44 -0800774 int disabledPorts = 0, errorPorts = 0, filteredPorts = 0;
Saurav Das07c74602016-04-27 18:35:50 -0700775
Saurav Dasf9332192017-02-18 14:05:44 -0800776 public PortFilterInfo(int disabledPorts, int errorPorts,
Saurav Dasd1872b02016-12-02 15:43:47 -0800777 int filteredPorts) {
778 this.disabledPorts = disabledPorts;
779 this.filteredPorts = filteredPorts;
Saurav Dasf9332192017-02-18 14:05:44 -0800780 this.errorPorts = errorPorts;
Saurav Dasd1872b02016-12-02 15:43:47 -0800781 }
782
783 @Override
784 public int hashCode() {
Saurav Dasf9332192017-02-18 14:05:44 -0800785 return Objects.hash(disabledPorts, filteredPorts, errorPorts);
Saurav Dasd1872b02016-12-02 15:43:47 -0800786 }
787
788 @Override
789 public boolean equals(Object obj) {
790 if (this == obj) {
791 return true;
792 }
793 if ((obj == null) || (!(obj instanceof PortFilterInfo))) {
794 return false;
795 }
796 PortFilterInfo other = (PortFilterInfo) obj;
797 return ((disabledPorts == other.disabledPorts) &&
798 (filteredPorts == other.filteredPorts) &&
Saurav Dasf9332192017-02-18 14:05:44 -0800799 (errorPorts == other.errorPorts));
Saurav Dasd1872b02016-12-02 15:43:47 -0800800 }
801
802 @Override
803 public String toString() {
804 MoreObjects.ToStringHelper helper = toStringHelper(this)
805 .add("disabledPorts", disabledPorts)
Saurav Dasf9332192017-02-18 14:05:44 -0800806 .add("errorPorts", errorPorts)
Saurav Dasd1872b02016-12-02 15:43:47 -0800807 .add("filteredPorts", filteredPorts);
808 return helper.toString();
809 }
810 }
811
812 /**
813 * RetryFilters populates filtering objectives for a device and keeps retrying
814 * till the number of ports filtered are constant for a predefined number
815 * of attempts.
816 */
817 protected final class RetryFilters implements Runnable {
818 int constantAttempts = MAX_CONSTANT_RETRY_ATTEMPTS;
819 DeviceId devId;
820 int counter;
821 PortFilterInfo prevRun;
822
823 private RetryFilters(DeviceId deviceId, PortFilterInfo previousRun) {
Saurav Das07c74602016-04-27 18:35:50 -0700824 devId = deviceId;
Saurav Dasd1872b02016-12-02 15:43:47 -0800825 prevRun = previousRun;
826 counter = 0;
Saurav Das07c74602016-04-27 18:35:50 -0700827 }
828
829 @Override
830 public void run() {
Saurav Dasd1872b02016-12-02 15:43:47 -0800831 log.info("RETRY FILTER ATTEMPT {} ** dev:{}", ++counter, devId);
Charles Chan18fa4252017-02-08 16:10:40 -0800832 PortFilterInfo thisRun = rulePopulator.populateVlanMacFilters(devId);
Saurav Dasd1872b02016-12-02 15:43:47 -0800833 boolean sameResult = prevRun.equals(thisRun);
834 log.debug("dev:{} prevRun:{} thisRun:{} sameResult:{}", devId, prevRun,
835 thisRun, sameResult);
836 if (thisRun == null || !sameResult || (sameResult && --constantAttempts > 0)) {
Saurav Dasf9332192017-02-18 14:05:44 -0800837 // exponentially increasing intervals for retries
838 executorService.schedule(this,
839 RETRY_INTERVAL_MS * (int) Math.pow(counter, RETRY_INTERVAL_SCALE),
840 TimeUnit.MILLISECONDS);
Saurav Dasd1872b02016-12-02 15:43:47 -0800841 if (!sameResult) {
842 constantAttempts = MAX_CONSTANT_RETRY_ATTEMPTS; //reset
843 }
Saurav Das07c74602016-04-27 18:35:50 -0700844 }
Saurav Dasd1872b02016-12-02 15:43:47 -0800845 prevRun = (thisRun == null) ? prevRun : thisRun;
Saurav Das07c74602016-04-27 18:35:50 -0700846 }
Saurav Das07c74602016-04-27 18:35:50 -0700847 }
848
sangho80f11cb2015-04-01 13:05:26 -0700849}