Adding auto-layout to the access-null simulation.
Enhancing the scale test for flows and routes.
Change-Id: Ib91720b409872e44eaff4263cf229bffa2e292fc
diff --git a/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java b/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java
index 103c9d3..f18b861 100644
--- a/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java
+++ b/apps/dhcp/app/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java
@@ -186,6 +186,7 @@
@Deactivate
protected void deactivate() {
+ componentConfigService.unregisterProperties(getClass(), false);
cfgService.removeListener(cfgListener);
factories.forEach(cfgService::unregisterConfigFactory);
packetService.removeProcessor(processor);
diff --git a/apps/test/route-scale/src/main/java/org/onosproject/routescale/CreateFlows.java b/apps/test/route-scale/src/main/java/org/onosproject/routescale/CreateFlows.java
index 6515def..8ab8e8f 100644
--- a/apps/test/route-scale/src/main/java/org/onosproject/routescale/CreateFlows.java
+++ b/apps/test/route-scale/src/main/java/org/onosproject/routescale/CreateFlows.java
@@ -17,23 +17,25 @@
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.cli.AbstractShellCommand;
/**
* Creates the specified number of routes for scale testing.
*/
-@Command(scope = "onos", name = "scale-create-flows",
- description = "Creates the specified number of flows for scale testing")
+@Command(scope = "onos", name = "scale-flows",
+ description = "Sets the specified number of flows for scale testing")
public class CreateFlows extends AbstractShellCommand {
- @Argument(index = 0, name = "flowCount", description = "Number of flows to create",
+ @Argument(index = 0, name = "flowCount", description = "Number of flows to maintain",
required = true)
int flowCount;
@Override
protected void execute() {
- ScaleTestManager service = get(ScaleTestManager.class);
- service.createFlows(flowCount);
+ ComponentConfigService service = get(ComponentConfigService.class);
+ service.setProperty("org.onosproject.routescale.ScaleManager",
+ "flowCount", String.valueOf(flowCount));
}
}
diff --git a/apps/test/route-scale/src/main/java/org/onosproject/routescale/CreateRoutes.java b/apps/test/route-scale/src/main/java/org/onosproject/routescale/CreateRoutes.java
index b43d2bb..d738b74 100644
--- a/apps/test/route-scale/src/main/java/org/onosproject/routescale/CreateRoutes.java
+++ b/apps/test/route-scale/src/main/java/org/onosproject/routescale/CreateRoutes.java
@@ -17,23 +17,26 @@
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.cli.AbstractShellCommand;
/**
* Creates the specified number of routes for scale testing.
*/
-@Command(scope = "onos", name = "scale-create-routes",
- description = "Creates the specified number of routes for scale testing")
+@Command(scope = "onos", name = "scale-routes",
+ description = "Sets the specified number of routes for scale testing")
public class CreateRoutes extends AbstractShellCommand {
- @Argument(index = 0, name = "routeCount", description = "Number of routes to create",
+ @Argument(index = 0, name = "routeCount", description = "Number of routes to maintain",
required = true)
int routeCount;
@Override
protected void execute() {
- ScaleTestManager service = get(ScaleTestManager.class);
- service.createRoutes(routeCount);
+ ComponentConfigService service = get(ComponentConfigService.class);
+ service.setProperty("org.onosproject.routescale.ScaleManager",
+ "routeCount", String.valueOf(routeCount));
+
}
}
diff --git a/apps/test/route-scale/src/main/java/org/onosproject/routescale/ScaleTestManager.java b/apps/test/route-scale/src/main/java/org/onosproject/routescale/ScaleTestManager.java
index 65c1c7f..25550f8 100644
--- a/apps/test/route-scale/src/main/java/org/onosproject/routescale/ScaleTestManager.java
+++ b/apps/test/route-scale/src/main/java/org/onosproject/routescale/ScaleTestManager.java
@@ -21,6 +21,8 @@
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.apache.felix.scr.annotations.Service;
@@ -29,29 +31,39 @@
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onosproject.app.ApplicationService;
+import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
+import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleOperations;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.host.HostAdminService;
+import org.onosproject.net.link.LinkService;
import org.onosproject.routeservice.Route;
import org.onosproject.routeservice.RouteAdminService;
+import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Dictionary;
import java.util.List;
+import java.util.Objects;
import java.util.Random;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.get;
+
@Component(immediate = true)
@Service(value = ScaleTestManager.class)
public class ScaleTestManager {
@@ -68,11 +80,25 @@
protected HostAdminService hostAdminService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected RouteAdminService routeAdminService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService componentConfigService;
+
+ @Property(name = "flowCount", intValue = 0,
+ label = "Number of flows to be maintained in the system")
+ private int flowCount = 0;
+
+ @Property(name = "routeCount", intValue = 0,
+ label = "Number of routes to be maintained in the system")
+ private int routeCount = 0;
+
private final Random random = new Random(System.currentTimeMillis());
private ApplicationId appId;
@@ -80,38 +106,128 @@
@Activate
protected void activate() {
appId = applicationService.getId("org.onosproject.routescale");
+ componentConfigService.registerProperties(getClass());
log.info("Started");
}
@Deactivate
protected void deactivate() {
+ componentConfigService.unregisterProperties(getClass(), false);
log.info("Stopped");
}
- public void createFlows(int flowCount) {
- for (Device device : deviceService.getAvailableDevices()) {
- DeviceId id = device.id();
- FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
+ @Modified
+ public void modified(ComponentContext context) {
+ if (context == null) {
+ return;
+ }
- for (int i = 0; i < flowCount; i++) {
- FlowRule.Builder frb = DefaultFlowRule.builder();
- frb.fromApp(appId).makePermanent().withPriority(1000 + i);
- TrafficSelector.Builder tsb = DefaultTrafficSelector.builder();
- TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
+ Dictionary<?, ?> properties = context.getProperties();
+ try {
+ String s = get(properties, "flowCount");
+ flowCount = isNullOrEmpty(s) ? flowCount : Integer.parseInt(s.trim());
- tsb.matchEthType(Ethernet.TYPE_IPV4);
- ttb.setEthDst(randomMac()).setEthSrc(randomMac());
- ttb.setOutput(PortNumber.portNumber(random.nextInt(512)));
- frb.withSelector(tsb.build()).withTreatment(ttb.build());
- ops.add(frb.forDevice(id).build());
- }
+ s = get(properties, "routeCount");
+ routeCount = isNullOrEmpty(s) ? routeCount : Integer.parseInt(s.trim());
- flowRuleService.apply(ops.build());
+ log.info("Reconfigured; flowCount={}; routeCount={}", flowCount, routeCount);
+ adjustFlows();
+ adjustRoutes();
+
+ } catch (NumberFormatException | ClassCastException e) {
+ log.warn("Misconfigured", e);
}
}
- public void createRoutes(int routeCount) {
+ private void adjustFlows() {
+ int deviceCount = deviceService.getAvailableDeviceCount();
+ if (deviceCount == 0) {
+ return;
+ }
+
+ int flowsPerDevice = flowCount / deviceCount;
+ for (Device device : deviceService.getAvailableDevices()) {
+ DeviceId id = device.id();
+ if (deviceService.getRole(id) != MastershipRole.MASTER ||
+ flowsPerDevice == 0) {
+ continue;
+ }
+
+ int currentFlowCount = flowRuleService.getFlowRuleCount(id);
+ if (flowsPerDevice > currentFlowCount) {
+ addMoreFlows(flowsPerDevice, device, id, currentFlowCount);
+
+ } else if (flowsPerDevice < currentFlowCount) {
+ removeExcessFlows(flowsPerDevice, id, currentFlowCount);
+ }
+ }
+ }
+
+ private void addMoreFlows(int flowsPerDevice, Device device, DeviceId id,
+ int currentFlowCount) {
+ int c = flowsPerDevice - currentFlowCount;
+ log.info("Adding {} flows for device {}", c, id);
+ List<PortNumber> ports = devicePorts(device);
+ FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
+ for (int i = 0; i < c; i++) {
+ FlowRule.Builder frb = DefaultFlowRule.builder();
+ frb.fromApp(appId).makePermanent().withPriority(1000 + i);
+ TrafficSelector.Builder tsb = DefaultTrafficSelector.builder();
+ TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
+
+ tsb.matchEthType(Ethernet.TYPE_IPV4);
+ ttb.setEthDst(randomMac()).setEthSrc(randomMac());
+ ttb.setOutput(randomPort(ports));
+ frb.withSelector(tsb.build()).withTreatment(ttb.build());
+ ops.add(frb.forDevice(id).build());
+ }
+ flowRuleService.apply(ops.build());
+ }
+
+ private void removeExcessFlows(int flowsPerDevice, DeviceId id,
+ int currentFlowCount) {
+ FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
+ int c = flowsPerDevice - currentFlowCount;
+ log.info("Removing {} flows from device {}", c, id);
+ for (FlowEntry e : flowRuleService.getFlowEntries(id)) {
+ if (Objects.equals(e.appId(), appId.id()) && c > 0) {
+ ops.remove(e);
+ c--;
+ }
+ }
+ flowRuleService.apply(ops.build());
+ }
+
+ private void adjustRoutes() {
+ int currentRouteCount =
+ routeAdminService.getRouteTables().parallelStream()
+ .mapToInt(t -> routeAdminService.getRoutes(t).size()).sum();
+ if (currentRouteCount < routeCount) {
+ createRoutes(routeCount - currentRouteCount);
+ } else if (currentRouteCount > routeCount) {
+ removeRoutes(currentRouteCount - routeCount);
+ }
+ }
+
+ // Returns a list of ports on the given device that have either links or
+ // hosts connected to them.
+ private List<PortNumber> devicePorts(Device device) {
+ DeviceId id = device.id();
+ ImmutableList.Builder<PortNumber> ports = ImmutableList.builder();
+ linkService.getDeviceEgressLinks(id).forEach(l -> ports.add(l.src().port()));
+ hostAdminService.getConnectedHosts(id)
+ .forEach(h -> h.locations().stream()
+ .filter(l -> Objects.equals(id, l.elementId()))
+ .findFirst()
+ .ifPresent(l -> ports.add(l.port())));
+ return ports.build();
+ }
+
+ // Creates the specified number of random routes. Such routes are generated
+ // using random IP prefices with next hop being an IP address of a randomly
+ // chosen hosts.
+ private void createRoutes(int routeCount) {
List<Host> hosts = ImmutableList.copyOf(hostAdminService.getHosts());
ImmutableSet.Builder<Route> routes = ImmutableSet.builder();
for (int i = 0; i < routeCount; i++) {
@@ -122,21 +238,34 @@
routeAdminService.update(routes.build());
}
+ // Removes the specified number of routes chosen at random.
+ private void removeRoutes(int routeCount) {
+ log.warn("Not implemented yet");
+ }
+
+ // Generates a random IP address.
private IpAddress randomIp() {
byte[] bytes = new byte[4];
random.nextBytes(bytes);
return IpAddress.valueOf(IpAddress.Version.INET, bytes, 0);
}
+ // Generates a random MAC address.
+ private MacAddress randomMac() {
+ byte[] bytes = new byte[6];
+ random.nextBytes(bytes);
+ return MacAddress.valueOf(bytes);
+ }
+
+ // Returns IP address of a host randomly chosen from the specified list.
private IpAddress randomIp(List<Host> hosts) {
Host host = hosts.get(random.nextInt(hosts.size()));
return host.ipAddresses().iterator().next();
}
- private MacAddress randomMac() {
- byte[] bytes = new byte[6];
- random.nextBytes(bytes);
- return MacAddress.valueOf(bytes);
+ // Returns port number randomly chosen from the given list of port numbers.
+ private PortNumber randomPort(List<PortNumber> ports) {
+ return ports.get(random.nextInt(ports.size()));
}
}
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/NullFlowRuleProvider.java b/providers/null/src/main/java/org/onosproject/provider/nil/NullFlowRuleProvider.java
index beaeb6a..f118f09 100644
--- a/providers/null/src/main/java/org/onosproject/provider/nil/NullFlowRuleProvider.java
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/NullFlowRuleProvider.java
@@ -74,12 +74,18 @@
@Override
public void applyFlowRule(FlowRule... flowRules) {
- throw new UnsupportedOperationException("Cannot apply individual flow rules");
+ for (FlowRule rule : flowRules) {
+ flowTable.getOrDefault(rule.deviceId(), Sets.newConcurrentHashSet())
+ .add(new DefaultFlowEntry(rule));
+ }
}
@Override
public void removeFlowRule(FlowRule... flowRules) {
- throw new UnsupportedOperationException("Cannot remove individual flow rules");
+ for (FlowRule rule : flowRules) {
+ flowTable.getOrDefault(rule.deviceId(), Sets.newConcurrentHashSet())
+ .remove(new DefaultFlowEntry(rule));
+ }
}
@Override
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullHosts.java b/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullHosts.java
index 0495038..c9b9301 100644
--- a/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullHosts.java
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullHosts.java
@@ -19,9 +19,12 @@
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.packet.IpAddress;
+import org.onlab.util.Tools;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.BasicHostConfig;
import org.onosproject.provider.nil.CustomTopologySimulator;
import org.onosproject.provider.nil.NullProviders;
import org.onosproject.provider.nil.TopologySimulator;
@@ -35,6 +38,8 @@
description = "Adds a simulated end-station host to the custom topology simulation")
public class CreateNullHosts extends CreateNullEntity {
+ private static final double NONE = -99999999.9999;
+
@Argument(index = 0, name = "deviceName", description = "Name of device where hosts are attached",
required = true)
String deviceName = null;
@@ -47,9 +52,26 @@
required = true)
int hostCount = 0;
+ @Argument(index = 3, name = "gridY", description = "Grid y-coord for top of host block")
+ double gridY = NONE;
+
+ @Argument(index = 4, name = "gridX", description = "Grid X-coord for center of host block")
+ double gridX = NONE;
+
+ @Argument(index = 5, name = "hostsPerRow", description = "Number of hosts to render per row in block")
+ int hostsPerRow = 5;
+
+ @Argument(index = 6, name = "rowGap", description = "Y gap between rows")
+ double rowGap = 70;
+
+ @Argument(index = 7, name = "colGap", description = "X gap between rows")
+ double colGap = 50;
+
+
@Override
protected void execute() {
NullProviders service = get(NullProviders.class);
+ NetworkConfigService cfgService = get(NetworkConfigService.class);
TopologySimulator simulator = service.currentSimulator();
if (!validateSimulator(simulator)) {
@@ -60,10 +82,27 @@
List<ConnectPoint> points = findAvailablePorts(sim.deviceId(deviceName));
String pattern = hostIpPattern.replace("*", "%d");
+ double yStep = rowGap / hostsPerRow;
+ double y = gridY;
+ double x = gridX - (colGap * (hostsPerRow - 1)) / 2;
+
for (int h = 0; h < hostCount; h++) {
HostLocation location = new HostLocation(points.get(h), System.currentTimeMillis());
IpAddress ip = IpAddress.valueOf(String.format(pattern, h));
HostId id = sim.nextHostId();
+
+ if (gridY != NONE) {
+ BasicHostConfig cfg = cfgService.addConfig(id, BasicHostConfig.class);
+ setUiCoordinates(cfg, GRID, y, x);
+ if (((h + 1) % hostsPerRow) == 0) {
+ x = gridX - (colGap * (hostsPerRow - 1)) / 2;
+ } else {
+ x += colGap;
+ y += yStep;
+ }
+ }
+
+ Tools.delay(10);
sim.createHost(id, location, ip);
}
}