blob: 2edd3c877f042efc9c01196689b86502363029bd [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.provider.nil;
import com.google.common.collect.Lists;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.ChassisId;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.NodeId;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.ConnectPoint;
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.Link;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DeviceAdminService;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceProviderService;
import org.onosproject.net.device.PortDescription;
import org.onosproject.net.host.DefaultHostDescription;
import org.onosproject.net.host.HostDescription;
import org.onosproject.net.host.HostProviderService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.link.DefaultLinkDescription;
import org.onosproject.net.link.LinkProviderService;
import org.onosproject.net.link.LinkService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.onlab.util.Tools.toHex;
import static org.onosproject.net.HostId.hostId;
import static org.onosproject.net.Link.Type.DIRECT;
import static org.onosproject.net.PortNumber.portNumber;
import static org.onosproject.net.device.DeviceEvent.Type.*;
import static org.onosproject.provider.nil.NullProviders.SCHEME;
/**
* Abstraction of a provider capable to simulate some network topology.
*/
public abstract class TopologySimulator {
protected final Logger log = LoggerFactory.getLogger(getClass());
protected String[] topoShape;
protected int deviceCount;
protected int hostCount;
protected ServiceDirectory directory;
protected NodeId localNode;
protected ClusterService clusterService;
protected MastershipService mastershipService;
protected DeviceAdminService deviceService;
protected HostService hostService;
protected LinkService linkService;
protected DeviceProviderService deviceProviderService;
protected HostProviderService hostProviderService;
protected LinkProviderService linkProviderService;
protected int maxWaitSeconds = 1;
protected int infrastructurePorts = 2;
protected CountDownLatch deviceLatch;
protected final List<DeviceId> deviceIds = Lists.newArrayList();
private DeviceListener deviceEventCounter = new DeviceEventCounter();
/**
* Initializes a new topology simulator with access to the specified service
* directory and various provider services.
*
* @param topoShape topology shape specifier
* @param deviceCount number of devices in the topology
* @param hostCount number of hosts per device
* @param directory service directory
* @param deviceProviderService device provider service
* @param hostProviderService host provider service
* @param linkProviderService link provider service
*/
protected void init(String topoShape, int deviceCount, int hostCount,
ServiceDirectory directory,
DeviceProviderService deviceProviderService,
HostProviderService hostProviderService,
LinkProviderService linkProviderService) {
this.deviceCount = deviceCount;
this.hostCount = hostCount;
this.directory = directory;
this.clusterService = directory.get(ClusterService.class);
this.mastershipService = directory.get(MastershipService.class);
this.deviceService = directory.get(DeviceAdminService.class);
this.hostService = directory.get(HostService.class);
this.linkService = directory.get(LinkService.class);
this.deviceProviderService = deviceProviderService;
this.hostProviderService = hostProviderService;
this.linkProviderService = linkProviderService;
localNode = clusterService.getLocalNode().id();
processTopoShape(topoShape);
}
/**
* Processes the topology shape specifier.
*
* @param shape topology shape specifier
*/
protected void processTopoShape(String shape) {
this.topoShape = shape.split(",");
}
/**
* Sets up network topology simulation.
*/
public void setUpTopology() {
prepareForDeviceEvents(deviceCount);
createDevices();
waitForDeviceEvents();
createLinks();
createHosts();
}
/**
* Creates simulated devices.
*/
protected void createDevices() {
for (int i = 0; i < deviceCount; i++) {
createDevice(i + 1);
}
}
/**
* Creates simulated links.
*/
protected abstract void createLinks();
/**
* Creates simulated hosts.
*/
protected abstract void createHosts();
/**
* Creates simulated device and adds its id to the list of devices ids.
*
* @param i index of the device id in the list.
*/
protected void createDevice(int i) {
DeviceId id = DeviceId.deviceId(SCHEME + ":" + toHex(i));
deviceIds.add(id);
createDevice(id, i);
}
/**
* Creates simulated device.
*
* @param id device identifier
* @param chassisId chassis identifier number
*/
public void createDevice(DeviceId id, int chassisId) {
createDevice(id, chassisId, Device.Type.SWITCH, hostCount + infrastructurePorts);
}
/**
* Creates simulated device.
*
* @param id device identifier
* @param chassisId chassis identifier number
* @param type device type
* @param portCount number of device ports
*/
public void createDevice(DeviceId id, int chassisId, Device.Type type, int portCount) {
DeviceDescription desc =
new DefaultDeviceDescription(id.uri(), type,
"ON.Lab", "0.1", "0.1", "1234",
new ChassisId(chassisId));
deviceProviderService.deviceConnected(id, desc);
deviceProviderService.updatePorts(id, buildPorts(portCount));
}
/**
* Creates simulated link between two devices.
*
* @param i index of one simulated device
* @param j index of another simulated device
* @param pi port number of i-th device
* @param pj port number of j-th device
*/
public void createLink(int i, int j, int pi, int pj) {
ConnectPoint one = new ConnectPoint(deviceIds.get(i), PortNumber.portNumber(pi));
ConnectPoint two = new ConnectPoint(deviceIds.get(j), PortNumber.portNumber(pj));
createLink(one, two);
}
/**
* Creates simulated link between two connection points.
*
* @param one one connection point
* @param two another connection point
*/
public void createLink(ConnectPoint one, ConnectPoint two) {
createLink(one, two, DIRECT, true);
}
/**
* Creates simulated link between two connection points.
*
* @param one one connection point
* @param two another connection point
* @param type link type
* @param isBidirectional true if link is bidirectional
*/
public void createLink(ConnectPoint one, ConnectPoint two, Link.Type type, boolean isBidirectional) {
linkProviderService.linkDetected(new DefaultLinkDescription(one, two, type));
if (isBidirectional) {
linkProviderService.linkDetected(new DefaultLinkDescription(two, one, type));
}
}
/**
* Creates simularted hosts for the specified device.
*
* @param deviceId device identifier
* @param portOffset port offset where to start attaching hosts
*/
public void createHosts(DeviceId deviceId, int portOffset) {
String s = deviceId.toString();
byte dByte = Byte.parseByte(s.substring(s.length() - 2), 16);
// TODO: this limits the simulation to 256 devices & 256 hosts/device.
byte[] macBytes = new byte[]{0, 0, 0, 0, dByte, 0};
byte[] ipBytes = new byte[]{(byte) 192, (byte) 168, dByte, 0};
for (int i = 0; i < hostCount; i++) {
int port = portOffset + i + 1;
macBytes[5] = (byte) (i + 1);
ipBytes[3] = (byte) (i + 1);
HostId id = hostId(MacAddress.valueOf(macBytes), VlanId.NONE);
IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET, ipBytes);
hostProviderService.hostDetected(id, description(id, ip, deviceId, port), false);
}
}
/**
* Prepares to count device added/available/removed events.
*
* @param count number of events to count
*/
protected void prepareForDeviceEvents(int count) {
deviceLatch = new CountDownLatch(count);
deviceService.addListener(deviceEventCounter);
}
/**
* Waits for all expected device added/available/removed events.
*/
protected void waitForDeviceEvents() {
try {
deviceLatch.await(maxWaitSeconds, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.warn("Device events did not arrive in time");
}
deviceService.removeListener(deviceEventCounter);
}
/**
* Tears down network topology simulation.
*/
public void tearDownTopology() {
removeHosts();
removeLinks();
removeDevices();
}
/**
* Removes any hosts previously advertised by this provider.
*/
protected void removeHosts() {
hostService.getHosts()
.forEach(host -> hostProviderService.hostVanished(host.id()));
}
/**
* Removes any links previously advertised by this provider.
*/
protected void removeLinks() {
linkService.getLinks()
.forEach(link -> linkProviderService.linkVanished(description(link)));
}
/**
* Removes any devices previously advertised by this provider.
*/
protected void removeDevices() {
prepareForDeviceEvents(deviceIds.size());
deviceIds.forEach(deviceProviderService::deviceDisconnected);
waitForDeviceEvents();
deviceIds.clear();
}
/**
* Produces a device description from the given device.
*
* @param device device to copy
* @return device description
*/
static DeviceDescription description(Device device) {
return new DefaultDeviceDescription(device.id().uri(), device.type(),
device.manufacturer(),
device.hwVersion(), device.swVersion(),
device.serialNumber(), device.chassisId());
}
/**
* Produces a link description from the given link.
*
* @param link link to copy
* @return link description
*/
static DefaultLinkDescription description(Link link) {
return new DefaultLinkDescription(link.src(), link.dst(), link.type());
}
/**
* Produces a host description from the given host.
*
* @param host host to copy
* @return host description
*/
static DefaultHostDescription description(Host host) {
return new DefaultHostDescription(host.mac(), host.vlan(), host.location(),
host.ipAddresses());
}
/**
* Generates a host description from the given id and location information.
*
* @param hostId host identifier
* @param ip host IP
* @param deviceId edge device
* @param port edge port
* @return host description
*/
static HostDescription description(HostId hostId, IpAddress ip,
DeviceId deviceId, int port) {
HostLocation location = new HostLocation(deviceId, portNumber(port), 0L);
return new DefaultHostDescription(hostId.mac(), hostId.vlanId(), location, ip);
}
/**
* Generates a list of a configured number of ports.
*
* @param portCount number of ports
* @return list of ports
*/
protected List<PortDescription> buildPorts(int portCount) {
List<PortDescription> ports = Lists.newArrayList();
for (int i = 0; i < portCount; i++) {
ports.add(new DefaultPortDescription(PortNumber.portNumber(i), true,
Port.Type.COPPER, 0));
}
return ports;
}
/**
* Indicates whether or not the simulation deeps the device as available.
*
* @param deviceId device identifier
* @return true if device is known
*/
public boolean contains(DeviceId deviceId) {
return deviceIds.contains(deviceId);
}
// Counts down number of device added/available/removed events.
private class DeviceEventCounter implements DeviceListener {
@Override
public void event(DeviceEvent event) {
DeviceEvent.Type type = event.type();
if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
type == DEVICE_AVAILABILITY_CHANGED) {
deviceLatch.countDown();
}
}
}
}