Charles Chan

CORD-60 Support dynamic vSG creation/deletion

We no longer need to configure /32 IP in interfaces.
SR will push a per-host route when discovering a host
with IP address(es) that does not belong to configured subnet.

Also includes:
- HostHandler refactoring

Change-Id: Ic1ad42d1ccdfee32be85f49e6fc94d9026000ffc
...@@ -15,11 +15,13 @@ ...@@ -15,11 +15,13 @@
15 */ 15 */
16 package org.onosproject.segmentrouting; 16 package org.onosproject.segmentrouting;
17 17
18 +import com.google.common.collect.ImmutableSet;
18 import com.google.common.collect.Maps; 19 import com.google.common.collect.Maps;
19 import com.google.common.collect.Sets; 20 import com.google.common.collect.Sets;
20 import org.onlab.packet.Ip4Address; 21 import org.onlab.packet.Ip4Address;
21 import org.onlab.packet.Ip4Prefix; 22 import org.onlab.packet.Ip4Prefix;
22 import org.onlab.packet.IpPrefix; 23 import org.onlab.packet.IpPrefix;
24 +import org.onosproject.net.ConnectPoint;
23 import org.onosproject.net.Device; 25 import org.onosproject.net.Device;
24 import org.onosproject.net.DeviceId; 26 import org.onosproject.net.DeviceId;
25 import org.onosproject.net.Link; 27 import org.onosproject.net.Link;
...@@ -45,9 +47,9 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -45,9 +47,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
45 * routing rule population. 47 * routing rule population.
46 */ 48 */
47 public class DefaultRoutingHandler { 49 public class DefaultRoutingHandler {
48 - 50 + private static final int MAX_RETRY_ATTEMPTS = 5;
49 - private static Logger log = LoggerFactory 51 + private static final String ECMPSPG_MISSING = "ECMP shortest path graph not found";
50 - .getLogger(DefaultRoutingHandler.class); 52 + private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
51 53
52 private SegmentRoutingManager srManager; 54 private SegmentRoutingManager srManager;
53 private RoutingRulePopulator rulePopulator; 55 private RoutingRulePopulator rulePopulator;
...@@ -56,7 +58,6 @@ public class DefaultRoutingHandler { ...@@ -56,7 +58,6 @@ public class DefaultRoutingHandler {
56 private DeviceConfiguration config; 58 private DeviceConfiguration config;
57 private final Lock statusLock = new ReentrantLock(); 59 private final Lock statusLock = new ReentrantLock();
58 private volatile Status populationStatus; 60 private volatile Status populationStatus;
59 - private static final int MAX_RETRY_ATTEMPTS = 5;
60 private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); 61 private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
61 62
62 /** 63 /**
...@@ -113,7 +114,7 @@ public class DefaultRoutingHandler { ...@@ -113,7 +114,7 @@ public class DefaultRoutingHandler {
113 } 114 }
114 115
115 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(sw.id(), srManager); 116 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(sw.id(), srManager);
116 - if (!populateEcmpRoutingRules(sw.id(), ecmpSpg)) { 117 + if (!populateEcmpRoutingRules(sw.id(), ecmpSpg, ImmutableSet.of())) {
117 log.debug("populateAllRoutingRules: populationStatus is ABORTED"); 118 log.debug("populateAllRoutingRules: populationStatus is ABORTED");
118 populationStatus = Status.ABORTED; 119 populationStatus = Status.ABORTED;
119 log.debug("Abort routing rule population"); 120 log.debug("Abort routing rule population");
...@@ -210,7 +211,7 @@ public class DefaultRoutingHandler { ...@@ -210,7 +211,7 @@ public class DefaultRoutingHandler {
210 if (link.size() == 1) { 211 if (link.size() == 1) {
211 log.trace("repopulateRoutingRulesForRoutes: running ECMP graph for device {}", link.get(0)); 212 log.trace("repopulateRoutingRulesForRoutes: running ECMP graph for device {}", link.get(0));
212 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(link.get(0), srManager); 213 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(link.get(0), srManager);
213 - if (populateEcmpRoutingRules(link.get(0), ecmpSpg)) { 214 + if (populateEcmpRoutingRules(link.get(0), ecmpSpg, ImmutableSet.of())) {
214 log.debug("Populating flow rules from {} to all is successful", 215 log.debug("Populating flow rules from {} to all is successful",
215 link.get(0)); 216 link.get(0));
216 currentEcmpSpgMap.put(link.get(0), ecmpSpg); 217 currentEcmpSpgMap.put(link.get(0), ecmpSpg);
...@@ -255,7 +256,8 @@ public class DefaultRoutingHandler { ...@@ -255,7 +256,8 @@ public class DefaultRoutingHandler {
255 nextHops.add(via.get(0)); 256 nextHops.add(via.get(0));
256 } 257 }
257 } 258 }
258 - if (!populateEcmpRoutingRulePartial(targetSw, dst, nextHops)) { 259 + if (!populateEcmpRoutingRulePartial(targetSw, dst,
260 + nextHops, ImmutableSet.of())) {
259 return false; 261 return false;
260 } 262 }
261 log.debug("Populating flow rules from {} to {} is successful", 263 log.debug("Populating flow rules from {} to {} is successful",
...@@ -422,8 +424,17 @@ public class DefaultRoutingHandler { ...@@ -422,8 +424,17 @@ public class DefaultRoutingHandler {
422 return subLinks; 424 return subLinks;
423 } 425 }
424 426
427 + /**
428 + * Populate ECMP rules for subnets from all switches to destination.
429 + *
430 + * @param destSw Device ID of destination switch
431 + * @param ecmpSPG ECMP shortest path graph
432 + * @param subnets Subnets to be populated. If empty, populate all configured subnets.
433 + * @return true if succeed
434 + */
425 private boolean populateEcmpRoutingRules(DeviceId destSw, 435 private boolean populateEcmpRoutingRules(DeviceId destSw,
426 - EcmpShortestPathGraph ecmpSPG) { 436 + EcmpShortestPathGraph ecmpSPG,
437 + Set<Ip4Prefix> subnets) {
427 438
428 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = ecmpSPG 439 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = ecmpSPG
429 .getAllLearnedSwitchesAndVia(); 440 .getAllLearnedSwitchesAndVia();
...@@ -440,7 +451,7 @@ public class DefaultRoutingHandler { ...@@ -440,7 +451,7 @@ public class DefaultRoutingHandler {
440 nextHops.add(via.get(0)); 451 nextHops.add(via.get(0));
441 } 452 }
442 } 453 }
443 - if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops)) { 454 + if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops, subnets)) {
444 return false; 455 return false;
445 } 456 }
446 } 457 }
...@@ -449,9 +460,19 @@ public class DefaultRoutingHandler { ...@@ -449,9 +460,19 @@ public class DefaultRoutingHandler {
449 return true; 460 return true;
450 } 461 }
451 462
463 + /**
464 + * Populate ECMP rules for subnets from target to destination via nexthops.
465 + *
466 + * @param targetSw Device ID of target switch
467 + * @param destSw Device ID of destination switch
468 + * @param nextHops List of next hops
469 + * @param subnets Subnets to be populated. If empty, populate all configured subnets.
470 + * @return true if succeed
471 + */
452 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw, 472 private boolean populateEcmpRoutingRulePartial(DeviceId targetSw,
453 DeviceId destSw, 473 DeviceId destSw,
454 - Set<DeviceId> nextHops) { 474 + Set<DeviceId> nextHops,
475 + Set<Ip4Prefix> subnets) {
455 boolean result; 476 boolean result;
456 477
457 if (nextHops.isEmpty()) { 478 if (nextHops.isEmpty()) {
...@@ -473,13 +494,11 @@ public class DefaultRoutingHandler { ...@@ -473,13 +494,11 @@ public class DefaultRoutingHandler {
473 } 494 }
474 495
475 if (targetIsEdge && destIsEdge) { 496 if (targetIsEdge && destIsEdge) {
476 - Set<Ip4Prefix> subnets = config.getSubnets(destSw); 497 + subnets = (subnets != null && !subnets.isEmpty()) ? subnets : config.getSubnets(destSw);
477 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for subnets {}", 498 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for subnets {}",
478 targetSw, destSw, subnets); 499 targetSw, destSw, subnets);
479 - result = rulePopulator.populateIpRuleForSubnet(targetSw, 500 + result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets,
480 - subnets, 501 + destSw, nextHops);
481 - destSw,
482 - nextHops);
483 if (!result) { 502 if (!result) {
484 return false; 503 return false;
485 } 504 }
...@@ -575,18 +594,54 @@ public class DefaultRoutingHandler { ...@@ -575,18 +594,54 @@ public class DefaultRoutingHandler {
575 } 594 }
576 } 595 }
577 596
578 - public void purgeEcmpGraph(DeviceId deviceId) { 597 + /**
598 + * Populate rules of given subnet at given location.
599 + *
600 + * @param cp connect point of the subnet being added
601 + * @param subnets subnet being added
602 + * @return true if succeed
603 + */
604 + protected boolean populateSubnet(ConnectPoint cp, Set<Ip4Prefix> subnets) {
605 + statusLock.lock();
606 + try {
607 + EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(cp.deviceId());
608 + if (ecmpSpg == null) {
609 + log.warn("Fail to populating subnet {}: {}", subnets, ECMPSPG_MISSING);
610 + return false;
611 + }
612 + return populateEcmpRoutingRules(cp.deviceId(), ecmpSpg, subnets);
613 + } finally {
614 + statusLock.unlock();
615 + }
616 + }
617 +
618 + /**
619 + * Revoke rules of given subnet at given location.
620 + *
621 + * @param subnets subnet being removed
622 + * @return true if succeed
623 + */
624 + protected boolean revokeSubnet(Set<Ip4Prefix> subnets) {
625 + statusLock.lock();
626 + try {
627 + return srManager.routingRulePopulator.revokeIpRuleForSubnet(subnets);
628 + } finally {
629 + statusLock.unlock();
630 + }
631 + }
632 +
633 + protected void purgeEcmpGraph(DeviceId deviceId) {
579 currentEcmpSpgMap.remove(deviceId); 634 currentEcmpSpgMap.remove(deviceId);
580 if (updatedEcmpSpgMap != null) { 635 if (updatedEcmpSpgMap != null) {
581 updatedEcmpSpgMap.remove(deviceId); 636 updatedEcmpSpgMap.remove(deviceId);
582 } 637 }
583 } 638 }
584 639
585 - private class RetryFilters implements Runnable { 640 + private final class RetryFilters implements Runnable {
586 int attempts = MAX_RETRY_ATTEMPTS; 641 int attempts = MAX_RETRY_ATTEMPTS;
587 DeviceId devId; 642 DeviceId devId;
588 643
589 - public RetryFilters(DeviceId deviceId) { 644 + private RetryFilters(DeviceId deviceId) {
590 devId = deviceId; 645 devId = deviceId;
591 } 646 }
592 647
......
...@@ -212,22 +212,33 @@ public class RoutingRulePopulator { ...@@ -212,22 +212,33 @@ public class RoutingRulePopulator {
212 * Populates IP flow rules for the subnets of the destination router. 212 * Populates IP flow rules for the subnets of the destination router.
213 * 213 *
214 * @param deviceId switch ID to set the rules 214 * @param deviceId switch ID to set the rules
215 - * @param subnets subnet information 215 + * @param subnets subnet being added
216 * @param destSw destination switch ID 216 * @param destSw destination switch ID
217 * @param nextHops next hop switch ID list 217 * @param nextHops next hop switch ID list
218 * @return true if all rules are set successfully, false otherwise 218 * @return true if all rules are set successfully, false otherwise
219 */ 219 */
220 - public boolean populateIpRuleForSubnet(DeviceId deviceId, 220 + public boolean populateIpRuleForSubnet(DeviceId deviceId, Set<Ip4Prefix> subnets,
221 - Set<Ip4Prefix> subnets, 221 + DeviceId destSw, Set<DeviceId> nextHops) {
222 - DeviceId destSw,
223 - Set<DeviceId> nextHops) {
224 -
225 for (IpPrefix subnet : subnets) { 222 for (IpPrefix subnet : subnets) {
226 if (!populateIpRuleForRouter(deviceId, subnet, destSw, nextHops)) { 223 if (!populateIpRuleForRouter(deviceId, subnet, destSw, nextHops)) {
227 return false; 224 return false;
228 } 225 }
229 } 226 }
227 + return true;
228 + }
230 229
230 + /**
231 + * Revokes IP flow rules for the subnets.
232 + *
233 + * @param subnets subnet being removed
234 + * @return true if all rules are removed successfully, false otherwise
235 + */
236 + public boolean revokeIpRuleForSubnet(Set<Ip4Prefix> subnets) {
237 + for (IpPrefix subnet : subnets) {
238 + if (!revokeIpRuleForRouter(subnet)) {
239 + return false;
240 + }
241 + }
231 return true; 242 return true;
232 } 243 }
233 244
...@@ -310,6 +321,40 @@ public class RoutingRulePopulator { ...@@ -310,6 +321,40 @@ public class RoutingRulePopulator {
310 } 321 }
311 322
312 /** 323 /**
324 + * Revokes IP flow rules for the router IP address.
325 + *
326 + * @param ipPrefix the IP address of the destination router
327 + * @return true if all rules are removed successfully, false otherwise
328 + */
329 + public boolean revokeIpRuleForRouter(IpPrefix ipPrefix) {
330 + TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
331 + sbuilder.matchIPDst(ipPrefix);
332 + sbuilder.matchEthType(Ethernet.TYPE_IPV4);
333 + TrafficSelector selector = sbuilder.build();
334 + TrafficTreatment dummyTreatment = DefaultTrafficTreatment.builder().build();
335 +
336 + ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
337 + .builder()
338 + .fromApp(srManager.appId)
339 + .makePermanent()
340 + .withSelector(selector)
341 + .withTreatment(dummyTreatment)
342 + .withPriority(getPriorityFromPrefix(ipPrefix))
343 + .withFlag(ForwardingObjective.Flag.SPECIFIC);
344 +
345 + ObjectiveContext context = new DefaultObjectiveContext(
346 + (objective) -> log.debug("IP rule for router {} revoked", ipPrefix),
347 + (objective, error) ->
348 + log.warn("Failed to revoke IP rule for router {}: {}", ipPrefix, error));
349 +
350 + srManager.deviceService.getAvailableDevices().forEach(device -> {
351 + srManager.flowObjectiveService.forward(device.id(), fwdBuilder.remove(context));
352 + });
353 +
354 + return true;
355 + }
356 +
357 + /**
313 * Populates MPLS flow rules to all routers. 358 * Populates MPLS flow rules to all routers.
314 * 359 *
315 * @param deviceId target device ID of the switch to set the rules 360 * @param deviceId target device ID of the switch to set the rules
...@@ -471,6 +516,7 @@ public class RoutingRulePopulator { ...@@ -471,6 +516,7 @@ public class RoutingRulePopulator {
471 * that drivers can obtain other information (like Router MAC and IP). 516 * that drivers can obtain other information (like Router MAC and IP).
472 * 517 *
473 * @param deviceId the switch dpid for the router 518 * @param deviceId the switch dpid for the router
519 + * @return true if operation succeeds
474 */ 520 */
475 public boolean populateRouterMacVlanFilters(DeviceId deviceId) { 521 public boolean populateRouterMacVlanFilters(DeviceId deviceId) {
476 log.debug("Installing per-port filtering objective for untagged " 522 log.debug("Installing per-port filtering objective for untagged "
......
...@@ -153,7 +153,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -153,7 +153,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
153 protected ApplicationId appId; 153 protected ApplicationId appId;
154 protected DeviceConfiguration deviceConfiguration = null; 154 protected DeviceConfiguration deviceConfiguration = null;
155 155
156 - private DefaultRoutingHandler defaultRoutingHandler = null; 156 + protected DefaultRoutingHandler defaultRoutingHandler = null;
157 private TunnelHandler tunnelHandler = null; 157 private TunnelHandler tunnelHandler = null;
158 private PolicyHandler policyHandler = null; 158 private PolicyHandler policyHandler = null;
159 private InternalPacketProcessor processor = null; 159 private InternalPacketProcessor processor = null;
......
...@@ -46,6 +46,8 @@ import java.util.Optional; ...@@ -46,6 +46,8 @@ import java.util.Optional;
46 import java.util.Set; 46 import java.util.Set;
47 import java.util.concurrent.ConcurrentHashMap; 47 import java.util.concurrent.ConcurrentHashMap;
48 48
49 +import static com.google.common.base.Preconditions.checkNotNull;
50 +
49 /** 51 /**
50 * Segment Routing configuration component that reads the 52 * Segment Routing configuration component that reads the
51 * segment routing related configuration from Network Configuration Manager 53 * segment routing related configuration from Network Configuration Manager
...@@ -167,7 +169,6 @@ public class DeviceConfiguration implements DeviceProperties { ...@@ -167,7 +169,6 @@ public class DeviceConfiguration implements DeviceProperties {
167 } 169 }
168 } 170 }
169 }); 171 });
170 -
171 }); 172 });
172 } 173 }
173 174
...@@ -517,4 +518,38 @@ public class DeviceConfiguration implements DeviceProperties { ...@@ -517,4 +518,38 @@ public class DeviceConfiguration implements DeviceProperties {
517 cfgService.getConfig(appId, SegmentRoutingAppConfig.class); 518 cfgService.getConfig(appId, SegmentRoutingAppConfig.class);
518 return (appConfig != null) ? appConfig.suppressHost() : ImmutableSet.of(); 519 return (appConfig != null) ? appConfig.suppressHost() : ImmutableSet.of();
519 } 520 }
521 +
522 + /**
523 + * Add subnet to specific connect point.
524 + *
525 + * @param cp connect point
526 + * @param ip4Prefix subnet being added to the device
527 + */
528 + public void addSubnet(ConnectPoint cp, Ip4Prefix ip4Prefix) {
529 + checkNotNull(cp);
530 + checkNotNull(ip4Prefix);
531 + SegmentRouterInfo srinfo = deviceConfigMap.get(cp.deviceId());
532 + if (srinfo == null) {
533 + log.warn("Device {} is not configured. Abort.", cp.deviceId());
534 + return;
535 + }
536 + srinfo.subnets.put(cp.port(), ip4Prefix);
537 + }
538 +
539 + /**
540 + * Remove subnet from specific connect point.
541 + *
542 + * @param cp connect point
543 + * @param ip4Prefix subnet being removed to the device
544 + */
545 + public void removeSubnet(ConnectPoint cp, Ip4Prefix ip4Prefix) {
546 + checkNotNull(cp);
547 + checkNotNull(ip4Prefix);
548 + SegmentRouterInfo srinfo = deviceConfigMap.get(cp.deviceId());
549 + if (srinfo == null) {
550 + log.warn("Device {} is not configured. Abort.", cp.deviceId());
551 + return;
552 + }
553 + srinfo.subnets.remove(cp.port(), ip4Prefix);
554 + }
520 } 555 }
......
...@@ -13,12 +13,6 @@ ...@@ -13,12 +13,6 @@
13 }, 13 },
14 { 14 {
15 "vlan" : "222" 15 "vlan" : "222"
16 - },
17 - {
18 - "ips" : [
19 - "A.A.A.146/32", "A.A.A.147/32", "A.A.A.148/32", "A.A.A.149/32",
20 - "A.A.A.150/32", "A.A.A.151/32", "A.A.A.152/32", "A.A.A.153/32"
21 - ]
22 } 16 }
23 ] 17 ]
24 }, 18 },
...@@ -52,9 +46,6 @@ ...@@ -52,9 +46,6 @@
52 "interfaces" : [ 46 "interfaces" : [
53 { 47 {
54 "ips" : [ "10.0.2.254/24" ] 48 "ips" : [ "10.0.2.254/24" ]
55 - },
56 - {
57 - "ips" : [ "A.A.A.130/32", "A.A.A.131/32" ]
58 } 49 }
59 ] 50 ]
60 }, 51 },
......