summaryrefslogtreecommitdiffstats
path: root/framework/src/onos/providers/null/src/main/java/org
diff options
context:
space:
mode:
authorAshlee Young <ashlee@onosfw.com>2015-09-09 22:15:21 -0700
committerAshlee Young <ashlee@onosfw.com>2015-09-09 22:15:21 -0700
commit13d05bc8458758ee39cb829098241e89616717ee (patch)
tree22a4d1ce65f15952f07a3df5af4b462b4697cb3a /framework/src/onos/providers/null/src/main/java/org
parent6139282e1e93c2322076de4b91b1c85d0bc4a8b3 (diff)
ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60
Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd
Diffstat (limited to 'framework/src/onos/providers/null/src/main/java/org')
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/AggLinkTopologySimulator.java49
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/CentipedeTopologySimulator.java30
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/ConfiguredTopologySimulator.java44
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/LinearTopologySimulator.java58
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/MeshTopologySimulator.java44
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullFlowRuleProvider.java132
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullPacketProvider.java169
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullProviders.java420
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/RerouteTopologySimulator.java57
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/SpineLeafTopologySimulator.java43
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologyMutationDriver.java223
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologySimulator.java382
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TreeTopologySimulator.java77
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullControlCommand.java52
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullLinkCommand.java68
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/TopologyShapeCompleter.java32
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/package-info.java20
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/package-info.java21
18 files changed, 1921 insertions, 0 deletions
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/AggLinkTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/AggLinkTopologySimulator.java
new file mode 100644
index 00000000..2d09ebed
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/AggLinkTopologySimulator.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 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.nil;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Simple triangle topology with multiple links between same devices.
+ */
+public class AggLinkTopologySimulator extends CentipedeTopologySimulator {
+
+ @Override
+ protected void processTopoShape(String shape) {
+ super.processTopoShape(shape);
+ infrastructurePorts = 2 * deviceCount - 1;
+ }
+
+ @Override
+ public void setUpTopology() {
+ checkArgument(deviceCount > 2, "There must be at least 3 devices");
+ super.setUpTopology();
+ }
+
+ @Override
+ protected void createLinks() {
+ int srcPortOffset = deviceCount + 1;
+ for (int i = 0, n = deviceCount; i < n; i++) {
+ int dstPortOffset = 1;
+ for (int j = 0; j <= i; j++) {
+ createLink(i, (i + 1) % n, srcPortOffset + j, dstPortOffset + j);
+ }
+ srcPortOffset = dstPortOffset + i + 1;
+ }
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/CentipedeTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/CentipedeTopologySimulator.java
new file mode 100644
index 00000000..5234d448
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/CentipedeTopologySimulator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 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.nil;
+
+/**
+ * Linear topology with hosts on every device.
+ */
+public class CentipedeTopologySimulator extends LinearTopologySimulator {
+
+ /**
+ * Creates simulated hosts.
+ */
+ protected void createHosts() {
+ deviceIds.forEach(id -> createHosts(id, infrastructurePorts));
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/ConfiguredTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/ConfiguredTopologySimulator.java
new file mode 100644
index 00000000..ad57bf3f
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/ConfiguredTopologySimulator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 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.nil;
+
+/**
+ * Topology simulator which operates on topology configured via the REST API
+ * config service.
+ */
+public class ConfiguredTopologySimulator extends TopologySimulator {
+
+ @Override
+ protected void createDevices() {
+ deviceService.getDevices()
+ .forEach(device -> deviceProviderService
+ .deviceConnected(device.id(), description(device)));
+ }
+
+ @Override
+ protected void createLinks() {
+ linkService.getLinks()
+ .forEach(link -> linkProviderService
+ .linkDetected(description(link)));
+ }
+
+ @Override
+ protected void createHosts() {
+ hostService.getHosts()
+ .forEach(host -> hostProviderService
+ .hostDetected(host.id(), description(host)));
+ }
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/LinearTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/LinearTopologySimulator.java
new file mode 100644
index 00000000..beb06c33
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/LinearTopologySimulator.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 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.nil;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Linear topology simulator.
+ */
+public class LinearTopologySimulator extends TopologySimulator {
+
+ @Override
+ protected void processTopoShape(String shape) {
+ super.processTopoShape(shape);
+ deviceCount = (topoShape.length == 1) ? deviceCount : Integer.parseInt(topoShape[1]);
+ }
+
+ @Override
+ public void setUpTopology() {
+ checkArgument(deviceCount > 1, "There must be at least 2 devices");
+
+ prepareForDeviceEvents(deviceCount);
+ createDevices();
+ waitForDeviceEvents();
+
+ createLinks();
+ createHosts();
+ }
+
+ @Override
+ protected void createLinks() {
+ int portOffset = 1;
+ for (int i = 0, n = deviceCount - 1; i < n; i++) {
+ createLink(i, i + 1, portOffset, 1);
+ portOffset = 2;
+ }
+ }
+
+ @Override
+ protected void createHosts() {
+ createHosts(deviceIds.get(0), infrastructurePorts);
+ createHosts(deviceIds.get(deviceCount - 1), infrastructurePorts);
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/MeshTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/MeshTopologySimulator.java
new file mode 100644
index 00000000..d3f2d6ad
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/MeshTopologySimulator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 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.nil;
+
+/**
+ * Full mesh topology with hosts at each device.
+ */
+public class MeshTopologySimulator extends TopologySimulator {
+
+ @Override
+ protected void processTopoShape(String shape) {
+ super.processTopoShape(shape);
+ // FIXME: implement this
+ }
+
+ @Override
+ public void setUpTopology() {
+ // FIXME: implement this
+ // checkArgument(FIXME, "There must be at least ...");
+ super.setUpTopology();
+ }
+
+ @Override
+ protected void createLinks() {
+ }
+
+ @Override
+ protected void createHosts() {
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullFlowRuleProvider.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullFlowRuleProvider.java
new file mode 100644
index 00000000..9b01d12a
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullFlowRuleProvider.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 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.nil;
+
+import com.google.common.collect.Sets;
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.util.Timer;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.CompletedBatchOperation;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleBatchEntry;
+import org.onosproject.net.flow.FlowRuleBatchOperation;
+import org.onosproject.net.flow.FlowRuleProvider;
+import org.onosproject.net.flow.FlowRuleProviderService;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Null provider to accept any flow and report them.
+ */
+class NullFlowRuleProvider extends NullProviders.AbstractNullProvider
+ implements FlowRuleProvider {
+
+ private final Logger log = getLogger(getClass());
+
+ private ConcurrentMap<DeviceId, Set<FlowEntry>> flowTable = new ConcurrentHashMap<>();
+
+ private FlowRuleProviderService providerService;
+
+ private HashedWheelTimer timer = Timer.getTimer();
+ private Timeout timeout;
+
+ /**
+ * Starts the flow rule provider simulation.
+ *
+ * @param providerService flow rule provider service
+ */
+ void start(FlowRuleProviderService providerService) {
+ this.providerService = providerService;
+ timeout = timer.newTimeout(new StatisticTask(), 5, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Stops the flow rule provider simulation.
+ */
+ void stop() {
+ timeout.cancel();
+ }
+
+ @Override
+ public void applyFlowRule(FlowRule... flowRules) {
+ // FIXME: invoke executeBatch
+ }
+
+ @Override
+ public void removeFlowRule(FlowRule... flowRules) {
+ // FIXME: invoke executeBatch
+ }
+
+ @Override
+ public void removeRulesById(ApplicationId id, FlowRule... flowRules) {
+ throw new UnsupportedOperationException("Cannot remove by appId from null provider");
+ }
+
+ @Override
+ public void executeBatch(FlowRuleBatchOperation batch) {
+ // TODO: consider checking mastership
+ Set<FlowEntry> entries =
+ flowTable.getOrDefault(batch.deviceId(),
+ Sets.newConcurrentHashSet());
+ for (FlowRuleBatchEntry fbe : batch.getOperations()) {
+ switch (fbe.operator()) {
+ case ADD:
+ entries.add(new DefaultFlowEntry(fbe.target()));
+ break;
+ case REMOVE:
+ entries.remove(new DefaultFlowEntry(fbe.target()));
+ break;
+ case MODIFY:
+ FlowEntry entry = new DefaultFlowEntry(fbe.target());
+ entries.remove(entry);
+ entries.add(entry);
+ break;
+ default:
+ log.error("Unknown flow operation: {}", fbe);
+ }
+ }
+ flowTable.put(batch.deviceId(), entries);
+ CompletedBatchOperation op =
+ new CompletedBatchOperation(true, Collections.emptySet(),
+ batch.deviceId());
+ providerService.batchOperationCompleted(batch.id(), op);
+ }
+
+ // Periodically reports flow rule statistics.
+ private class StatisticTask implements TimerTask {
+ @Override
+ public void run(Timeout to) throws Exception {
+ for (DeviceId devId : flowTable.keySet()) {
+ Set<FlowEntry> entries =
+ flowTable.getOrDefault(devId, Collections.emptySet());
+ providerService.pushFlowMetrics(devId, entries);
+ }
+ timeout = timer.newTimeout(to.getTask(), 5, TimeUnit.SECONDS);
+ }
+ }
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullPacketProvider.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullPacketProvider.java
new file mode 100644
index 00000000..07a137e9
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullPacketProvider.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 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.nil;
+
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP;
+import org.onlab.util.Timer;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceAdminService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.DefaultPacketContext;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketProvider;
+import org.onosproject.net.packet.PacketProviderService;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static com.google.common.collect.ImmutableList.copyOf;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.onosproject.net.MastershipRole.MASTER;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider which generates simulated packets and acts as a sink for outbound
+ * packets. To be used for benchmarking only.
+ */
+class NullPacketProvider extends NullProviders.AbstractNullProvider
+ implements PacketProvider {
+
+ private static final int INITIAL_DELAY = 5;
+ private final Logger log = getLogger(getClass());
+
+ // Arbitrary host src/dst
+ private static final int SRC_HOST = 2;
+ private static final int DST_HOST = 5;
+
+ // Time between event firing, in milliseconds
+ private int delay;
+
+ // TODO: use host service to pick legitimate hosts connected to devices
+ private HostService hostService;
+ private PacketProviderService providerService;
+
+ private List<Device> devices;
+ private int currentDevice = 0;
+
+ private HashedWheelTimer timer = Timer.getTimer();
+ private Timeout timeout;
+
+ /**
+ * Starts the packet generation process.
+ *
+ * @param packetRate packets per second
+ * @param hostService host service
+ * @param deviceService device service
+ * @param providerService packet provider service
+ */
+ void start(int packetRate, HostService hostService,
+ DeviceAdminService deviceService,
+ PacketProviderService providerService) {
+ this.hostService = hostService;
+ this.providerService = providerService;
+
+ this.devices = copyOf(deviceService.getDevices()).stream()
+ .filter(d -> deviceService.getRole(d.id()) == MASTER)
+ .collect(Collectors.toList());
+
+ adjustRate(packetRate);
+ timeout = timer.newTimeout(new PacketDriverTask(), INITIAL_DELAY, SECONDS);
+ }
+
+ /**
+ * Adjusts packet rate.
+ *
+ * @param packetRate new packet rate
+ */
+ void adjustRate(int packetRate) {
+ delay = 1000 / packetRate;
+ log.info("Settings: packetRate={}, delay={}", packetRate, delay);
+ }
+
+ /**
+ * Stops the packet generation process.
+ */
+ void stop() {
+ if (timeout != null) {
+ timeout.cancel();
+ }
+ }
+
+ @Override
+ public void emit(OutboundPacket packet) {
+ // We don't have a network to emit to. Keep a counter here, maybe?
+ }
+
+ /**
+ * Generates packet events at a given rate.
+ */
+ private class PacketDriverTask implements TimerTask {
+
+ // Filler echo request
+ ICMP icmp;
+ Ethernet eth;
+
+ public PacketDriverTask() {
+ icmp = new ICMP();
+ icmp.setIcmpType((byte) 8).setIcmpCode((byte) 0).setChecksum((short) 0);
+ eth = new Ethernet();
+ eth.setEtherType(Ethernet.TYPE_IPV4);
+ eth.setPayload(icmp);
+ }
+
+ @Override
+ public void run(Timeout to) {
+ if (!devices.isEmpty() && !to.isCancelled()) {
+ sendEvent(devices.get(Math.min(currentDevice, devices.size() - 1)));
+ currentDevice = (currentDevice + 1) % devices.size();
+ timeout = timer.newTimeout(to.getTask(), delay, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private void sendEvent(Device device) {
+ // Make it look like things came from ports attached to hosts
+ eth.setSourceMACAddress("00:00:10:00:00:0" + SRC_HOST)
+ .setDestinationMACAddress("00:00:10:00:00:0" + DST_HOST);
+ InboundPacket inPkt = new DefaultInboundPacket(
+ new ConnectPoint(device.id(), PortNumber.portNumber(SRC_HOST)),
+ eth, ByteBuffer.wrap(eth.serialize()));
+ providerService.processPacket(new NullPacketContext(inPkt, null));
+ }
+ }
+
+ // Minimal PacketContext to make core and applications happy.
+ private final class NullPacketContext extends DefaultPacketContext {
+ private NullPacketContext(InboundPacket inPkt, OutboundPacket outPkt) {
+ super(System.currentTimeMillis(), inPkt, outPkt, false);
+ }
+
+ @Override
+ public void send() {
+ // We don't send anything out.
+ }
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullProviders.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullProviders.java
new file mode 100644
index 00000000..c5688419
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullProviders.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright 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.nil;
+
+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;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.mastership.MastershipAdminService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.device.DeviceAdminService;
+import org.onosproject.net.device.DeviceProvider;
+import org.onosproject.net.device.DeviceProviderRegistry;
+import org.onosproject.net.device.DeviceProviderService;
+import org.onosproject.net.flow.FlowRuleProviderRegistry;
+import org.onosproject.net.flow.FlowRuleProviderService;
+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.link.LinkProvider;
+import org.onosproject.net.link.LinkProviderRegistry;
+import org.onosproject.net.link.LinkProviderService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.packet.PacketProviderRegistry;
+import org.onosproject.net.packet.PacketProviderService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.util.Dictionary;
+import java.util.Properties;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.delay;
+import static org.onlab.util.Tools.get;
+import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.net.MastershipRole.MASTER;
+import static org.onosproject.net.MastershipRole.NONE;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider of a fake network environment, i.e. devices, links, hosts, etc.
+ * To be used for benchmarking only.
+ */
+@Component(immediate = true)
+@Service(value = NullProviders.class)
+public class NullProviders {
+
+ private static final Logger log = getLogger(NullProviders.class);
+
+ static final String SCHEME = "null";
+ static final String PROVIDER_ID = "org.onosproject.provider.nil";
+
+ private static final String FORMAT =
+ "Settings: enabled={}, topoShape={}, deviceCount={}, " +
+ "hostCount={}, packetRate={}, mutationRate={}";
+
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipAdminService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceAdminService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceProviderRegistry deviceProviderRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostProviderRegistry hostProviderRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkProviderRegistry linkProviderRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleProviderRegistry flowRuleProviderRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketProviderRegistry packetProviderRegistry;
+
+
+ private final NullDeviceProvider deviceProvider = new NullDeviceProvider();
+ private final NullLinkProvider linkProvider = new NullLinkProvider();
+ private final NullHostProvider hostProvider = new NullHostProvider();
+ private final NullFlowRuleProvider flowRuleProvider = new NullFlowRuleProvider();
+ private final NullPacketProvider packetProvider = new NullPacketProvider();
+ private final TopologyMutationDriver topologyMutationDriver = new TopologyMutationDriver();
+
+ private DeviceProviderService deviceProviderService;
+ private HostProviderService hostProviderService;
+ private LinkProviderService linkProviderService;
+ private FlowRuleProviderService flowRuleProviderService;
+ private PacketProviderService packetProviderService;
+
+ private TopologySimulator simulator;
+
+ @Property(name = "enabled", boolValue = false,
+ label = "Enables or disables the provider")
+ private boolean enabled = false;
+
+ private static final String DEFAULT_TOPO_SHAPE = "configured";
+ @Property(name = "topoShape", value = DEFAULT_TOPO_SHAPE,
+ label = "Topology shape: configured, linear, reroute, tree, spineleaf, mesh")
+ private String topoShape = DEFAULT_TOPO_SHAPE;
+
+ private static final int DEFAULT_DEVICE_COUNT = 10;
+ @Property(name = "deviceCount", intValue = DEFAULT_DEVICE_COUNT,
+ label = "Number of devices to generate")
+ private int deviceCount = DEFAULT_DEVICE_COUNT;
+
+ private static final int DEFAULT_HOST_COUNT = 5;
+ @Property(name = "hostCount", intValue = DEFAULT_HOST_COUNT,
+ label = "Number of host to generate per device")
+ private int hostCount = DEFAULT_HOST_COUNT;
+
+ private static final int DEFAULT_PACKET_RATE = 5;
+ @Property(name = "packetRate", intValue = DEFAULT_PACKET_RATE,
+ label = "Packet-in/s rate; 0 for no packets")
+ private int packetRate = DEFAULT_PACKET_RATE;
+
+ private static final double DEFAULT_MUTATION_RATE = 0;
+ @Property(name = "mutationRate", doubleValue = DEFAULT_MUTATION_RATE,
+ label = "Link event/s topology mutation rate; 0 for no mutations")
+ private double mutationRate = DEFAULT_MUTATION_RATE;
+
+ private static final String DEFAULT_MASTERSHIP = "random";
+ @Property(name = "mastership", value = DEFAULT_MASTERSHIP,
+ label = "Mastership given as 'random' or 'node1=dpid,dpid/node2=dpid,...'")
+ private String mastership = DEFAULT_MASTERSHIP;
+
+
+ @Activate
+ public void activate(ComponentContext context) {
+ cfgService.registerProperties(getClass());
+
+ deviceProviderService = deviceProviderRegistry.register(deviceProvider);
+ hostProviderService = hostProviderRegistry.register(hostProvider);
+ linkProviderService = linkProviderRegistry.register(linkProvider);
+ flowRuleProviderService = flowRuleProviderRegistry.register(flowRuleProvider);
+ packetProviderService = packetProviderRegistry.register(packetProvider);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate(ComponentContext context) {
+ cfgService.unregisterProperties(getClass(), false);
+ tearDown();
+
+ deviceProviderRegistry.unregister(deviceProvider);
+ hostProviderRegistry.unregister(hostProvider);
+ linkProviderRegistry.unregister(linkProvider);
+ flowRuleProviderRegistry.unregister(flowRuleProvider);
+ packetProviderRegistry.unregister(packetProvider);
+
+ deviceProviderService = null;
+ hostProviderService = null;
+ linkProviderService = null;
+ flowRuleProviderService = null;
+ packetProviderService = null;
+
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
+
+ boolean newEnabled;
+ int newDeviceCount, newHostCount, newPacketRate;
+ double newMutationRate;
+ String newTopoShape, newMastership;
+ try {
+ String s = get(properties, "enabled");
+ newEnabled = isNullOrEmpty(s) ? enabled : Boolean.parseBoolean(s.trim());
+
+ newTopoShape = get(properties, "topoShape");
+ newMastership = get(properties, "mastership");
+
+ s = get(properties, "deviceCount");
+ newDeviceCount = isNullOrEmpty(s) ? deviceCount : Integer.parseInt(s.trim());
+
+ s = get(properties, "hostCount");
+ newHostCount = isNullOrEmpty(s) ? hostCount : Integer.parseInt(s.trim());
+
+ s = get(properties, "packetRate");
+ newPacketRate = isNullOrEmpty(s) ? packetRate : Integer.parseInt(s.trim());
+
+ s = get(properties, "mutationRate");
+ newMutationRate = isNullOrEmpty(s) ? mutationRate : Double.parseDouble(s.trim());
+
+ } catch (NumberFormatException e) {
+ log.warn(e.getMessage());
+ newEnabled = enabled;
+ newTopoShape = topoShape;
+ newDeviceCount = deviceCount;
+ newHostCount = hostCount;
+ newPacketRate = packetRate;
+ newMutationRate = mutationRate;
+ newMastership = mastership;
+ }
+
+ // Any change in the following parameters implies hard restart
+ if (newEnabled != enabled || !newTopoShape.equals(topoShape) ||
+ newDeviceCount != deviceCount || newHostCount != hostCount) {
+ enabled = newEnabled;
+ topoShape = newTopoShape;
+ deviceCount = newDeviceCount;
+ hostCount = newHostCount;
+ packetRate = newPacketRate;
+ mutationRate = newMutationRate;
+ restartSimulation();
+ }
+
+ // Any change in the following parameters implies just a rate change
+ if (newPacketRate != packetRate || newMutationRate != mutationRate) {
+ packetRate = newPacketRate;
+ mutationRate = newMutationRate;
+ adjustRates();
+ }
+
+ // Any change in mastership implies just reassignments.
+ if (!newMastership.equals(mastership)) {
+ mastership = newMastership;
+ reassignMastership();
+ }
+
+ log.info(FORMAT, enabled, topoShape, deviceCount, hostCount,
+ packetRate, mutationRate);
+ }
+
+ /**
+ * Severs the link between the specified end-points in both directions.
+ *
+ * @param one link endpoint
+ * @param two link endpoint
+ */
+ public void severLink(ConnectPoint one, ConnectPoint two) {
+ if (enabled) {
+ topologyMutationDriver.severLink(one, two);
+ }
+ }
+
+ /**
+ * Severs the link between the specified end-points in both directions.
+ *
+ * @param one link endpoint
+ * @param two link endpoint
+ */
+ public void repairLink(ConnectPoint one, ConnectPoint two) {
+ if (enabled) {
+ topologyMutationDriver.repairLink(one, two);
+ }
+ }
+
+ // Resets simulation based on the current configuration parameters.
+ private void restartSimulation() {
+ tearDown();
+ if (enabled) {
+ setUp();
+ }
+ }
+
+ // Sets up the topology simulation and all providers.
+ private void setUp() {
+ simulator = selectSimulator(topoShape);
+ simulator.init(topoShape, deviceCount, hostCount,
+ new DefaultServiceDirectory(),
+ deviceProviderService, hostProviderService,
+ linkProviderService);
+ simulator.setUpTopology();
+ flowRuleProvider.start(flowRuleProviderService);
+ packetProvider.start(packetRate, hostService, deviceService,
+ packetProviderService);
+ topologyMutationDriver.start(mutationRate, linkService, deviceService,
+ linkProviderService);
+ }
+
+ // Selects the simulator based on the specified name.
+ private TopologySimulator selectSimulator(String topoShape) {
+ if (topoShape.matches("linear([,].*|$)")) {
+ return new LinearTopologySimulator();
+ } else if (topoShape.matches("centipede([,].*|$)")) {
+ return new CentipedeTopologySimulator();
+ } else if (topoShape.matches("reroute([,].*|$)")) {
+ return new RerouteTopologySimulator();
+ } else if (topoShape.matches("tree([,].*|$)")) {
+ return new TreeTopologySimulator();
+ } else if (topoShape.matches("agglink([,].*|$)")) {
+ return new AggLinkTopologySimulator();
+ } else if (topoShape.matches("spineleaf([,].*|$)")) {
+ return new SpineLeafTopologySimulator();
+ } else if (topoShape.matches("mesh([,].*|$)")) {
+ return new MeshTopologySimulator();
+ } else {
+ return new ConfiguredTopologySimulator();
+ }
+ }
+
+ // Shuts down the topology simulator and all providers.
+ private void tearDown() {
+ if (simulator != null) {
+ topologyMutationDriver.stop();
+ packetProvider.stop();
+ flowRuleProvider.stop();
+ delay(500);
+ simulator.tearDownTopology();
+ simulator = null;
+ }
+ }
+
+ // Changes packet and mutation rates.
+ private void adjustRates() {
+ packetProvider.adjustRate(packetRate);
+ topologyMutationDriver.adjustRate(mutationRate);
+ }
+
+ // Re-assigns mastership roles.
+ private void reassignMastership() {
+ if (mastership.equals(DEFAULT_MASTERSHIP)) {
+ mastershipService.balanceRoles();
+ } else {
+ NodeId localNode = clusterService.getLocalNode().id();
+ rejectMastership();
+ String[] nodeSpecs = mastership.split("/");
+ for (int i = 0; i < nodeSpecs.length; i++) {
+ String[] specs = nodeSpecs[i].split("=");
+ if (specs[0].equals(localNode.toString())) {
+ String[] ids = specs[1].split(",");
+ for (String id : ids) {
+ mastershipService.setRole(localNode, deviceId(id), MASTER);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // Rejects mastership of all devices.
+ private void rejectMastership() {
+ NodeId localNode = clusterService.getLocalNode().id();
+ deviceService.getDevices()
+ .forEach(device -> mastershipService.setRole(localNode, device.id(),
+ NONE));
+ }
+
+ // Null provider base class.
+ abstract static class AbstractNullProvider extends AbstractProvider {
+ protected AbstractNullProvider() {
+ super(new ProviderId(SCHEME, PROVIDER_ID));
+ }
+ }
+
+ // Device provider facade.
+ private class NullDeviceProvider extends AbstractNullProvider implements DeviceProvider {
+ @Override
+ public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
+ deviceProviderService.receivedRoleReply(deviceId, newRole, newRole);
+ }
+
+ @Override
+ public boolean isReachable(DeviceId deviceId) {
+ return topoShape.equals("configured") ||
+ (simulator != null && simulator.contains(deviceId));
+ }
+
+ @Override
+ public void triggerProbe(DeviceId deviceId) {
+ }
+ }
+
+ // Host provider facade.
+ private class NullHostProvider extends AbstractNullProvider implements HostProvider {
+ @Override
+ public void triggerProbe(Host host) {
+ }
+ }
+
+ // Host provider facade.
+ private class NullLinkProvider extends AbstractNullProvider implements LinkProvider {
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/RerouteTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/RerouteTopologySimulator.java
new file mode 100644
index 00000000..d17466b9
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/RerouteTopologySimulator.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 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.nil;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Re-routable linear topology simulator with an alternate path in the middle.
+ */
+public class RerouteTopologySimulator extends LinearTopologySimulator {
+
+ @Override
+ protected void processTopoShape(String shape) {
+ super.processTopoShape(shape);
+ infrastructurePorts = 5;
+ deviceCount = (topoShape.length == 1) ? deviceCount : Integer.parseInt(topoShape[1]);
+ }
+
+ @Override
+ public void setUpTopology() {
+ checkArgument(deviceCount > 2, "There must be at least 3 devices");
+ super.setUpTopology();
+ }
+
+ @Override
+ protected void createLinks() {
+ int portOffset = 1;
+ for (int i = 0, n = deviceCount - 2; i < n; i++) {
+ createLink(i, i + 1, portOffset, 1);
+ portOffset = 2;
+ }
+ int middle = (deviceCount - 1) / 2;
+ int alternate = deviceCount - 1;
+ createLink(middle - 1, alternate, 3, 1);
+ createLink(middle, alternate, 3, 2);
+ }
+
+ @Override
+ protected void createHosts() {
+ createHosts(deviceIds.get(0), infrastructurePorts);
+ createHosts(deviceIds.get(deviceCount - 2), infrastructurePorts);
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/SpineLeafTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/SpineLeafTopologySimulator.java
new file mode 100644
index 00000000..876fc5dc
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/SpineLeafTopologySimulator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 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.nil;
+
+/**
+ * Spine-leaf topology with hosts at the leaf devices.
+ */
+public class SpineLeafTopologySimulator extends TopologySimulator {
+
+ @Override
+ protected void processTopoShape(String shape) {
+ super.processTopoShape(shape);
+ // FIXME: implement this
+ }
+
+ @Override
+ public void setUpTopology() {
+ // checkArgument(FIXME, "There must be at least one spine tier");
+ super.setUpTopology();
+ }
+
+ @Override
+ protected void createLinks() {
+ }
+
+ @Override
+ protected void createHosts() {
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologyMutationDriver.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologyMutationDriver.java
new file mode 100644
index 00000000..ccf7e08a
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologyMutationDriver.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 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.nil;
+
+import com.google.common.collect.Lists;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.link.DefaultLinkDescription;
+import org.onosproject.net.link.LinkDescription;
+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.Random;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.delay;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.Link.Type.DIRECT;
+import static org.onosproject.net.MastershipRole.MASTER;
+import static org.onosproject.provider.nil.TopologySimulator.description;
+
+/**
+ * Drives topology mutations at a specified rate of events per second.
+ */
+class TopologyMutationDriver implements Runnable {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final int WAIT_DELAY = 2_000;
+ private static final int MAX_DOWN_LINKS = 5;
+
+ private final Random random = new Random();
+
+ private volatile boolean stopped = true;
+
+ private double mutationRate;
+ private int millis, nanos;
+
+ private LinkService linkService;
+ private DeviceService deviceService;
+ private LinkProviderService linkProviderService;
+
+ private List<LinkDescription> activeLinks;
+ private List<LinkDescription> inactiveLinks;
+
+ private final ExecutorService executor =
+ newSingleThreadScheduledExecutor(groupedThreads("onos/null", "topo-mutator"));
+
+ /**
+ * Starts the mutation process.
+ *
+ * @param mutationRate link events per second
+ * @param linkService link service
+ * @param deviceService device service
+ * @param linkProviderService link provider service
+ */
+ void start(double mutationRate,
+ LinkService linkService, DeviceService deviceService,
+ LinkProviderService linkProviderService) {
+ stopped = false;
+ this.linkService = linkService;
+ this.deviceService = deviceService;
+ this.linkProviderService = linkProviderService;
+ activeLinks = reduceLinks();
+ inactiveLinks = Lists.newArrayList();
+ adjustRate(mutationRate);
+ executor.submit(this);
+ }
+
+ /**
+ * Adjusts the topology mutation rate.
+ *
+ * @param mutationRate new topology mutation rate
+ */
+ void adjustRate(double mutationRate) {
+ this.mutationRate = mutationRate;
+ if (mutationRate > 0) {
+ this.millis = (int) (1_000 / mutationRate / 2);
+ this.nanos = (int) (1_000_000 / mutationRate / 2) % 1_000_000;
+ } else {
+ this.millis = 0;
+ this.nanos = 0;
+ }
+ log.info("Settings: millis={}, nanos={}", millis, nanos);
+ }
+
+ /**
+ * Stops the mutation process.
+ */
+ void stop() {
+ stopped = true;
+ }
+
+ /**
+ * Severs the link between the specified end-points in both directions.
+ *
+ * @param one link endpoint
+ * @param two link endpoint
+ */
+ void severLink(ConnectPoint one, ConnectPoint two) {
+ LinkDescription link = new DefaultLinkDescription(one, two, DIRECT);
+ linkProviderService.linkVanished(link);
+ linkProviderService.linkVanished(reverse(link));
+
+ }
+
+ /**
+ * Repairs the link between the specified end-points in both directions.
+ *
+ * @param one link endpoint
+ * @param two link endpoint
+ */
+ void repairLink(ConnectPoint one, ConnectPoint two) {
+ LinkDescription link = new DefaultLinkDescription(one, two, DIRECT);
+ linkProviderService.linkDetected(link);
+ linkProviderService.linkDetected(reverse(link));
+ }
+
+ @Override
+ public void run() {
+ delay(WAIT_DELAY);
+
+ while (!stopped) {
+ if (mutationRate > 0 && inactiveLinks.isEmpty()) {
+ primeInactiveLinks();
+ } else if (mutationRate <= 0 && !inactiveLinks.isEmpty()) {
+ repairInactiveLinks();
+ } else if (inactiveLinks.isEmpty()) {
+ delay(WAIT_DELAY);
+
+ } else {
+ activeLinks.add(repairLink());
+ pause();
+ inactiveLinks.add(severLink());
+ pause();
+ }
+ }
+ }
+
+ // Primes the inactive links with a few random links.
+ private void primeInactiveLinks() {
+ for (int i = 0, n = Math.min(MAX_DOWN_LINKS, activeLinks.size()); i < n; i++) {
+ inactiveLinks.add(severLink());
+ }
+ }
+
+ // Repairs all inactive links.
+ private void repairInactiveLinks() {
+ while (!inactiveLinks.isEmpty()) {
+ repairLink();
+ }
+ }
+
+ // Picks a random active link and severs it.
+ private LinkDescription severLink() {
+ LinkDescription link = getRandomLink(activeLinks);
+ linkProviderService.linkVanished(link);
+ linkProviderService.linkVanished(reverse(link));
+ return link;
+ }
+
+ // Picks a random inactive link and repairs it.
+ private LinkDescription repairLink() {
+ LinkDescription link = getRandomLink(inactiveLinks);
+ linkProviderService.linkDetected(link);
+ linkProviderService.linkDetected(reverse(link));
+ return link;
+ }
+
+ // Produces a reverse of the specified link.
+ private LinkDescription reverse(LinkDescription link) {
+ return new DefaultLinkDescription(link.dst(), link.src(), link.type());
+ }
+
+ // Returns a random link from the specified list of links.
+ private LinkDescription getRandomLink(List<LinkDescription> links) {
+ return links.remove(random.nextInt(links.size()));
+ }
+
+ // Reduces the given list of links to just a single link in each original pair.
+ private List<LinkDescription> reduceLinks() {
+ List<LinkDescription> links = Lists.newArrayList();
+ linkService.getLinks().forEach(link -> links.add(description(link)));
+ return links.stream()
+ .filter(this::isOurLink)
+ .filter(this::isRightDirection)
+ .collect(Collectors.toList());
+ }
+
+ // Returns true if the specified link is ours.
+ private boolean isOurLink(LinkDescription linkDescription) {
+ return deviceService.getRole(linkDescription.src().deviceId()) == MASTER;
+ }
+
+ // Returns true if the link source is greater than the link destination.
+ private boolean isRightDirection(LinkDescription link) {
+ return link.src().deviceId().toString().compareTo(link.dst().deviceId().toString()) > 0;
+ }
+
+ // Pauses the current thread for the pre-computed time of millis & nanos.
+ private void pause() {
+ delay(millis, nanos);
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologySimulator.java
new file mode 100644
index 00000000..9f2320ed
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologySimulator.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright 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.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.
+ *
+ * @param i index of the device id in the list.
+ */
+ protected void createDevice(int i) {
+ DeviceId id = DeviceId.deviceId(SCHEME + ":" + toHex(i));
+ DeviceDescription desc =
+ new DefaultDeviceDescription(id.uri(), Device.Type.SWITCH,
+ "ON.Lab", "0.1", "0.1", "1234",
+ new ChassisId(i));
+ deviceIds.add(id);
+ deviceProviderService.deviceConnected(id, desc);
+ deviceProviderService.updatePorts(id, buildPorts(hostCount + infrastructurePorts));
+ }
+
+// /**
+// * Creates simulated link between two devices on port 1 and port 2.
+// *
+// * @param i index of one simulated device
+// * @param j index of another simulated device
+// */
+// protected void createLink(int i, int j) {
+// createLink(i, j, 1, 2);
+// }
+
+ /**
+ * 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
+ */
+ protected 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));
+ linkProviderService.linkDetected(new DefaultLinkDescription(one, two, DIRECT));
+ linkProviderService.linkDetected(new DefaultLinkDescription(two, one, DIRECT));
+ }
+
+ /**
+ * Creates simularted hosts for the specified device.
+ *
+ * @param deviceId device identifier
+ * @param portOffset port offset where to start attaching hosts
+ */
+ protected void createHosts(DeviceId deviceId, int portOffset) {
+ String s = deviceId.toString();
+ byte dByte = Byte.parseByte(s.substring(s.length() - 1), 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));
+ }
+ }
+
+ /**
+ * 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 knows of this device.
+ *
+ * @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();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TreeTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TreeTopologySimulator.java
new file mode 100644
index 00000000..2c049333
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TreeTopologySimulator.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 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.nil;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Tree topology with hosts at the leaf devices.
+ */
+public class TreeTopologySimulator extends TopologySimulator {
+
+ private int[] tierMultiplier;
+ private int[] tierDeviceCount;
+ private int[] tierOffset;
+
+ @Override
+ protected void processTopoShape(String shape) {
+ super.processTopoShape(shape);
+ tierOffset = new int[topoShape.length];
+ tierMultiplier = new int[topoShape.length];
+ tierDeviceCount = new int[topoShape.length];
+
+ deviceCount = 1;
+
+ tierOffset[0] = 0;
+ tierMultiplier[0] = 1;
+ tierDeviceCount[0] = deviceCount;
+
+ for (int i = 1; i < topoShape.length; i++) {
+ tierOffset[i] = deviceCount;
+ tierMultiplier[i] = Integer.parseInt(topoShape[i]);
+ tierDeviceCount[i] = tierDeviceCount[i - 1] * tierMultiplier[i];
+ deviceCount = deviceCount + tierDeviceCount[i];
+ }
+ }
+
+ @Override
+ public void setUpTopology() {
+ checkArgument(tierDeviceCount.length > 0, "There must be at least one tree tier");
+ super.setUpTopology();
+ }
+
+ @Override
+ protected void createLinks() {
+ int portOffset = 1;
+ for (int t = 1; t < tierOffset.length; t++) {
+ int child = tierOffset[t];
+ for (int parent = tierOffset[t - 1]; parent < tierOffset[t]; parent++) {
+ for (int i = 0; i < tierMultiplier[t]; i++) {
+ createLink(parent, child, i + portOffset, 1);
+ child++;
+ }
+ }
+ portOffset = 2; // beyond first tier, allow for up-links
+ }
+ }
+
+ @Override
+ protected void createHosts() {
+ for (int i = tierOffset[tierOffset.length - 1]; i < deviceCount; i++) {
+ createHosts(deviceIds.get(i), hostCount);
+ }
+ }
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullControlCommand.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullControlCommand.java
new file mode 100644
index 00000000..261f46de
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullControlCommand.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 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.nil.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.provider.nil.NullProviders;
+
+import static org.onosproject.cli.StartStopCompleter.START;
+
+/**
+ * Starts or stops topology simulation.
+ */
+@Command(scope = "onos", name = "null-simulation",
+ description = "Starts or stops topology simulation")
+public class NullControlCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "cmd", description = "Control command: start/stop",
+ required = true, multiValued = false)
+ String cmd = null;
+
+ @Argument(index = 1, name = "topoShape",
+ description = "Topology shape: e.g. configured, linear, reroute, centipede, tree, spineleaf, mesh",
+ required = false, multiValued = false)
+ String topoShape = null;
+
+ @Override
+ protected void execute() {
+ ComponentConfigService service = get(ComponentConfigService.class);
+ if (topoShape != null) {
+ service.setProperty(NullProviders.class.getName(), "topoShape", topoShape);
+ }
+ service.setProperty(NullProviders.class.getName(), "enabled",
+ cmd.equals(START) ? "true" : "false");
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullLinkCommand.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullLinkCommand.java
new file mode 100644
index 00000000..a76da3b8
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullLinkCommand.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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.nil.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.provider.nil.NullProviders;
+
+import static org.onosproject.cli.UpDownCompleter.DOWN;
+import static org.onosproject.cli.UpDownCompleter.UP;
+
+/**
+ * Servers or repairs a simulated link.
+ */
+@Command(scope = "onos", name = "null-link",
+ description = "Severs or repairs a simulated link")
+public class NullLinkCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "one", description = "One link end-point as device/port",
+ required = true, multiValued = false)
+ String one = null;
+
+ @Argument(index = 1, name = "two", description = "Another link end-point as device/port",
+ required = true, multiValued = false)
+ String two = null;
+
+ @Argument(index = 2, name = "cmd", description = "up/down",
+ required = true, multiValued = false)
+ String cmd = null;
+
+
+ @Override
+ protected void execute() {
+ NullProviders service = get(NullProviders.class);
+
+ try {
+ ConnectPoint onePoint = ConnectPoint.deviceConnectPoint(one);
+
+ ConnectPoint twoPoint = ConnectPoint.deviceConnectPoint(two);
+
+ if (cmd.equals(UP)) {
+ service.repairLink(onePoint, twoPoint);
+ } else if (cmd.equals(DOWN)) {
+ service.severLink(onePoint, twoPoint);
+ } else {
+ error("Illegal command %s; must be up or down", cmd);
+ }
+ } catch (NumberFormatException e) {
+ error("Invalid port number specified", e);
+ }
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/TopologyShapeCompleter.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/TopologyShapeCompleter.java
new file mode 100644
index 00000000..fad29b6e
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/TopologyShapeCompleter.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 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.nil.cli;
+
+import com.google.common.collect.ImmutableList;
+import org.onosproject.cli.AbstractChoicesCompleter;
+
+import java.util.List;
+
+/**
+ * Topology shape completer.
+ */
+public class TopologyShapeCompleter extends AbstractChoicesCompleter {
+ @Override
+ public List<String> choices() {
+ return ImmutableList.of("configured", "linear", "reroute", "centipede",
+ "tree", "spineleaf", "mesh");
+ }
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/package-info.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/package-info.java
new file mode 100644
index 00000000..f876ced3
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 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.
+ */
+
+/**
+ * Null provider CLI commands and completers.
+ */
+package org.onosproject.provider.nil.cli;
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/package-info.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/package-info.java
new file mode 100644
index 00000000..e8d309f7
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 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.
+ */
+
+/**
+ * Set of null south-bound providers which permit simulating a network
+ * topology using fake devices, links, hosts, etc.
+ */
+package org.onosproject.provider.nil;