Adding a grid topology simulator to the null provider.

Change-Id: Ie655840febc11d3986f1c8ec5c8ef2374014794c
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/CentipedeTopologySimulator.java b/providers/null/src/main/java/org/onosproject/provider/nil/CentipedeTopologySimulator.java
index 5234d44..877f7f6 100644
--- a/providers/null/src/main/java/org/onosproject/provider/nil/CentipedeTopologySimulator.java
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/CentipedeTopologySimulator.java
@@ -20,9 +20,7 @@
  */
 public class CentipedeTopologySimulator extends LinearTopologySimulator {
 
-    /**
-     * Creates simulated hosts.
-     */
+    @Override
     protected void createHosts() {
         deviceIds.forEach(id -> createHosts(id, infrastructurePorts));
     }
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/GridTopologySimulator.java b/providers/null/src/main/java/org/onosproject/provider/nil/GridTopologySimulator.java
new file mode 100644
index 0000000..2f9c644
--- /dev/null
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/GridTopologySimulator.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016 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;
+
+/**
+ * Rectangular grid topology with hosts at each device.
+ */
+public class GridTopologySimulator extends TopologySimulator {
+
+    private int cols;
+    private int rows;
+
+    @Override
+    protected void processTopoShape(String shape) {
+        super.processTopoShape(shape);
+        rows = topoShape.length > 1 ? Integer.parseInt(topoShape[1]) : 10;
+        cols = topoShape.length > 2 ? Integer.parseInt(topoShape[2]) : rows;
+        hostCount = topoShape.length > 3 ? Integer.parseInt(topoShape[3]) : 1;
+        infrastructurePorts = 4;
+        deviceCount = rows * cols;
+    }
+
+    @Override
+    public void setUpTopology() {
+        checkArgument(rows > 1, "There must be at least 2 rows");
+        checkArgument(cols > 1, "There must be at least 2 columns");
+        super.setUpTopology();
+    }
+
+    @Override
+    protected void createLinks() {
+        for (int r = 0; r < rows; r++) {
+            for (int c = 0; c < cols; c++) {
+                int i = r * cols + c;
+                if (c < cols - 1) {
+                    createLink(i, i + 1, 3, 1);
+                }
+                if (r < rows - 1) {
+                    createLink(i, (r + 1) * cols + c, 4, 2);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void createHosts() {
+        deviceIds.forEach(id -> createHosts(id, infrastructurePorts));
+    }
+
+}
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
index 68c536b..fb1823d 100644
--- a/providers/null/src/main/java/org/onosproject/provider/nil/NullProviders.java
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/NullProviders.java
@@ -54,6 +54,7 @@
 import org.slf4j.Logger;
 
 import java.util.Dictionary;
+import java.util.Objects;
 import java.util.Properties;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
@@ -116,7 +117,6 @@
     @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();
@@ -138,7 +138,7 @@
 
     private static final String DEFAULT_TOPO_SHAPE = "configured";
     @Property(name = "topoShape", value = DEFAULT_TOPO_SHAPE,
-            label = "Topology shape: configured, linear, reroute, tree, spineleaf, mesh")
+            label = "Topology shape: configured, linear, reroute, tree, spineleaf, mesh, grid")
     private String topoShape = DEFAULT_TOPO_SHAPE;
 
     private static final int DEFAULT_DEVICE_COUNT = 10;
@@ -238,7 +238,7 @@
         }
 
         // Any change in the following parameters implies hard restart
-        if (newEnabled != enabled || !newTopoShape.equals(topoShape) ||
+        if (newEnabled != enabled || !Objects.equals(newTopoShape, topoShape) ||
                 newDeviceCount != deviceCount || newHostCount != hostCount) {
             enabled = newEnabled;
             topoShape = newTopoShape;
@@ -257,7 +257,7 @@
         }
 
         // Any change in mastership implies just reassignments.
-        if (!newMastership.equals(mastership)) {
+        if (!Objects.equals(newMastership, mastership)) {
             mastership = newMastership;
             reassignMastership();
         }
@@ -290,6 +290,29 @@
         }
     }
 
+    /**
+     * Fails the specified device.
+     *
+     * @param deviceId device identifier
+     */
+    public void failDevice(DeviceId deviceId) {
+        if (enabled) {
+            topologyMutationDriver.failDevice(deviceId);
+        }
+    }
+
+    /**
+     * Repairs the specified device.
+     *
+     * @param deviceId device identifier
+     */
+    public void repairDevice(DeviceId deviceId) {
+        if (enabled) {
+            topologyMutationDriver.repairDevice(deviceId);
+        }
+    }
+
+
     // Resets simulation based on the current configuration parameters.
     private void restartSimulation() {
         tearDown();
@@ -310,7 +333,8 @@
         packetProvider.start(packetRate, hostService, deviceService,
                              packetProviderService);
         topologyMutationDriver.start(mutationRate, linkService, deviceService,
-                                     linkProviderService);
+                                     linkProviderService, deviceProviderService,
+                                     simulator);
     }
 
     // Selects the simulator based on the specified name.
@@ -329,6 +353,8 @@
             return new SpineLeafTopologySimulator();
         } else if (topoShape.matches("mesh([,].*|$)")) {
             return new MeshTopologySimulator();
+        } else if (topoShape.matches("grid([,].*|$)")) {
+            return new GridTopologySimulator();
         } else {
             return new ConfiguredTopologySimulator();
         }
@@ -398,7 +424,8 @@
         @Override
         public boolean isReachable(DeviceId deviceId) {
             return topoShape.equals("configured") ||
-                    (simulator != null && simulator.contains(deviceId));
+                    (simulator != null && simulator.contains(deviceId) &&
+                            topologyMutationDriver.isReachable(deviceId));
         }
 
         @Override
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/TopologyMutationDriver.java b/providers/null/src/main/java/org/onosproject/provider/nil/TopologyMutationDriver.java
index ccf7e08..032692d 100644
--- a/providers/null/src/main/java/org/onosproject/provider/nil/TopologyMutationDriver.java
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/TopologyMutationDriver.java
@@ -16,7 +16,11 @@
 package org.onosproject.provider.nil;
 
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.device.DeviceProviderService;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.link.DefaultLinkDescription;
 import org.onosproject.net.link.LinkDescription;
@@ -26,7 +30,9 @@
 import org.slf4j.LoggerFactory;
 
 import java.util.List;
+import java.util.Map;
 import java.util.Random;
+import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.stream.Collectors;
 
@@ -57,6 +63,8 @@
     private LinkService linkService;
     private DeviceService deviceService;
     private LinkProviderService linkProviderService;
+    private DeviceProviderService deviceProviderService;
+    private TopologySimulator simulator;
 
     private List<LinkDescription> activeLinks;
     private List<LinkDescription> inactiveLinks;
@@ -64,21 +72,30 @@
     private final ExecutorService executor =
             newSingleThreadScheduledExecutor(groupedThreads("onos/null", "topo-mutator"));
 
+    private Map<DeviceId, Set<Link>> savedLinks = Maps.newConcurrentMap();
+
     /**
      * Starts the mutation process.
      *
-     * @param mutationRate        link events per second
-     * @param linkService         link service
-     * @param deviceService       device service
-     * @param linkProviderService link provider service
+     * @param mutationRate          link events per second
+     * @param linkService           link service
+     * @param deviceService         device service
+     * @param linkProviderService   link provider service
+     * @param deviceProviderService device provider service
+     * @param simulator             topology simulator
      */
     void start(double mutationRate,
                LinkService linkService, DeviceService deviceService,
-               LinkProviderService linkProviderService) {
+               LinkProviderService linkProviderService,
+               DeviceProviderService deviceProviderService,
+               TopologySimulator simulator) {
+        savedLinks.clear();
         stopped = false;
         this.linkService = linkService;
         this.deviceService = deviceService;
         this.linkProviderService = linkProviderService;
+        this.deviceProviderService = deviceProviderService;
+        this.simulator = simulator;
         activeLinks = reduceLinks();
         inactiveLinks = Lists.newArrayList();
         adjustRate(mutationRate);
@@ -134,6 +151,41 @@
         linkProviderService.linkDetected(reverse(link));
     }
 
+    /**
+     * Fails the specified device.
+     *
+     * @param deviceId device identifier
+     */
+    void failDevice(DeviceId deviceId) {
+        savedLinks.put(deviceId, linkService.getDeviceLinks(deviceId));
+        deviceProviderService.deviceDisconnected(deviceId);
+    }
+
+    /**
+     * Repairs the specified device.
+     *
+     * @param deviceId device identifier
+     */
+    void repairDevice(DeviceId deviceId) {
+        int chassisId = Integer.parseInt(deviceId.uri().getSchemeSpecificPart());
+        simulator.createDevice(deviceId, chassisId);
+        Set<Link> links = savedLinks.remove(deviceId);
+        if (links != null) {
+            links.forEach(l -> linkProviderService
+                    .linkDetected(new DefaultLinkDescription(l.src(), l.dst(), DIRECT)));
+        }
+    }
+
+    /**
+     * Returns whether the given device is considered reachable or not.
+     *
+     * @param deviceId device identifier
+     * @return true if device is reachable
+     */
+    boolean isReachable(DeviceId deviceId) {
+        return !savedLinks.containsKey(deviceId);
+    }
+
     @Override
     public void run() {
         delay(WAIT_DELAY);
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/TopologySimulator.java b/providers/null/src/main/java/org/onosproject/provider/nil/TopologySimulator.java
index a00456e..d0d238b 100644
--- a/providers/null/src/main/java/org/onosproject/provider/nil/TopologySimulator.java
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/TopologySimulator.java
@@ -172,31 +172,31 @@
     protected abstract void createHosts();
 
     /**
-     * Creates simulated device.
+     * Creates simulated device and adds its id to the list of devices ids.
      *
      * @param i index of the device id in the list.
      */
     protected void createDevice(int i) {
         DeviceId id = DeviceId.deviceId(SCHEME + ":" + toHex(i));
+        deviceIds.add(id);
+        createDevice(id, i);
+    }
+
+    /**
+     * Creates simulated device.
+     *
+     * @param id device identifier
+     * @param chassisId chassis identifier number
+     */
+    protected void createDevice(DeviceId id, int chassisId) {
         DeviceDescription desc =
                 new DefaultDeviceDescription(id.uri(), Device.Type.SWITCH,
                                              "ON.Lab", "0.1", "0.1", "1234",
-                                             new ChassisId(i));
-        deviceIds.add(id);
+                                             new ChassisId(chassisId));
         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.
      *
@@ -208,6 +208,16 @@
     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));
+        createLink(one, two);
+    }
+
+    /**
+     * Creates simulated link between two connection points.
+     *
+     * @param one  one connection point
+     * @param two  another connection point
+     */
+    protected void createLink(ConnectPoint one, ConnectPoint two) {
         linkProviderService.linkDetected(new DefaultLinkDescription(one, two, DIRECT));
         linkProviderService.linkDetected(new DefaultLinkDescription(two, one, DIRECT));
     }
@@ -359,7 +369,7 @@
     }
 
     /**
-     * Indicates whether or not the simulation knows of this device.
+     * Indicates whether or not the simulation deeps the device as available.
      *
      * @param deviceId device identifier
      * @return true if device is known
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/TreeTopologySimulator.java b/providers/null/src/main/java/org/onosproject/provider/nil/TreeTopologySimulator.java
index 2c04933..64aafa4 100644
--- a/providers/null/src/main/java/org/onosproject/provider/nil/TreeTopologySimulator.java
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/TreeTopologySimulator.java
@@ -55,6 +55,7 @@
 
     @Override
     protected void createLinks() {
+
         int portOffset = 1;
         for (int t = 1; t < tierOffset.length; t++) {
             int child = tierOffset[t];
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullDeviceCommand.java b/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullDeviceCommand.java
new file mode 100644
index 0000000..00bc5b9
--- /dev/null
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullDeviceCommand.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 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.DeviceId;
+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-device",
+        description = "Severs or repairs a simulated link")
+public class NullDeviceCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "id", description = "Device identifier",
+            required = true, multiValued = false)
+    String id = null;
+
+    @Argument(index = 1, name = "cmd", description = "up/down",
+            required = true, multiValued = false)
+    String cmd = null;
+
+
+    @Override
+    protected void execute() {
+        NullProviders service = get(NullProviders.class);
+        DeviceId deviceId = DeviceId.deviceId(id);
+
+        if (cmd.equals(UP)) {
+            service.repairDevice(deviceId);
+        } else if (cmd.equals(DOWN)) {
+            service.failDevice(deviceId);
+        } else {
+            error("Illegal command %s; must be up or down", cmd);
+        }
+    }
+
+}
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullLinkCommand.java b/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullLinkCommand.java
index a76da3b..be7d375 100644
--- a/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullLinkCommand.java
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullLinkCommand.java
@@ -50,7 +50,6 @@
 
         try {
             ConnectPoint onePoint = ConnectPoint.deviceConnectPoint(one);
-
             ConnectPoint twoPoint = ConnectPoint.deviceConnectPoint(two);
 
             if (cmd.equals(UP)) {
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/cli/TopologyShapeCompleter.java b/providers/null/src/main/java/org/onosproject/provider/nil/cli/TopologyShapeCompleter.java
index fad29b6..dbb386d 100644
--- a/providers/null/src/main/java/org/onosproject/provider/nil/cli/TopologyShapeCompleter.java
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/cli/TopologyShapeCompleter.java
@@ -27,6 +27,6 @@
     @Override
     public List<String> choices() {
         return ImmutableList.of("configured", "linear", "reroute", "centipede",
-                                "tree", "spineleaf", "mesh");
+                                "tree", "spineleaf", "mesh", "grid");
     }
 }
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 f96f10c..1fc180a 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
@@ -33,6 +33,14 @@
                 <null/>
             </completers>
         </command>
+        <command>
+            <action class="org.onosproject.provider.nil.cli.NullDeviceCommand"/>
+            <completers>
+                <ref component-id="deviceIdCompleter"/>
+                <ref component-id="upDownCompleter"/>
+                <null/>
+            </completers>
+        </command>
     </command-bundle>
 
     <bean id="startStopCompleter" class="org.onosproject.cli.StartStopCompleter"/>
@@ -40,5 +48,6 @@
     <bean id="topoShapeCompleter" class="org.onosproject.provider.nil.cli.TopologyShapeCompleter"/>
     <bean id="linkSrcCompleter" class="org.onosproject.cli.net.LinkSrcCompleter"/>
     <bean id="linkDstCompleter" class="org.onosproject.cli.net.LinkDstCompleter"/>
+    <bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/>
 
 </blueprint>