Consolidating null providers and making them fully configurable and integrated with the ConfigProvider to allow arbitrary topologies.

Change-Id: I899e27a9771af4013a3ce6da7f683a4927ffb438
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/NullProviders.java b/providers/null/src/main/java/org/onosproject/provider/nil/NullProviders.java
new file mode 100644
index 0000000..d8ccb3e
--- /dev/null
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/NullProviders.java
@@ -0,0 +1,418 @@
+/*
+ * 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("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);
+            rejectMastership();
+            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") || deviceService.isAvailable(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 {
+    }
+
+}