Adding command to add routes and to generate flows from them.
Enhanced FlowRuleStore and FlowRuleService with a new method.
Change-Id: I011371c1931294448e361fc1ceb120d89c14489d
diff --git a/apps/test/route-scale/BUCK b/apps/test/route-scale/BUCK
new file mode 100644
index 0000000..d0853d4
--- /dev/null
+++ b/apps/test/route-scale/BUCK
@@ -0,0 +1,20 @@
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+ '//lib:org.apache.karaf.shell.console',
+ '//cli:onos-cli',
+ '//utils/rest:onlab-rest',
+ '//apps/route-service/api:onos-apps-route-service-api',
+]
+
+osgi_jar_with_tests (
+ deps = COMPILE_DEPS,
+)
+
+onos_app (
+ app_name = 'org.onosproject.routescale',
+ title = 'Route and Flow Scalability Test',
+ category = 'Test Utility',
+ url = 'http://onosproject.org',
+ description = 'Route and flow scalability test facility.',
+ required_apps = [ 'org.onosproject.route-service' ],
+)
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
new file mode 100644
index 0000000..6515def
--- /dev/null
+++ b/apps/test/route-scale/src/main/java/org/onosproject/routescale/CreateFlows.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.routescale;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+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")
+public class CreateFlows extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "flowCount", description = "Number of flows to create",
+ required = true)
+ int flowCount;
+
+ @Override
+ protected void execute() {
+ ScaleTestManager service = get(ScaleTestManager.class);
+ service.createFlows(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
new file mode 100644
index 0000000..b43d2bb
--- /dev/null
+++ b/apps/test/route-scale/src/main/java/org/onosproject/routescale/CreateRoutes.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.routescale;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+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")
+public class CreateRoutes extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "routeCount", description = "Number of routes to create",
+ required = true)
+ int routeCount;
+
+ @Override
+ protected void execute() {
+ ScaleTestManager service = get(ScaleTestManager.class);
+ service.createRoutes(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
new file mode 100644
index 0000000..65c1c7f
--- /dev/null
+++ b/apps/test/route-scale/src/main/java/org/onosproject/routescale/ScaleTestManager.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.routescale;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+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.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.app.ApplicationService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+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.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.routeservice.Route;
+import org.onosproject.routeservice.RouteAdminService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Random;
+
+@Component(immediate = true)
+@Service(value = ScaleTestManager.class)
+public class ScaleTestManager {
+
+ private Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ApplicationService applicationService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostAdminService hostAdminService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected RouteAdminService routeAdminService;
+
+ private final Random random = new Random(System.currentTimeMillis());
+
+ private ApplicationId appId;
+
+ @Activate
+ protected void activate() {
+ appId = applicationService.getId("org.onosproject.routescale");
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ log.info("Stopped");
+ }
+
+ public void createFlows(int flowCount) {
+ for (Device device : deviceService.getAvailableDevices()) {
+ DeviceId id = device.id();
+ FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
+
+ 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();
+
+ 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());
+ }
+
+ flowRuleService.apply(ops.build());
+
+ }
+ }
+
+ public void createRoutes(int routeCount) {
+ List<Host> hosts = ImmutableList.copyOf(hostAdminService.getHosts());
+ ImmutableSet.Builder<Route> routes = ImmutableSet.builder();
+ for (int i = 0; i < routeCount; i++) {
+ IpPrefix prefix = randomIp().toIpPrefix();
+ IpAddress nextHop = randomIp(hosts);
+ routes.add(new Route(Route.Source.STATIC, prefix, nextHop));
+ }
+ routeAdminService.update(routes.build());
+ }
+
+ private IpAddress randomIp() {
+ byte[] bytes = new byte[4];
+ random.nextBytes(bytes);
+ return IpAddress.valueOf(IpAddress.Version.INET, bytes, 0);
+ }
+
+ 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);
+ }
+
+}
diff --git a/apps/test/route-scale/src/main/java/org/onosproject/routescale/package-info.java b/apps/test/route-scale/src/main/java/org/onosproject/routescale/package-info.java
new file mode 100644
index 0000000..a9fe7a9
--- /dev/null
+++ b/apps/test/route-scale/src/main/java/org/onosproject/routescale/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/**
+ * Facilities for creating a scale stress test using routes and flows across
+ * the discovered topology.
+ */
+package org.onosproject.routescale;
\ No newline at end of file
diff --git a/apps/test/route-scale/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/test/route-scale/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..0c9dbc5
--- /dev/null
+++ b/apps/test/route-scale/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright 2018-present Open Networking Foundation
+ ~
+ ~ 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.
+ -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.routescale.CreateRoutes"/>
+ </command>
+ <command>
+ <action class="org.onosproject.routescale.CreateFlows"/>
+ </command>
+ </command-bundle>
+
+</blueprint>
diff --git a/core/api/src/main/java/org/onosproject/net/flow/FlowRuleService.java b/core/api/src/main/java/org/onosproject/net/flow/FlowRuleService.java
index a4cfdfd..0ade2ed 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/FlowRuleService.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/FlowRuleService.java
@@ -43,6 +43,16 @@
int getFlowRuleCount();
/**
+ * Returns the number of flow rules for the given device.
+ *
+ * @param deviceId device identifier
+ * @return number of flow rules for the given device
+ */
+ default int getFlowRuleCount(DeviceId deviceId) {
+ return 0;
+ }
+
+ /**
* Returns the collection of flow entries applied on the specified device.
* This will include flow rules which may not yet have been applied to
* the device.
diff --git a/core/api/src/main/java/org/onosproject/net/flow/FlowRuleStore.java b/core/api/src/main/java/org/onosproject/net/flow/FlowRuleStore.java
index ff0bbe1..449a1fd 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/FlowRuleStore.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/FlowRuleStore.java
@@ -15,26 +15,36 @@
*/
package org.onosproject.net.flow;
-import java.util.List;
-
import org.onosproject.net.DeviceId;
import org.onosproject.net.flow.oldbatch.FlowRuleBatchEvent;
import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
import org.onosproject.store.Store;
+import java.util.List;
+
/**
* Manages inventory of flow rules; not intended for direct use.
*/
public interface FlowRuleStore extends Store<FlowRuleBatchEvent, FlowRuleStoreDelegate> {
/**
- * Returns the number of flow rule in the store.
+ * Returns the number of flow rules in the store.
*
* @return number of flow rules
*/
int getFlowRuleCount();
/**
+ * Returns the number of flow rules for the given device in the store.
+ *
+ * @param deviceId device identifier
+ * @return number of flow rules for the given device
+ */
+ default int getFlowRuleCount(DeviceId deviceId) {
+ return 0;
+ }
+
+ /**
* Returns the stored flow.
*
* @param rule the rule to look for
diff --git a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
index 9c1a31a..ffcfe0b 100644
--- a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
@@ -101,6 +101,7 @@
FlowRuleProvider, FlowRuleProviderService>
implements FlowRuleService, FlowRuleProviderRegistry {
+ public static final String DEVICE_ID_NULL = "Device ID cannot be null";
private final Logger log = getLogger(getClass());
public static final String FLOW_RULE_NULL = "FlowRule cannot be null";
@@ -233,8 +234,16 @@
}
@Override
+ public int getFlowRuleCount(DeviceId deviceId) {
+ checkPermission(FLOWRULE_READ);
+ checkNotNull(deviceId, "Device ID cannot be null");
+ return store.getFlowRuleCount(deviceId);
+ }
+
+ @Override
public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
checkPermission(FLOWRULE_READ);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
return store.getFlowEntries(deviceId);
}
@@ -252,6 +261,7 @@
@Override
public void purgeFlowRules(DeviceId deviceId) {
checkPermission(FLOWRULE_WRITE);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
store.purgeFlowRule(deviceId);
}
@@ -343,6 +353,7 @@
*/
@Override
protected synchronized FlowRuleProvider getProvider(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
// if device supports FlowRuleProgrammable,
// use FlowRuleProgrammable via FlowRuleDriverProvider
return Optional.ofNullable(deviceService.getDevice(deviceId))
@@ -714,11 +725,13 @@
@Override
public Iterable<TableStatisticsEntry> getFlowTableStatistics(DeviceId deviceId) {
checkPermission(FLOWRULE_READ);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
return store.getTableStatistics(deviceId);
}
@Override
public long getActiveFlowRuleCount(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
return store.getActiveFlowRuleCount(deviceId);
}
diff --git a/core/store/dist/src/main/java/org/onosproject/store/flow/impl/ECFlowRuleStore.java b/core/store/dist/src/main/java/org/onosproject/store/flow/impl/ECFlowRuleStore.java
index 64c09cf..e1a8cff 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/flow/impl/ECFlowRuleStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/flow/impl/ECFlowRuleStore.java
@@ -186,6 +186,7 @@
.register(KryoNamespaces.API)
.register(MastershipBasedTimestamp.class);
+ private EventuallyConsistentMap<DeviceId, Integer> flowCounts;
private IdGenerator idGenerator;
private NodeId local;
@@ -212,6 +213,14 @@
backupPeriod,
TimeUnit.MILLISECONDS);
+ flowCounts = storageService.<DeviceId, Integer>eventuallyConsistentMapBuilder()
+ .withName("onos-flow-counts")
+ .withSerializer(serializerBuilder)
+ .withAntiEntropyPeriod(5, TimeUnit.SECONDS)
+ .withTimestampProvider((k, v) -> new WallClockTimestamp())
+ .withTombstonesDisabled()
+ .build();
+
deviceTableStats = storageService.<DeviceId, List<TableStatisticsEntry>>eventuallyConsistentMapBuilder()
.withName("onos-flow-table-stats")
.withSerializer(serializerBuilder)
@@ -333,8 +342,14 @@
@Override
public int getFlowRuleCount() {
return Streams.stream(deviceService.getDevices()).parallel()
- .mapToInt(device -> Iterables.size(getFlowEntries(device.id())))
- .sum();
+ .mapToInt(device -> getFlowRuleCount(device.id()))
+ .sum();
+ }
+
+ @Override
+ public int getFlowRuleCount(DeviceId deviceId) {
+ Integer count = flowCounts.get(deviceId);
+ return count != null ? count : 0;
}
@Override
@@ -703,7 +718,13 @@
log.debug("Sending flowEntries for devices {} to {} for backup.", deviceIds, nodeId);
Map<DeviceId, Map<FlowId, Map<StoredFlowEntry, StoredFlowEntry>>>
deviceFlowEntries = Maps.newConcurrentMap();
- deviceIds.forEach(id -> deviceFlowEntries.put(id, getFlowTableCopy(id)));
+ deviceIds.forEach(id -> {
+ Map<FlowId, Map<StoredFlowEntry, StoredFlowEntry>> copy = getFlowTableCopy(id);
+ int flowCount = copy.entrySet().stream()
+ .mapToInt(e -> e.getValue().values().size()).sum();
+ flowCounts.put(id, flowCount);
+ deviceFlowEntries.put(id, copy);
+ });
clusterCommunicator.<Map<DeviceId,
Map<FlowId, Map<StoredFlowEntry, StoredFlowEntry>>>,
Set<DeviceId>>
diff --git a/modules.defs b/modules.defs
index e1b6fdb..65bef76 100644
--- a/modules.defs
+++ b/modules.defs
@@ -179,6 +179,7 @@
'//apps/test/election:onos-apps-test-election-oar',
'//apps/test/flow-perf:onos-apps-test-flow-perf-oar',
'//apps/test/intent-perf:onos-apps-test-intent-perf-oar',
+ '//apps/test/route-scale:onos-apps-test-route-scale-oar',
'//apps/test/loadtest:onos-apps-test-loadtest-oar',
'//apps/test/netcfg-monitor:onos-apps-test-netcfg-monitor-oar',
'//apps/test/messaging-perf:onos-apps-test-messaging-perf-oar',
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 2cbb4d0..beaeb6a 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,12 @@
@Override
public void applyFlowRule(FlowRule... flowRules) {
- // FIXME: invoke executeBatch
+ throw new UnsupportedOperationException("Cannot apply individual flow rules");
}
@Override
public void removeFlowRule(FlowRule... flowRules) {
- // FIXME: invoke executeBatch
+ throw new UnsupportedOperationException("Cannot remove individual flow rules");
}
@Override
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullEntity.java b/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullEntity.java
index 368fa3d..a9efa78 100644
--- a/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullEntity.java
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullEntity.java
@@ -34,9 +34,12 @@
* Base command for adding simulated entities to the custom topology simulation.
*/
public abstract class CreateNullEntity extends AbstractShellCommand {
+
protected static final String GEO = "geo";
protected static final String GRID = "grid";
+ protected static final int MAX_EDGE_PORT_TRIES = 5;
+
/**
* Validates that the simulator is custom.
*
@@ -94,22 +97,32 @@
* @return connect point available for link or host attachment
*/
protected ConnectPoint findAvailablePort(DeviceId deviceId, ConnectPoint otherPoint) {
- EdgePortService eps = get(EdgePortService.class);
HostService hs = get(HostService.class);
+ return findAvailablePorts(deviceId).stream()
+ .filter(p -> !Objects.equals(p, otherPoint) && hs.getConnectedHosts(p).isEmpty())
+ .findFirst().orElse(null);
+ }
+
+ /**
+ * Finds an available connect points among edge ports of the specified device.
+ *
+ * @param deviceId device identifier
+ * @return list of connect points available for link or host attachments
+ */
+ protected List<ConnectPoint> findAvailablePorts(DeviceId deviceId) {
+ EdgePortService eps = get(EdgePortService.class);
// As there may be a slight delay in edge service getting updated, retry a few times
- for (int i = 0; i < 3; i++) {
+ for (int i = 0; i < MAX_EDGE_PORT_TRIES; i++) {
List<ConnectPoint> points = ImmutableList
.sortedCopyOf((l, r) -> Longs.compare(l.port().toLong(), r.port().toLong()),
eps.getEdgePoints(deviceId));
- ConnectPoint point = points.stream()
- .filter(p -> !Objects.equals(p, otherPoint) && hs.getConnectedHosts(p).isEmpty())
- .findFirst().orElse(null);
- if (point != null) {
- return point;
+ if (!points.isEmpty()) {
+ return points;
}
Tools.delay(100);
}
- return null;
+ return ImmutableList.of();
}
+
}
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
new file mode 100644
index 0000000..0495038
--- /dev/null
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullHosts.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.provider.nil.CustomTopologySimulator;
+import org.onosproject.provider.nil.NullProviders;
+import org.onosproject.provider.nil.TopologySimulator;
+
+import java.util.List;
+
+/**
+ * Adds a simulated end-station host to the custom topology simulation.
+ */
+@Command(scope = "onos", name = "null-create-hosts",
+ description = "Adds a simulated end-station host to the custom topology simulation")
+public class CreateNullHosts extends CreateNullEntity {
+
+ @Argument(index = 0, name = "deviceName", description = "Name of device where hosts are attached",
+ required = true)
+ String deviceName = null;
+
+ @Argument(index = 1, name = "hostIpPattern", description = "Host IP pattern",
+ required = true)
+ String hostIpPattern = null;
+
+ @Argument(index = 2, name = "hostCount", description = "Number of hosts to create",
+ required = true)
+ int hostCount = 0;
+
+ @Override
+ protected void execute() {
+ NullProviders service = get(NullProviders.class);
+
+ TopologySimulator simulator = service.currentSimulator();
+ if (!validateSimulator(simulator)) {
+ return;
+ }
+
+ CustomTopologySimulator sim = (CustomTopologySimulator) simulator;
+
+ List<ConnectPoint> points = findAvailablePorts(sim.deviceId(deviceName));
+ String pattern = hostIpPattern.replace("*", "%d");
+ 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();
+ sim.createHost(id, location, ip);
+ }
+ }
+
+}
diff --git a/providers/null/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/providers/null/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index e1abc40..7be09e8 100644
--- a/providers/null/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/providers/null/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -50,6 +50,9 @@
<command>
<action class="org.onosproject.provider.nil.cli.CreateNullHost"/>
</command>
+ <command>
+ <action class="org.onosproject.provider.nil.cli.CreateNullHosts"/>
+ </command>
</command-bundle>
<bean id="startStopCompleter" class="org.onosproject.cli.StartStopCompleter"/>
diff --git a/tools/test/topos/access-null b/tools/test/topos/access-null
new file mode 100755
index 0000000..0d9a13f
--- /dev/null
+++ b/tools/test/topos/access-null
@@ -0,0 +1,123 @@
+#!/bin/bash
+# -----------------------------------------------------------------------------
+# Creates a spine-leaf fabric with large number of hosts using null providers
+#
+# Default setup as follows:
+# 2 spines, can potentially be few more
+# 12 leaves in total
+# 2 leaf pair
+# 8 non-paired
+# Host per leaf up to 1K
+#
+# -----------------------------------------------------------------------------
+
+function usage {
+ echo "usage: $(basename $0) [options] [onos-ip]"
+ echo ""
+ echo "Options:"
+ echo " -s spines"
+ echo " -l spineLinks"
+ echo " -S serviceHosts"
+ echo " -a accessLeaves"
+ echo " -A accessHosts"
+ exit 1
+}
+
+spines=2
+spineLinks=2
+serviceLeafGroups="A B"
+serviceHosts=10
+accessLeaves=8
+accessHosts=100
+
+# Scan arguments for user/password or other options...
+while getopts s:l:a:A:S:?h o; do
+ case "$o" in
+ s) spines=$OPTARG;;
+ l) spineLinks=$OPTARG;;
+ a) accessLeaves=$OPTARG;;
+ A) accessHosts=$OPTARG;;
+ S) serviceHosts=$OPTARG;;
+ *) usage $0;;
+ esac
+done
+
+spinePorts=48
+let leafPorts=serviceHosts+8 # derive service ports from service hosts
+let accessPorts=accessHosts+8 # derive access ports from access hosts
+
+let OPC=$OPTIND-1
+shift $OPC
+
+# config
+node=${1:-$OCI}
+
+
+# Create the script of ONOS commands first and then execute it all at once.
+export CMDS="/tmp/fab-onos.cmds"
+rm $CMDS
+
+function sim {
+ echo "$@" >> $CMDS
+}
+
+# Create spines
+for spine in $(seq 1 $spines); do
+ sim "null-create-device switch Spine-${spine} ${spinePorts}"
+done
+
+# Create 2 leaf pairs with dual links to the spines and a link between the pair
+for pair in $serviceLeafGroups; do
+ sim "null-create-device switch Leaf-${pair}1 ${leafPorts}"
+ sim "null-create-device switch Leaf-${pair}2 ${leafPorts}"
+ sim "null-create-link direct Leaf-${pair}1 Leaf-${pair}2"
+
+ for spine in $(seq 1 $spines); do
+ for link in $(seq 1 $spineLinks); do
+ sim "null-create-link direct Spine-${spine} Leaf-${pair}1"
+ sim "null-create-link direct Spine-${spine} Leaf-${pair}2"
+ done
+ done
+
+ # Create hosts for each leaf group; multi-homed to each leaf in the pair
+ [ $pair = A ] && pn=1 || pn=2
+ for host in $(seq 1 $serviceHosts); do
+ sim "null-create-host Leaf-${pair}1,Leaf-${pair}2 10.${pn}.1.${host}"
+ done
+done
+
+# Create single access leafs with dual links to the spines
+for access in $(seq $accessLeaves); do
+ sim "null-create-device switch Access-${access} ${accessPorts}"
+
+ for spine in $(seq 1 $spines); do
+ for link in $(seq 1 $spineLinks); do
+ sim "null-create-link direct Spine-${spine} Access-${access}"
+ done
+ done
+
+ # Create hosts for each access single leaf
+ sim "null-create-hosts Access-${access} 10.1${access}.1.*" $accessHosts
+ # sim "null-create-hosts Access-${access} 10.1${access}.2.*" $accessHosts
+done
+
+
+# make sure null providers are activated and any running simulation is stopped
+onos ${node} app activate org.onosproject.null
+sleep 2
+onos ${node} null-simulation stop
+
+# wait until the masterships clear-out across the cluster
+while onos ${node} masters | grep -qv " 0 devices"; do sleep 1; done
+
+# clean-up
+onos ${node} wipe-out please
+sleep 1
+
+# start custom simulation..
+onos ${node} null-simulation start custom
+sleep 2
+
+# Add devices, links, and hosts
+cat $CMDS | onos ${node}
+
diff --git a/tools/test/topos/cfab-null b/tools/test/topos/cfab-null
deleted file mode 100755
index f8eb3f0..0000000
--- a/tools/test/topos/cfab-null
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/bin/bash
-# -----------------------------------------------------------------------------
-# Creates a spine-leaf fabric with large number of hosts using null providers
-# -----------------------------------------------------------------------------
-
-# config
-node=${1:-$OCI}
-
-# Create the script of ONOS commands first and then execute it all at once.
-export CMDS="/tmp/fab-onos.cmds"
-rm $CMDS
-
-function sim {
- echo "$@" >> $CMDS
-}
-
-# Generate the recipe using the following:
-# 2 spines, can potentially be few more
-# 12 leaves in total
-# 2 leaf pair
-# 8 non-paired
-# Host per leaf up to 1K
-
-spinePorts=48
-leafPorts=64
-accessPorts=128
-
-# Create spines
-for spine in {1..2}; do
- sim "null-create-device switch Spine-${spine} ${spinePorts}"
-done
-
-# Create 2 leaf pairs with dual links to the spines and a link between the pair
-for pair in A B; do
- sim "null-create-device switch Leaf-${pair}1 ${leafPorts}"
- sim "null-create-device switch Leaf-${pair}2 ${leafPorts}"
- sim "null-create-link direct Leaf-${pair}1 Leaf-${pair}2"
-
- for spine in {1..2}; do
- for link in {1..2}; do
- sim "null-create-link direct Spine-${spine} Leaf-${pair}1"
- sim "null-create-link direct Spine-${spine} Leaf-${pair}2"
- done
- done
-
- # Create hosts for each leaf group; multi-homed to each leaf in the pair
- [ $pair = A ] && pn=1 || pn=2
- for host in {1..10}; do
- sim "null-create-host Leaf-${pair}1,Leaf-${pair}2 10.${pn}.1.${host}"
- done
-done
-
-# Create 8 single leafs with dual links to the spines
-for access in {1..8}; do
- sim "null-create-device switch Access-${access} ${accessPorts}"
-
- for spine in {1..2}; do
- for link in {1..2}; do
- sim "null-create-link direct Spine-${spine} Access-${access}"
- done
- done
-
- # Create hosts for each single leaf
- for host in {1..50}; do
- sim "null-create-host Access-${access} 10.0.${access}.${host}"
- done
-done
-
-
-# make sure null providers are activated and any running simulation is stopped
-onos ${node} app activate org.onosproject.null
-sleep 2
-onos ${node} null-simulation stop
-
-# wait until the masterships clear-out across the cluster
-while onos ${node} masters | grep -qv " 0 devices"; do sleep 1; done
-
-# clean-up
-onos ${node} wipe-out please
-sleep 1
-
-# start custom simulation..
-onos ${node} null-simulation start custom
-sleep 2
-
-# Add devices, links, and hosts
-cat $CMDS | onos ${node}
-
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
index d84fbda..789c252 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
@@ -43,7 +43,6 @@
import org.onosproject.net.HostLocation;
import org.onosproject.net.Link;
import org.onosproject.net.device.DeviceEvent;
-import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.link.LinkEvent;
import org.onosproject.net.provider.ProviderId;
@@ -544,11 +543,7 @@
}
protected int getFlowCount(DeviceId deviceId) {
- int count = 0;
- for (FlowEntry flowEntry : services.flow().getFlowEntries(deviceId)) {
- count++;
- }
- return count;
+ return services.flow().getFlowRuleCount(deviceId);
}
protected int getTunnelCount(DeviceId deviceId) {