blob: dae673dab0a205709f19625e12e2780bfc8f4e19 [file] [log] [blame]
Charles Chandebfea32016-10-24 14:52:01 -07001/*
Brian O'Connor0947d7e2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Charles Chandebfea32016-10-24 14:52:01 -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 */
16
17package org.onosproject.segmentrouting;
18
Saurav Das261c3002017-06-13 15:35:54 -070019import com.google.common.collect.Sets;
20
Charles Chandebfea32016-10-24 14:52:01 -070021import org.onlab.packet.IpPrefix;
22import org.onlab.packet.MacAddress;
Charles Chan90772a72017-02-08 15:52:08 -080023import org.onlab.packet.VlanId;
Charles Chanf0ae41e2017-08-23 13:55:39 -070024import org.onosproject.net.ConnectPoint;
pierventrea3989be2021-01-08 16:43:17 +010025import org.onosproject.net.Host;
Charles Chan910be6a2017-08-23 14:46:43 -070026import org.onosproject.net.PortNumber;
27import org.onosproject.net.host.HostEvent;
Ray Milkeya8154312017-08-08 13:00:43 -070028import org.onosproject.routeservice.ResolvedRoute;
29import org.onosproject.routeservice.RouteEvent;
Charles Chandebfea32016-10-24 14:52:01 -070030import org.onosproject.net.DeviceId;
Charles Chan910be6a2017-08-23 14:46:43 -070031import org.onosproject.routeservice.RouteInfo;
Charles Chanfab61472021-06-14 23:31:23 -070032import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
Charles Chandebfea32016-10-24 14:52:01 -070033import org.slf4j.Logger;
34import org.slf4j.LoggerFactory;
35
Charles Chan5eec3b12019-04-18 14:30:41 -070036import java.util.List;
Charles Chan910be6a2017-08-23 14:46:43 -070037import java.util.Collection;
Charles Chanee8dbf82017-06-22 14:15:05 -070038import java.util.Objects;
Charles Chan910be6a2017-08-23 14:46:43 -070039import java.util.Optional;
40import java.util.Set;
Charles Chan910be6a2017-08-23 14:46:43 -070041import java.util.stream.Collectors;
Charles Chanee8dbf82017-06-22 14:15:05 -070042
Charles Chandebfea32016-10-24 14:52:01 -070043/**
44 * Handles RouteEvent and manages routing entries.
45 */
46public class RouteHandler {
47 private static final Logger log = LoggerFactory.getLogger(RouteHandler.class);
48 private final SegmentRoutingManager srManager;
49
Charles Chanf0ae41e2017-08-23 13:55:39 -070050 RouteHandler(SegmentRoutingManager srManager) {
Charles Chandebfea32016-10-24 14:52:01 -070051 this.srManager = srManager;
52 }
53
54 protected void init(DeviceId deviceId) {
Charles Chan7d20a4e2018-04-13 14:01:49 -040055 srManager.routeService.getRouteTables().stream()
56 .map(srManager.routeService::getRoutes)
57 .flatMap(Collection::stream)
58 .map(RouteInfo::allRoutes)
Charles Chan12a8a842020-02-14 13:23:57 -080059 .filter(allRoutes -> allRoutes.stream().anyMatch(resolvedRoute ->
60 srManager.nextHopLocations(resolvedRoute).stream()
61 .anyMatch(cp -> deviceId.equals(cp.deviceId()))))
62 .forEach(rr -> processRouteAddedInternal(rr, true));
Charles Chandebfea32016-10-24 14:52:01 -070063 }
64
Charles Chanf0ae41e2017-08-23 13:55:39 -070065 void processRouteAdded(RouteEvent event) {
Charles Chan12a8a842020-02-14 13:23:57 -080066 processRouteAddedInternal(event.alternatives(), false);
Charles Chandebfea32016-10-24 14:52:01 -070067 }
68
Charles Chan12a8a842020-02-14 13:23:57 -080069 /**
70 * Internal logic that handles route addition.
71 *
72 * @param routes collection of routes to be processed
73 * @param populateRouteOnly true if we only want to populateRoute but not populateSubnet.
74 * Set it to true when initializing a device coming up.
75 * populateSubnet will be done when link comes up later so it is redundant.
76 * populateRoute still needs to be done for statically configured next hop hosts.
77 */
78 private void processRouteAddedInternal(Collection<ResolvedRoute> routes, boolean populateRouteOnly) {
Charles Chanee8dbf82017-06-22 14:15:05 -070079 if (!isReady()) {
Charles Chan910be6a2017-08-23 14:46:43 -070080 log.info("System is not ready. Skip adding route for {}", routes);
Charles Chanee8dbf82017-06-22 14:15:05 -070081 return;
82 }
83
Charles Chan910be6a2017-08-23 14:46:43 -070084 log.info("processRouteAddedInternal. routes={}", routes);
Charles Chanf0ae41e2017-08-23 13:55:39 -070085
Charles Chan482b6422018-04-09 11:52:08 -040086 if (routes.size() > 2) {
87 log.info("Route {} has more than two next hops. Do not process route change", routes);
88 return;
89 }
90
Charles Chanfab61472021-06-14 23:31:23 -070091 if (routes.isEmpty()) {
Charles Chan12a8a842020-02-14 13:23:57 -080092 log.warn("No resolved route found. Abort processRouteAddedInternal");
Charles Chanfab61472021-06-14 23:31:23 -070093 return;
Charles Chan12a8a842020-02-14 13:23:57 -080094 }
95
Charles Chan910be6a2017-08-23 14:46:43 -070096 Set<ConnectPoint> allLocations = Sets.newHashSet();
97 Set<IpPrefix> allPrefixes = Sets.newHashSet();
98 routes.forEach(route -> {
99 allLocations.addAll(srManager.nextHopLocations(route));
100 allPrefixes.add(route.prefix());
101 });
102 log.debug("RouteAdded. populateSubnet {}, {}", allLocations, allPrefixes);
103 srManager.defaultRoutingHandler.populateSubnet(allLocations, allPrefixes);
Charles Chandebfea32016-10-24 14:52:01 -0700104
Charles Chan910be6a2017-08-23 14:46:43 -0700105 routes.forEach(route -> {
106 IpPrefix prefix = route.prefix();
107 MacAddress nextHopMac = route.nextHopMac();
108 VlanId nextHopVlan = route.nextHopVlan();
109 Set<ConnectPoint> locations = srManager.nextHopLocations(route);
110
111 locations.forEach(location -> {
112 log.debug("RouteAdded. addSubnet {}, {}", location, prefix);
113 srManager.deviceConfiguration.addSubnet(location, prefix);
114 log.debug("RouteAdded populateRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
115 srManager.defaultRoutingHandler.populateRoute(location.deviceId(), prefix,
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000116 nextHopMac, nextHopVlan, location.port(), false);
Charles Chanfab61472021-06-14 23:31:23 -0700117
118 processSingleLeafPairIfNeeded(locations, location, prefix, nextHopVlan);
Charles Chan910be6a2017-08-23 14:46:43 -0700119 });
120 });
Charles Chandebfea32016-10-24 14:52:01 -0700121 }
122
Charles Chanf0ae41e2017-08-23 13:55:39 -0700123 void processRouteUpdated(RouteEvent event) {
Charles Chanfbcb8812018-04-18 18:41:05 -0700124 processRouteUpdatedInternal(Sets.newHashSet(event.alternatives()),
125 Sets.newHashSet(event.prevAlternatives()));
Charles Chan910be6a2017-08-23 14:46:43 -0700126 }
127
128 void processAlternativeRoutesChanged(RouteEvent event) {
Charles Chanfbcb8812018-04-18 18:41:05 -0700129 processRouteUpdatedInternal(Sets.newHashSet(event.alternatives()),
130 Sets.newHashSet(event.prevAlternatives()));
Charles Chan910be6a2017-08-23 14:46:43 -0700131 }
132
133 private void processRouteUpdatedInternal(Set<ResolvedRoute> routes, Set<ResolvedRoute> oldRoutes) {
134 if (!isReady()) {
135 log.info("System is not ready. Skip updating route for {} -> {}", oldRoutes, routes);
136 return;
137 }
138
139 log.info("processRouteUpdatedInternal. routes={}, oldRoutes={}", routes, oldRoutes);
140
Charles Chan482b6422018-04-09 11:52:08 -0400141 if (routes.size() > 2) {
142 log.info("Route {} has more than two next hops. Do not process route change", routes);
143 return;
144 }
145
Charles Chan910be6a2017-08-23 14:46:43 -0700146 Set<ConnectPoint> allLocations = Sets.newHashSet();
147 Set<IpPrefix> allPrefixes = Sets.newHashSet();
148 routes.forEach(route -> {
149 allLocations.addAll(srManager.nextHopLocations(route));
150 allPrefixes.add(route.prefix());
151 });
Charles Chan482b6422018-04-09 11:52:08 -0400152
153 // Just come back from an invalid next hop count
154 // Revoke subnet from all locations and reset oldRoutes such that system will be reprogrammed from scratch
155 if (oldRoutes.size() > 2) {
156 log.info("Revoke subnet {} and reset oldRoutes");
pierventre0dcbf0e2021-10-11 13:07:09 +0200157 // FIXME remove routes more precisely by memorizing the old locations
158 srManager.defaultRoutingHandler.revokeSubnet(allPrefixes, null);
Charles Chan482b6422018-04-09 11:52:08 -0400159 oldRoutes = Sets.newHashSet();
160 }
161
Charles Chan910be6a2017-08-23 14:46:43 -0700162 log.debug("RouteUpdated. populateSubnet {}, {}", allLocations, allPrefixes);
163 srManager.defaultRoutingHandler.populateSubnet(allLocations, allPrefixes);
164
Charles Chan910be6a2017-08-23 14:46:43 -0700165 Set<ResolvedRoute> toBeRemoved = Sets.difference(oldRoutes, routes).immutableCopy();
166 Set<ResolvedRoute> toBeAdded = Sets.difference(routes, oldRoutes).immutableCopy();
167
168 toBeRemoved.forEach(route -> {
169 srManager.nextHopLocations(route).forEach(oldLocation -> {
Charles Chan06f626c2018-02-05 17:20:05 -0800170 if (toBeAdded.stream().map(srManager::nextHopLocations)
171 .flatMap(Set::stream).map(ConnectPoint::deviceId)
172 .noneMatch(deviceId -> deviceId.equals(oldLocation.deviceId()))) {
173 IpPrefix prefix = route.prefix();
174 log.debug("RouteUpdated. removeSubnet {}, {}", oldLocation, prefix);
175 srManager.deviceConfiguration.removeSubnet(oldLocation, prefix);
176 // We don't remove the flow on the old location in occasion of two next hops becoming one
177 // since the populateSubnet will point the old location to the new location via spine.
178 }
Charles Chan910be6a2017-08-23 14:46:43 -0700179 });
180 });
181
182 toBeAdded.forEach(route -> {
183 IpPrefix prefix = route.prefix();
184 MacAddress nextHopMac = route.nextHopMac();
185 VlanId nextHopVlan = route.nextHopVlan();
186 Set<ConnectPoint> locations = srManager.nextHopLocations(route);
187
188 locations.forEach(location -> {
189 log.debug("RouteUpdated. addSubnet {}, {}", location, prefix);
190 srManager.deviceConfiguration.addSubnet(location, prefix);
191 log.debug("RouteUpdated. populateRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
192 srManager.defaultRoutingHandler.populateRoute(location.deviceId(), prefix,
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000193 nextHopMac, nextHopVlan, location.port(), false);
Charles Chanfab61472021-06-14 23:31:23 -0700194
195 processSingleLeafPairIfNeeded(locations, location, prefix, nextHopVlan);
Charles Chan910be6a2017-08-23 14:46:43 -0700196 });
197 });
Charles Chandebfea32016-10-24 14:52:01 -0700198 }
199
Charles Chanf0ae41e2017-08-23 13:55:39 -0700200 void processRouteRemoved(RouteEvent event) {
Charles Chanfbcb8812018-04-18 18:41:05 -0700201 processRouteRemovedInternal(event.alternatives());
Charles Chandebfea32016-10-24 14:52:01 -0700202 }
203
Charles Chan910be6a2017-08-23 14:46:43 -0700204 private void processRouteRemovedInternal(Collection<ResolvedRoute> routes) {
Charles Chanee8dbf82017-06-22 14:15:05 -0700205 if (!isReady()) {
Charles Chan910be6a2017-08-23 14:46:43 -0700206 log.info("System is not ready. Skip removing route for {}", routes);
Charles Chanee8dbf82017-06-22 14:15:05 -0700207 return;
208 }
209
Charles Chan910be6a2017-08-23 14:46:43 -0700210 log.info("processRouteRemovedInternal. routes={}", routes);
Charles Chanf0ae41e2017-08-23 13:55:39 -0700211
Charles Chan910be6a2017-08-23 14:46:43 -0700212 Set<IpPrefix> allPrefixes = Sets.newHashSet();
213 routes.forEach(route -> {
214 allPrefixes.add(route.prefix());
215 });
216 log.debug("RouteRemoved. revokeSubnet {}", allPrefixes);
pierventre0dcbf0e2021-10-11 13:07:09 +0200217 // FIXME remove routes more precisely by memorizing the old locations
218 srManager.defaultRoutingHandler.revokeSubnet(allPrefixes, null);
Charles Chandebfea32016-10-24 14:52:01 -0700219
Charles Chan910be6a2017-08-23 14:46:43 -0700220 routes.forEach(route -> {
221 IpPrefix prefix = route.prefix();
222 MacAddress nextHopMac = route.nextHopMac();
223 VlanId nextHopVlan = route.nextHopVlan();
224 Set<ConnectPoint> locations = srManager.nextHopLocations(route);
225
226 locations.forEach(location -> {
227 log.debug("RouteRemoved. removeSubnet {}, {}", location, prefix);
228 srManager.deviceConfiguration.removeSubnet(location, prefix);
Charles Chanc4d68882018-03-15 16:41:10 -0700229 // We don't need to call revokeRoute again since revokeSubnet will remove the prefix
230 // from all devices, including the ones that next hop attaches to.
Charles Chanfab61472021-06-14 23:31:23 -0700231 // revokeSubnet will also remove flow on the pair device (if exist) pointing to current location.
Charles Chan910be6a2017-08-23 14:46:43 -0700232 });
233 });
234 }
235
236 void processHostMovedEvent(HostEvent event) {
237 log.info("processHostMovedEvent {}", event);
238 MacAddress hostMac = event.subject().mac();
239 VlanId hostVlanId = event.subject().vlan();
Ruchi Sahota07869322019-05-09 17:26:14 -0400240
Charles Chanfab61472021-06-14 23:31:23 -0700241 Set<ConnectPoint> prevLocations = event.prevSubject().locations()
242 .stream().map(h -> (ConnectPoint) h)
243 .collect(Collectors.toSet());
244 Set<ConnectPoint> newLocations = event.subject().locations()
245 .stream().map(h -> (ConnectPoint) h)
246 .collect(Collectors.toSet());
Charles Chan5eec3b12019-04-18 14:30:41 -0700247 List<Set<IpPrefix>> batchedSubnets =
248 srManager.deviceConfiguration.getBatchedSubnets(event.subject().id());
Charles Chanfab61472021-06-14 23:31:23 -0700249 Set<DeviceId> newDeviceIds = newLocations.stream().map(ConnectPoint::deviceId)
Ruchi Sahota07869322019-05-09 17:26:14 -0400250 .collect(Collectors.toSet());
251
252 // Set of deviceIDs of the previous locations where the host was connected
253 // Used to determine if host moved to different connect points
254 // on same device or moved to a different device altogether
Charles Chanfab61472021-06-14 23:31:23 -0700255 Set<DeviceId> oldDeviceIds = prevLocations.stream().map(ConnectPoint::deviceId)
Ruchi Sahota07869322019-05-09 17:26:14 -0400256 .collect(Collectors.toSet());
257
258 // L3 Ucast bucket needs to be updated only once per host
259 // and only when the no. of routes with the host as next-hop is not zero
260 if (!batchedSubnets.isEmpty()) {
261 // For each new location, if NextObj exists for the host, update with new location ..
262 Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
pierventrea3989be2021-01-08 16:43:17 +0100263 // NOTE: that we use the nexthop vlanId to retrieve the nextId
264 // while the vlanId used to program the L3 unicast chain
265 // is derived from the port configuration. In case of
266 // a tagged interface we use host vlanId. Host vlan should
267 // be part of the tags configured for that port. See the
268 // code in DefaultGroupHandler.updateL3UcastGroupBucket
269 int nextId = srManager.getMacVlanNextObjectiveId(newLocation.deviceId(),
270 hostMac, hostVlanId, null, false);
271 if (nextId != -1) {
272 //Update the nextId group bucket
273 log.debug("HostMoved. NextId exists, update L3 Ucast Group Bucket {}, {}, {} --> {}",
274 newLocation, hostMac, hostVlanId, nextId);
275 srManager.updateMacVlanTreatment(newLocation.deviceId(), hostMac, hostVlanId,
276 newLocation.port(), nextId);
277 } else {
278 log.debug("HostMoved. NextId does not exist for this location {}, host {}/{}",
279 newLocation, hostMac, hostVlanId);
280 }
Ruchi Sahota07869322019-05-09 17:26:14 -0400281 });
282 }
Charles Chan910be6a2017-08-23 14:46:43 -0700283
Charles Chan5eec3b12019-04-18 14:30:41 -0700284 batchedSubnets.forEach(subnets -> {
285 log.debug("HostMoved. populateSubnet {}, {}", newLocations, subnets);
Charles Chanfab61472021-06-14 23:31:23 -0700286 srManager.defaultRoutingHandler.populateSubnet(newLocations, subnets);
Charles Chan910be6a2017-08-23 14:46:43 -0700287
Charles Chan5eec3b12019-04-18 14:30:41 -0700288 subnets.forEach(prefix -> {
Charles Chan5eec3b12019-04-18 14:30:41 -0700289 // For each old location
290 Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000291
Charles Chan5eec3b12019-04-18 14:30:41 -0700292 // Remove flows for unchanged IPs only when the host moves from a switch to another.
293 // Otherwise, do not remove and let the adding part update the old flow
294 if (newDeviceIds.contains(prevLocation.deviceId())) {
295 return;
296 }
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000297
Charles Chan5eec3b12019-04-18 14:30:41 -0700298 log.debug("HostMoved. removeSubnet {}, {}", prevLocation, prefix);
299 srManager.deviceConfiguration.removeSubnet(prevLocation, prefix);
Charles Chan4dcd5102019-02-28 15:40:57 -0800300
Charles Chan5eec3b12019-04-18 14:30:41 -0700301 // Do not remove flow from a device if the route is still reachable via its pair device.
Charles Chanfab61472021-06-14 23:31:23 -0700302 // If spine exists,
303 // populateSubnet above will update the flow to point to its pair device via spine.
304 // If spine does not exist,
305 // processSingleLeafPair below will update the flow to point to its pair device via pair port.
Charles Chan5eec3b12019-04-18 14:30:41 -0700306 DeviceId pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId()).orElse(null);
307 if (newLocations.stream().anyMatch(n -> n.deviceId().equals(pairDeviceId))) {
308 return;
309 }
Charles Chan4dcd5102019-02-28 15:40:57 -0800310
Charles Chan5eec3b12019-04-18 14:30:41 -0700311 log.debug("HostMoved. revokeRoute {}, {}, {}, {}", prevLocation, prefix, hostMac, hostVlanId);
312 srManager.defaultRoutingHandler.revokeRoute(prevLocation.deviceId(), prefix,
313 hostMac, hostVlanId, prevLocation.port(), false);
314 });
Charles Chan4dcd5102019-02-28 15:40:57 -0800315
Charles Chan5eec3b12019-04-18 14:30:41 -0700316 // For each new location, add all new IPs.
317 Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
318 log.debug("HostMoved. addSubnet {}, {}", newLocation, prefix);
319 srManager.deviceConfiguration.addSubnet(newLocation, prefix);
320
321 //its a new connect point, not a move from an existing device, populateRoute
322 if (!oldDeviceIds.contains(newLocation.deviceId())) {
323 log.debug("HostMoved. populateRoute {}, {}, {}, {}", newLocation, prefix, hostMac, hostVlanId);
324 srManager.defaultRoutingHandler.populateRoute(newLocation.deviceId(), prefix,
325 hostMac, hostVlanId, newLocation.port(), false);
Charles Chan5eec3b12019-04-18 14:30:41 -0700326 }
327 });
Charles Chanfab61472021-06-14 23:31:23 -0700328
329 newLocations.forEach(location -> {
330 processSingleLeafPairIfNeeded(newLocations, location, prefix, hostVlanId);
331 });
Charles Chan910be6a2017-08-23 14:46:43 -0700332 });
Charles Chan910be6a2017-08-23 14:46:43 -0700333 });
Ruchi Sahota07869322019-05-09 17:26:14 -0400334
Charles Chan910be6a2017-08-23 14:46:43 -0700335 }
336
pierventrea3989be2021-01-08 16:43:17 +0100337 public void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum) {
338 log.info("processIntfVlanUpdatedEvent {}/{}", deviceId, portNum);
339
340 // Verify there are hosts attached to the port
341 ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
342 Set<Host> hosts = srManager.hostService.getConnectedHosts(connectPoint);
343 if (hosts == null || hosts.size() == 0) {
344 log.debug("processIntfVlanUpdatedEvent: No hosts connected to {}", connectPoint);
345 return;
346 }
347
348 // Iterate over the hosts and update if needed the vlan of the nextobjective
349 hosts.forEach(host -> {
350 // Verify if there is a nexthop and only then update the l3 indirect unicast chain
351 int nextId = srManager.getMacVlanNextObjectiveId(deviceId, host.mac(), host.vlan(),
352 null, false);
353 if (nextId != -1) {
354 //Update the nextId group bucket
355 log.debug("intfVlanUpdated. NextId exists, update L3 Ucast Group Bucket {}, {}, {} --> {}",
356 connectPoint, host.mac(), host.vlan(), nextId);
357 // NOTE: that we use the nexthop vlanId to retrieve the nextId
358 // while the vlanId used to program the L3 unicast chain
359 // is derived from the port configuration. In case of
360 // a tagged interface we use host vlanId. Host vlan should
361 // be part of the tags configured for that port. See the
362 // code in DefaultGroupHandler.updateL3UcastGroupBucket
363 srManager.updateMacVlanTreatment(deviceId, host.mac(), host.vlan(), portNum, nextId);
364 } else {
365 log.debug("intfVlanUpdated. NextId does not exist for this location {}, host {}/{}",
366 connectPoint, host.mac(), host.vlan());
367 }
368 });
369 }
370
Charles Chanee8dbf82017-06-22 14:15:05 -0700371 private boolean isReady() {
372 return Objects.nonNull(srManager.deviceConfiguration) &&
Charles Chan910be6a2017-08-23 14:46:43 -0700373 Objects.nonNull(srManager.defaultRoutingHandler);
374 }
Charles Chanfab61472021-06-14 23:31:23 -0700375
376 protected boolean processSingleLeafPairIfNeeded(Set<ConnectPoint> locations, ConnectPoint location, IpPrefix prefix,
377 VlanId nextHopVlan) {
378 // Special handling for single leaf pair
379 if (!srManager.getInfraDeviceIds().isEmpty()) {
380 log.debug("Spine found. Skip single leaf pair handling");
381 return false;
382 }
383 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
384 if (pairDeviceId.isEmpty()) {
385 log.debug("Pair device of {} not found", location.deviceId());
386 return false;
387 }
388 if (locations.stream().anyMatch(l -> l.deviceId().equals(pairDeviceId.get()))) {
389 log.debug("Pair device has a next hop available. Leave it as is.");
390 return false;
391 }
392 Optional<PortNumber> pairRemotePort = srManager.getPairLocalPort(pairDeviceId.get());
393 if (pairRemotePort.isEmpty()) {
394 log.debug("Pair remote port of {} not found", pairDeviceId.get());
395 return false;
396 }
397 // Use routerMac of the pair device as the next hop
398 MacAddress effectiveMac;
399 try {
400 effectiveMac = srManager.getDeviceMacAddress(location.deviceId());
401 } catch (DeviceConfigNotFoundException e) {
402 log.warn("Abort populateRoute on pair device {}. routerMac not found", pairDeviceId);
403 return false;
404 }
405 // Since the pairLocalPort is trunk port, use assigned vlan of original port
406 // when the host is untagged
407 VlanId effectiveVlan = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(nextHopVlan);
408
409 log.debug("Single leaf pair. populateRoute {}/{}, {}, {}, {}", pairDeviceId, pairRemotePort, prefix,
410 effectiveMac, effectiveVlan);
411 srManager.defaultRoutingHandler.populateRoute(pairDeviceId.get(), prefix,
412 effectiveMac, effectiveVlan, pairRemotePort.get(), false);
413
414 return true;
415 }
Charles Chandebfea32016-10-24 14:52:01 -0700416}