diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:15:21 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:15:21 -0700 |
commit | 13d05bc8458758ee39cb829098241e89616717ee (patch) | |
tree | 22a4d1ce65f15952f07a3df5af4b462b4697cb3a /framework/src/onos/providers/host | |
parent | 6139282e1e93c2322076de4b91b1c85d0bc4a8b3 (diff) |
ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60
Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd
Diffstat (limited to 'framework/src/onos/providers/host')
4 files changed, 890 insertions, 0 deletions
diff --git a/framework/src/onos/providers/host/pom.xml b/framework/src/onos/providers/host/pom.xml new file mode 100644 index 00000000..d5231f8d --- /dev/null +++ b/framework/src/onos/providers/host/pom.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2014 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-providers</artifactId> + <version>1.3.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-host-provider</artifactId> + <packaging>bundle</packaging> + + <description>ONOS host tracking provider</description> + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-osgi</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + +</project> 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 new file mode 100644 index 00000000..9a823630 --- /dev/null +++ b/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java @@ -0,0 +1,439 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.provider.host.impl; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Modified; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onlab.packet.ARP; +import org.onlab.packet.Ethernet; +import org.onlab.packet.ICMP6; +import org.onlab.packet.IPacket; +import org.onlab.packet.IPv6; +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onlab.packet.ipv6.IExtensionHeader; +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.ComponentConfigService; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.Device; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.HostLocation; +import org.onosproject.net.device.DeviceEvent; +import org.onosproject.net.device.DeviceListener; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.host.DefaultHostDescription; +import org.onosproject.net.host.HostDescription; +import org.onosproject.net.host.HostProvider; +import org.onosproject.net.host.HostProviderRegistry; +import org.onosproject.net.host.HostProviderService; +import org.onosproject.net.host.HostService; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketPriority; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketService; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.net.topology.Topology; +import org.onosproject.net.topology.TopologyService; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; + +import java.util.Dictionary; +import java.util.Set; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Provider which uses an OpenFlow controller to detect network end-station + * hosts. + */ +@Component(immediate = true) +public class HostLocationProvider extends AbstractProvider implements HostProvider { + + private final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected HostProviderRegistry providerRegistry; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PacketService packetService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected TopologyService topologyService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected HostService hostService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceService deviceService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ComponentConfigService cfgService; + + private HostProviderService providerService; + + private final InternalHostProvider processor = new InternalHostProvider(); + private final DeviceListener deviceListener = new InternalDeviceListener(); + + private ApplicationId appId; + + @Property(name = "hostRemovalEnabled", boolValue = true, + label = "Enable host removal on port/device down events") + private boolean hostRemovalEnabled = true; + + @Property(name = "ipv6NeighborDiscovery", boolValue = false, + label = "Enable using IPv6 Neighbor Discovery by the " + + "Host Location Provider; default is false") + private boolean ipv6NeighborDiscovery = false; + + /** + * Creates an OpenFlow host provider. + */ + public HostLocationProvider() { + super(new ProviderId("of", "org.onosproject.provider.host")); + } + + @Activate + public void activate(ComponentContext context) { + cfgService.registerProperties(getClass()); + appId = coreService.registerApplication("org.onosproject.provider.host"); + + providerService = providerRegistry.register(this); + packetService.addProcessor(processor, PacketProcessor.advisor(1)); + deviceService.addListener(deviceListener); + readComponentConfiguration(context); + requestIntercepts(); + + log.info("Started with Application ID {}", appId.id()); + } + + @Deactivate + public void deactivate() { + cfgService.unregisterProperties(getClass(), false); + + withdrawIntercepts(); + + providerRegistry.unregister(this); + packetService.removeProcessor(processor); + deviceService.removeListener(deviceListener); + providerService = null; + log.info("Stopped"); + } + + @Modified + public void modified(ComponentContext context) { + readComponentConfiguration(context); + requestIntercepts(); + } + + /** + * Request packet intercepts. + */ + private void requestIntercepts() { + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + selector.matchEthType(Ethernet.TYPE_ARP); + packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId); + + // IPv6 Neighbor Solicitation packet. + selector.matchEthType(Ethernet.TYPE_IPV6); + selector.matchIPProtocol(IPv6.PROTOCOL_ICMP6); + selector.matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION); + if (ipv6NeighborDiscovery) { + packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId); + } else { + packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId); + } + + // IPv6 Neighbor Advertisement packet. + selector.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT); + if (ipv6NeighborDiscovery) { + packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId); + } else { + packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId); + } + } + + /** + * Withdraw packet intercepts. + */ + private void withdrawIntercepts() { + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + selector.matchEthType(Ethernet.TYPE_ARP); + packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId); + + // IPv6 Neighbor Solicitation packet. + selector.matchEthType(Ethernet.TYPE_IPV6); + selector.matchIPProtocol(IPv6.PROTOCOL_ICMP6); + selector.matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION); + packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId); + + // IPv6 Neighbor Advertisement packet. + selector.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT); + packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId); + } + + /** + * Extracts properties from the component configuration context. + * + * @param context the component context + */ + private void readComponentConfiguration(ComponentContext context) { + Dictionary<?, ?> properties = context.getProperties(); + Boolean flag; + + flag = isPropertyEnabled(properties, "hostRemovalEnabled"); + if (flag == null) { + log.info("Host removal on port/device down events is not configured, " + + "using current value of {}", hostRemovalEnabled); + } else { + hostRemovalEnabled = flag; + log.info("Configured. Host removal on port/device down events is {}", + hostRemovalEnabled ? "enabled" : "disabled"); + } + + flag = isPropertyEnabled(properties, "ipv6NeighborDiscovery"); + if (flag == null) { + log.info("Using IPv6 Neighbor Discovery is not configured, " + + "using current value of {}", ipv6NeighborDiscovery); + } else { + ipv6NeighborDiscovery = flag; + log.info("Configured. Using IPv6 Neighbor Discovery is {}", + ipv6NeighborDiscovery ? "enabled" : "disabled"); + } + } + + /** + * Check property name is defined and set to true. + * + * @param properties properties to be looked up + * @param propertyName the name of the property to look up + * @return value when the propertyName is defined or return null + */ + private static Boolean isPropertyEnabled(Dictionary<?, ?> properties, + String propertyName) { + Boolean value = null; + try { + String s = (String) properties.get(propertyName); + value = isNullOrEmpty(s) ? null : s.trim().equals("true"); + } catch (ClassCastException e) { + // No propertyName defined. + value = null; + } + return value; + } + + @Override + public void triggerProbe(Host host) { + log.info("Triggering probe on device {}", host); + } + + private class InternalHostProvider implements PacketProcessor { + /** + * Update host location only. + * + * @param hid host ID + * @param mac source Mac address + * @param vlan VLAN ID + * @param hloc host location + */ + private void updateLocation(HostId hid, MacAddress mac, + VlanId vlan, HostLocation hloc) { + HostDescription desc = new DefaultHostDescription(mac, vlan, hloc); + try { + providerService.hostDetected(hid, desc); + } catch (IllegalStateException e) { + log.debug("Host {} suppressed", hid); + } + } + + /** + * Update host location and IP address. + * + * @param hid host ID + * @param mac source Mac address + * @param vlan VLAN ID + * @param hloc host location + * @param ip source IP address + */ + private void updateLocationIP(HostId hid, MacAddress mac, + VlanId vlan, HostLocation hloc, + IpAddress ip) { + HostDescription desc = ip.isZero() || ip.isSelfAssigned() ? + new DefaultHostDescription(mac, vlan, hloc) : + new DefaultHostDescription(mac, vlan, hloc, ip); + try { + providerService.hostDetected(hid, desc); + } catch (IllegalStateException e) { + log.debug("Host {} suppressed", hid); + } + } + + @Override + public void process(PacketContext context) { + if (context == null) { + return; + } + + Ethernet eth = context.inPacket().parsed(); + if (eth == null) { + return; + } + MacAddress srcMac = eth.getSourceMAC(); + + VlanId vlan = VlanId.vlanId(eth.getVlanID()); + ConnectPoint heardOn = context.inPacket().receivedFrom(); + + // If this arrived on control port, bail out. + if (heardOn.port().isLogical()) { + return; + } + + // If this is not an edge port, bail out. + Topology topology = topologyService.currentTopology(); + if (topologyService.isInfrastructure(topology, heardOn)) { + return; + } + + HostLocation hloc = new HostLocation(heardOn, System.currentTimeMillis()); + HostId hid = HostId.hostId(eth.getSourceMAC(), vlan); + + // ARP: possible new hosts, update both location and IP + if (eth.getEtherType() == Ethernet.TYPE_ARP) { + ARP arp = (ARP) eth.getPayload(); + IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET, + arp.getSenderProtocolAddress()); + updateLocationIP(hid, srcMac, vlan, hloc, ip); + + // 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 + } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) { + IPv6 ipv6 = (IPv6) eth.getPayload(); + IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET6, + ipv6.getSourceAddress()); + + // skip extension headers + IPacket pkt = ipv6; + while (pkt.getPayload() != null && + pkt.getPayload() instanceof IExtensionHeader) { + pkt = pkt.getPayload(); + } + + // Neighbor Discovery Protocol + pkt = pkt.getPayload(); + if (pkt != null && pkt instanceof ICMP6) { + pkt = pkt.getPayload(); + // RouterSolicitation, RouterAdvertisement + if (pkt != null && (pkt instanceof RouterAdvertisement || + pkt instanceof RouterSolicitation)) { + return; + } + if (pkt != null && (pkt instanceof NeighborSolicitation || + pkt instanceof NeighborAdvertisement)) { + // Duplicate Address Detection + if (ip.isZero()) { + return; + } + // NeighborSolicitation, NeighborAdvertisement + updateLocationIP(hid, srcMac, vlan, hloc, ip); + return; + } + } + + // multicast + if (eth.isMulticast()) { + return; + } + + // normal IPv6 packets + updateLocation(hid, srcMac, vlan, hloc); + } + } + } + + // Auxiliary listener to device events. + private class InternalDeviceListener implements DeviceListener { + @Override + public void event(DeviceEvent event) { + Device device = event.subject(); + switch (event.type()) { + case DEVICE_ADDED: + break; + case DEVICE_AVAILABILITY_CHANGED: + if (hostRemovalEnabled && + !deviceService.isAvailable(device.id())) { + removeHosts(hostService.getConnectedHosts(device.id())); + } + break; + case DEVICE_SUSPENDED: + case DEVICE_UPDATED: + // Nothing to do? + break; + case DEVICE_REMOVED: + if (hostRemovalEnabled) { + removeHosts(hostService.getConnectedHosts(device.id())); + } + break; + case PORT_ADDED: + break; + case PORT_UPDATED: + if (hostRemovalEnabled) { + ConnectPoint point = + new ConnectPoint(device.id(), event.port().number()); + removeHosts(hostService.getConnectedHosts(point)); + } + break; + case PORT_REMOVED: + // Nothing to do? + break; + default: + break; + } + } + } + + // Signals host vanish for all specified hosts. + private void removeHosts(Set<Host> hosts) { + for (Host host : hosts) { + providerService.hostVanished(host.id()); + } + } + +} diff --git a/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/package-info.java b/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/package-info.java new file mode 100644 index 00000000..57c5c3f6 --- /dev/null +++ b/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2014 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. + */ + +/** + * Provider that uses packet service as a means of host discovery and tracking. + */ +package org.onosproject.provider.host.impl; 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 new file mode 100644 index 00000000..b9d90976 --- /dev/null +++ b/framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java @@ -0,0 +1,369 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.provider.host.impl; + +import com.google.common.collect.ImmutableSet; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.osgi.ComponentContextAdapter; +import org.onlab.packet.ARP; +import org.onlab.packet.ChassisId; +import org.onlab.packet.Ethernet; +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onosproject.cfg.ComponentConfigAdapter; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.core.DefaultApplicationId; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultDevice; +import org.onosproject.net.DefaultHost; +import org.onosproject.net.DefaultPort; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.HostLocation; +import org.onosproject.net.device.DeviceEvent; +import org.onosproject.net.device.DeviceListener; +import org.onosproject.net.device.DeviceServiceAdapter; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.host.HostDescription; +import org.onosproject.net.host.HostProvider; +import org.onosproject.net.host.HostProviderRegistry; +import org.onosproject.net.host.HostProviderService; +import org.onosproject.net.host.HostServiceAdapter; +import org.onosproject.net.packet.DefaultInboundPacket; +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketServiceAdapter; +import org.onosproject.net.provider.AbstractProviderService; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.net.topology.Topology; +import org.onosproject.net.topology.TopologyServiceAdapter; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.Set; + +import static org.easymock.EasyMock.*; +import static org.junit.Assert.*; +import static org.onlab.packet.VlanId.vlanId; +import static org.onosproject.net.Device.Type.SWITCH; +import static org.onosproject.net.DeviceId.deviceId; +import static org.onosproject.net.HostId.hostId; +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 VlanId VLAN = vlanId(); + 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)); + + private static final ComponentContextAdapter CTX_FOR_REMOVE = + new ComponentContextAdapter() { + @Override + public Dictionary getProperties() { + Hashtable<String, String> props = new Hashtable<>(); + props.put("hostRemovalEnabled", "true"); + return props; + } + }; + + public static final ComponentContextAdapter CTX_FOR_NO_REMOVE = + new ComponentContextAdapter() { + @Override + public Dictionary getProperties() { + return new Hashtable(); + } + }; + + private final HostLocationProvider provider = new HostLocationProvider(); + private final TestHostRegistry hostRegistry = new TestHostRegistry(); + private final TestTopologyService topoService = new TestTopologyService(); + private final TestDeviceService deviceService = new TestDeviceService(); + private final TestHostService hostService = new TestHostService(); + private final TestPacketService packetService = new TestPacketService(); + + private PacketProcessor testProcessor; + private CoreService coreService; + private TestHostProviderService providerService; + + private ApplicationId appId = + new DefaultApplicationId(100, "org.onosproject.provider.host"); + + @Before + public void setUp() { + + coreService = createMock(CoreService.class); + expect(coreService.registerApplication(appId.name())) + .andReturn(appId).anyTimes(); + replay(coreService); + + provider.cfgService = new ComponentConfigAdapter(); + provider.coreService = coreService; + + provider.providerRegistry = hostRegistry; + provider.topologyService = topoService; + provider.packetService = packetService; + provider.deviceService = deviceService; + provider.hostService = hostService; + + provider.activate(CTX_FOR_NO_REMOVE); + } + + @Test + public void basics() { + assertNotNull("registration expected", providerService); + assertEquals("incorrect provider", provider, providerService.provider()); + } + + @Test + public void events() { + // new host + testProcessor.process(new TestPacketContext(DEV1)); + assertNotNull("new host expected", providerService.added); + assertNull("host motion unexpected", providerService.moved); + + // the host moved to new switch + testProcessor.process(new TestPacketContext(DEV2)); + assertNotNull("host motion expected", providerService.moved); + + // the host was misheard on a spine + testProcessor.process(new TestPacketContext(DEV3)); + assertNull("host misheard on spine switch", providerService.spine); + } + + @Test + public void removeHostByDeviceRemove() { + provider.modified(CTX_FOR_REMOVE); + testProcessor.process(new TestPacketContext(DEV1)); + 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); + } + + @Test + public void removeHostByDeviceOffline() { + provider.modified(CTX_FOR_REMOVE); + testProcessor.process(new TestPacketContext(DEV1)); + 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); + } + + @Test + public void removeHostByDevicePortDown() { + provider.modified(CTX_FOR_REMOVE); + testProcessor.process(new TestPacketContext(DEV1)); + 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))); + assertEquals("incorrect remove count", 1, providerService.removeCount); + } + + + @After + public void tearDown() { + provider.deactivate(); + provider.coreService = null; + provider.providerRegistry = null; + } + + private class TestHostRegistry implements HostProviderRegistry { + + @Override + public HostProviderService register(HostProvider provider) { + providerService = new TestHostProviderService(provider); + return providerService; + } + + @Override + public void unregister(HostProvider provider) { + } + + @Override + public Set<ProviderId> getProviders() { + return null; + } + + } + + private class TestHostProviderService + extends AbstractProviderService<HostProvider> + implements HostProviderService { + + DeviceId added = null; + DeviceId moved = null; + DeviceId spine = null; + public int removeCount; + + protected TestHostProviderService(HostProvider provider) { + super(provider); + } + + @Override + public void hostDetected(HostId hostId, HostDescription hostDescription) { + DeviceId descr = hostDescription.location().deviceId(); + if (added == null) { + added = descr; + } else if ((moved == null) && !descr.equals(added)) { + moved = descr; + } else { + spine = descr; + } + } + + @Override + public void hostVanished(HostId hostId) { + removeCount++; + } + + } + + private class TestPacketService extends PacketServiceAdapter { + @Override + public void addProcessor(PacketProcessor processor, int priority) { + testProcessor = processor; + } + } + + + 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))) { + return true; + } + return false; + } + } + + private class TestPacketContext implements PacketContext { + + private final String deviceId; + + public TestPacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + ARP arp = new ARP(); + arp.setSenderProtocolAddress(IP) + .setSenderHardwareAddress(MAC.toBytes()) + .setTargetHardwareAddress(BCMAC.toBytes()) + .setTargetProtocolAddress(IP); + + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_ARP) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC.toBytes()) + .setDestinationMACAddress(BCMAC) + .setPayload(arp); + 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; + + @Override + public void addListener(DeviceListener listener) { + this.listener = listener; + } + + @Override + public Iterable<Device> getDevices() { + return Collections.emptyList(); + } + } + + private class TestHostService extends HostServiceAdapter { + @Override + public Set<Host> getConnectedHosts(ConnectPoint connectPoint) { + return ImmutableSet.of(HOST); + } + + @Override + public Set<Host> getConnectedHosts(DeviceId deviceId) { + return ImmutableSet.of(HOST); + } + + } +} |