diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-10-09 18:32:44 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-10-09 18:32:44 -0700 |
commit | 6a07d2d622eaa06953f3353e39c080984076e8de (patch) | |
tree | bfb50a2090fce186c2cc545a400c969bf2ea702b /framework/src/onos/providers | |
parent | e6d71622143ff9b2421a1abbe8434b954b5b1099 (diff) |
Updated master to commit id 6ee8aa3e67ce89908a8c93aa9445c6f71a18f986
Change-Id: I94b055ee2f298daf71e2ec794fd0f2495bd8081f
Diffstat (limited to 'framework/src/onos/providers')
14 files changed, 2162 insertions, 119 deletions
diff --git a/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java b/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java index 9a823630..93f6bf8c 100644 --- a/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java +++ b/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java @@ -335,15 +335,15 @@ public class HostLocationProvider extends AbstractProvider implements HostProvid arp.getSenderProtocolAddress()); updateLocationIP(hid, srcMac, vlan, hloc, ip); - // IPv4: update location only + // IPv4: update location only } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) { updateLocation(hid, srcMac, vlan, hloc); - // - // NeighborAdvertisement and NeighborSolicitation: possible - // new hosts, update both location and IP. - // - // IPv6: update location only + // + // NeighborAdvertisement and NeighborSolicitation: possible + // new hosts, update both location and IP. + // + // IPv6: update location only } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) { IPv6 ipv6 = (IPv6) eth.getPayload(); IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET6, diff --git a/framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java b/framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java index d010f178..6cbb623b 100644 --- a/framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java +++ b/framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java @@ -23,9 +23,17 @@ import org.onlab.osgi.ComponentContextAdapter; import org.onlab.packet.ARP; import org.onlab.packet.ChassisId; import org.onlab.packet.Ethernet; +import org.onlab.packet.ICMP6; +import org.onlab.packet.IPv4; +import org.onlab.packet.IPv6; +import org.onlab.packet.Ip6Address; import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; +import org.onlab.packet.ndp.NeighborAdvertisement; +import org.onlab.packet.ndp.NeighborSolicitation; +import org.onlab.packet.ndp.RouterAdvertisement; +import org.onlab.packet.ndp.RouterSolicitation; import org.onosproject.cfg.ComponentConfigAdapter; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; @@ -66,6 +74,7 @@ import java.util.Hashtable; import java.util.Set; import static org.easymock.EasyMock.*; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.*; import static org.onlab.packet.VlanId.vlanId; import static org.onosproject.net.Device.Type.SWITCH; @@ -75,27 +84,42 @@ import static org.onosproject.net.PortNumber.portNumber; import static org.onosproject.net.device.DeviceEvent.Type.*; public class HostLocationProviderTest { - private static final Integer INPORT = 10; private static final String DEV1 = "of:1"; private static final String DEV2 = "of:2"; private static final String DEV3 = "of:3"; + private static final String DEV4 = "of:4"; + private static final String DEV5 = "of:5"; + private static final String DEV6 = "of:6"; private static final VlanId VLAN = vlanId(); + + // IPv4 Host private static final MacAddress MAC = MacAddress.valueOf("00:00:11:00:00:01"); private static final MacAddress BCMAC = MacAddress.valueOf("ff:ff:ff:ff:ff:ff"); private static final byte[] IP = new byte[]{10, 0, 0, 1}; - private static final IpAddress IP_ADDRESS = IpAddress.valueOf(IpAddress.Version.INET, IP); private static final HostLocation LOCATION = new HostLocation(deviceId(DEV1), portNumber(INPORT), 0L); - private static final DefaultHost HOST = new DefaultHost(ProviderId.NONE, hostId(MAC), MAC, vlanId(VlanId.UNTAGGED), LOCATION, ImmutableSet.of(IP_ADDRESS)); + // IPv6 Host + private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02"); + private static final MacAddress BCMAC2 = MacAddress.valueOf("33:33:00:00:00:01"); + private static final byte[] IP2 = Ip6Address.valueOf("1000::1").toOctets(); + private static final IpAddress IP_ADDRESS2 = + IpAddress.valueOf(IpAddress.Version.INET6, IP2); + private static final HostLocation LOCATION2 = + new HostLocation(deviceId(DEV4), portNumber(INPORT), 0L); + private static final DefaultHost HOST2 = + new DefaultHost(ProviderId.NONE, hostId(MAC2), MAC2, + vlanId(VlanId.UNTAGGED), LOCATION2, + ImmutableSet.of(IP_ADDRESS2)); + private static final ComponentContextAdapter CTX_FOR_REMOVE = new ComponentContextAdapter() { @Override @@ -157,51 +181,189 @@ public class HostLocationProviderTest { @Test public void events() { // new host - testProcessor.process(new TestPacketContext(DEV1)); + testProcessor.process(new TestArpPacketContext(DEV1)); + assertNotNull("new host expected", providerService.added); + assertNull("host motion unexpected", providerService.moved); + + // the host moved to new switch + testProcessor.process(new TestArpPacketContext(DEV2)); + assertNotNull("host motion expected", providerService.moved); + + // the host was misheard on a spine + testProcessor.process(new TestArpPacketContext(DEV3)); + assertNull("host misheard on spine switch", providerService.spine); + + providerService.clear(); + + // new host + testProcessor.process(new TestNAPacketContext(DEV4)); assertNotNull("new host expected", providerService.added); assertNull("host motion unexpected", providerService.moved); // the host moved to new switch - testProcessor.process(new TestPacketContext(DEV2)); + testProcessor.process(new TestNAPacketContext(DEV5)); assertNotNull("host motion expected", providerService.moved); // the host was misheard on a spine - testProcessor.process(new TestPacketContext(DEV3)); + testProcessor.process(new TestNAPacketContext(DEV6)); assertNull("host misheard on spine switch", providerService.spine); } @Test public void removeHostByDeviceRemove() { provider.modified(CTX_FOR_REMOVE); - testProcessor.process(new TestPacketContext(DEV1)); + testProcessor.process(new TestArpPacketContext(DEV1)); + testProcessor.process(new TestNAPacketContext(DEV4)); + Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH, "m", "h", "s", "n", new ChassisId(0L)); deviceService.listener.event(new DeviceEvent(DEVICE_REMOVED, device)); assertEquals("incorrect remove count", 1, providerService.removeCount); + + device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH, + "m", "h", "s", "n", new ChassisId(0L)); + deviceService.listener.event(new DeviceEvent(DEVICE_REMOVED, device)); + assertEquals("incorrect remove count", 2, providerService.removeCount); } @Test public void removeHostByDeviceOffline() { provider.modified(CTX_FOR_REMOVE); - testProcessor.process(new TestPacketContext(DEV1)); + testProcessor.process(new TestArpPacketContext(DEV1)); + testProcessor.process(new TestArpPacketContext(DEV4)); + Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH, "m", "h", "s", "n", new ChassisId(0L)); deviceService.listener.event(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device)); assertEquals("incorrect remove count", 1, providerService.removeCount); + + device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH, + "m", "h", "s", "n", new ChassisId(0L)); + deviceService.listener.event(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device)); + assertEquals("incorrect remove count", 2, providerService.removeCount); } @Test public void removeHostByDevicePortDown() { provider.modified(CTX_FOR_REMOVE); - testProcessor.process(new TestPacketContext(DEV1)); + testProcessor.process(new TestArpPacketContext(DEV1)); + testProcessor.process(new TestArpPacketContext(DEV4)); + Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH, "m", "h", "s", "n", new ChassisId(0L)); deviceService.listener.event(new DeviceEvent(PORT_UPDATED, device, - new DefaultPort(device, portNumber(INPORT), - false))); + new DefaultPort(device, portNumber(INPORT), false))); assertEquals("incorrect remove count", 1, providerService.removeCount); + + device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH, + "m", "h", "s", "n", new ChassisId(0L)); + deviceService.listener.event(new DeviceEvent(PORT_UPDATED, device, + new DefaultPort(device, portNumber(INPORT), false))); + assertEquals("incorrect remove count", 2, providerService.removeCount); } + /** + * When receiving ARP, updates location and IP. + */ + @Test + public void testReceiveArp() { + testProcessor.process(new TestArpPacketContext(DEV1)); + HostDescription descr = providerService.added; + assertThat(descr.location(), is(LOCATION)); + assertThat(descr.hwAddress(), is(MAC)); + assertThat(descr.ipAddress().toArray()[0], is(IP_ADDRESS)); + assertThat(descr.vlan(), is(VLAN)); + } + + /** + * When receiving IPv4, updates location only. + */ + @Test + public void testReceiveIpv4() { + testProcessor.process(new TestIpv4PacketContext(DEV1)); + HostDescription descr = providerService.added; + assertThat(descr.location(), is(LOCATION)); + assertThat(descr.hwAddress(), is(MAC)); + assertThat(descr.ipAddress().size(), is(0)); + assertThat(descr.vlan(), is(VLAN)); + } + + /** + * When receiving NeighborAdvertisement, updates location and IP. + */ + @Test + public void testReceiveNA() { + testProcessor.process(new TestNAPacketContext(DEV4)); + assertNotNull(providerService.added); + HostDescription descr = providerService.added; + assertThat(descr.location(), is(LOCATION2)); + assertThat(descr.hwAddress(), is(MAC2)); + assertThat(descr.ipAddress().toArray()[0], is(IP_ADDRESS2)); + assertThat(descr.vlan(), is(VLAN)); + } + + /** + * When receiving NeighborSolicitation, updates location and IP. + */ + @Test + public void testReceiveNS() { + testProcessor.process(new TestNSPacketContext(DEV4)); + HostDescription descr = providerService.added; + assertThat(descr.location(), is(LOCATION2)); + assertThat(descr.hwAddress(), is(MAC2)); + assertThat(descr.ipAddress().toArray()[0], is(IP_ADDRESS2)); + assertThat(descr.vlan(), is(VLAN)); + } + + /** + * When receiving RouterAdvertisement, ignores it. + */ + @Test + public void testReceivesRA() { + testProcessor.process(new TestRAPacketContext(DEV4)); + assertNull(providerService.added); + } + + /** + * When receiving RouterSolicitation, ignores it. + */ + @Test + public void testReceiveRS() { + testProcessor.process(new TestRSPacketContext(DEV4)); + assertNull(providerService.added); + } + + /** + * When receiving Duplicate Address Detection (DAD), ignores it. + */ + @Test + public void testReceiveDAD() { + testProcessor.process(new TestDADPacketContext(DEV4)); + assertNull(providerService.added); + } + + /** + * When receiving IPv6 multicast packet, ignores it. + */ + @Test + public void testReceiveIpv6Multicast() { + testProcessor.process(new TestIpv6McastPacketContext(DEV4)); + assertNull(providerService.added); + } + + /** + * When receiving IPv6 unicast packet, updates location only. + */ + @Test + public void testReceiveIpv6Unicast() { + testProcessor.process(new TestIpv6PacketContext(DEV4)); + assertNotNull(providerService.added); + HostDescription descr = providerService.added; + assertThat(descr.location(), is(LOCATION2)); + assertThat(descr.hwAddress(), is(MAC2)); + assertThat(descr.ipAddress().size(), is(0)); + assertThat(descr.vlan(), is(VLAN)); + } @After public void tearDown() { @@ -233,24 +395,30 @@ public class HostLocationProviderTest { extends AbstractProviderService<HostProvider> implements HostProviderService { - DeviceId added = null; - DeviceId moved = null; - DeviceId spine = null; + HostDescription added = null; + HostDescription moved = null; + HostDescription spine = null; public int removeCount; + public void clear() { + added = null; + moved = null; + spine = null; + removeCount = 0; + } + protected TestHostProviderService(HostProvider provider) { super(provider); } @Override public void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps) { - DeviceId descr = hostDescription.location().deviceId(); if (added == null) { - added = descr; - } else if ((moved == null) && !descr.equals(added)) { - moved = descr; + added = hostDescription; + } else if ((moved == null) && !hostDescription.equals(added)) { + moved = hostDescription; } else { - spine = descr; + spine = hostDescription; } } @@ -259,6 +427,10 @@ public class HostLocationProviderTest { removeCount++; } + @Override + public void removeIpFromHost(HostId hostId, IpAddress ipAddress) { + } + } private class TestPacketService extends PacketServiceAdapter { @@ -268,24 +440,26 @@ public class HostLocationProviderTest { } } - private class TestTopologyService extends TopologyServiceAdapter { @Override public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) { //simulate DPID3 as an infrastructure switch - if ((connectPoint.deviceId()).equals(deviceId(DEV3))) { + if ((connectPoint.deviceId()).equals(deviceId(DEV3)) || + connectPoint.deviceId().equals(deviceId(DEV6))) { return true; } return false; } } - private class TestPacketContext implements PacketContext { - + /** + * Generates ARP packet. + */ + private class TestArpPacketContext implements PacketContext { private final String deviceId; - public TestPacketContext(String deviceId) { + public TestArpPacketContext(String deviceId) { this.deviceId = deviceId; } @@ -340,6 +514,490 @@ public class HostLocationProviderTest { } } + /** + * Generates IPv6 Unicast packet. + */ + private class TestIpv4PacketContext implements PacketContext { + private final String deviceId; + + public TestIpv4PacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + IPv4 ipv4 = new IPv4(); + ipv4.setDestinationAddress("10.0.0.1"); + ipv4.setSourceAddress(IP_ADDRESS.toString()); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV4) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC) + .setDestinationMACAddress(MacAddress.valueOf("00:00:00:00:00:01")) + .setPayload(ipv4); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates NeighborAdvertisement packet. + */ + private class TestNAPacketContext implements PacketContext { + private final String deviceId; + + public TestNAPacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + NeighborAdvertisement na = new NeighborAdvertisement(); + ICMP6 icmp6 = new ICMP6(); + icmp6.setPayload(na); + IPv6 ipv6 = new IPv6(); + ipv6.setPayload(icmp6); + ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets()); + ipv6.setSourceAddress(IP2); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2.toBytes()) + .setDestinationMACAddress(BCMAC2) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates NeighborSolicitation packet. + */ + private class TestNSPacketContext implements PacketContext { + private final String deviceId; + + public TestNSPacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + NeighborSolicitation ns = new NeighborSolicitation(); + ICMP6 icmp6 = new ICMP6(); + icmp6.setPayload(ns); + IPv6 ipv6 = new IPv6(); + ipv6.setPayload(icmp6); + ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1:ff00:0000").toOctets()); + ipv6.setSourceAddress(IP2); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2.toBytes()) + .setDestinationMACAddress(BCMAC2) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates Duplicate Address Detection packet. + */ + private class TestDADPacketContext implements PacketContext { + private final String deviceId; + + public TestDADPacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + NeighborSolicitation ns = new NeighborSolicitation(); + ICMP6 icmp6 = new ICMP6(); + icmp6.setPayload(ns); + IPv6 ipv6 = new IPv6(); + ipv6.setPayload(icmp6); + ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets()); + ipv6.setSourceAddress(Ip6Address.valueOf("::").toOctets()); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2.toBytes()) + .setDestinationMACAddress(BCMAC2) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates Router Solicitation packet. + */ + private class TestRSPacketContext implements PacketContext { + private final String deviceId; + + public TestRSPacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + RouterSolicitation ns = new RouterSolicitation(); + ICMP6 icmp6 = new ICMP6(); + icmp6.setPayload(ns); + IPv6 ipv6 = new IPv6(); + ipv6.setPayload(icmp6); + ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::2").toOctets()); + ipv6.setSourceAddress(Ip6Address.valueOf("::").toOctets()); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2.toBytes()) + .setDestinationMACAddress(MacAddress.valueOf("33:33:00:00:00:02")) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates Router Advertisement packet. + */ + private class TestRAPacketContext implements PacketContext { + private final String deviceId; + + public TestRAPacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + RouterAdvertisement ns = new RouterAdvertisement(); + ICMP6 icmp6 = new ICMP6(); + icmp6.setPayload(ns); + IPv6 ipv6 = new IPv6(); + ipv6.setPayload(icmp6); + ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets()); + ipv6.setSourceAddress(IP2); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2.toBytes()) + .setDestinationMACAddress(MacAddress.valueOf("33:33:00:00:00:01")) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates IPv6 Multicast packet. + */ + private class TestIpv6McastPacketContext implements PacketContext { + private final String deviceId; + + public TestIpv6McastPacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + IPv6 ipv6 = new IPv6(); + ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets()); + ipv6.setSourceAddress(IP2); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2.toBytes()) + .setDestinationMACAddress(MacAddress.valueOf("33:33:00:00:00:01")) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates IPv6 Unicast packet. + */ + private class TestIpv6PacketContext implements PacketContext { + private final String deviceId; + + public TestIpv6PacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + IPv6 ipv6 = new IPv6(); + ipv6.setDestinationAddress(Ip6Address.valueOf("1000::1").toOctets()); + ipv6.setSourceAddress(IP2); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2) + .setDestinationMACAddress(MacAddress.valueOf("00:00:00:00:00:01")) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + private class TestDeviceService extends DeviceServiceAdapter { private DeviceListener listener; @@ -357,12 +1015,26 @@ public class HostLocationProviderTest { private class TestHostService extends HostServiceAdapter { @Override public Set<Host> getConnectedHosts(ConnectPoint connectPoint) { - return ImmutableSet.of(HOST); + ConnectPoint cp1 = new ConnectPoint(deviceId(DEV1), portNumber(INPORT)); + ConnectPoint cp2 = new ConnectPoint(deviceId(DEV4), portNumber(INPORT)); + if (connectPoint.equals(cp1)) { + return ImmutableSet.of(HOST); + } else if (connectPoint.equals(cp2)) { + return ImmutableSet.of(HOST2); + } else { + return ImmutableSet.of(); + } } @Override public Set<Host> getConnectedHosts(DeviceId deviceId) { - return ImmutableSet.of(HOST); + if (deviceId.equals(deviceId(DEV1))) { + return ImmutableSet.of(HOST); + } else if (deviceId.equals(deviceId(DEV4))) { + return ImmutableSet.of(HOST2); + } else { + return ImmutableSet.of(); + } } } diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java index 386d838f..a840f856 100644 --- a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java +++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java @@ -67,6 +67,8 @@ import java.util.concurrent.ScheduledExecutorService; import static com.google.common.base.Strings.isNullOrEmpty; import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.onlab.packet.Ethernet.TYPE_BSN; +import static org.onlab.packet.Ethernet.TYPE_LLDP; import static org.onlab.util.Tools.get; import static org.onlab.util.Tools.groupedThreads; import static org.onosproject.net.Link.Type.DIRECT; @@ -326,10 +328,10 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider { */ private void requestIntercepts() { TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); - selector.matchEthType(Ethernet.TYPE_LLDP); + selector.matchEthType(TYPE_LLDP); packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId); - selector.matchEthType(Ethernet.TYPE_BSN); + selector.matchEthType(TYPE_BSN); if (useBDDP) { packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId); } else { @@ -342,9 +344,9 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider { */ private void withdrawIntercepts() { TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); - selector.matchEthType(Ethernet.TYPE_LLDP); + selector.matchEthType(TYPE_LLDP); packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId); - selector.matchEthType(Ethernet.TYPE_BSN); + selector.matchEthType(TYPE_BSN); packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId); } @@ -394,7 +396,7 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider { synchronized (discoverers) { ld = discoverers.get(deviceId); if (ld == null) { - if (rules.isSuppressed(device)) { + if (rules != null && rules.isSuppressed(device)) { log.debug("LinkDiscovery from {} disabled by configuration", device.id()); return; } @@ -474,9 +476,15 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider { private class InternalPacketProcessor implements PacketProcessor { @Override public void process(PacketContext context) { - if (context == null) { + if (context == null || context.isHandled()) { return; } + + Ethernet eth = context.inPacket().parsed(); + if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) { + return; + } + LinkDiscovery ld = discoverers.get(context.inPacket().receivedFrom().deviceId()); if (ld == null) { return; diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java index cb19dc52..4fa961f8 100644 --- a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java +++ b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java @@ -15,10 +15,24 @@ */ package org.onosproject.provider.of.device.impl; -import com.google.common.base.Strings; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.onlab.util.Tools.get; +import static org.onosproject.net.DeviceId.deviceId; +import static org.onosproject.net.Port.Type.COPPER; +import static org.onosproject.net.Port.Type.FIBER; +import static org.onosproject.openflow.controller.Dpid.dpid; +import static org.onosproject.openflow.controller.Dpid.uri; +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -28,15 +42,17 @@ import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.onlab.packet.ChassisId; import org.onlab.util.Frequency; -import org.onosproject.cfg.ComponentConfigService; import org.onlab.util.Spectrum; +import org.onosproject.cfg.ComponentConfigService; import org.onosproject.net.AnnotationKeys; import org.onosproject.net.ChannelSpacing; import org.onosproject.net.DefaultAnnotations; +import org.onosproject.net.Device; import org.onosproject.net.DeviceId; import org.onosproject.net.GridType; import org.onosproject.net.MastershipRole; import org.onosproject.net.OchSignal; +import org.onosproject.net.OduCltPort; import org.onosproject.net.OduSignalType; import org.onosproject.net.Port; import org.onosproject.net.PortNumber; @@ -49,6 +65,7 @@ import org.onosproject.net.device.DeviceProvider; import org.onosproject.net.device.DeviceProviderRegistry; import org.onosproject.net.device.DeviceProviderService; import org.onosproject.net.device.OchPortDescription; +import org.onosproject.net.device.OduCltPortDescription; import org.onosproject.net.device.OmsPortDescription; import org.onosproject.net.device.PortDescription; import org.onosproject.net.device.PortStatistics; @@ -64,13 +81,19 @@ import org.onosproject.openflow.controller.PortDescPropertyType; import org.onosproject.openflow.controller.RoleState; import org.osgi.service.component.ComponentContext; import org.projectfloodlight.openflow.protocol.OFCalientPortDescStatsEntry; +import org.projectfloodlight.openflow.protocol.OFExpPort; +import org.projectfloodlight.openflow.protocol.OFExpPortDescPropOpticalTransport; +import org.projectfloodlight.openflow.protocol.OFExpPortOpticalTransportLayerEntry; import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFObject; import org.projectfloodlight.openflow.protocol.OFPortConfig; import org.projectfloodlight.openflow.protocol.OFPortDesc; import org.projectfloodlight.openflow.protocol.OFPortDescPropOpticalTransport; import org.projectfloodlight.openflow.protocol.OFPortFeatures; import org.projectfloodlight.openflow.protocol.OFPortOptical; +import org.projectfloodlight.openflow.protocol.OFPortOpticalTransportLayerClass; +import org.projectfloodlight.openflow.protocol.OFPortOpticalTransportSignalType; import org.projectfloodlight.openflow.protocol.OFPortReason; import org.projectfloodlight.openflow.protocol.OFPortState; import org.projectfloodlight.openflow.protocol.OFPortStatsEntry; @@ -83,23 +106,10 @@ import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.PortSpeed; import org.slf4j.Logger; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Dictionary; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Strings.isNullOrEmpty; -import static org.onlab.util.Tools.get; -import static org.onosproject.net.DeviceId.deviceId; -import static org.onosproject.net.Port.Type.COPPER; -import static org.onosproject.net.Port.Type.FIBER; -import static org.onosproject.openflow.controller.Dpid.dpid; -import static org.onosproject.openflow.controller.Dpid.uri; -import static org.slf4j.LoggerFactory.getLogger; +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; /** * Provider which uses an OpenFlow controller to detect network @@ -109,7 +119,11 @@ import static org.slf4j.LoggerFactory.getLogger; public class OpenFlowDeviceProvider extends AbstractProvider implements DeviceProvider { private static final Logger LOG = getLogger(OpenFlowDeviceProvider.class); + private static final long MBPS = 1_000 * 1_000; + private static final Frequency FREQ100 = Frequency.ofGHz(100); + private static final Frequency FREQ193_1 = Frequency.ofTHz(193.1); + private static final Frequency FREQ4_4 = Frequency.ofTHz(4.4); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected DeviceProviderRegistry providerRegistry; @@ -145,27 +159,16 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr providerService = providerRegistry.register(this); controller.addListener(listener); controller.addEventListener(listener); - for (OpenFlowSwitch sw : controller.getSwitches()) { - try { - listener.switchAdded(new Dpid(sw.getId())); - } catch (Exception e) { - LOG.warn("Failed initially adding {} : {}", sw.getStringId(), e.getMessage()); - LOG.debug("Error details:", e); - // disconnect to trigger switch-add later - sw.disconnectSwitch(); - } - PortStatsCollector psc = new PortStatsCollector(sw, portStatsPollFrequency); - psc.start(); - collectors.put(new Dpid(sw.getId()), psc); - } + connectInitialDevices(); LOG.info("Started"); } @Deactivate public void deactivate(ComponentContext context) { cfgService.unregisterProperties(getClass(), false); - providerRegistry.unregister(this); controller.removeListener(listener); + disconnectDevices(); + providerRegistry.unregister(this); collectors.values().forEach(PortStatsCollector::stop); providerService = null; LOG.info("Stopped"); @@ -191,13 +194,31 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr LOG.info("Settings: portStatsPollFrequency={}", portStatsPollFrequency); } + private void connectInitialDevices() { + for (OpenFlowSwitch sw : controller.getSwitches()) { + try { + listener.switchAdded(new Dpid(sw.getId())); + } catch (Exception e) { + LOG.warn("Failed initially adding {} : {}", sw.getStringId(), e.getMessage()); + LOG.debug("Error details:", e); + // disconnect to trigger switch-add later + sw.disconnectSwitch(); + } + PortStatsCollector psc = new PortStatsCollector(sw, portStatsPollFrequency); + psc.start(); + collectors.put(new Dpid(sw.getId()), psc); + } + } + + private void disconnectDevices() { + // Only disconnect the devices for which we are currently master. + controller.getMasterSwitches().forEach(sw -> listener.switchRemoved(new Dpid(sw.getId()))); + } + @Override public boolean isReachable(DeviceId deviceId) { OpenFlowSwitch sw = controller.getSwitch(dpid(deviceId.uri())); - if (sw == null || !sw.isConnected()) { - return false; - } - return true; + return sw != null && sw.isConnected(); } @Override @@ -302,8 +323,9 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr ChassisId cId = new ChassisId(dpid.value()); SparseAnnotations annotations = DefaultAnnotations.builder() - .set("protocol", sw.factory().getVersion().toString()) - .set("channelId", sw.channelId()) + .set(AnnotationKeys.PROTOCOL, sw.factory().getVersion().toString()) + .set(AnnotationKeys.CHANNEL_ID, sw.channelId()) + .set(AnnotationKeys.MANAGEMENT_ADDRESS, sw.channelId().split(":")[0]) .build(); DeviceDescription description = @@ -386,18 +408,27 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr */ private List<PortDescription> buildPortDescriptions(OpenFlowSwitch sw) { final List<PortDescription> portDescs = new ArrayList<>(sw.getPorts().size()); - sw.getPorts().forEach(port -> portDescs.add(buildPortDescription(port))); + if (!(Device.Type.ROADM.equals(sw.deviceType()))) { + sw.getPorts().forEach(port -> portDescs.add(buildPortDescription(port))); + } OpenFlowOpticalSwitch opsw; switch (sw.deviceType()) { case ROADM: opsw = (OpenFlowOpticalSwitch) sw; + List<OFPortDesc> ports = opsw.getPorts(); + LOG.debug("SW ID {} , ETH- ODU CLT Ports {}", opsw.getId(), ports); + // ODU client ports are reported as ETH + ports.forEach(port -> portDescs.add(buildOduCltPortDescription(port))); + opsw.getPortTypes().forEach(type -> { - opsw.getPortsOf(type).forEach( - op -> { - portDescs.add(buildPortDescription(type, (OFPortOptical) op)); - } - ); + List<? extends OFObject> portsOf = opsw.getPortsOf(type); + LOG.debug("Ports Of{}", portsOf); + portsOf.forEach( + op -> { + portDescs.add(buildPortDescription(type, (OFObject) op)); + } + ); }); break; case FIBER_SWITCH: @@ -417,6 +448,105 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr return portDescs; } + private PortDescription buildOduCltPortDescription(OFPortDesc port) { + PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber()); + boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN) && + !port.getConfig().contains(OFPortConfig.PORT_DOWN); + Long portSpeed = portSpeed(port); + OduCltPort.SignalType sigType = null; + + switch (portSpeed.toString()) { + case "1": + sigType = OduCltPort.SignalType.CLT_1GBE; + break; + case "10": + sigType = OduCltPort.SignalType.CLT_10GBE; + break; + case "40": + sigType = OduCltPort.SignalType.CLT_40GBE; + break; + case "100": + sigType = OduCltPort.SignalType.CLT_100GBE; + break; + default: + throw new RuntimeException("Un recognize OduClt speed: " + portSpeed.toString()); + } + + SparseAnnotations annotations = buildOduCltAnnotation(port); + return new OduCltPortDescription(portNo, enabled, sigType, annotations); + } + + private SparseAnnotations buildOduCltAnnotation(OFPortDesc port) { + SparseAnnotations annotations = null; + String portName = Strings.emptyToNull(port.getName()); + if (portName != null) { + annotations = DefaultAnnotations.builder() + .set(AnnotationKeys.PORT_NAME, portName) + .set(AnnotationKeys.STATIC_PORT, Boolean.TRUE.toString()).build(); + } + return annotations; + } + + private PortDescription buildPortDescription(PortDescPropertyType ptype, OFObject port) { + if (port instanceof OFPortOptical) { + return buildPortDescription(ptype, (OFPortOptical) port); + } + return buildPortDescription(ptype, (OFExpPort) port); + } + + /** + * Build a portDescription from a given a port description describing some + * Optical port. + * + * @param ptype description property type. + * @param port the port to build from. + * @return portDescription for the port. + */ + private PortDescription buildPortDescription(PortDescPropertyType ptype, OFExpPort port) { + PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber()); + boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN) + && !port.getConfig().contains(OFPortConfig.PORT_DOWN); + SparseAnnotations annotations = makePortNameAnnotation(port.getName()); + + OFExpPortDescPropOpticalTransport firstProp = port.getProperties().get(0); + OFPortOpticalTransportSignalType sigType = firstProp.getPortSignalType(); + + DefaultPortDescription portDes = null; + switch (sigType) { + case OMSN: + portDes = new OmsPortDescription(portNo, enabled, FREQ193_1, FREQ193_1.add(FREQ4_4), + FREQ100, annotations); + break; + case OCH: + OFExpPortOpticalTransportLayerEntry entry = firstProp.getFeatures().get(0).getValue().get(0); + OFPortOpticalTransportLayerClass layerClass = entry.getLayerClass(); + if (!OFPortOpticalTransportLayerClass.ODU.equals(layerClass)) { + LOG.error("Unsupported layer Class {} ", layerClass); + return null; + } + + // convert to ONOS OduSignalType + OduSignalType oduSignalType = OpenFlowDeviceValueMapper. + lookupOduSignalType((byte) entry.getSignalType()); + //OchSignal is needed for OchPortDescription constructor, + //yet not relevant for tunable OCH port, creating with default parameters + OchSignal signalId = new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, 1, 1); + + portDes = new OchPortDescription(portNo, enabled, + oduSignalType, true, signalId, annotations); + + break; + case OTU2: + case OTU4: + LOG.error("Signal tpye OTU2/4 not supported yet ", port.toString()); + break; + default: + break; + } + + return portDes; + } + /** * Creates an annotation for the port name if one is available. * @@ -565,5 +695,4 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr } } } - } diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceValueMapper.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceValueMapper.java new file mode 100644 index 00000000..7bdf06fd --- /dev/null +++ b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceValueMapper.java @@ -0,0 +1,73 @@ +/* + * Copyright 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.provider.of.device.impl; + +import org.onosproject.net.OduSignalType; + +import com.google.common.collect.BiMap; +import com.google.common.collect.EnumHashBiMap; + +/** + * Collection of helper methods to convert protocol agnostic models to values used in OpenFlow spec. + */ +final class OpenFlowDeviceValueMapper { + + // prohibit instantiation + private OpenFlowDeviceValueMapper() {} + + private static final BiMap<OduSignalType, Byte> ODU_SIGNAL_TYPES = EnumHashBiMap.create(OduSignalType.class); + static { + // See ONF "Optical Transport Protocol Extensions Version 1.0" for the following values + ODU_SIGNAL_TYPES.put(OduSignalType.ODU1, (byte) 1); // OFPODUT_ODU1 of enum ofp_odu_signal_type + ODU_SIGNAL_TYPES.put(OduSignalType.ODU2, (byte) 2); // OFPODUT_ODU2 of enum ofp_odu_signal_type + ODU_SIGNAL_TYPES.put(OduSignalType.ODU3, (byte) 3); // OFPODUT_ODU3 of enum ofp_odu_signal_type + ODU_SIGNAL_TYPES.put(OduSignalType.ODU4, (byte) 4); // OFPODUT_ODU4 of enum ofp_odu_signal_type + ODU_SIGNAL_TYPES.put(OduSignalType.ODU0, (byte) 10); // OFPODUT_ODU0 of enum ofp_odu_signal_type + ODU_SIGNAL_TYPES.put(OduSignalType.ODU2e, (byte) 11); // OFPODUT_ODU2E of enum ofp_odu_signal_type + } + + /** + * Looks up the specified input value to the corresponding value with the specified map. + * + * @param map bidirectional mapping + * @param input input value + * @param cls class of output value + * @param <I> type of input value + * @param <O> type of output value + * @return the corresponding value stored in the specified map + */ + private static <I, O> O lookup(BiMap<I, O> map, I input, Class<O> cls) { + if (!map.containsKey(input)) { + throw new RuntimeException( + String.format("No mapping found for %s when converting to %s", input, cls.getName())); + } + + return map.get(input); + } + + /** + * Looks up the the corresponding {@link OduSignalType} instance + * from the specified byte value for ODU signal type defined in + * ONF "Optical Transport Protocol Extensions Version 1.0". + * + * @param signalType byte value as ODU (Optical channel Data Unit) signal type defined the spec + * @return the corresponding OchSignalType instance + */ + static OduSignalType lookupOduSignalType(byte signalType) { + return lookup(ODU_SIGNAL_TYPES.inverse(), signalType, OduSignalType.class); + } + +} diff --git a/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java b/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java index 7b4d7922..d0838bb8 100644 --- a/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java +++ b/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java @@ -16,6 +16,7 @@ package org.onosproject.provider.of.device.impl; import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; @@ -236,7 +237,7 @@ public class OpenFlowDeviceProviderTest { @Override public Iterable<OpenFlowSwitch> getMasterSwitches() { - return null; + return ImmutableSet.of(); } @Override diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java index f238bdb1..cf918605 100644 --- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java @@ -221,11 +221,6 @@ public class FlowEntryBuilder { private TrafficTreatment buildTreatment() { TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); - // If this is a drop rule - if (instructions.size() == 0) { - builder.drop(); - return builder.build(); - } for (OFInstruction in : instructions) { switch (in.getType()) { case GOTO_TABLE: diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java index c9de4500..f77819d5 100644 --- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java @@ -142,7 +142,7 @@ public class FlowModBuilderVer10 extends FlowModBuilder { for (Instruction i : treatment.immediate()) { switch (i.type()) { case DROP: - log.warn("Saw drop action; assigning drop action"); + case NOACTION: return Collections.emptyList(); case L2MODIFICATION: act = buildL2Modification(i); diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java index 8918d337..cc265758 100644 --- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java @@ -123,6 +123,9 @@ public class FlowModBuilderVer13 extends FlowModBuilder { if (treatment.writeMetadata() != null) { instructions.add(buildMetadata(treatment.writeMetadata())); } + if (treatment.metered() != null) { + instructions.add(buildMeter(treatment.metered())); + } long cookie = flowRule().id().value(); @@ -212,6 +215,7 @@ public class FlowModBuilderVer13 extends FlowModBuilder { for (Instruction i : treatments) { switch (i.type()) { case DROP: + case NOACTION: return Collections.emptyList(); case L0MODIFICATION: actions.add(buildL0Modification(i)); diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java new file mode 100644 index 00000000..a81367cd --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java @@ -0,0 +1,881 @@ +/*
+ * Copyright 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.provider.of.flow.impl;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import org.onosproject.net.flow.DefaultTypedFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowId;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.StoredFlowEntry;
+import org.onosproject.net.flow.TypedStoredFlowEntry;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TableId;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.flow.TypedStoredFlowEntry.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Efficiently and adaptively collects flow statistics for the specified switch.
+ */
+public class NewAdaptiveFlowStatsCollector {
+
+ private final Logger log = getLogger(getClass());
+
+ private final OpenFlowSwitch sw;
+
+ private ScheduledExecutorService adaptiveFlowStatsScheduler =
+ Executors.newScheduledThreadPool(4, groupedThreads("onos/flow", "device-stats-collector-%d"));
+ private ScheduledFuture<?> calAndShortFlowsThread;
+ private ScheduledFuture<?> midFlowsThread;
+ private ScheduledFuture<?> longFlowsThread;
+
+ // Task that calculates all flowEntries' FlowLiveType and collects stats IMMEDIATE flows every calAndPollInterval
+ private CalAndShortFlowsTask calAndShortFlowsTask;
+ // Task that collects stats MID flows every 2*calAndPollInterval
+ private MidFlowsTask midFlowsTask;
+ // Task that collects stats LONG flows every 3*calAndPollInterval
+ private LongFlowsTask longFlowsTask;
+
+ private static final int CAL_AND_POLL_TIMES = 1; // must be always 0
+ private static final int MID_POLL_TIMES = 2; // variable greater or equal than 1
+ private static final int LONG_POLL_TIMES = 3; // variable greater or equal than MID_POLL_TIMES
+ //TODO: make ENTIRE_POLL_TIMES configurable with enable or disable
+ // must be variable greater or equal than common multiple of MID_POLL_TIMES and LONG_POLL_TIMES
+ private static final int ENTIRE_POLL_TIMES = 6;
+
+ private static final int DEFAULT_CAL_AND_POLL_FREQUENCY = 5;
+ private static final int MIN_CAL_AND_POLL_FREQUENCY = 2;
+ private static final int MAX_CAL_AND_POLL_FREQUENCY = 60;
+
+ private int calAndPollInterval; // CAL_AND_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
+ private int midPollInterval; // MID_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
+ private int longPollInterval; // LONG_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
+ // only used for checking condition at each task if it collects entire flows from a given switch or not
+ private int entirePollInterval; // ENTIRE_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
+
+ // Number of call count of each Task,
+ // for undoing collection except only entire flows collecting task in CalAndShortFlowsTask
+ private int callCountCalAndShortFlowsTask = 0; // increased CAL_AND_POLL_TIMES whenever Task is called
+ private int callCountMidFlowsTask = 0; // increased MID_POLL_TIMES whenever Task is called
+ private int callCountLongFlowsTask = 0; // increased LONG_POLL_TIMES whenever Task is called
+
+ private InternalDeviceFlowTable deviceFlowTable = new InternalDeviceFlowTable();
+
+ private boolean isFirstTimeStart = true;
+
+ public static final long NO_FLOW_MISSING_XID = (-1);
+ private long flowMissingXid = NO_FLOW_MISSING_XID;
+
+ /**
+ * Creates a new adaptive collector for the given switch and default cal_and_poll frequency.
+ *
+ * @param sw switch to pull
+ * @param pollInterval cal and immediate poll frequency in seconds
+ */
+ NewAdaptiveFlowStatsCollector(OpenFlowSwitch sw, int pollInterval) {
+ this.sw = sw;
+
+ initMemberVars(pollInterval);
+ }
+
+ // check calAndPollInterval validity and set all pollInterval values and finally initialize each task call count
+ private void initMemberVars(int pollInterval) {
+ if (pollInterval < MIN_CAL_AND_POLL_FREQUENCY) {
+ this.calAndPollInterval = MIN_CAL_AND_POLL_FREQUENCY;
+ } else if (pollInterval >= MAX_CAL_AND_POLL_FREQUENCY) {
+ this.calAndPollInterval = MAX_CAL_AND_POLL_FREQUENCY;
+ } else {
+ this.calAndPollInterval = pollInterval;
+ }
+
+ calAndPollInterval = CAL_AND_POLL_TIMES * calAndPollInterval;
+ midPollInterval = MID_POLL_TIMES * calAndPollInterval;
+ longPollInterval = LONG_POLL_TIMES * calAndPollInterval;
+ entirePollInterval = ENTIRE_POLL_TIMES * calAndPollInterval;
+
+ callCountCalAndShortFlowsTask = 0;
+ callCountMidFlowsTask = 0;
+ callCountLongFlowsTask = 0;
+
+ flowMissingXid = NO_FLOW_MISSING_XID;
+ }
+
+ /**
+ * Adjusts adaptive poll frequency.
+ *
+ * @param pollInterval poll frequency in seconds
+ */
+ synchronized void adjustCalAndPollInterval(int pollInterval) {
+ initMemberVars(pollInterval);
+
+ if (calAndShortFlowsThread != null) {
+ calAndShortFlowsThread.cancel(false);
+ }
+ if (midFlowsThread != null) {
+ midFlowsThread.cancel(false);
+ }
+ if (longFlowsThread != null) {
+ longFlowsThread.cancel(false);
+ }
+
+ calAndShortFlowsTask = new CalAndShortFlowsTask();
+ calAndShortFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ calAndShortFlowsTask,
+ 0,
+ calAndPollInterval,
+ TimeUnit.SECONDS);
+
+ midFlowsTask = new MidFlowsTask();
+ midFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ midFlowsTask,
+ 0,
+ midPollInterval,
+ TimeUnit.SECONDS);
+
+ longFlowsTask = new LongFlowsTask();
+ longFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ longFlowsTask,
+ 0,
+ longPollInterval,
+ TimeUnit.SECONDS);
+
+ log.debug("calAndPollInterval=" + calAndPollInterval + "is adjusted");
+ }
+
+ private class CalAndShortFlowsTask implements Runnable {
+ @Override
+ public void run() {
+ if (sw.getRole() == RoleState.MASTER) {
+ log.trace("CalAndShortFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());
+
+ if (isFirstTimeStart) {
+ // isFirstTimeStart, get entire flow stats from a given switch sw
+ log.trace("CalAndShortFlowsTask Collecting Entire AdaptiveStats at first time start for {}",
+ sw.getStringId());
+ ofFlowStatsRequestAllSend();
+
+ callCountCalAndShortFlowsTask += CAL_AND_POLL_TIMES;
+ isFirstTimeStart = false;
+ } else if (callCountCalAndShortFlowsTask == ENTIRE_POLL_TIMES) {
+ // entire_poll_times, get entire flow stats from a given switch sw
+ log.trace("CalAndShortFlowsTask Collecting Entire AdaptiveStats for {}", sw.getStringId());
+ ofFlowStatsRequestAllSend();
+
+ callCountCalAndShortFlowsTask = CAL_AND_POLL_TIMES;
+ //TODO: check flows deleted in switch, but exist in controller flow table, then remove them
+ //
+ } else {
+ calAndShortFlowsTaskInternal();
+ callCountCalAndShortFlowsTask += CAL_AND_POLL_TIMES;
+ }
+ }
+ }
+ }
+
+ // send openflow flow stats request message with getting all flow entries to a given switch sw
+ private void ofFlowStatsRequestAllSend() {
+ OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest()
+ .setMatch(sw.factory().matchWildcardAll())
+ .setTableId(TableId.ALL)
+ .setOutPort(OFPort.NO_MASK)
+ .build();
+
+ synchronized (this) {
+ // set the request xid to check the reply in OpenFlowRuleProvider
+ // After processing the reply of this request message,
+ // this must be set to NO_FLOW_MISSING_XID(-1) by provider
+ setFlowMissingXid(request.getXid());
+ log.debug("ofFlowStatsRequestAllSend,Request={},for {}", request.toString(), sw.getStringId());
+
+ sw.sendMsg(request);
+ }
+ }
+
+ // send openflow flow stats request message with getting the specific flow entry(fe) to a given switch sw
+ private void ofFlowStatsRequestFlowSend(FlowEntry fe) {
+ // set find match
+ Match match = FlowModBuilder.builder(fe, sw.factory(), Optional.empty()).buildMatch();
+ // set find tableId
+ TableId tableId = TableId.of(fe.tableId());
+ // set output port
+ Instruction ins = fe.treatment().allInstructions().stream()
+ .filter(i -> (i.type() == Instruction.Type.OUTPUT))
+ .findFirst()
+ .orElse(null);
+ OFPort ofPort = OFPort.NO_MASK;
+ if (ins != null) {
+ Instructions.OutputInstruction out = (Instructions.OutputInstruction) ins;
+ ofPort = OFPort.of((int) ((out.port().toLong())));
+ }
+
+ OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest()
+ .setMatch(match)
+ .setTableId(tableId)
+ .setOutPort(ofPort)
+ .build();
+
+ synchronized (this) {
+ if (getFlowMissingXid() != NO_FLOW_MISSING_XID) {
+ log.debug("ofFlowStatsRequestFlowSend: previous FlowStatsRequestAll does not be processed yet,"
+ + " set no flow missing xid anyway, for {}",
+ sw.getStringId());
+ setFlowMissingXid(NO_FLOW_MISSING_XID);
+ }
+
+ sw.sendMsg(request);
+ }
+ }
+
+ private void calAndShortFlowsTaskInternal() {
+ deviceFlowTable.checkAndMoveLiveFlowAll();
+
+ deviceFlowTable.getShortFlows().forEach(fe -> {
+ ofFlowStatsRequestFlowSend(fe);
+ });
+ }
+
+ private class MidFlowsTask implements Runnable {
+ @Override
+ public void run() {
+ if (sw.getRole() == RoleState.MASTER) {
+ log.trace("MidFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());
+
+ // skip collecting because CalAndShortFlowsTask collects entire flow stats from a given switch sw
+ if (callCountMidFlowsTask == ENTIRE_POLL_TIMES) {
+ callCountMidFlowsTask = MID_POLL_TIMES;
+ } else {
+ midFlowsTaskInternal();
+ callCountMidFlowsTask += MID_POLL_TIMES;
+ }
+ }
+ }
+ }
+
+ private void midFlowsTaskInternal() {
+ deviceFlowTable.getMidFlows().forEach(fe -> {
+ ofFlowStatsRequestFlowSend(fe);
+ });
+ }
+
+ private class LongFlowsTask implements Runnable {
+ @Override
+ public void run() {
+ if (sw.getRole() == RoleState.MASTER) {
+ log.trace("LongFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());
+
+ // skip collecting because CalAndShortFlowsTask collects entire flow stats from a given switch sw
+ if (callCountLongFlowsTask == ENTIRE_POLL_TIMES) {
+ callCountLongFlowsTask = LONG_POLL_TIMES;
+ } else {
+ longFlowsTaskInternal();
+ callCountLongFlowsTask += LONG_POLL_TIMES;
+ }
+ }
+ }
+ }
+
+ private void longFlowsTaskInternal() {
+ deviceFlowTable.getLongFlows().forEach(fe -> {
+ ofFlowStatsRequestFlowSend(fe);
+ });
+ }
+
+ /**
+ * start adaptive flow statistic collection.
+ *
+ */
+ public synchronized void start() {
+ log.debug("Starting AdaptiveStats collection thread for {}", sw.getStringId());
+ callCountCalAndShortFlowsTask = 0;
+ callCountMidFlowsTask = 0;
+ callCountLongFlowsTask = 0;
+
+ isFirstTimeStart = true;
+
+ // Initially start polling quickly. Then drop down to configured value
+ calAndShortFlowsTask = new CalAndShortFlowsTask();
+ calAndShortFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ calAndShortFlowsTask,
+ 1,
+ calAndPollInterval,
+ TimeUnit.SECONDS);
+
+ midFlowsTask = new MidFlowsTask();
+ midFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ midFlowsTask,
+ 1,
+ midPollInterval,
+ TimeUnit.SECONDS);
+
+ longFlowsTask = new LongFlowsTask();
+ longFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ longFlowsTask,
+ 1,
+ longPollInterval,
+ TimeUnit.SECONDS);
+
+ log.info("Started");
+ }
+
+ /**
+ * stop adaptive flow statistic collection.
+ *
+ */
+ public synchronized void stop() {
+ log.debug("Stopping AdaptiveStats collection thread for {}", sw.getStringId());
+ if (calAndShortFlowsThread != null) {
+ calAndShortFlowsThread.cancel(true);
+ }
+ if (midFlowsThread != null) {
+ midFlowsThread.cancel(true);
+ }
+ if (longFlowsThread != null) {
+ longFlowsThread.cancel(true);
+ }
+
+ adaptiveFlowStatsScheduler.shutdownNow();
+
+ isFirstTimeStart = false;
+
+ log.info("Stopped");
+ }
+
+ /**
+ * add typed flow entry from flow rule into the internal flow table.
+ *
+ * @param flowRules the flow rules
+ *
+ */
+ public synchronized void addWithFlowRule(FlowRule... flowRules) {
+ for (FlowRule fr : flowRules) {
+ // First remove old entry unconditionally, if exist
+ deviceFlowTable.remove(fr);
+
+ // add new flow entry, we suppose IMMEDIATE_FLOW
+ TypedStoredFlowEntry newFlowEntry = new DefaultTypedFlowEntry(fr,
+ FlowLiveType.IMMEDIATE_FLOW);
+ deviceFlowTable.addWithCalAndSetFlowLiveType(newFlowEntry);
+ }
+ }
+
+ /**
+ * add or update typed flow entry from flow entry into the internal flow table.
+ *
+ * @param flowEntries the flow entries
+ *
+ */
+ public synchronized void addOrUpdateFlows(FlowEntry... flowEntries) {
+ for (FlowEntry fe : flowEntries) {
+ // check if this new rule is an update to an existing entry
+ TypedStoredFlowEntry stored = deviceFlowTable.getFlowEntry(fe);
+
+ if (stored != null) {
+ // duplicated flow entry is collected!, just skip
+ if (fe.bytes() == stored.bytes() && fe.packets() == stored.packets()
+ && fe.life() == stored.life()) {
+ log.debug("addOrUpdateFlows:, FlowId=" + Long.toHexString(fe.id().value())
+ + ",is DUPLICATED stats collection, just skip."
+ + " AdaptiveStats collection thread for {}",
+ sw.getStringId());
+
+ stored.setLastSeen();
+ continue;
+ } else if (fe.life() < stored.life()) {
+ // Invalid updates the stats values, i.e., bytes, packets, durations ...
+ log.debug("addOrUpdateFlows():" +
+ " Invalid Flow Update! The new life is SMALLER than the previous one, jus skip." +
+ " new flowId=" + Long.toHexString(fe.id().value()) +
+ ", old flowId=" + Long.toHexString(stored.id().value()) +
+ ", new bytes=" + fe.bytes() + ", old bytes=" + stored.bytes() +
+ ", new life=" + fe.life() + ", old life=" + stored.life() +
+ ", new lastSeen=" + fe.lastSeen() + ", old lastSeen=" + stored.lastSeen());
+ // go next
+ stored.setLastSeen();
+ continue;
+ }
+
+ // update now
+ stored.setLife(fe.life());
+ stored.setPackets(fe.packets());
+ stored.setBytes(fe.bytes());
+ stored.setLastSeen();
+ if (stored.state() == FlowEntry.FlowEntryState.PENDING_ADD) {
+ // flow is really RULE_ADDED
+ stored.setState(FlowEntry.FlowEntryState.ADDED);
+ }
+ // flow is RULE_UPDATED, skip adding and just updating flow live table
+ //deviceFlowTable.calAndSetFlowLiveType(stored);
+ continue;
+ }
+
+ // add new flow entry, we suppose IMMEDIATE_FLOW
+ TypedStoredFlowEntry newFlowEntry = new DefaultTypedFlowEntry(fe,
+ FlowLiveType.IMMEDIATE_FLOW);
+ deviceFlowTable.addWithCalAndSetFlowLiveType(newFlowEntry);
+ }
+ }
+
+ /**
+ * remove typed flow entry from the internal flow table.
+ *
+ * @param flowRules the flow entries
+ *
+ */
+ public synchronized void removeFlows(FlowRule... flowRules) {
+ for (FlowRule rule : flowRules) {
+ deviceFlowTable.remove(rule);
+ }
+ }
+
+ // same as removeFlows() function
+ /**
+ * remove typed flow entry from the internal flow table.
+ *
+ * @param flowRules the flow entries
+ *
+ */
+ public void flowRemoved(FlowRule... flowRules) {
+ removeFlows(flowRules);
+ }
+
+ // same as addOrUpdateFlows() function
+ /**
+ * add or update typed flow entry from flow entry into the internal flow table.
+ *
+ * @param flowEntries the flow entry list
+ *
+ */
+ public void pushFlowMetrics(List<FlowEntry> flowEntries) {
+ flowEntries.forEach(fe -> {
+ addOrUpdateFlows(fe);
+ });
+ }
+
+ /**
+ * returns flowMissingXid that indicates the execution of flowMissing process or not(NO_FLOW_MISSING_XID(-1)).
+ *
+ */
+ public long getFlowMissingXid() {
+ return flowMissingXid;
+ }
+
+ /**
+ * set flowMissingXid, namely OFFlowStatsRequest match any ALL message Id.
+ *
+ * @param flowMissingXid the OFFlowStatsRequest message Id
+ *
+ */
+ public void setFlowMissingXid(long flowMissingXid) {
+ this.flowMissingXid = flowMissingXid;
+ }
+
+ private class InternalDeviceFlowTable {
+
+ private final Map<FlowId, Set<TypedStoredFlowEntry>>
+ flowEntries = Maps.newConcurrentMap();
+
+ private final Set<StoredFlowEntry> shortFlows = new HashSet<>();
+ private final Set<StoredFlowEntry> midFlows = new HashSet<>();
+ private final Set<StoredFlowEntry> longFlows = new HashSet<>();
+
+ // Assumed latency adjustment(default=500 millisecond) between FlowStatsRequest and Reply
+ private final long latencyFlowStatsRequestAndReplyMillis = 500;
+
+
+ // Statistics for table operation
+ private long addCount = 0, addWithSetFlowLiveTypeCount = 0;
+ private long removeCount = 0;
+
+ /**
+ * Resets all count values with zero.
+ *
+ */
+ public void resetAllCount() {
+ addCount = 0;
+ addWithSetFlowLiveTypeCount = 0;
+ removeCount = 0;
+ }
+
+ // get set of flow entries for the given flowId
+ private Set<TypedStoredFlowEntry> getFlowEntriesInternal(FlowId flowId) {
+ return flowEntries.computeIfAbsent(flowId, id -> Sets.newCopyOnWriteArraySet());
+ }
+
+ // get flow entry for the given flow rule
+ private TypedStoredFlowEntry getFlowEntryInternal(FlowRule rule) {
+ Set<TypedStoredFlowEntry> flowEntries = getFlowEntriesInternal(rule.id());
+ return flowEntries.stream()
+ .filter(entry -> Objects.equal(entry, rule))
+ .findAny()
+ .orElse(null);
+ }
+
+ // get the flow entries for all flows in flow table
+ private Set<TypedStoredFlowEntry> getFlowEntriesInternal() {
+ Set<TypedStoredFlowEntry> result = Sets.newHashSet();
+
+ flowEntries.values().forEach(result::addAll);
+ return result;
+ }
+
+ /**
+ * Gets the number of flow entry in flow table.
+ *
+ * @return the number of flow entry.
+ *
+ */
+ public long getFlowCount() {
+ return flowEntries.values().stream().mapToLong(Set::size).sum();
+ }
+
+ /**
+ * Gets the number of flow entry in flow table.
+ *
+ * @param rule the flow rule
+ * @return the typed flow entry.
+ *
+ */
+ public TypedStoredFlowEntry getFlowEntry(FlowRule rule) {
+ checkNotNull(rule);
+
+ return getFlowEntryInternal(rule);
+ }
+
+ /**
+ * Gets the all typed flow entries in flow table.
+ *
+ * @return the set of typed flow entry.
+ *
+ */
+ public Set<TypedStoredFlowEntry> getFlowEntries() {
+ return getFlowEntriesInternal();
+ }
+
+ /**
+ * Gets the short typed flow entries in flow table.
+ *
+ * @return the set of typed flow entry.
+ *
+ */
+ public Set<StoredFlowEntry> getShortFlows() {
+ return ImmutableSet.copyOf(shortFlows); //Sets.newHashSet(shortFlows);
+ }
+
+ /**
+ * Gets the mid typed flow entries in flow table.
+ *
+ * @return the set of typed flow entry.
+ *
+ */
+ public Set<StoredFlowEntry> getMidFlows() {
+ return ImmutableSet.copyOf(midFlows); //Sets.newHashSet(midFlows);
+ }
+
+ /**
+ * Gets the long typed flow entries in flow table.
+ *
+ * @return the set of typed flow entry.
+ *
+ */
+ public Set<StoredFlowEntry> getLongFlows() {
+ return ImmutableSet.copyOf(longFlows); //Sets.newHashSet(longFlows);
+ }
+
+ /**
+ * Add typed flow entry into table only.
+ *
+ * @param rule the flow rule
+ *
+ */
+ public synchronized void add(TypedStoredFlowEntry rule) {
+ checkNotNull(rule);
+
+ //rule have to be new DefaultTypedFlowEntry
+ boolean result = getFlowEntriesInternal(rule.id()).add(rule);
+
+ if (result) {
+ addCount++;
+ }
+ }
+
+ /**
+ * Calculates and set the flow live type at the first time,
+ * and then add it into a corresponding typed flow table.
+ *
+ * @param rule the flow rule
+ *
+ */
+ public void calAndSetFlowLiveType(TypedStoredFlowEntry rule) {
+ checkNotNull(rule);
+
+ calAndSetFlowLiveTypeInternal(rule);
+ }
+
+ /**
+ * Add the typed flow entry into table, and calculates and set the flow live type,
+ * and then add it into a corresponding typed flow table.
+ *
+ * @param rule the flow rule
+ *
+ */
+ public synchronized void addWithCalAndSetFlowLiveType(TypedStoredFlowEntry rule) {
+ checkNotNull(rule);
+
+ //rule have to be new DefaultTypedFlowEntry
+ boolean result = getFlowEntriesInternal(rule.id()).add(rule);
+ if (result) {
+ calAndSetFlowLiveTypeInternal(rule);
+ addWithSetFlowLiveTypeCount++;
+ } else {
+ log.debug("addWithCalAndSetFlowLiveType, FlowId=" + Long.toHexString(rule.id().value())
+ + " ADD Failed, cause it may already exists in table !!!,"
+ + " AdaptiveStats collection thread for {}",
+ sw.getStringId());
+ }
+ }
+
+ // In real, calculates and set the flow live type at the first time,
+ // and then add it into a corresponding typed flow table
+ private void calAndSetFlowLiveTypeInternal(TypedStoredFlowEntry rule) {
+ long life = rule.life();
+ FlowLiveType prevFlowLiveType = rule.flowLiveType();
+
+ if (life >= longPollInterval) {
+ rule.setFlowLiveType(FlowLiveType.LONG_FLOW);
+ longFlows.add(rule);
+ } else if (life >= midPollInterval) {
+ rule.setFlowLiveType(FlowLiveType.MID_FLOW);
+ midFlows.add(rule);
+ } else if (life >= calAndPollInterval) {
+ rule.setFlowLiveType(FlowLiveType.SHORT_FLOW);
+ shortFlows.add(rule);
+ } else if (life >= 0) {
+ rule.setFlowLiveType(FlowLiveType.IMMEDIATE_FLOW);
+ } else { // life < 0
+ rule.setFlowLiveType(FlowLiveType.UNKNOWN_FLOW);
+ }
+
+ if (rule.flowLiveType() != prevFlowLiveType) {
+ switch (prevFlowLiveType) {
+ // delete it from previous flow table
+ case SHORT_FLOW:
+ shortFlows.remove(rule);
+ break;
+ case MID_FLOW:
+ midFlows.remove(rule);
+ break;
+ case LONG_FLOW:
+ longFlows.remove(rule);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+
+ // check the flow live type based on current time, then set and add it into corresponding table
+ private boolean checkAndMoveLiveFlowInternal(TypedStoredFlowEntry fe, long cTime) {
+ long curTime = (cTime > 0 ? cTime : System.currentTimeMillis());
+ // For latency adjustment(default=500 millisecond) between FlowStatsRequest and Reply
+ long fromLastSeen = ((curTime - fe.lastSeen() + latencyFlowStatsRequestAndReplyMillis) / 1000);
+ // fe.life() unit is SECOND!
+ long liveTime = fe.life() + fromLastSeen;
+
+
+ switch (fe.flowLiveType()) {
+ case IMMEDIATE_FLOW:
+ if (liveTime >= longPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.LONG_FLOW);
+ longFlows.add(fe);
+ } else if (liveTime >= midPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.MID_FLOW);
+ midFlows.add(fe);
+ } else if (liveTime >= calAndPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.SHORT_FLOW);
+ shortFlows.add(fe);
+ }
+ break;
+ case SHORT_FLOW:
+ if (liveTime >= longPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.LONG_FLOW);
+ shortFlows.remove(fe);
+ longFlows.add(fe);
+ } else if (liveTime >= midPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.MID_FLOW);
+ shortFlows.remove(fe);
+ midFlows.add(fe);
+ }
+ break;
+ case MID_FLOW:
+ if (liveTime >= longPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.LONG_FLOW);
+ midFlows.remove(fe);
+ longFlows.add(fe);
+ }
+ break;
+ case LONG_FLOW:
+ if (fromLastSeen > entirePollInterval) {
+ log.trace("checkAndMoveLiveFlowInternal, flow is already removed at switch.");
+ return false;
+ }
+ break;
+ case UNKNOWN_FLOW: // Unknown flow is an internal error flow type, just fall through
+ default :
+ // Error Unknown Live Type
+ log.error("checkAndMoveLiveFlowInternal, Unknown Live Type error!"
+ + "AdaptiveStats collection thread for {}",
+ sw.getStringId());
+ return false;
+ }
+
+ log.debug("checkAndMoveLiveFlowInternal, FlowId=" + Long.toHexString(fe.id().value())
+ + ", state=" + fe.state()
+ + ", After liveType=" + fe.flowLiveType()
+ + ", liveTime=" + liveTime
+ + ", life=" + fe.life()
+ + ", bytes=" + fe.bytes()
+ + ", packets=" + fe.packets()
+ + ", fromLastSeen=" + fromLastSeen
+ + ", priority=" + fe.priority()
+ + ", selector=" + fe.selector().criteria()
+ + ", treatment=" + fe.treatment()
+ + " AdaptiveStats collection thread for {}",
+ sw.getStringId());
+
+ return true;
+ }
+
+ /**
+ * Check and move live type for all type flow entries in table at every calAndPollInterval time.
+ *
+ */
+ public void checkAndMoveLiveFlowAll() {
+ Set<TypedStoredFlowEntry> typedFlowEntries = getFlowEntriesInternal();
+
+ long calCurTime = System.currentTimeMillis();
+ typedFlowEntries.forEach(fe -> {
+ if (!checkAndMoveLiveFlowInternal(fe, calCurTime)) {
+ remove(fe);
+ }
+ });
+
+ // print table counts for debug
+ if (log.isDebugEnabled()) {
+ synchronized (this) {
+ long totalFlowCount = getFlowCount();
+ long shortFlowCount = shortFlows.size();
+ long midFlowCount = midFlows.size();
+ long longFlowCount = longFlows.size();
+ long immediateFlowCount = totalFlowCount - shortFlowCount - midFlowCount - longFlowCount;
+ long calTotalCount = addCount + addWithSetFlowLiveTypeCount - removeCount;
+
+ log.debug("--------------------------------------------------------------------------- for {}",
+ sw.getStringId());
+ log.debug("checkAndMoveLiveFlowAll, Total Flow_Count=" + totalFlowCount
+ + ", add - remove_Count=" + calTotalCount
+ + ", IMMEDIATE_FLOW_Count=" + immediateFlowCount
+ + ", SHORT_FLOW_Count=" + shortFlowCount
+ + ", MID_FLOW_Count=" + midFlowCount
+ + ", LONG_FLOW_Count=" + longFlowCount
+ + ", add_Count=" + addCount
+ + ", addWithSetFlowLiveType_Count=" + addWithSetFlowLiveTypeCount
+ + ", remove_Count=" + removeCount
+ + " AdaptiveStats collection thread for {}", sw.getStringId());
+ log.debug("--------------------------------------------------------------------------- for {}",
+ sw.getStringId());
+ if (totalFlowCount != calTotalCount) {
+ log.error("checkAndMoveLiveFlowAll, Real total flow count and "
+ + "calculated total flow count do NOT match, something is wrong internally "
+ + "or check counter value bound is over!");
+ }
+ if (immediateFlowCount < 0) {
+ log.error("checkAndMoveLiveFlowAll, IMMEDIATE_FLOW count is negative, "
+ + "something is wrong internally "
+ + "or check counter value bound is over!");
+ }
+ }
+ }
+ log.trace("checkAndMoveLiveFlowAll, AdaptiveStats for {}", sw.getStringId());
+ }
+
+ /**
+ * Remove the typed flow entry from table.
+ *
+ * @param rule the flow rule
+ *
+ */
+ public synchronized void remove(FlowRule rule) {
+ checkNotNull(rule);
+
+ TypedStoredFlowEntry removeStore = getFlowEntryInternal(rule);
+ if (removeStore != null) {
+ removeLiveFlowsInternal((TypedStoredFlowEntry) removeStore);
+ boolean result = getFlowEntriesInternal(rule.id()).remove(removeStore);
+
+ if (result) {
+ removeCount++;
+ }
+ }
+ }
+
+ // Remove the typed flow entry from corresponding table
+ private void removeLiveFlowsInternal(TypedStoredFlowEntry fe) {
+ switch (fe.flowLiveType()) {
+ case IMMEDIATE_FLOW:
+ // do nothing
+ break;
+ case SHORT_FLOW:
+ shortFlows.remove(fe);
+ break;
+ case MID_FLOW:
+ midFlows.remove(fe);
+ break;
+ case LONG_FLOW:
+ longFlows.remove(fe);
+ break;
+ default: // error in Flow Live Type
+ log.error("removeLiveFlowsInternal, Unknown Live Type error!");
+ break;
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java index de079e03..6374ca55 100644 --- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 Open Networking Laboratory + * Copyright 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. @@ -21,6 +21,7 @@ import com.google.common.cache.RemovalCause; import com.google.common.cache.RemovalNotification; import com.google.common.collect.Maps; import com.google.common.collect.Sets; + import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -32,6 +33,7 @@ import org.onosproject.cfg.ComponentConfigService; import org.onosproject.core.ApplicationId; import org.onosproject.net.DeviceId; import org.onosproject.net.flow.CompletedBatchOperation; +import org.onosproject.net.flow.DefaultTableStatisticsEntry; import org.onosproject.net.flow.FlowEntry; import org.onosproject.net.flow.FlowRule; import org.onosproject.net.flow.FlowRuleBatchEntry; @@ -40,6 +42,7 @@ import org.onosproject.net.flow.FlowRuleExtPayLoad; import org.onosproject.net.flow.FlowRuleProvider; import org.onosproject.net.flow.FlowRuleProviderRegistry; import org.onosproject.net.flow.FlowRuleProviderService; +import org.onosproject.net.flow.TableStatisticsEntry; import org.onosproject.net.provider.AbstractProvider; import org.onosproject.net.provider.ProviderId; import org.onosproject.net.statistic.DefaultLoad; @@ -58,6 +61,8 @@ import org.projectfloodlight.openflow.protocol.OFErrorType; import org.projectfloodlight.openflow.protocol.OFFlowMod; import org.projectfloodlight.openflow.protocol.OFFlowRemoved; import org.projectfloodlight.openflow.protocol.OFFlowStatsReply; +import org.projectfloodlight.openflow.protocol.OFTableStatsReply; +import org.projectfloodlight.openflow.protocol.OFTableStatsEntry; import org.projectfloodlight.openflow.protocol.OFMessage; import org.projectfloodlight.openflow.protocol.OFPortStatus; import org.projectfloodlight.openflow.protocol.OFStatsReply; @@ -70,12 +75,14 @@ import java.util.Collections; import java.util.Dictionary; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.Timer; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Strings.isNullOrEmpty; import static org.onlab.util.Tools.get; import static org.slf4j.LoggerFactory.getLogger; @@ -99,11 +106,16 @@ public class OpenFlowRuleProvider extends AbstractProvider @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ComponentConfigService cfgService; - private static final int DEFAULT_POLL_FREQUENCY = 10; + private static final int DEFAULT_POLL_FREQUENCY = 5; @Property(name = "flowPollFrequency", intValue = DEFAULT_POLL_FREQUENCY, label = "Frequency (in seconds) for polling flow statistics") private int flowPollFrequency = DEFAULT_POLL_FREQUENCY; + private static final boolean DEFAULT_ADAPTIVE_FLOW_SAMPLING = true; + @Property(name = "adaptiveFlowSampling", boolValue = DEFAULT_ADAPTIVE_FLOW_SAMPLING, + label = "Adaptive Flow Sampling is on or off") + private boolean adaptiveFlowSampling = DEFAULT_ADAPTIVE_FLOW_SAMPLING; + private FlowRuleProviderService providerService; private final InternalFlowProvider listener = new InternalFlowProvider(); @@ -111,7 +123,12 @@ public class OpenFlowRuleProvider extends AbstractProvider private Cache<Long, InternalCacheEntry> pendingBatches; private final Timer timer = new Timer("onos-openflow-collector"); + private final Map<Dpid, FlowStatsCollector> simpleCollectors = Maps.newHashMap(); + + // NewAdaptiveFlowStatsCollector Set + private final Map<Dpid, NewAdaptiveFlowStatsCollector> afsCollectors = Maps.newHashMap(); private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap(); + private final Map<Dpid, TableStatisticsCollector> tableStatsCollectors = Maps.newHashMap(); /** * Creates an OpenFlow host provider. @@ -128,9 +145,11 @@ public class OpenFlowRuleProvider extends AbstractProvider controller.addEventListener(listener); pendingBatches = createBatchCache(); + createCollectors(); - log.info("Started"); + log.info("Started with flowPollFrequency = {}, adaptiveFlowSampling = {}", + flowPollFrequency, adaptiveFlowSampling); } @Deactivate @@ -161,6 +180,20 @@ public class OpenFlowRuleProvider extends AbstractProvider } log.info("Settings: flowPollFrequency={}", flowPollFrequency); + + boolean newAdaptiveFlowSampling; + String s = get(properties, "adaptiveFlowSampling"); + newAdaptiveFlowSampling = isNullOrEmpty(s) ? adaptiveFlowSampling : Boolean.parseBoolean(s.trim()); + + if (newAdaptiveFlowSampling != adaptiveFlowSampling) { + // stop previous collector + stopCollectors(); + adaptiveFlowSampling = newAdaptiveFlowSampling; + // create new collectors + createCollectors(); + } + + log.info("Settings: adaptiveFlowSampling={}", adaptiveFlowSampling); } private Cache<Long, InternalCacheEntry> createBatchCache() { @@ -179,19 +212,43 @@ public class OpenFlowRuleProvider extends AbstractProvider } private void createCollector(OpenFlowSwitch sw) { - FlowStatsCollector fsc = new FlowStatsCollector(timer, sw, flowPollFrequency); - fsc.start(); - collectors.put(new Dpid(sw.getId()), fsc); + if (adaptiveFlowSampling) { + // NewAdaptiveFlowStatsCollector Constructor + NewAdaptiveFlowStatsCollector fsc = new NewAdaptiveFlowStatsCollector(sw, flowPollFrequency); + fsc.start(); + afsCollectors.put(new Dpid(sw.getId()), fsc); + } else { + FlowStatsCollector fsc = new FlowStatsCollector(timer, sw, flowPollFrequency); + fsc.start(); + simpleCollectors.put(new Dpid(sw.getId()), fsc); + } + TableStatisticsCollector tsc = new TableStatisticsCollector(timer, sw, flowPollFrequency); + tsc.start(); + tableStatsCollectors.put(new Dpid(sw.getId()), tsc); } private void stopCollectors() { - collectors.values().forEach(FlowStatsCollector::stop); - collectors.clear(); + if (adaptiveFlowSampling) { + // NewAdaptiveFlowStatsCollector Destructor + afsCollectors.values().forEach(NewAdaptiveFlowStatsCollector::stop); + afsCollectors.clear(); + } else { + simpleCollectors.values().forEach(FlowStatsCollector::stop); + simpleCollectors.clear(); + } + tableStatsCollectors.values().forEach(TableStatisticsCollector::stop); + tableStatsCollectors.clear(); } private void adjustRate() { DefaultLoad.setPollInterval(flowPollFrequency); - collectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency)); + if (adaptiveFlowSampling) { + // NewAdaptiveFlowStatsCollector calAndPollInterval + afsCollectors.values().forEach(fsc -> fsc.adjustCalAndPollInterval(flowPollFrequency)); + } else { + simpleCollectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency)); + } + tableStatsCollectors.values().forEach(tsc -> tsc.adjustPollInterval(flowPollFrequency)); } @Override @@ -202,8 +259,9 @@ public class OpenFlowRuleProvider extends AbstractProvider } private void applyRule(FlowRule flowRule) { - OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId() - .uri())); + Dpid dpid = Dpid.dpid(flowRule.deviceId().uri()); + OpenFlowSwitch sw = controller.getSwitch(dpid); + FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad(); if (hasPayload(flowRuleExtPayLoad)) { OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad()); @@ -212,6 +270,14 @@ public class OpenFlowRuleProvider extends AbstractProvider } sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(), Optional.empty()).buildFlowAdd()); + + if (adaptiveFlowSampling) { + // Add TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid); + if (collector != null) { + collector.addWithFlowRule(flowRule); + } + } } @Override @@ -222,8 +288,9 @@ public class OpenFlowRuleProvider extends AbstractProvider } private void removeRule(FlowRule flowRule) { - OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId() - .uri())); + Dpid dpid = Dpid.dpid(flowRule.deviceId().uri()); + OpenFlowSwitch sw = controller.getSwitch(dpid); + FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad(); if (hasPayload(flowRuleExtPayLoad)) { OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad()); @@ -232,6 +299,14 @@ public class OpenFlowRuleProvider extends AbstractProvider } sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(), Optional.empty()).buildFlowDel()); + + if (adaptiveFlowSampling) { + // Remove TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid); + if (collector != null) { + collector.removeFlows(flowRule); + } + } } @Override @@ -242,11 +317,12 @@ public class OpenFlowRuleProvider extends AbstractProvider @Override public void executeBatch(FlowRuleBatchOperation batch) { + checkNotNull(batch); pendingBatches.put(batch.id(), new InternalCacheEntry(batch)); - OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(batch.deviceId() - .uri())); + Dpid dpid = Dpid.dpid(batch.deviceId().uri()); + OpenFlowSwitch sw = controller.getSwitch(dpid); OFFlowMod mod; for (FlowRuleBatchEntry fbe : batch.getOperations()) { // flow is the third party privacy flow @@ -257,21 +333,35 @@ public class OpenFlowRuleProvider extends AbstractProvider sw.sendMsg(msg); continue; } - FlowModBuilder builder = FlowModBuilder.builder(fbe.target(), sw - .factory(), Optional.of(batch.id())); + FlowModBuilder builder = + FlowModBuilder.builder(fbe.target(), sw.factory(), Optional.of(batch.id())); + NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid); switch (fbe.operator()) { case ADD: mod = builder.buildFlowAdd(); + if (adaptiveFlowSampling && collector != null) { + // Add TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + collector.addWithFlowRule(fbe.target()); + } break; case REMOVE: mod = builder.buildFlowDel(); + if (adaptiveFlowSampling && collector != null) { + // Remove TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + collector.removeFlows(fbe.target()); + } break; case MODIFY: mod = builder.buildFlowMod(); + if (adaptiveFlowSampling && collector != null) { + // Add or Update TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + // afsCollectors.get(dpid).addWithFlowRule(fbe.target()); //check if add is good or not + collector.addOrUpdateFlows((FlowEntry) fbe.target()); + } break; default: log.error("Unsupported batch operation {}; skipping flowmod {}", - fbe.operator(), fbe); + fbe.operator(), fbe); continue; } sw.sendMsg(mod); @@ -292,14 +382,28 @@ public class OpenFlowRuleProvider extends AbstractProvider @Override public void switchAdded(Dpid dpid) { + + OpenFlowSwitch sw = controller.getSwitch(dpid); + createCollector(controller.getSwitch(dpid)); } @Override public void switchRemoved(Dpid dpid) { - FlowStatsCollector collector = collectors.remove(dpid); - if (collector != null) { - collector.stop(); + if (adaptiveFlowSampling) { + NewAdaptiveFlowStatsCollector collector = afsCollectors.remove(dpid); + if (collector != null) { + collector.stop(); + } + } else { + FlowStatsCollector collector = simpleCollectors.remove(dpid); + if (collector != null) { + collector.stop(); + } + } + TableStatisticsCollector tsc = tableStatsCollectors.remove(dpid); + if (tsc != null) { + tsc.stop(); } } @@ -321,10 +425,20 @@ public class OpenFlowRuleProvider extends AbstractProvider FlowEntry fr = new FlowEntryBuilder(dpid, removed).build(); providerService.flowRemoved(fr); + + if (adaptiveFlowSampling) { + // Removed TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid); + if (collector != null) { + collector.flowRemoved(fr); + } + } break; case STATS_REPLY: if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW) { pushFlowMetrics(dpid, (OFFlowStatsReply) msg); + } else if (((OFStatsReply) msg).getStatsType() == OFStatsType.TABLE) { + pushTableStatistics(dpid, (OFTableStatsReply) msg); } break; case BARRIER_REPLY: @@ -370,11 +484,10 @@ public class OpenFlowRuleProvider extends AbstractProvider + " tell us which one."); } } - break; + default: log.debug("Unhandled message type: {}", msg.getType()); } - } @Override @@ -386,13 +499,68 @@ public class OpenFlowRuleProvider extends AbstractProvider private void pushFlowMetrics(Dpid dpid, OFFlowStatsReply replies) { DeviceId did = DeviceId.deviceId(Dpid.uri(dpid)); - OpenFlowSwitch sw = controller.getSwitch(dpid); List<FlowEntry> flowEntries = replies.getEntries().stream() .map(entry -> new FlowEntryBuilder(dpid, entry).build()) .collect(Collectors.toList()); - providerService.pushFlowMetrics(did, flowEntries); + if (adaptiveFlowSampling) { + NewAdaptiveFlowStatsCollector afsc = afsCollectors.get(dpid); + + synchronized (afsc) { + if (afsc.getFlowMissingXid() != NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID) { + log.debug("OpenFlowRuleProvider:pushFlowMetrics, flowMissingXid={}, " + + "OFFlowStatsReply Xid={}, for {}", + afsc.getFlowMissingXid(), replies.getXid(), dpid); + } + + // Check that OFFlowStatsReply Xid is same with the one of OFFlowStatsRequest? + if (afsc.getFlowMissingXid() != NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID) { + if (afsc.getFlowMissingXid() == replies.getXid()) { + // call entire flow stats update with flowMissing synchronization. + // used existing pushFlowMetrics + providerService.pushFlowMetrics(did, flowEntries); + } + // reset flowMissingXid to NO_FLOW_MISSING_XID + afsc.setFlowMissingXid(NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID); + + } else { + // call individual flow stats update + providerService.pushFlowMetricsWithoutFlowMissing(did, flowEntries); + } + + // Update TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + afsc.pushFlowMetrics(flowEntries); + } + } else { + // call existing entire flow stats update with flowMissing synchronization + providerService.pushFlowMetrics(did, flowEntries); + } + } + + private void pushTableStatistics(Dpid dpid, OFTableStatsReply replies) { + + DeviceId did = DeviceId.deviceId(Dpid.uri(dpid)); + List<TableStatisticsEntry> tableStatsEntries = replies.getEntries().stream() + .map(entry -> buildTableStatistics(did, entry)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + providerService.pushTableStatistics(did, tableStatsEntries); + } + + private TableStatisticsEntry buildTableStatistics(DeviceId deviceId, + OFTableStatsEntry ofEntry) { + TableStatisticsEntry entry = null; + if (ofEntry != null) { + entry = new DefaultTableStatisticsEntry(deviceId, + ofEntry.getTableId().getValue(), + ofEntry.getActiveCount(), + ofEntry.getLookupCount().getValue(), + ofEntry.getMatchedCount().getValue()); + } + + return entry; + } } diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java new file mode 100644 index 00000000..922a470a --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java @@ -0,0 +1,95 @@ +/* + * Copyright 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.provider.of.flow.impl; + +import org.onlab.util.SharedExecutors; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFTableStatsRequest; +import org.slf4j.Logger; + +import java.util.Timer; +import java.util.TimerTask; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Collects Table statistics for the specified switch. + */ +class TableStatisticsCollector { + + private final Logger log = getLogger(getClass()); + + public static final int SECONDS = 1000; + + private final OpenFlowSwitch sw; + private Timer timer; + private TimerTask task; + + private int pollInterval; + + /** + * Creates a new table statistics collector for the given switch and poll frequency. + * + * @param timer timer to use for scheduling + * @param sw switch to pull + * @param pollInterval poll frequency in seconds + */ + TableStatisticsCollector(Timer timer, OpenFlowSwitch sw, int pollInterval) { + this.timer = timer; + this.sw = sw; + this.pollInterval = pollInterval; + } + + /** + * Adjusts poll frequency. + * + * @param pollInterval poll frequency in seconds + */ + synchronized void adjustPollInterval(int pollInterval) { + this.pollInterval = pollInterval; + task.cancel(); + task = new InternalTimerTask(); + timer.scheduleAtFixedRate(task, pollInterval * SECONDS, pollInterval * 1000); + } + + private class InternalTimerTask extends TimerTask { + @Override + public void run() { + if (sw.getRole() == RoleState.MASTER) { + log.trace("Collecting stats for {}", sw.getStringId()); + OFTableStatsRequest request = sw.factory().buildTableStatsRequest() + .build(); + sw.sendMsg(request); + } + } + } + + public synchronized void start() { + // Initially start polling quickly. Then drop down to configured value + log.debug("Starting Table Stats collection thread for {}", sw.getStringId()); + task = new InternalTimerTask(); + SharedExecutors.getTimer().scheduleAtFixedRate(task, 1 * SECONDS, + pollInterval * SECONDS); + } + + public synchronized void stop() { + log.debug("Stopping Table Stats collection thread for {}", sw.getStringId()); + task.cancel(); + task = null; + } + +} diff --git a/framework/src/onos/providers/ovsdb/device/src/test/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProviderTest.java b/framework/src/onos/providers/ovsdb/device/src/test/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProviderTest.java index 5e4c5677..7663a64d 100644 --- a/framework/src/onos/providers/ovsdb/device/src/test/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProviderTest.java +++ b/framework/src/onos/providers/ovsdb/device/src/test/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProviderTest.java @@ -27,6 +27,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.onlab.packet.IpAddress; +import org.onlab.packet.TpPort; import org.onosproject.net.DeviceId; import org.onosproject.net.MastershipRole; import org.onosproject.net.device.DeviceDescription; @@ -193,6 +194,10 @@ public class OvsdbDeviceProviderTest { return null; } + @Override + public void connect(IpAddress ip, TpPort port) { + + } } } diff --git a/framework/src/onos/providers/ovsdb/host/src/test/java/org/onosproject/ovsdb/provider/host/OvsdbHostProviderTest.java b/framework/src/onos/providers/ovsdb/host/src/test/java/org/onosproject/ovsdb/provider/host/OvsdbHostProviderTest.java index ad720c85..01e07dd8 100644 --- a/framework/src/onos/providers/ovsdb/host/src/test/java/org/onosproject/ovsdb/provider/host/OvsdbHostProviderTest.java +++ b/framework/src/onos/providers/ovsdb/host/src/test/java/org/onosproject/ovsdb/provider/host/OvsdbHostProviderTest.java @@ -24,7 +24,9 @@ import java.util.Set; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; +import org.onlab.packet.TpPort; import org.onosproject.net.DeviceId; import org.onosproject.net.HostId; import org.onosproject.net.host.HostDescription; @@ -159,6 +161,11 @@ public class OvsdbHostProviderTest { removeCount++; } + @Override + public void removeIpFromHost(HostId hostId, IpAddress ipAddress) { + + } + } private class OvsdbControllerTest implements OvsdbController { @@ -195,5 +202,10 @@ public class OvsdbHostProviderTest { public OvsdbClientService getOvsdbClient(OvsdbNodeId nodeId) { return null; } + + @Override + public void connect(IpAddress ip, TpPort port) { + + } } } |