blob: 93234d3015ebfd2f1621d585df70e24cfe6c084b [file] [log] [blame]
/*
* Copyright 2015-present 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.net.edgeservice.impl;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.common.event.impl.TestEventDispatcher;
import org.onosproject.event.Event;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultPort;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.NetTestTools;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceServiceAdapter;
import org.onosproject.net.edge.EdgePortEvent;
import org.onosproject.net.edge.EdgePortListener;
import org.onosproject.net.link.LinkEvent;
import org.onosproject.net.link.LinkListener;
import org.onosproject.net.link.LinkServiceAdapter;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.PacketServiceAdapter;
import org.onosproject.net.topology.TopologyEvent;
import org.onosproject.net.topology.TopologyEvent.Type;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.onosproject.net.NetTestTools.injectEventDispatcher;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
import static org.onosproject.net.edge.EdgePortEvent.Type.EDGE_PORT_ADDED;
import static org.onosproject.net.edge.EdgePortEvent.Type.EDGE_PORT_REMOVED;
import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
/**
* Test of the edge port manager. Each device has ports '0' through 'numPorts - 1'
* as specified by the variable 'numPorts'.
*/
public class EdgeManagerTest {
private EdgeManager mgr;
private int totalPorts = 10;
private boolean alwaysReturnPorts = false;
private final Set<ConnectPoint> infrastructurePorts = Sets.newConcurrentHashSet();
private List<EdgePortEvent> events = Lists.newArrayList();
private final Map<DeviceId, Device> devices = Maps.newConcurrentMap();
private Set<OutboundPacket> packets = Sets.newConcurrentHashSet();
private final EdgePortListener testListener = new TestListener(events);
private TestDeviceManager testDeviceManager;
private TestLinkService testLinkService;
@Before
public void setUp() {
mgr = new EdgeManager();
injectEventDispatcher(mgr, new TestEventDispatcher());
testDeviceManager = new TestDeviceManager(devices);
mgr.deviceService = testDeviceManager;
testLinkService = new TestLinkService();
mgr.linkService = testLinkService;
mgr.packetService = new TestPacketManager();
mgr.activate();
mgr.addListener(testListener);
}
@After
public void tearDown() {
mgr.removeListener(testListener);
mgr.deactivate();
}
@Test
public void testBasics() {
//Setup
int numDevices = 20;
int numPorts = 4;
defaultPopulator(numDevices, numPorts);
assertEquals("Unexpected number of ports", numDevices * numPorts, infrastructurePorts.size());
assertFalse("Expected isEdge to return false",
mgr.isEdgePoint(NetTestTools.connectPoint(Integer.toString(1), 1)));
removeInfraPort(NetTestTools.connectPoint(Integer.toString(1), 1));
postTopologyEvent(new LinkEvent(LINK_REMOVED, NetTestTools.link(Integer.toString(1), 1, "b", 2)));
assertTrue("Expected isEdge to return true",
mgr.isEdgePoint(NetTestTools.connectPoint(Integer.toString(1), 1)));
}
@Test
public void testLinkUpdates() {
//Setup
ConnectPoint testPoint, referencePoint;
//Testing link removal
devices.put(NetTestTools.did("a"), NetTestTools.device("a"));
devices.put(NetTestTools.did("b"), NetTestTools.device("b"));
postTopologyEvent(new LinkEvent(LINK_REMOVED, NetTestTools.link("a", 1, "b", 2)));
assertEquals("The list contained an unexpected number of events",
2, events.size());
assertTrue("The first element is of the wrong type.",
events.get(0).type() == EDGE_PORT_ADDED);
testPoint = events.get(0).subject();
referencePoint = NetTestTools.connectPoint("a", 1);
assertTrue("The port numbers of the first element are incorrect",
testPoint.port().toLong() == referencePoint.port().toLong());
assertTrue("The device id of the first element is incorrect.",
testPoint.deviceId().equals(referencePoint.deviceId()));
testPoint = events.get(1).subject();
referencePoint = NetTestTools.connectPoint("b", 2);
assertTrue("The port numbers of the second element are incorrect",
testPoint.port().toLong() == referencePoint.port().toLong());
assertTrue("The device id of the second element is incorrect.",
testPoint.deviceId().equals(referencePoint.deviceId()));
//Rebroadcast event to ensure it results in no additional events
postTopologyEvent(new LinkEvent(LINK_REMOVED, NetTestTools.link("a", 1, "b", 2)));
assertTrue("The list contained an unexpected number of events", events.size() == 2);
//Testing link adding when links to remove exist
events.clear();
postTopologyEvent(new LinkEvent(LINK_ADDED, NetTestTools.link("a", 1, "b", 2)));
assertTrue("The list contained an unexpected number of events", events.size() == 2);
assertTrue("The first element is of the wrong type.",
events.get(0).type() == EDGE_PORT_REMOVED);
assertTrue("The second element is of the wrong type.",
events.get(1).type() == EDGE_PORT_REMOVED);
testPoint = events.get(0).subject();
referencePoint = NetTestTools.connectPoint("a", 1);
assertTrue("The port numbers of the first element are incorrect",
testPoint.port().toLong() == referencePoint.port().toLong());
assertTrue("The device id of the first element is incorrect.",
testPoint.deviceId().equals(referencePoint.deviceId()));
testPoint = events.get(1).subject();
referencePoint = NetTestTools.connectPoint("b", 2);
assertTrue("The port numbers of the second element are incorrect",
testPoint.port().toLong() == referencePoint.port().toLong());
assertTrue("The device id of the second element is incorrect.",
testPoint.deviceId().equals(referencePoint.deviceId()));
//Apparent duplicate of previous method tests removal when the elements have already been removed
events.clear();
postTopologyEvent(new LinkEvent(LINK_ADDED, NetTestTools.link("a", 1, "b", 2)));
assertTrue("The list should contain no events, the removed elements don't exist.", events.size() == 0);
}
@Test
public void testDeviceUpdates() {
//Setup
Device referenceDevice;
DeviceEvent event;
int numDevices = 10;
int numInfraPorts = 5;
totalPorts = 10;
defaultPopulator(numDevices, numInfraPorts);
events.clear();
//Test response to device added events
referenceDevice = NetTestTools.device("11");
devices.put(referenceDevice.id(), referenceDevice);
for (int port = 1; port <= numInfraPorts; port++) {
infrastructurePorts.add(NetTestTools.connectPoint("11", port));
}
event = new DeviceEvent(DEVICE_ADDED, referenceDevice,
new DefaultPort(referenceDevice, PortNumber.portNumber(1), true));
postTopologyEvent(event);
//Check that ports were populated correctly
assertTrue("Unexpected number of new ports added",
mgr.deviceService.getPorts(NetTestTools.did("11")).size() == 10);
//Check that of the ten ports the half that are infrastructure ports aren't added
assertEquals("Unexpected number of new edge ports added", (totalPorts - numInfraPorts), events.size());
for (int index = 0; index < numInfraPorts; index++) {
assertTrue("Unexpected type of event", events.get(index).type() == EDGE_PORT_ADDED);
}
//Names here are irrelevant, the first 5 ports are populated as infrastructure, 6-10 are edge
for (int index = 0; index < events.size(); index++) {
assertEquals("Port added had unexpected port number.",
events.get(index).subject().port(),
NetTestTools.connectPoint("a", index + numInfraPorts + 1).port());
}
events.clear();
//Repost the event to test repeated posts
postTopologyEvent(event);
assertEquals("The redundant notification should not have created additional notifications.",
0, events.size());
//Calculate the size of the returned iterable of edge points.
Iterable<ConnectPoint> pts = mgr.getEdgePoints();
Iterator pointIterator = pts.iterator();
int count = 0;
for (; pointIterator.hasNext(); count++) {
pointIterator.next();
}
assertEquals("Unexpected number of edge points", (numDevices + 1) * numInfraPorts, count);
//Testing device removal
events.clear();
event = (new DeviceEvent(DEVICE_REMOVED, referenceDevice,
new DefaultPort(referenceDevice, PortNumber.portNumber(1), true)));
postTopologyEvent(event);
assertEquals("There should be five new events from removal of edge points",
totalPorts - numInfraPorts, events.size());
for (int index = 0; index < events.size(); index++) {
//Assert that the correct port numbers were removed in the correct order
assertThat("Port removed had unexpected port number.",
events.get(index).subject().port().toLong(),
is(greaterThanOrEqualTo((long) numInfraPorts)));
//Assert that the events are of the correct type
assertEquals("Unexpected type of event", events.get(index).type(), EDGE_PORT_REMOVED);
}
events.clear();
//Rebroadcast event to check that it triggers no new behavior
postTopologyEvent(event);
assertEquals("Rebroadcast of removal event should not produce additional events",
0, events.size());
//Testing device status change, changed from unavailable to available
events.clear();
//Make sure that the devicemanager shows the device as available.
addDevice(referenceDevice, "1", 5);
devices.put(referenceDevice.id(), referenceDevice);
event = new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, referenceDevice);
postTopologyEvent(event);
//An earlier setup set half of the reference device ports to infrastructure
assertEquals("An unexpected number of events were generated.", totalPorts - numInfraPorts, events.size());
for (int i = 0; i < 5; i++) {
assertEquals("The event was not of the right type", events.get(i).type(), EDGE_PORT_ADDED);
}
events.clear();
postTopologyEvent(event);
assertEquals("No events should have been generated for a set of existing ports.", 0, events.size());
//Test removal when state changes when the device becomes unavailable
//Ensure that the deviceManager shows the device as unavailable
removeDevice(referenceDevice);
// This variable copies the behavior of the topology by returning ports
// attached to an unavailable device this behavior is necessary for the
// following event to execute properly, if these statements are removed
// no events will be generated since no ports will be provided in
// getPorts() to EdgeManager.
alwaysReturnPorts = true;
postTopologyEvent(event);
alwaysReturnPorts = false;
assertEquals("An unexpected number of events were created.", totalPorts - numInfraPorts, events.size());
for (int i = 0; i < 5; i++) {
EdgePortEvent edgeEvent = events.get(i);
assertEquals("The event is of an unexpected type.",
EdgePortEvent.Type.EDGE_PORT_REMOVED, edgeEvent.type());
assertThat("The event pertains to an unexpected port",
edgeEvent.subject().port().toLong(),
is(greaterThanOrEqualTo((long) numInfraPorts)));
}
}
@Test
public void testInternalCache() {
int numDevices = 10;
//Number of infrastructure ports per device
int numPorts = 5;
//Total ports per device when requesting all devices
totalPorts = 10;
defaultPopulator(numDevices, numPorts);
for (int i = 0; i < numDevices; i++) {
Device newDevice = NetTestTools.device(Integer.toString(i));
devices.put(newDevice.id(), newDevice);
postTopologyEvent(new DeviceEvent(DEVICE_ADDED, newDevice));
}
//Check all ports have correct designations
ConnectPoint testPoint;
for (int deviceNum = 0; deviceNum < numDevices; deviceNum++) {
for (int portNum = 1; portNum <= totalPorts; portNum++) {
testPoint = NetTestTools.connectPoint(Integer.toString(deviceNum), portNum);
if (portNum <= numPorts) {
assertFalse("This should not be an edge point", mgr.isEdgePoint(testPoint));
} else {
assertTrue("This should be an edge point", mgr.isEdgePoint(testPoint));
}
}
}
int count = 0;
for (ConnectPoint ignored : mgr.getEdgePoints()) {
count++;
}
assertEquals("There are an unexpeceted number of edge points returned.",
(totalPorts - numPorts) * numDevices, count);
for (int deviceNumber = 0; deviceNumber < numDevices; deviceNumber++) {
count = 0;
for (ConnectPoint ignored : mgr.getEdgePoints(NetTestTools.did("1"))) {
count++;
}
assertEquals("This element has an unexpected number of edge points.", (totalPorts - numPorts), count);
}
}
@Test
public void testEmit() {
byte[] arr = new byte[10];
Device referenceDevice;
DeviceEvent event;
int numDevices = 10;
int numInfraPorts = 5;
totalPorts = 10;
defaultPopulator(numDevices, numInfraPorts);
for (byte byteIndex = 0; byteIndex < arr.length; byteIndex++) {
arr[byteIndex] = byteIndex;
}
for (int i = 0; i < numDevices; i++) {
referenceDevice = NetTestTools.device(Integer.toString(i));
testDeviceManager.listener.event(new DeviceEvent(DEVICE_ADDED, referenceDevice,
new DefaultPort(referenceDevice,
PortNumber.portNumber(1),
true)));
}
mgr.emitPacket(ByteBuffer.wrap(arr), Optional.empty());
assertEquals("There were an unexpected number of emitted packets",
(totalPorts - numInfraPorts) * numDevices, packets.size());
Iterator<OutboundPacket> packetIter = packets.iterator();
OutboundPacket packet;
while (packetIter.hasNext()) {
packet = packetIter.next();
assertEquals("The packet had an incorrect payload.", arr, packet.data().array());
}
//Start testing emission to a specific device
packets.clear();
mgr.emitPacket(NetTestTools.did(Integer.toString(1)), ByteBuffer.wrap(arr), Optional.empty());
assertEquals("Unexpected number of outbound packets were emitted.",
totalPorts - numInfraPorts, packets.size());
packetIter = packets.iterator();
while (packetIter.hasNext()) {
packet = packetIter.next();
assertEquals("The packet had an incorrect payload", arr, packet.data().array());
}
}
/**
* Creates TopologyEvent triggered by {@code event}.
*
* @param event reason of the TopologyEvent
* @return TopologyEvent
*/
private TopologyEvent topologyEventOf(Event event) {
return new TopologyEvent(Type.TOPOLOGY_CHANGED, null, ImmutableList.of(event));
}
/**
* Post Event dispatched from TopologyManager.
*
* @param event Event
*/
private void postTopologyEvent(Event event) {
if (event instanceof DeviceEvent) {
testDeviceManager.listener.event((DeviceEvent) event);
}
if (event instanceof LinkEvent) {
testLinkService.listener.event((LinkEvent) event);
}
//testTopologyManager.listener.event(topologyEventOf(event));
}
/**
* @param numDevices the number of devices to populate.
* @param numInfraPorts the number of ports to be set as infrastructure on each device, numbered base 0, ports 0
* through numInfraPorts - 1
*/
private void defaultPopulator(int numDevices, int numInfraPorts) {
for (int device = 0; device < numDevices; device++) {
String str = Integer.toString(device);
Device deviceToAdd = NetTestTools.device(str);
devices.put(deviceToAdd.id(), deviceToAdd);
testDeviceManager.listener.event(new DeviceEvent(DEVICE_ADDED, deviceToAdd));
for (int port = 1; port <= numInfraPorts; port++) {
testLinkService.listener.event(new LinkEvent(LINK_ADDED, NetTestTools.link(str, port, "other", 1)));
infrastructurePorts.add(NetTestTools.connectPoint(str, port));
}
}
}
/**
* Adds the specified device with the specified number of edge ports so long as it is less than the total ports.
*
* @param device The device to be added
* @param deviceName The name given to generate the devices DID
* @param numInfraPorts The number of ports to be added numbered 1 ... numInfraPorts
*/
private void addDevice(Device device, String deviceName, int numInfraPorts) {
if (!devices.keySet().contains(device.id())) {
devices.put(device.id(), device);
for (int i = 1; i <= numInfraPorts && i <= totalPorts; i++) {
infrastructurePorts.add(NetTestTools.connectPoint(deviceName, i));
}
}
}
private void removeDevice(Device device) {
devices.remove(device.id());
}
private void removeInfraPort(ConnectPoint port) {
infrastructurePorts.remove(port);
}
private class TestDeviceManager extends DeviceServiceAdapter {
private DeviceListener listener;
private Map<DeviceId, Device> devices;
public TestDeviceManager(Map<DeviceId, Device> devices) {
this.devices = devices;
}
@Override
public boolean isAvailable(DeviceId deviceId) {
for (DeviceId id : devices.keySet()) {
if (id.equals(deviceId)) {
return true;
}
}
return false;
}
@Override
public List<Port> getPorts(DeviceId deviceId) {
List<Port> ports = new ArrayList<>();
Device device = devices.get(deviceId);
if (device == null && !alwaysReturnPorts) {
return ports;
}
for (int portNum = 1; portNum <= totalPorts; portNum++) {
//String is generated using 'of:' + the passed name, this creates a
ports.add(new DefaultPort(device, PortNumber.portNumber(portNum), true));
}
return ports;
}
@Override
public Iterable<Device> getAvailableDevices() {
return devices.values();
}
@Override
public void addListener(DeviceListener listener) {
this.listener = listener;
}
@Override
public void removeListener(DeviceListener listener) {
this.listener = null;
}
}
private class TestLinkService extends LinkServiceAdapter {
private LinkListener listener;
@Override
public Set<Link> getLinks(ConnectPoint connectPoint) {
if (infrastructurePorts.contains(connectPoint)) {
return Collections.singleton(NetTestTools.link("1", 1, "2", 1));
} else {
return Collections.emptySet();
}
}
@Override
public void addListener(LinkListener listener) {
this.listener = listener;
}
}
private class TestPacketManager extends PacketServiceAdapter {
@Override
public void emit(OutboundPacket packet) {
packets.add(packet);
}
}
private class TestListener implements EdgePortListener {
private List<EdgePortEvent> events;
public TestListener(List<EdgePortEvent> events) {
this.events = events;
}
@Override
public void event(EdgePortEvent event) {
events.add(event);
}
}
}