T3: Handling null or offline device

Change-Id: Ic93f2844263fea1874b8ff1d2c5f0bc835adcb64
(cherry picked from commit 63a0ac99fedb017a6450c4484620ac29b0462dee)
diff --git a/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java b/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
index 1a5c946..f3edbd6 100644
--- a/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
+++ b/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.t3.impl;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import org.apache.felix.scr.annotations.Component;
@@ -28,6 +29,7 @@
 import org.onosproject.net.Host;
 import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.FlowEntry;
@@ -94,9 +96,16 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DriverService driverService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
     @Override
     public StaticPacketTrace trace(TrafficSelector packet, ConnectPoint in) {
         log.info("Tracing packet {} coming in through {}", packet, in);
+        //device must exist in ONOS
+        Preconditions.checkNotNull(deviceService.getDevice(in.deviceId()),
+                "Device " + in.deviceId() + " must exist in ONOS");
+
         StaticPacketTrace trace = new StaticPacketTrace(packet, in);
         //FIXME this can be done recursively
         trace = traceInDevice(trace, packet, in);
@@ -253,6 +262,13 @@
      */
     private StaticPacketTrace traceInDevice(StaticPacketTrace trace, TrafficSelector packet, ConnectPoint in) {
         log.debug("Packet {} coming in from {}", packet, in);
+
+        //if device is not available exit here.
+        if (!deviceService.isAvailable(in.deviceId())) {
+            trace.addResultMessage("Device is offline " + in.deviceId());
+            return trace;
+        }
+
         List<FlowEntry> flows = new ArrayList<>();
         List<FlowEntry> outputFlows = new ArrayList<>();
 
diff --git a/apps/t3/src/test/java/org/onosproject/t3/impl/T3TestObjects.java b/apps/t3/src/test/java/org/onosproject/t3/impl/T3TestObjects.java
index ff44e43..b1edb86 100644
--- a/apps/t3/src/test/java/org/onosproject/t3/impl/T3TestObjects.java
+++ b/apps/t3/src/test/java/org/onosproject/t3/impl/T3TestObjects.java
@@ -61,6 +61,9 @@
     private static final String HOST_ONE = HOST_ONE_MAC + "/" + HOST_ONE_VLAN;
     private static final String HOST_TWO = HOST_TWO_MAC + "/" + HOST_TWO_VLAN;
 
+    //offline device
+    static final DeviceId OFFLINE_DEVICE = DeviceId.deviceId("offlineDevice");
+
     //Single Flow Test
     static final DeviceId SINGLE_FLOW_DEVICE = DeviceId.deviceId("SingleFlowDevice");
     private static final TrafficSelector SINGLE_FLOW_SELECTOR = DefaultTrafficSelector.builder()
diff --git a/apps/t3/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java b/apps/t3/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
index 28ab5b2..382a468 100644
--- a/apps/t3/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
+++ b/apps/t3/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
@@ -19,15 +19,20 @@
 import com.google.common.collect.ImmutableSet;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.packet.ChassisId;
 import org.onlab.packet.EthType;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
 import org.onosproject.net.DefaultLink;
+import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
 import org.onosproject.net.Link;
+import org.onosproject.net.device.DeviceServiceAdapter;
 import org.onosproject.net.driver.DefaultDriver;
 import org.onosproject.net.driver.Driver;
 import org.onosproject.net.driver.DriverServiceAdapter;
@@ -52,6 +57,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.onosproject.net.Device.Type.SWITCH;
 import static org.onosproject.t3.impl.T3TestObjects.*;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -72,6 +78,7 @@
         mngr.linkService = new TestLinkService();
         mngr.driverService = new TestDriverService();
         mngr.groupService = new TestGroupService();
+        mngr.deviceService = new TestDeviceService();
 
         assertNotNull("Manager should not be null", mngr);
 
@@ -80,6 +87,26 @@
         assertNotNull("Group Service should not be null", mngr.groupService);
         assertNotNull("Driver Service should not be null", mngr.driverService);
         assertNotNull("Link Service should not be null", mngr.linkService);
+        assertNotNull("Device Service should not be null", mngr.deviceService);
+    }
+
+    /**
+     * Tests failure on non existent device.
+     */
+    @Test(expected = NullPointerException.class)
+    public void nonExistentDevice() {
+        StaticPacketTrace traceFail = mngr.trace(PACKET_OK, ConnectPoint.deviceConnectPoint("nonexistent" + "/1"));
+    }
+
+    /**
+     * Tests failure on offline device.
+     */
+    @Test
+    public void offlineDevice() {
+        StaticPacketTrace traceFail = mngr.trace(PACKET_OK, ConnectPoint.deviceConnectPoint(OFFLINE_DEVICE + "/1"));
+        assertNotNull("Trace should not be null", traceFail);
+        assertNull("Trace should have 0 output", traceFail.getGroupOuputs(SINGLE_FLOW_DEVICE));
+        log.info("trace {}", traceFail.resultMessage());
     }
 
     /**
@@ -332,4 +359,24 @@
             return ImmutableSet.of();
         }
     }
+
+    private class TestDeviceService extends DeviceServiceAdapter {
+        @Override
+        public Device getDevice(DeviceId deviceId) {
+            if (deviceId.equals(DeviceId.deviceId("nonexistent"))) {
+                return null;
+            }
+            return new DefaultDevice(ProviderId.NONE, DeviceId.deviceId("test"), SWITCH,
+                    "test", "test", "test", "test", new ChassisId(),
+                    DefaultAnnotations.builder().set("foo", "bar").build());
+        }
+
+        @Override
+        public boolean isAvailable(DeviceId deviceId) {
+            if (deviceId.equals(OFFLINE_DEVICE)) {
+                return false;
+            }
+            return true;
+        }
+    }
 }
\ No newline at end of file