From 76dc892491948adae5e5e62cf94448967e8d865b Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Sun, 6 Dec 2015 07:15:03 -0800 Subject: Fixes bad POM file with ONOS commit 8c68536972f63069c263635c9d9f4f31d7f3e9a2 Change-Id: I7adb5a2d3738d53dbc41db7577768b0e7ced5450 Signed-off-by: Ashlee Young --- framework/src/onos/apps/segmentrouting/pom.xml | 5 + .../org/onosproject/segmentrouting/ArpHandler.java | 26 ++- .../segmentrouting/DefaultRoutingHandler.java | 1 + .../onosproject/segmentrouting/IcmpHandler.java | 2 +- .../org/onosproject/segmentrouting/IpHandler.java | 2 +- .../segmentrouting/RoutingRulePopulator.java | 63 +++++- .../segmentrouting/SegmentRoutingManager.java | 146 +++++++++++--- .../onosproject/segmentrouting/TunnelHandler.java | 2 +- .../segmentrouting/config/DeviceConfiguration.java | 45 ++--- .../config/SegmentRoutingConfig.java | 219 +++++++++++++++------ .../grouphandler/DefaultEdgeGroupHandler.java | 6 +- .../grouphandler/DefaultGroupHandler.java | 139 +++++++++---- .../grouphandler/DefaultTransitGroupHandler.java | 6 +- .../grouphandler/PolicyGroupHandler.java | 6 +- .../grouphandler/PortNextObjectiveStoreKey.java | 77 ++++++++ .../config/SegmentRoutingConfigTest.java | 157 +++++++++++++++ 16 files changed, 722 insertions(+), 180 deletions(-) create mode 100644 framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PortNextObjectiveStoreKey.java create mode 100644 framework/src/onos/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingConfigTest.java (limited to 'framework/src/onos/apps/segmentrouting') diff --git a/framework/src/onos/apps/segmentrouting/pom.xml b/framework/src/onos/apps/segmentrouting/pom.xml index 83ae76db..d170a7ab 100644 --- a/framework/src/onos/apps/segmentrouting/pom.xml +++ b/framework/src/onos/apps/segmentrouting/pom.xml @@ -89,6 +89,11 @@ org.osgi org.osgi.core + + org.onosproject + onlab-junit + test + diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java index 2c6412cf..7f4bcb15 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ArpHandler.java @@ -107,7 +107,7 @@ public class ArpHandler { vlanId); // ARP request for router. Send ARP reply. - if (isArpReqForRouter(deviceId, arpRequest)) { + if (isArpForRouter(deviceId, arpRequest)) { Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress()); sendArpResponse(arpRequest, config.getRouterMacForAGatewayIp(targetAddress), vlanId); } else { @@ -130,7 +130,7 @@ public class ArpHandler { vlanId); // ARP reply for router. Process all pending IP packets. - if (isArpReqForRouter(deviceId, arpReply)) { + if (isArpForRouter(deviceId, arpReply)) { Ip4Address hostIpAddress = Ip4Address.valueOf(arpReply.getSenderProtocolAddress()); srManager.ipHandler.forwardPackets(deviceId, hostIpAddress); } else { @@ -141,7 +141,8 @@ public class ArpHandler { // ARP reply for unknown host, Flood in the subnet. } else { // Don't flood to non-edge ports - if (vlanId.equals(VlanId.vlanId(srManager.ASSIGNED_VLAN_NO_SUBNET))) { + if (vlanId.equals( + VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET))) { return; } removeVlanAndFlood(payload, inPort); @@ -150,14 +151,21 @@ public class ArpHandler { } - private boolean isArpReqForRouter(DeviceId deviceId, ARP arpRequest) { - Set gatewayIpAddresses = config.getPortIPs(deviceId); - if (gatewayIpAddresses != null) { - Ip4Address targetProtocolAddress = Ip4Address.valueOf(arpRequest - .getTargetProtocolAddress()); - if (gatewayIpAddresses.contains(targetProtocolAddress)) { + private boolean isArpForRouter(DeviceId deviceId, ARP arpMsg) { + Ip4Address targetProtocolAddress = Ip4Address.valueOf( + arpMsg.getTargetProtocolAddress()); + Set gatewayIpAddresses = null; + try { + if (targetProtocolAddress.equals(config.getRouterIp(deviceId))) { return true; } + gatewayIpAddresses = config.getPortIPs(deviceId); + } catch (DeviceConfigNotFoundException e) { + log.warn(e.getMessage() + " Aborting check for router IP in processing arp"); + } + if (gatewayIpAddresses != null && + gatewayIpAddresses.contains(targetProtocolAddress)) { + return true; } return false; } diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java index 99225874..e6451653 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java @@ -513,6 +513,7 @@ public class DefaultRoutingHandler { public void populatePortAddressingRules(DeviceId deviceId) { rulePopulator.populateRouterMacVlanFilters(deviceId); rulePopulator.populateRouterIpPunts(deviceId); + rulePopulator.populateArpPunts(deviceId); } /** diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java index eb3b3fd5..d1dc8ddc 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java @@ -88,10 +88,10 @@ public class IcmpHandler { (destinationAddress.equals(routerIpAddress) || gatewayIpAddresses.contains(destinationAddress))) { sendICMPResponse(ethernet, connectPoint); - // TODO: do we need to set the flow rule again ?? // ICMP for any known host } else if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) { + // TODO: known host packet should not be coming to controller - resend flows? srManager.ipHandler.forwardPackets(deviceId, destinationAddress); // ICMP for an unknown host in the subnet of the router diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java index b1682e77..d6a9dcfc 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java @@ -98,7 +98,7 @@ public class IpHandler { */ public void addToPacketBuffer(IPv4 ipPacket) { - // Better not buffer TPC packets due to out-of-order packet transfer + // Better not buffer TCP packets due to out-of-order packet transfer if (ipPacket.getProtocol() == IPv4.PROTOCOL_TCP) { return; } diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java index a07a15d2..d4aa770c 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java @@ -147,20 +147,34 @@ public class RoutingRulePopulator { TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); - sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, IpPrefix.MAX_INET_MASK_LENGTH)); sbuilder.matchEthType(Ethernet.TYPE_IPV4); + sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, IpPrefix.MAX_INET_MASK_LENGTH)); + TrafficSelector selector = sbuilder.build(); tbuilder.deferred() .setEthDst(hostMac) .setEthSrc(deviceMac) .setOutput(outPort); - TrafficTreatment treatment = tbuilder.build(); - TrafficSelector selector = sbuilder.build(); + + // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed. + // for switch pipelines that need it, provide outgoing vlan as metadata + VlanId outvlan = null; + Ip4Prefix subnet = srManager.deviceConfiguration.getPortSubnet(deviceId, outPort); + if (subnet == null) { + outvlan = VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET); + } else { + outvlan = srManager.getSubnetAssignedVlanId(deviceId, subnet); + } + TrafficSelector meta = DefaultTrafficSelector.builder() + .matchVlanId(outvlan).build(); + int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outPort, + treatment, meta); return DefaultForwardingObjective.builder() + .withSelector(selector) + .nextStep(portNextObjId) .fromApp(srManager.appId).makePermanent() - .withSelector(selector).withTreatment(treatment) .withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC); } @@ -454,7 +468,7 @@ public class RoutingRulePopulator { if (srManager.mastershipService.isLocalMaster(deviceId)) { TrafficTreatment tt = DefaultTrafficTreatment.builder() .pushVlan().setVlanId(assignedVlan).build(); - fob.setMeta(tt); + fob.withMeta(tt); } fob.permit().fromApp(srManager.appId); srManager.flowObjectiveService. @@ -510,6 +524,39 @@ public class RoutingRulePopulator { } } + /** + * Creates a forwarding objective to punt all IP packets, destined to the + * router's port IP addresses, to the controller. Note that the input + * port should not be matched on, as these packets can come from any input. + * Furthermore, these are applied only by the master instance. + * + * @param deviceId the switch dpid for the router + */ + public void populateArpPunts(DeviceId deviceId) { + if (!srManager.mastershipService.isLocalMaster(deviceId)) { + log.debug("Not installing port-IP punts - not the master for dev:{} ", + deviceId); + return; + } + + ForwardingObjective.Builder puntArp = DefaultForwardingObjective.builder(); + TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); + sbuilder.matchEthType(Ethernet.TYPE_ARP); + tbuilder.setOutput(PortNumber.CONTROLLER); + puntArp.withSelector(sbuilder.build()); + puntArp.withTreatment(tbuilder.build()); + puntArp.withFlag(Flag.VERSATILE) + .withPriority(HIGHEST_PRIORITY) + .makePermanent() + .fromApp(srManager.appId); + log.debug("Installing forwarding objective to punt ARPs"); + srManager.flowObjectiveService. + forward(deviceId, + puntArp.add(new SRObjectiveContext(deviceId, + SRObjectiveContext.ObjectiveType.FORWARDING))); + } + /** * Populates a forwarding objective to send packets that miss other high * priority Bridging Table entries to a group that contains all ports of @@ -526,6 +573,12 @@ public class RoutingRulePopulator { int nextId = srManager.getSubnetNextObjectiveId(deviceId, subnet); VlanId vlanId = srManager.getSubnetAssignedVlanId(deviceId, subnet); + if (nextId < 0 || vlanId == null) { + log.error("Cannot install subnet broadcast rule in dev:{} due" + + "to vlanId:{} or nextId:{}", vlanId, nextId); + return; + } + /* Driver should treat objective with MacAddress.NONE as the * subnet broadcast rule */ diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java index f6bf649c..62722f02 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java @@ -57,6 +57,7 @@ import org.onosproject.segmentrouting.config.SegmentRoutingConfig; import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler; import org.onosproject.segmentrouting.grouphandler.NeighborSet; import org.onosproject.segmentrouting.grouphandler.NeighborSetNextObjectiveStoreKey; +import org.onosproject.segmentrouting.grouphandler.PortNextObjectiveStoreKey; import org.onosproject.mastership.MastershipService; import org.onosproject.net.Device; import org.onosproject.net.DeviceId; @@ -97,7 +98,6 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -@SuppressWarnings("ALL") @Service @Component(immediate = true) public class SegmentRoutingManager implements SegmentRoutingService { @@ -150,21 +150,27 @@ public class SegmentRoutingManager implements SegmentRoutingService { private ScheduledExecutorService executorService = Executors .newScheduledThreadPool(1); + @SuppressWarnings("unused") private static ScheduledFuture eventHandlerFuture = null; + @SuppressWarnings("rawtypes") private ConcurrentLinkedQueue eventQueue = new ConcurrentLinkedQueue(); private Map groupHandlerMap = new ConcurrentHashMap(); // Per device next objective ID store with (device id + neighbor set) as key private EventuallyConsistentMap nsNextObjStore = null; + // Per device next objective ID store with (device id + subnet) as key private EventuallyConsistentMap subnetNextObjStore = null; - private EventuallyConsistentMap tunnelStore = null; - private EventuallyConsistentMap policyStore = null; + // Per device next objective ID store with (device id + port) as key + private EventuallyConsistentMap + portNextObjStore = null; // Per device, per-subnet assigned-vlans store, with (device id + subnet // IPv4 prefix) as key private EventuallyConsistentMap subnetVidStore = null; + private EventuallyConsistentMap tunnelStore = null; + private EventuallyConsistentMap policyStore = null; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected StorageService storageService; @@ -175,6 +181,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { private final InternalConfigListener cfgListener = new InternalConfigListener(this); + @SuppressWarnings({ "unchecked", "rawtypes" }) private final ConfigFactory cfgFactory = new ConfigFactory(SubjectFactories.DEVICE_SUBJECT_FACTORY, SegmentRoutingConfig.class, @@ -185,7 +192,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { } }; - private final HostListener hostListener = new InternalHostListener(); + private final InternalHostListener hostListener = new InternalHostListener(); private Object threadSchedulerLock = new Object(); private static int numOfEventsQueued = 0; @@ -228,7 +235,6 @@ public class SegmentRoutingManager implements SegmentRoutingService { log.debug("Creating EC map nsnextobjectivestore"); EventuallyConsistentMapBuilder nsNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder(); - nsNextObjStore = nsNextObjMapBuilder .withName("nsnextobjectivestore") .withSerializer(kryoBuilder) @@ -239,16 +245,23 @@ public class SegmentRoutingManager implements SegmentRoutingService { log.debug("Creating EC map subnetnextobjectivestore"); EventuallyConsistentMapBuilder subnetNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder(); - subnetNextObjStore = subnetNextObjMapBuilder .withName("subnetnextobjectivestore") .withSerializer(kryoBuilder) .withTimestampProvider((k, v) -> new WallClockTimestamp()) .build(); + log.debug("Creating EC map subnetnextobjectivestore"); + EventuallyConsistentMapBuilder + portNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder(); + portNextObjStore = portNextObjMapBuilder + .withName("portnextobjectivestore") + .withSerializer(kryoBuilder) + .withTimestampProvider((k, v) -> new WallClockTimestamp()) + .build(); + EventuallyConsistentMapBuilder tunnelMapBuilder = storageService.eventuallyConsistentMapBuilder(); - tunnelStore = tunnelMapBuilder .withName("tunnelstore") .withSerializer(kryoBuilder) @@ -257,7 +270,6 @@ public class SegmentRoutingManager implements SegmentRoutingService { EventuallyConsistentMapBuilder policyMapBuilder = storageService.eventuallyConsistentMapBuilder(); - policyStore = policyMapBuilder .withName("policystore") .withSerializer(kryoBuilder) @@ -266,7 +278,6 @@ public class SegmentRoutingManager implements SegmentRoutingService { EventuallyConsistentMapBuilder subnetVidStoreMapBuilder = storageService.eventuallyConsistentMapBuilder(); - subnetVidStore = subnetVidStoreMapBuilder .withName("subnetvidstore") .withSerializer(kryoBuilder) @@ -425,8 +436,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { /** * Returns the next objective ID for the given NeighborSet. * If the nextObjective does not exist, a new one is created and - * it's id is returned. - * TODO move the side-effect creation of a Next Objective into a new method + * its id is returned. * * @param deviceId Device ID * @param ns NegighborSet @@ -441,18 +451,19 @@ public class SegmentRoutingManager implements SegmentRoutingService { return groupHandlerMap .get(deviceId).getNextObjectiveId(ns, meta); } else { - log.warn("getNextObjectiveId query in device {} not found", deviceId); + log.warn("getNextObjectiveId query - groupHandler for device {} " + + "not found", deviceId); return -1; } } /** - * Returns the next objective ID for the Subnet given. If the nextObjectiveID does not exist, - * a new one is created and returned. + * Returns the next objective ID for the given subnet prefix. It is expected + * that the next-objective has been pre-created from configuration. * * @param deviceId Device ID * @param prefix Subnet - * @return next objective ID + * @return next objective ID or -1 if it was not found */ public int getSubnetNextObjectiveId(DeviceId deviceId, IpPrefix prefix) { if (groupHandlerMap.get(deviceId) != null) { @@ -460,7 +471,33 @@ public class SegmentRoutingManager implements SegmentRoutingService { return groupHandlerMap .get(deviceId).getSubnetNextObjectiveId(prefix); } else { - log.warn("getSubnetNextObjectiveId query in device {} not found", deviceId); + log.warn("getSubnetNextObjectiveId query - groupHandler for " + + "device {} not found", deviceId); + return -1; + } + } + + /** + * Returns the next objective ID for the given portNumber, given the treatment. + * There could be multiple different treatments to the same outport, which + * would result in different objectives. If the next object + * does not exist, a new one is created and its id is returned. + * + * @param deviceId Device ID + * @param portNum port number on device for which NextObjective is queried + * @param treatment the actions to apply on the packets (should include outport) + * @param meta metadata passed into the creation of a Next Objective if necessary + * @return next objective ID or -1 if it was not found + */ + public int getPortNextObjectiveId(DeviceId deviceId, PortNumber portNum, + TrafficTreatment treatment, + TrafficSelector meta) { + DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId); + if (ghdlr != null) { + return ghdlr.getPortNextObjectiveId(portNum, treatment, meta); + } else { + log.warn("getPortNextObjectiveId query - groupHandler for device {}" + + " not found", deviceId); return -1; } } @@ -475,7 +512,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { InboundPacket pkt = context.inPacket(); Ethernet ethernet = pkt.parsed(); - + log.trace("Rcvd pktin: {}", ethernet); if (ethernet.getEtherType() == Ethernet.TYPE_ARP) { arpHandler.processPacketIn(pkt); } else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) { @@ -517,6 +554,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { } } + @SuppressWarnings("rawtypes") private void scheduleEventHandlerIfNotScheduled(Event event) { synchronized (threadSchedulerLock) { eventQueue.add(event); @@ -539,6 +577,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { public void run() { try { while (true) { + @SuppressWarnings("rawtypes") Event event = null; synchronized (threadSchedulerLock) { if (!eventQueue.isEmpty()) { @@ -647,7 +686,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { linkService, flowObjectiveService, nsNextObjStore, - subnetNextObjStore); + subnetNextObjStore, + portNextObjStore); } catch (DeviceConfigNotFoundException e) { log.warn(e.getMessage() + " Aborting processDeviceAdded."); return; @@ -658,6 +698,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { // port addressing rules to the driver as well irrespective of whether // this instance is the master or not. defaultRoutingHandler.populatePortAddressingRules(device.id()); + hostListener.readInitialHosts(); } if (mastershipService.isLocalMaster(device.id())) { DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id()); @@ -713,7 +754,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { linkService, flowObjectiveService, nsNextObjStore, - subnetNextObjStore); + subnetNextObjStore, + portNextObjStore); } catch (DeviceConfigNotFoundException e) { log.warn(e.getMessage() + " Aborting configureNetwork."); return; @@ -725,6 +767,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { // port addressing rules to the driver as well, irrespective of whether // this instance is the master or not. defaultRoutingHandler.populatePortAddressingRules(device.id()); + hostListener.readInitialHosts(); } if (mastershipService.isLocalMaster(device.id())) { DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id()); @@ -751,22 +794,66 @@ public class SegmentRoutingManager implements SegmentRoutingService { } } + // TODO Move bridging table population to a separate class private class InternalHostListener implements HostListener { + private void readInitialHosts() { + hostService.getHosts().forEach(host -> { + MacAddress mac = host.mac(); + VlanId vlanId = host.vlan(); + DeviceId deviceId = host.location().deviceId(); + PortNumber port = host.location().port(); + Set ips = host.ipAddresses(); + log.debug("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port); + + // Populate bridging table entry + ForwardingObjective.Builder fob = + getForwardingObjectiveBuilder(deviceId, mac, vlanId, port); + flowObjectiveService.forward(deviceId, fob.add( + new BridgingTableObjectiveContext(mac, vlanId) + )); + + // Populate IP table entry + ips.forEach(ip -> { + if (ip.isIp4()) { + routingRulePopulator.populateIpRuleForHost( + deviceId, ip.getIp4Address(), mac, port); + } + }); + }); + } + private ForwardingObjective.Builder getForwardingObjectiveBuilder( - MacAddress mac, VlanId vlanId, PortNumber port) { + DeviceId deviceId, MacAddress mac, VlanId vlanId, + PortNumber outport) { + // match rule TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); sbuilder.matchEthDst(mac); sbuilder.matchVlanId(vlanId); TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); - // TODO Move popVlan from flow action to group action tbuilder.immediate().popVlan(); - tbuilder.immediate().setOutput(port); + tbuilder.immediate().setOutput(outport); + + // for switch pipelines that need it, provide outgoing vlan as metadata + VlanId outvlan = null; + Ip4Prefix subnet = deviceConfiguration.getPortSubnet(deviceId, outport); + if (subnet == null) { + outvlan = VlanId.vlanId(ASSIGNED_VLAN_NO_SUBNET); + } else { + outvlan = getSubnetAssignedVlanId(deviceId, subnet); + } + TrafficSelector meta = DefaultTrafficSelector.builder() + .matchVlanId(outvlan).build(); + + // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed. + int portNextObjId = getPortNextObjectiveId(deviceId, outport, + tbuilder.build(), + meta); return DefaultForwardingObjective.builder() .withFlag(ForwardingObjective.Flag.SPECIFIC) .withSelector(sbuilder.build()) - .withTreatment(tbuilder.build()) + .nextStep(portNextObjId) .withPriority(100) .fromApp(appId) .makePermanent(); @@ -778,12 +865,13 @@ public class SegmentRoutingManager implements SegmentRoutingService { DeviceId deviceId = event.subject().location().deviceId(); PortNumber port = event.subject().location().port(); Set ips = event.subject().ipAddresses(); - log.debug("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port); + log.info("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port); - // TODO Move bridging table population to a separate class // Populate bridging table entry + log.debug("Populate L2 table entry for host {} at {}:{}", + mac, deviceId, port); ForwardingObjective.Builder fob = - getForwardingObjectiveBuilder(mac, vlanId, port); + getForwardingObjectiveBuilder(deviceId, mac, vlanId, port); flowObjectiveService.forward(deviceId, fob.add( new BridgingTableObjectiveContext(mac, vlanId) )); @@ -807,7 +895,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { // Revoke bridging table entry ForwardingObjective.Builder fob = - getForwardingObjectiveBuilder(mac, vlanId, port); + getForwardingObjectiveBuilder(deviceId, mac, vlanId, port); flowObjectiveService.forward(deviceId, fob.remove( new BridgingTableObjectiveContext(mac, vlanId) )); @@ -835,7 +923,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { // Revoke previous bridging table entry ForwardingObjective.Builder prevFob = - getForwardingObjectiveBuilder(mac, vlanId, prevPort); + getForwardingObjectiveBuilder(prevDeviceId, mac, vlanId, prevPort); flowObjectiveService.forward(prevDeviceId, prevFob.remove( new BridgingTableObjectiveContext(mac, vlanId) )); @@ -850,7 +938,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { // Populate new bridging table entry ForwardingObjective.Builder newFob = - getForwardingObjectiveBuilder(mac, vlanId, prevPort); + getForwardingObjectiveBuilder(newDeviceId, mac, vlanId, newPort); flowObjectiveService.forward(newDeviceId, newFob.add( new BridgingTableObjectiveContext(mac, vlanId) )); diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java index b86adada..5a82e712 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java @@ -158,7 +158,7 @@ public class TunnelHandler { private int createGroupsForTunnel(Tunnel tunnel) { - List portNumbers; + Set portNumbers; final int groupError = -1; DeviceId deviceId = config.getDeviceId(tunnel.labelIds().get(0)); diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java index 0ad00679..dbac596d 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java @@ -16,7 +16,6 @@ package org.onosproject.segmentrouting.config; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; import org.onlab.packet.Ip4Address; import org.onlab.packet.Ip4Prefix; import org.onlab.packet.MacAddress; @@ -26,7 +25,6 @@ import org.onosproject.incubator.net.intf.Interface; import org.onosproject.net.ConnectPoint; import org.onosproject.net.config.NetworkConfigRegistry; import org.onosproject.net.host.InterfaceIpAddress; -import org.onosproject.segmentrouting.config.SegmentRoutingConfig.AdjacencySid; import org.onosproject.net.DeviceId; import org.onosproject.net.PortNumber; import org.slf4j.Logger; @@ -34,6 +32,7 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -60,7 +59,7 @@ public class DeviceConfiguration implements DeviceProperties { boolean isEdge; HashMap gatewayIps; HashMap subnets; - List adjacencySids; + Map> adjacencySids; public SegmentRouterInfo() { this.gatewayIps = new HashMap<>(); @@ -83,11 +82,11 @@ public class DeviceConfiguration implements DeviceProperties { cfgService.getConfig(subject, SegmentRoutingConfig.class); SegmentRouterInfo info = new SegmentRouterInfo(); info.deviceId = subject; - info.nodeSid = config.getSid(); - info.ip = config.getIp(); - info.mac = config.getMac(); + info.nodeSid = config.nodeSid(); + info.ip = config.routerIp(); + info.mac = config.routerMac(); info.isEdge = config.isEdgeRouter(); - info.adjacencySids = config.getAdjacencySids(); + info.adjacencySids = config.adjacencySids(); this.deviceConfigMap.put(info.deviceId, info); this.allSegmentIds.add(info.nodeSid); @@ -410,19 +409,13 @@ public class DeviceConfiguration implements DeviceProperties { * * @param deviceId device identification of the router * @param sid adjacency Sid - * @return list of port numbers + * @return set of port numbers */ - public List getPortsForAdjacencySid(DeviceId deviceId, int sid) { + public Set getPortsForAdjacencySid(DeviceId deviceId, int sid) { SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId); - if (srinfo != null) { - for (AdjacencySid asid : srinfo.adjacencySids) { - if (asid.getAsid() == sid) { - return asid.getPorts(); - } - } - } - - return Lists.newArrayList(); + return srinfo != null ? + ImmutableSet.copyOf(srinfo.adjacencySids.get(sid)) : + ImmutableSet.copyOf(new HashSet<>()); } /** @@ -435,20 +428,6 @@ public class DeviceConfiguration implements DeviceProperties { */ public boolean isAdjacencySid(DeviceId deviceId, int sid) { SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId); - if (srinfo != null) { - if (srinfo.adjacencySids.isEmpty()) { - return false; - } else { - for (AdjacencySid asid: - srinfo.adjacencySids) { - if (asid.getAsid() == sid) { - return true; - } - } - return false; - } - } - - return false; + return srinfo != null && srinfo.adjacencySids.containsKey(sid); } } \ No newline at end of file diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java index 6dc3f0db..f788925c 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java @@ -16,113 +16,210 @@ package org.onosproject.segmentrouting.config; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableMap; import org.onlab.packet.Ip4Address; import org.onlab.packet.MacAddress; import org.onosproject.net.DeviceId; import org.onosproject.net.config.Config; -import org.onosproject.net.config.basics.BasicElementConfig; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.Optional; +import java.util.Set; /** * Configuration object for Segment Routing Application. */ public class SegmentRoutingConfig extends Config { - private static final String NAME = "name"; - private static final String IP = "routerIp"; - private static final String MAC = "routerMac"; - private static final String SID = "nodeSid"; - private static final String EDGE = "isEdgeRouter"; - private static final String ADJSID = "adjacencySids"; - - public Optional getName() { + public static final String NAME = "name"; + public static final String IP = "routerIp"; + public static final String MAC = "routerMac"; + public static final String SID = "nodeSid"; + public static final String EDGE = "isEdgeRouter"; + public static final String ADJSIDS = "adjacencySids"; + public static final String ADJSID = "adjSid"; + public static final String PORTS = "ports"; + + @Override + public boolean isValid() { + return hasOnlyFields(NAME, IP, MAC, SID, EDGE, ADJSIDS, ADJSID, PORTS) && + this.name() != null && + this.routerIp() != null && + this.routerMac() != null && + this.nodeSid() != -1 && + this.isEdgeRouter() != null && + this.adjacencySids() != null; + } + + /** + * Gets the name of the router. + * + * @return Optional name of the router. May be empty if not configured. + */ + public Optional name() { String name = get(NAME, null); return name != null ? Optional.of(name) : Optional.empty(); } - public BasicElementConfig setName(String name) { - return (BasicElementConfig) setOrClear(NAME, name); + /** + * Sets the name of the router. + * + * @param name name of the router. + * @return the config of the router. + */ + public SegmentRoutingConfig setName(String name) { + return (SegmentRoutingConfig) setOrClear(NAME, name); } - public Ip4Address getIp() { + /** + * Gets the IP address of the router. + * + * @return IP address of the router. Or null if not configured. + */ + public Ip4Address routerIp() { String ip = get(IP, null); return ip != null ? Ip4Address.valueOf(ip) : null; } - public BasicElementConfig setIp(String ip) { - return (BasicElementConfig) setOrClear(IP, ip); + /** + * Sets the IP address of the router. + * + * @param ip IP address of the router. + * @return the config of the router. + */ + public SegmentRoutingConfig setRouterIp(String ip) { + return (SegmentRoutingConfig) setOrClear(IP, ip); } - public MacAddress getMac() { + /** + * Gets the MAC address of the router. + * + * @return MAC address of the router. Or null if not configured. + */ + public MacAddress routerMac() { String mac = get(MAC, null); return mac != null ? MacAddress.valueOf(mac) : null; } - public BasicElementConfig setMac(String mac) { - return (BasicElementConfig) setOrClear(MAC, mac); + /** + * Sets the MAC address of the router. + * + * @param mac MAC address of the router. + * @return the config of the router. + */ + public SegmentRoutingConfig setRouterMac(String mac) { + return (SegmentRoutingConfig) setOrClear(MAC, mac); } - public int getSid() { + /** + * Gets the node SID of the router. + * + * @return node SID of the router. Or -1 if not configured. + */ + public int nodeSid() { return get(SID, -1); } - public BasicElementConfig setSid(int sid) { - return (BasicElementConfig) setOrClear(SID, sid); + /** + * Sets the node SID of the router. + * + * @param sid node SID of the router. + * @return the config of the router. + */ + public SegmentRoutingConfig setNodeSid(int sid) { + return (SegmentRoutingConfig) setOrClear(SID, sid); } - public boolean isEdgeRouter() { - return get(EDGE, false); + /** + * Checks if the router is an edge router. + * + * @return true if the router is an edge router. + * false if the router is not an edge router. + * null if the value is not configured. + */ + public Boolean isEdgeRouter() { + String isEdgeRouter = get(EDGE, null); + return isEdgeRouter != null ? + Boolean.valueOf(isEdgeRouter) : + null; } - public BasicElementConfig setEdgeRouter(boolean isEdgeRouter) { - return (BasicElementConfig) setOrClear(EDGE, isEdgeRouter); + /** + * Specifies if the router is an edge router. + * + * @param isEdgeRouter true if the router is an edge router. + * @return the config of the router. + */ + public SegmentRoutingConfig setIsEdgeRouter(boolean isEdgeRouter) { + return (SegmentRoutingConfig) setOrClear(EDGE, isEdgeRouter); } - public List getAdjacencySids() { - ArrayList adjacencySids = new ArrayList<>(); - - if (!object.has(ADJSID)) { - return adjacencySids; + /** + * Gets the adjacency SIDs of the router. + * + * @return adjacency SIDs of the router. Or null if not configured. + */ + public Map> adjacencySids() { + if (!object.has(ADJSIDS)) { + return null; } - ArrayNode adjacencySidNodes = (ArrayNode) object.path(ADJSID); - adjacencySidNodes.forEach(adjacencySidNode -> { - int asid = adjacencySidNode.path(AdjacencySid.ASID).asInt(); - - ArrayList ports = new ArrayList(); - ArrayNode portsNodes = (ArrayNode) adjacencySidNode.path(AdjacencySid.PORTS); - portsNodes.forEach(portNode -> { - ports.add(portNode.asInt()); - }); - - AdjacencySid adjacencySid = new AdjacencySid(asid, ports); - adjacencySids.add(adjacencySid); - }); + Map> adjacencySids = new HashMap<>(); + ArrayNode adjacencySidsNode = (ArrayNode) object.path(ADJSIDS); + for (JsonNode adjacencySidNode : adjacencySidsNode) { + int asid = adjacencySidNode.path(ADJSID).asInt(-1); + if (asid == -1) { + return null; + } + + HashSet ports = new HashSet<>(); + ArrayNode portsNode = (ArrayNode) adjacencySidNode.path(PORTS); + for (JsonNode portNode : portsNode) { + int port = portNode.asInt(-1); + if (port == -1) { + return null; + } + ports.add(port); + } + adjacencySids.put(asid, ports); + } - return adjacencySids; + return ImmutableMap.copyOf(adjacencySids); } - public class AdjacencySid { - private static final String ASID = "adjSid"; - private static final String PORTS = "ports"; - - int asid; - List ports; - - public AdjacencySid(int asid, List ports) { - this.asid = asid; - this.ports = ports; - } + /** + * Sets the adjacency SIDs of the router. + * + * @param adjacencySids adjacency SIDs of the router. + * @return the config of the router. + */ + public SegmentRoutingConfig setAdjacencySids(Map> adjacencySids) { + if (adjacencySids == null) { + object.remove(ADJSIDS); + } else { + ArrayNode adjacencySidsNode = mapper.createArrayNode(); + + adjacencySids.forEach((sid, ports) -> { + ObjectNode adjacencySidNode = mapper.createObjectNode(); + + adjacencySidNode.put(ADJSID, sid); + + ArrayNode portsNode = mapper.createArrayNode(); + ports.forEach(port -> { + portsNode.add(port.toString()); + }); + adjacencySidNode.set(PORTS, portsNode); + + adjacencySidsNode.add(adjacencySidNode); + }); - public int getAsid() { - return asid; + object.set(ADJSIDS, adjacencySidsNode); } - public List getPorts() { - return ports; - } + return this; } } \ No newline at end of file diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java index 6b6d960a..32c53654 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java @@ -56,9 +56,11 @@ public class DefaultEdgeGroupHandler extends DefaultGroupHandler { NeighborSetNextObjectiveStoreKey, Integer> nsNextObjStore, EventuallyConsistentMap subnetNextObjStore) { + Integer> subnetNextObjStore, + EventuallyConsistentMap portNextObjStore) { super(deviceId, appId, config, linkService, flowObjService, - nsNextObjStore, subnetNextObjStore); + nsNextObjStore, subnetNextObjStore, portNextObjStore); } @Override diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java index e792bf66..bc394b84 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java @@ -80,6 +80,8 @@ public class DefaultGroupHandler { NeighborSetNextObjectiveStoreKey, Integer> nsNextObjStore = null; protected EventuallyConsistentMap< SubnetNextObjectiveStoreKey, Integer> subnetNextObjStore = null; + protected EventuallyConsistentMap< + PortNextObjectiveStoreKey, Integer> portNextObjStore = null; protected KryoNamespace.Builder kryo = new KryoNamespace.Builder() .register(URI.class).register(HashSet.class) @@ -93,11 +95,12 @@ public class DefaultGroupHandler { DeviceProperties config, LinkService linkService, FlowObjectiveService flowObjService, - EventuallyConsistentMap< - NeighborSetNextObjectiveStoreKey, + EventuallyConsistentMap nsNextObjStore, EventuallyConsistentMap subnetNextObjStore) { + Integer> subnetNextObjStore, + EventuallyConsistentMap portNextObjStore) { this.deviceId = checkNotNull(deviceId); this.appId = checkNotNull(appId); this.deviceConfig = checkNotNull(config); @@ -114,6 +117,7 @@ public class DefaultGroupHandler { this.flowObjectiveService = flowObjService; this.nsNextObjStore = nsNextObjStore; this.subnetNextObjStore = subnetNextObjStore; + this.portNextObjStore = portNextObjStore; populateNeighborMaps(); } @@ -133,30 +137,34 @@ public class DefaultGroupHandler { * @throws DeviceConfigNotFoundException if the device configuration is not found * @return default group handler type */ - public static DefaultGroupHandler createGroupHandler(DeviceId deviceId, - ApplicationId appId, - DeviceProperties config, - LinkService linkService, - FlowObjectiveService flowObjService, - EventuallyConsistentMap< - NeighborSetNextObjectiveStoreKey, - Integer> nsNextObjStore, - EventuallyConsistentMap subnetNextObjStore) - throws DeviceConfigNotFoundException { + public static DefaultGroupHandler createGroupHandler( + DeviceId deviceId, + ApplicationId appId, + DeviceProperties config, + LinkService linkService, + FlowObjectiveService flowObjService, + EventuallyConsistentMap nsNextObjStore, + EventuallyConsistentMap subnetNextObjStore, + EventuallyConsistentMap portNextObjStore) + throws DeviceConfigNotFoundException { // handle possible exception in the caller if (config.isEdgeDevice(deviceId)) { return new DefaultEdgeGroupHandler(deviceId, appId, config, linkService, flowObjService, nsNextObjStore, - subnetNextObjStore); + subnetNextObjStore, + portNextObjStore); } else { return new DefaultTransitGroupHandler(deviceId, appId, config, linkService, flowObjService, nsNextObjStore, - subnetNextObjStore); + subnetNextObjStore, + portNextObjStore); } } @@ -231,25 +239,21 @@ public class DefaultGroupHandler { Integer nextId = nsNextObjStore. get(new NeighborSetNextObjectiveStoreKey(deviceId, ns)); - if (nextId != null) { + if (nextId != null && isMaster) { NextObjective.Builder nextObjBuilder = DefaultNextObjective .builder().withId(nextId) .withType(NextObjective.Type.HASHED).fromApp(appId); nextObjBuilder.addTreatment(tBuilder.build()); - log.info("**linkUp in device {}: Adding Bucket " - + "with Port {} to next object id {} and amIMaster:{}", + + "with Port {} to next object id {}", deviceId, newLink.src().port(), - nextId, isMaster); - - if (isMaster) { - NextObjective nextObjective = nextObjBuilder. - addToExisting(new SRNextObjectiveContext(deviceId)); - flowObjectiveService.next(deviceId, nextObjective); - } - } else { + nextId); + NextObjective nextObjective = nextObjBuilder. + addToExisting(new SRNextObjectiveContext(deviceId)); + flowObjectiveService.next(deviceId, nextObjective); + } else if (isMaster) { log.warn("linkUp in device {}, but global store has no record " + "for neighbor-set {}", deviceId, ns); } @@ -331,8 +335,8 @@ public class DefaultGroupHandler { } /** - * Returns the next objective associated with the neighborset. - * If there is no next objective for this neighborset, this API + * Returns the next objective of type hashed associated with the neighborset. + * If there is no next objective for this neighborset, this method * would create a next objective and return. Optionally metadata can be * passed in for the creation of the next objective. * @@ -372,9 +376,10 @@ public class DefaultGroupHandler { } /** - * Returns the next objective associated with the subnet. - * If there is no next objective for this subnet, this API - * would create a next objective and return. + * Returns the next objective of type broadcast associated with the subnet, + * or -1 if no such objective exists. Note that this method does NOT create + * the next objective as a side-effect. It is expected that is objective is + * created at startup from network configuration. * * @param prefix subnet information * @return int if found or -1 @@ -386,6 +391,38 @@ public class DefaultGroupHandler { return (nextId != null) ? nextId : -1; } + /** + * Returns the next objective of type simple associated with the port on the + * device, given the treatment. Different treatments to the same port result + * in different next objectives. If no such objective exists, this method + * creates one and returns the id. Optionally metadata can be passed in for + * the creation of the objective. + * + * @param portNum the port number for the simple next objective + * @param treatment the actions to apply on the packets (should include outport) + * @param meta optional metadata passed into the creation of the next objective + * @return int if found or created, -1 if there are errors during the + * creation of the next objective. + */ + public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment, + TrafficSelector meta) { + Integer nextId = portNextObjStore. + get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment)); + if (nextId == null) { + log.trace("getPortNextObjectiveId in device{}: Next objective id " + + "not found for {} and {} creating", deviceId, portNum); + createGroupFromPort(portNum, treatment, meta); + nextId = portNextObjStore.get( + new PortNextObjectiveStoreKey(deviceId, portNum, treatment)); + if (nextId == null) { + log.warn("getPortNextObjectiveId: unable to create next obj" + + "for dev:{} port{}", deviceId, portNum); + return -1; + } + } + return nextId; + } + /** * Checks if the next objective ID (group) for the neighbor set exists or not. * @@ -561,7 +598,7 @@ public class DefaultGroupHandler { } } if (meta != null) { - nextObjBuilder.setMeta(meta); + nextObjBuilder.withMeta(meta); } NextObjective nextObj = nextObjBuilder. add(new SRNextObjectiveContext(deviceId)); @@ -574,7 +611,10 @@ public class DefaultGroupHandler { } } - + /** + * Creates broadcast groups for all ports in the same configured subnet. + * + */ public void createGroupsFromSubnetConfig() { Map> subnetPortMap = this.deviceConfig.getSubnetPortsMap(this.deviceId); @@ -612,6 +652,37 @@ public class DefaultGroupHandler { }); } + + /** + * Create simple next objective for a single port. The treatments can include + * all outgoing actions that need to happen on the packet. + * + * @param portNum the outgoing port on the device + * @param treatment the actions to apply on the packets (should include outport) + * @param meta optional data to pass to the driver + */ + public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment, + TrafficSelector meta) { + int nextId = flowObjectiveService.allocateNextId(); + PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey( + deviceId, portNum, treatment); + + NextObjective.Builder nextObjBuilder = DefaultNextObjective + .builder().withId(nextId) + .withType(NextObjective.Type.SIMPLE) + .addTreatment(treatment) + .fromApp(appId) + .withMeta(meta); + + NextObjective nextObj = nextObjBuilder.add(); + flowObjectiveService.next(deviceId, nextObj); + log.debug("createGroupFromPort: Submited next objective {} in device {} " + + "for port {}", nextId, deviceId, portNum); + + portNextObjStore.put(key, nextId); + } + + public GroupKey getGroupKey(Object obj) { return new DefaultGroupKey(kryo.build().serialize(obj)); } diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java index 14d77ba6..7a43e73d 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java @@ -50,9 +50,11 @@ public class DefaultTransitGroupHandler extends DefaultGroupHandler { NeighborSetNextObjectiveStoreKey, Integer> nsNextObjStore, EventuallyConsistentMap subnetNextObjStore) { + Integer> subnetNextObjStore, + EventuallyConsistentMap portNextObjStore) { super(deviceId, appId, config, linkService, flowObjService, - nsNextObjStore, subnetNextObjStore); + nsNextObjStore, subnetNextObjStore, portNextObjStore); } @Override diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java index 55142078..ef143dc7 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java @@ -68,9 +68,11 @@ public class PolicyGroupHandler extends DefaultGroupHandler { EventuallyConsistentMap nsNextObjStore, EventuallyConsistentMap subnetNextObjStore) { + Integer> subnetNextObjStore, + EventuallyConsistentMap portNextObjStore) { super(deviceId, appId, config, linkService, flowObjService, - nsNextObjStore, subnetNextObjStore); + nsNextObjStore, subnetNextObjStore, portNextObjStore); } public PolicyGroupIdentifier createPolicyGroupChain(String id, diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PortNextObjectiveStoreKey.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PortNextObjectiveStoreKey.java new file mode 100644 index 00000000..5555565c --- /dev/null +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/PortNextObjectiveStoreKey.java @@ -0,0 +1,77 @@ +package org.onosproject.segmentrouting.grouphandler; + +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.TrafficTreatment; + +import java.util.Objects; + +/** + * Class definition of Key for Device/Port to NextObjective store. Since there + * can be multiple next objectives to the same physical port, we differentiate + * between them by including the treatment in the key. + */ +public class PortNextObjectiveStoreKey { + private final DeviceId deviceId; + private final PortNumber portNum; + private final TrafficTreatment treatment; + + public PortNextObjectiveStoreKey(DeviceId deviceId, PortNumber portNum, + TrafficTreatment treatment) { + this.deviceId = deviceId; + this.portNum = portNum; + this.treatment = treatment; + } + + /** + * Gets device id in this PortNextObjectiveStoreKey. + * + * @return device id + */ + public DeviceId deviceId() { + return deviceId; + } + + /** + * Gets port information in this PortNextObjectiveStoreKey. + * + * @return port information + */ + public PortNumber portNumber() { + return portNum; + } + + /** + * Gets treatment information in this PortNextObjectiveStoreKey. + * + * @return treatment information + */ + public TrafficTreatment treatment() { + return treatment; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof PortNextObjectiveStoreKey)) { + return false; + } + PortNextObjectiveStoreKey that = + (PortNextObjectiveStoreKey) o; + return (Objects.equals(this.deviceId, that.deviceId) && + Objects.equals(this.portNum, that.portNum) && + Objects.equals(this.treatment, that.treatment)); + } + + @Override + public int hashCode() { + return Objects.hash(deviceId, portNum, treatment); + } + + @Override + public String toString() { + return "Device: " + deviceId + " Port: " + portNum + " Treatment: " + treatment; + } +} diff --git a/framework/src/onos/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingConfigTest.java b/framework/src/onos/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingConfigTest.java new file mode 100644 index 00000000..3e5daa5b --- /dev/null +++ b/framework/src/onos/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingConfigTest.java @@ -0,0 +1,157 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.segmentrouting.config; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onosproject.net.DeviceId; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.ConfigApplyDelegate; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertTrue; + +/** + * Tests for class {@link SegmentRoutingConfig}. + */ +public class SegmentRoutingConfigTest { + private SegmentRoutingConfig config; + private Map> adjacencySids1; + private Map> adjacencySids2; + + @Before + public void setUp() throws Exception { + String jsonString = "{" + + "\"name\" : \"Leaf-R1\"," + + "\"nodeSid\" : 101," + + "\"routerIp\" : \"10.0.1.254\"," + + "\"routerMac\" : \"00:00:00:00:01:80\"," + + "\"isEdgeRouter\" : true," + + "\"adjacencySids\" : [" + + " { \"adjSid\" : 100, \"ports\" : [2, 3] }," + + " { \"adjSid\" : 200, \"ports\" : [4, 5] }" + + "]}"; + + adjacencySids1 = new HashMap<>(); + Set ports1 = new HashSet<>(); + ports1.add(2); + ports1.add(3); + adjacencySids1.put(100, ports1); + Set ports2 = new HashSet<>(); + ports2.add(4); + ports2.add(5); + adjacencySids1.put(200, ports2); + + adjacencySids2 = new HashMap<>(); + Set ports3 = new HashSet<>(); + ports3.add(6); + adjacencySids2.put(300, ports3); + + DeviceId subject = DeviceId.deviceId("of:0000000000000001"); + String key = "org.onosproject.segmentrouting"; + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = mapper.readTree(jsonString); + ConfigApplyDelegate delegate = new MockDelegate(); + + config = new SegmentRoutingConfig(); + config.init(subject, key, jsonNode, mapper, delegate); + } + + @Test + public void testName() throws Exception { + assertTrue(config.name().isPresent()); + assertThat(config.name().get(), is("Leaf-R1")); + } + + @Test + public void testSetName() throws Exception { + config.setName("Spine-R1"); + assertTrue(config.name().isPresent()); + assertThat(config.name().get(), is("Spine-R1")); + } + + @Test + public void testRouterIp() throws Exception { + assertThat(config.routerIp(), is(IpAddress.valueOf("10.0.1.254"))); + } + + @Test + public void testSetRouterIp() throws Exception { + config.setRouterIp("10.0.2.254"); + assertThat(config.routerIp(), is(IpAddress.valueOf("10.0.2.254"))); + } + + @Test + public void testRouterMac() throws Exception { + assertThat(config.routerMac(), is(MacAddress.valueOf("00:00:00:00:01:80"))); + } + + @Test + public void testSetRouterMac() throws Exception { + config.setRouterMac("00:00:00:00:02:80"); + assertThat(config.routerMac(), is(MacAddress.valueOf("00:00:00:00:02:80"))); + } + + @Test + public void testNodeSid() throws Exception { + assertThat(config.nodeSid(), is(101)); + } + + @Test + public void testSetNodeSid() throws Exception { + config.setNodeSid(200); + assertThat(config.nodeSid(), is(200)); + } + + @Test + public void testIsEdgeRouter() throws Exception { + assertThat(config.isEdgeRouter(), is(true)); + } + + @Test + public void testSetIsEdgeRouter() throws Exception { + config.setIsEdgeRouter(false); + assertThat(config.isEdgeRouter(), is(false)); + } + + @Test + public void testAdjacencySids() throws Exception { + assertThat(config.adjacencySids(), is(adjacencySids1)); + } + + @Test + public void testSetAdjacencySids() throws Exception { + config.setAdjacencySids(adjacencySids2); + assertThat(config.adjacencySids(), is(adjacencySids2)); + } + + private class MockDelegate implements ConfigApplyDelegate { + @Override + public void onApply(Config config) { + } + } +} \ No newline at end of file -- cgit 1.2.3-korg