Fix an NPE that happens when DHCP server is not discovered yet

Also address the following issues:
- HOST_MOVED can include both movement and IP update. We should update server info when we see a HOST_MOVED event.
- Over 2k lines in this class
- Fix typo in extractClientId method name

Change-Id: I238027e650fb6b15c29b363caa09539170bdd3a4
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
index 7999466..ece4323 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
@@ -1593,6 +1593,7 @@
             switch (event.type()) {
                 case HOST_ADDED:
                 case HOST_UPDATED:
+                case HOST_MOVED:
                     log.trace("Scheduled host event {}", event);
                     hostEventExecutor.execute(() -> hostUpdated(event.subject()));
                     break;
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
index b7fba34..38cfd1d 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
@@ -126,7 +126,6 @@
 import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
 import java.util.concurrent.Semaphore;
 
-
 @Component
 @Service
 @Property(name = "version", value = "6")
@@ -696,36 +695,6 @@
     }
 
     /**
-     * extract from dhcp6 packet ClientIdOption.
-     *
-     * @param directConnFlag directly connected host
-     * @param dhcp6Payload the dhcp6 payload
-     * @return Dhcp6ClientIdOption clientIdOption, or null if not exists.
-     */
-    private Dhcp6ClientIdOption extractClinedId(Boolean directConnFlag, DHCP6 dhcp6Payload) {
-        Dhcp6ClientIdOption clientIdOption;
-
-        if (directConnFlag) {
-            clientIdOption = dhcp6Payload.getOptions()
-                    .stream()
-                    .filter(opt -> opt instanceof Dhcp6ClientIdOption)
-                    .map(opt -> (Dhcp6ClientIdOption) opt)
-                    .findFirst()
-                    .orElse(null);
-        } else {
-            DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Payload);
-            clientIdOption = leafDhcp.getOptions()
-                    .stream()
-                    .filter(opt -> opt instanceof Dhcp6ClientIdOption)
-                    .map(opt -> (Dhcp6ClientIdOption) opt)
-                    .findFirst()
-                    .orElse(null);
-        }
-
-        return clientIdOption;
-    }
-
-    /**
      * remove host or route and update dhcp relay record attributes.
      *
      * @param directConnFlag  flag to show that packet is from directly connected client
@@ -746,7 +715,7 @@
         byte leafMsgType;
         log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
 
-        Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, dhcp6Packet);
+        Dhcp6ClientIdOption clientIdOption = Dhcp6HandlerUtil.extractClientId(directConnFlag, dhcp6Packet);
         if (clientIdOption != null) {
             if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
                     (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
@@ -895,7 +864,7 @@
         MacAddress leafClientMac;
         Byte leafMsgType;
 
-        Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, embeddedDhcp6);
+        Dhcp6ClientIdOption clientIdOption = Dhcp6HandlerUtil.extractClientId(directConnFlag, embeddedDhcp6);
         if (clientIdOption != null) {
             log.debug("CLIENTID option found {}", clientIdOption);
             if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
@@ -1048,8 +1017,14 @@
         List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
 
         for (DhcpServerInfo dhcpServer : serverInfoList) {
+            Interface serverInterface = getServerInterface(dhcpServer);
+            if (serverInterface == null) {
+                log.warn("Can't get server interface, ignore");
+                continue;
+            }
+
             Ethernet newPacket = Dhcp6HandlerUtil.buildDhcp6PacketFromClient(context,
-                    clientPacket, clientInterfaces, dhcpServer, getServerInterface(dhcpServer));
+                    clientPacket, clientInterfaces, dhcpServer, serverInterface);
             log.trace("Built packet for server {} : {}", dhcpServer, newPacket);
             internalPackets.add(InternalPacket.internalPacket(newPacket,
                     dhcpServer.getDhcpServerConnectPoint().get()));
@@ -1430,6 +1405,7 @@
             switch (event.type()) {
                 case HOST_ADDED:
                 case HOST_UPDATED:
+                case HOST_MOVED:
                     log.trace("Scheduled host event {}", event);
                     hostEventExecutor.execute(() -> hostUpdated(event.subject()));
                     break;
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerUtil.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerUtil.java
index 81748c6..9b3097e 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerUtil.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerUtil.java
@@ -22,6 +22,7 @@
 import org.onlab.packet.Ip6Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.VlanId;
+import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
 import org.onlab.packet.dhcp.Dhcp6RelayOption;
 import org.onlab.packet.dhcp.Dhcp6Option;
 
@@ -611,4 +612,34 @@
         }
         return true;
     }
+
+    /**
+     * extract from dhcp6 packet ClientIdOption.
+     *
+     * @param directConnFlag directly connected host
+     * @param dhcp6Payload the dhcp6 payload
+     * @return Dhcp6ClientIdOption clientIdOption, or null if not exists.
+     */
+    static Dhcp6ClientIdOption extractClientId(Boolean directConnFlag, DHCP6 dhcp6Payload) {
+        Dhcp6ClientIdOption clientIdOption;
+
+        if (directConnFlag) {
+            clientIdOption = dhcp6Payload.getOptions()
+                    .stream()
+                    .filter(opt -> opt instanceof Dhcp6ClientIdOption)
+                    .map(opt -> (Dhcp6ClientIdOption) opt)
+                    .findFirst()
+                    .orElse(null);
+        } else {
+            DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Payload);
+            clientIdOption = leafDhcp.getOptions()
+                    .stream()
+                    .filter(opt -> opt instanceof Dhcp6ClientIdOption)
+                    .map(opt -> (Dhcp6ClientIdOption) opt)
+                    .findFirst()
+                    .orElse(null);
+        }
+
+        return clientIdOption;
+    }
 }