ONOS-4505: Bug Fixes

Change-Id: Ia030aa3aff9e2ad34a5e27fbe4ba088dda65bfa7
diff --git a/protocols/isis/ctl/src/main/java/org/onosproject/isis/controller/impl/Controller.java b/protocols/isis/ctl/src/main/java/org/onosproject/isis/controller/impl/Controller.java
old mode 100755
new mode 100644
index c12c520..f16aeb4
--- a/protocols/isis/ctl/src/main/java/org/onosproject/isis/controller/impl/Controller.java
+++ b/protocols/isis/ctl/src/main/java/org/onosproject/isis/controller/impl/Controller.java
@@ -19,6 +19,7 @@
 import org.jboss.netty.bootstrap.ClientBootstrap;
 import org.jboss.netty.channel.AdaptiveReceiveBufferSizePredictor;
 import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelFutureListener;
 import org.jboss.netty.channel.ChannelPipelineFactory;
 import org.jboss.netty.channel.FixedReceiveBufferSizePredictorFactory;
 import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
@@ -35,10 +36,14 @@
 
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Enumeration;
 import java.util.List;
 import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 import static org.onlab.util.Tools.groupedThreads;
 
@@ -48,12 +53,17 @@
 public class Controller {
     protected static final int BUFFER_SIZE = 4 * 1024 * 1024;
     private static final Logger log = LoggerFactory.getLogger(Controller.class);
+    private static final int RETRY_INTERVAL = 4;
     private final int peerWorkerThreads = 16;
+    byte[] configPacket = null;
     private List<IsisProcess> processes = null;
     private IsisChannelHandler isisChannelHandler;
     private NioClientSocketChannelFactory peerExecFactory;
     private ClientBootstrap peerBootstrap = null;
     private TpPort isisPort = TpPort.tpPort(IsisConstants.SPORT);
+    private ScheduledExecutorService connectExecutor = null;
+    private int connectRetryCounter = 0;
+    private int connectRetryTime;
 
     /**
      * Deactivates ISIS controller.
@@ -70,12 +80,11 @@
      */
     public void updateConfig(JsonNode jsonNode) throws Exception {
         log.debug("Controller::UpdateConfig called");
-        byte[] configPacket = new byte[IsisConstants.CONFIG_LENGTH];
+        configPacket = new byte[IsisConstants.CONFIG_LENGTH];
         byte numberOfInterface = 0; // number of interfaces to configure
 
         configPacket[0] = (byte) 0xFF; // its a conf packet - identifier
         List<IsisProcess> isisProcesses = getConfig(jsonNode);
-
         for (IsisProcess isisProcess : isisProcesses) {
             log.debug("IsisProcessDetails : " + isisProcess);
             for (IsisInterface isisInterface : isisProcess.isisInterfaceList()) {
@@ -100,16 +109,19 @@
         configPacket[1] = numberOfInterface;
         //First time configuration
         if (processes == null) {
-            processes = isisProcesses;
-            //Initialize connection by creating a channel handler instance and sent the config packet);
-            initConnection();
-            //Initializing the interface map in channel handler
-            isisChannelHandler.initializeInterfaceMap();
+            if (isisProcesses.size() > 0) {
+                processes = isisProcesses;
+                connectPeer();
+                //Initializing the interface map in channel handler
+                if (isisChannelHandler != null) {
+                    isisChannelHandler.initializeInterfaceMap();
+                }
+            }
         } else {
             isisChannelHandler.updateInterfaceMap(isisProcesses);
+            //Send the config packet
+            isisChannelHandler.sentConfigPacket(configPacket);
         }
-        //Send the config packet
-        isisChannelHandler.sentConfigPacket(configPacket);
     }
 
     /**
@@ -182,10 +194,8 @@
      * @return list of processes configured
      */
     private List<IsisProcess> getConfig(JsonNode json) throws Exception {
-
         List<IsisProcess> isisProcessesList = new ArrayList<>();
         JsonNode jsonNodes = json;
-
         if (jsonNodes == null) {
             return isisProcessesList;
         }
@@ -193,49 +203,335 @@
             List<IsisInterface> interfaceList = new ArrayList<>();
             for (JsonNode jsonNode1 : jsonNode.path(IsisConstants.INTERFACE)) {
                 IsisInterface isisInterface = new DefaultIsisInterface();
-                isisInterface.setInterfaceIndex(jsonNode1.path(IsisConstants.INTERFACEINDEX).asInt());
-                isisInterface.setInterfaceIpAddress(Ip4Address.valueOf(jsonNode1
-                                                                               .path(IsisConstants.INTERFACEIP)
-                                                                               .asText()));
-                try {
-                    isisInterface.setNetworkMask(InetAddress.getByName((jsonNode1
-                            .path(IsisConstants.NETWORKMASK).asText())).getAddress());
-                } catch (UnknownHostException e) {
-                    log.debug("Error:: Parsing network mask");
+                String index = jsonNode1.path(IsisConstants.INTERFACEINDEX).asText();
+                if (isPrimitive(index)) {
+                    int input = Integer.parseInt(index);
+                    if (input < 1 || input > 255) {
+                        log.debug("Wrong interface index: {}", index);
+                        continue;
+                    }
+                    isisInterface.setInterfaceIndex(Integer.parseInt(index));
+                } else {
+                    log.debug("Wrong interface index {}", index);
+                    continue;
                 }
-                isisInterface.setInterfaceMacAddress(MacAddress.valueOf(jsonNode1
-                                                                                .path(IsisConstants.MACADDRESS)
-                                                                                .asText()));
+                Ip4Address ipAddress = getInterfaceIp(isisInterface.interfaceIndex());
+                if (ipAddress != null && !ipAddress.equals(IsisConstants.DEFAULTIP)) {
+                    isisInterface.setInterfaceIpAddress(ipAddress);
+                } else {
+                    log.debug("Wrong interface index {}. No matching interface in system.", index);
+                    continue;
+                }
+                MacAddress macAddress = getInterfaceMac(isisInterface.interfaceIndex());
+                if (macAddress != null) {
+                    isisInterface.setInterfaceMacAddress(macAddress);
+                } else {
+                    log.debug("Wrong interface index {}. No matching interface in system.", index);
+                    continue;
+                }
+                String mask = getInterfaceMask(isisInterface.interfaceIndex());
+                if (mask != null) {
+                    try {
+                        isisInterface.setNetworkMask(InetAddress.getByName(mask).getAddress());
+                    } catch (UnknownHostException e) {
+                        log.debug("Wrong interface index {}. Error while getting network mask.", index);
+                    }
+                } else {
+                    log.debug("Wrong interface index {}. Error while getting network mask.", index);
+                    continue;
+                }
                 isisInterface.setIntermediateSystemName(jsonNode1
                                                                 .path(IsisConstants.INTERMEDIATESYSTEMNAME)
                                                                 .asText());
-                isisInterface.setSystemId(jsonNode1.path(IsisConstants.SYSTEMID).asText());
-                isisInterface.setReservedPacketCircuitType(jsonNode1
-                                                                   .path(IsisConstants.RESERVEDPACKETCIRCUITTYPE)
-                                                                   .asInt());
-                if (isisInterface.reservedPacketCircuitType() == IsisRouterType.L1.value()) {
-                    isisInterface.setL1LanId(jsonNode1.path(IsisConstants.LANID).asText());
+                String systemId = jsonNode1.path(IsisConstants.SYSTEMID).asText();
+                if (isValidSystemId(systemId)) {
+                    isisInterface.setSystemId(systemId);
+                } else {
+                    log.debug("Wrong systemId: {} for interface index {}.", systemId, index);
+                    continue;
                 }
-                isisInterface.setIdLength(jsonNode1.path(IsisConstants.IDLENGTH).asInt());
-                isisInterface.setMaxAreaAddresses(jsonNode1.path(IsisConstants.MAXAREAADDRESSES).asInt());
-                isisInterface.setNetworkType(IsisNetworkType.get(jsonNode1
-                                                                         .path(IsisConstants.NETWORKTYPE)
-                                                                         .asInt()));
-                isisInterface.setAreaAddress(jsonNode1.path(IsisConstants.AREAADDRESS).asText());
-                isisInterface.setAreaLength(jsonNode1.path(IsisConstants.AREALENGTH).asInt());
-                isisInterface.setLspId(jsonNode1.path(IsisConstants.LSPID).asText());
-                isisInterface.setCircuitId(jsonNode1.path(IsisConstants.CIRCUITID).asText());
-                isisInterface.setHoldingTime(jsonNode1.path(IsisConstants.HOLDINGTIME).asInt());
-                isisInterface.setPriority(jsonNode1.path(IsisConstants.PRIORITY).asInt());
-                isisInterface.setHelloInterval(jsonNode1.path(IsisConstants.HELLOINTERVAL).asInt());
+                String circuitType = jsonNode1.path(IsisConstants.RESERVEDPACKETCIRCUITTYPE).asText();
+                if (isPrimitive(circuitType)) {
+                    int input = Integer.parseInt(circuitType);
+                    if (input < 1 || input > 3) {
+                        log.debug("Wrong ReservedPacketCircuitType: {} for interface index {}.", circuitType, index);
+                        continue;
+                    }
+                    isisInterface.setReservedPacketCircuitType(input);
+                } else {
+                    log.debug("Wrong ReservedPacketCircuitType: {} for interface index {}.", circuitType, index);
+                    continue;
+                }
+                String networkType = jsonNode1.path(IsisConstants.NETWORKTYPE).asText();
+                if (isPrimitive(networkType)) {
+                    int input = Integer.parseInt(networkType);
+                    if (input < 1 || input > 2) {
+                        log.debug("Wrong networkType: {} for interface index {}.", networkType, index);
+                        continue;
+                    }
+                    isisInterface.setNetworkType(IsisNetworkType.get(input));
+                } else {
+                    log.debug("Wrong networkType: {} for interface index {}.", networkType, index);
+                    continue;
+                }
+                String areaAddress = jsonNode1.path(IsisConstants.AREAADDRESS).asText();
+                if (isPrimitive(areaAddress)) {
+                    if (areaAddress.length() > 7) {
+                        log.debug("Wrong areaAddress: {} for interface index {}.", areaAddress, index);
+                        continue;
+                    }
+                    isisInterface.setAreaAddress(areaAddress);
+                } else {
+                    log.debug("Wrong areaAddress: {} for interface index {}.", areaAddress, index);
+                    continue;
+                }
+                String circuitId = jsonNode1.path(IsisConstants.CIRCUITID).asText();
+                if (isPrimitive(circuitId)) {
+                    int input = Integer.parseInt(circuitId);
+                    if (input < 1) {
+                        log.debug("Wrong circuitId: {} for interface index {}.", circuitId, index);
+                        continue;
+                    }
+                    isisInterface.setCircuitId(circuitId);
+                } else {
+                    log.debug("Wrong circuitId: {} for interface index {}.", circuitId, index);
+                    continue;
+                }
+                String holdingTime = jsonNode1.path(IsisConstants.HOLDINGTIME).asText();
+                if (isPrimitive(holdingTime)) {
+                    int input = Integer.parseInt(holdingTime);
+                    if (input < 1 || input > 255) {
+                        log.debug("Wrong holdingTime: {} for interface index {}.", holdingTime, index);
+                        continue;
+                    }
+                    isisInterface.setHoldingTime(input);
+                } else {
+                    log.debug("Wrong holdingTime: {} for interface index {}.", holdingTime, index);
+                    continue;
+                }
+                String helloInterval = jsonNode1.path(IsisConstants.HELLOINTERVAL).asText();
+                if (isPrimitive(helloInterval)) {
+                    int interval = Integer.parseInt(helloInterval);
+                    if (interval > 0 && interval <= 255) {
+                        isisInterface.setHelloInterval(interval);
+                    } else {
+                        log.debug("Wrong hello interval: {} for interface index {}.", helloInterval, index);
+                        continue;
+                    }
+                } else {
+                    log.debug("Wrong hello interval: {} for interface index {}.", helloInterval, index);
+                    continue;
+                }
                 interfaceList.add(isisInterface);
             }
-            IsisProcess process = new DefaultIsisProcess();
-            process.setProcessId(jsonNode.path(IsisConstants.PROCESSESID).asText());
-            process.setIsisInterfaceList(interfaceList);
-            isisProcessesList.add(process);
+            if (interfaceList.size() > 0) {
+                IsisProcess process = new DefaultIsisProcess();
+                process.setProcessId(jsonNode.path(IsisConstants.PROCESSESID).asText());
+                process.setIsisInterfaceList(interfaceList);
+                isisProcessesList.add(process);
+            }
         });
 
         return isisProcessesList;
     }
+
+    /**
+     * Returns interface MAC by index.
+     *
+     * @param interfaceIndex interface index
+     * @return interface IP by index
+     */
+    private MacAddress getInterfaceMac(int interfaceIndex) {
+        MacAddress macAddress = null;
+        try {
+            NetworkInterface networkInterface = NetworkInterface.getByIndex(interfaceIndex);
+            macAddress = MacAddress.valueOf(networkInterface.getHardwareAddress());
+        } catch (Exception e) {
+            log.debug("Error while getting Interface IP by index");
+            return macAddress;
+        }
+
+        return macAddress;
+    }
+
+    /**
+     * Returns interface IP by index.
+     *
+     * @param interfaceIndex interface index
+     * @return interface IP by index
+     */
+    private Ip4Address getInterfaceIp(int interfaceIndex) {
+        Ip4Address ipAddress = null;
+        try {
+            NetworkInterface networkInterface = NetworkInterface.getByIndex(interfaceIndex);
+            Enumeration ipAddresses = networkInterface.getInetAddresses();
+            while (ipAddresses.hasMoreElements()) {
+                InetAddress address = (InetAddress) ipAddresses.nextElement();
+                if (!address.isLinkLocalAddress()) {
+                    ipAddress = Ip4Address.valueOf(address.getAddress());
+                    break;
+                }
+            }
+        } catch (Exception e) {
+            log.debug("Error while getting Interface IP by index");
+            return IsisConstants.DEFAULTIP;
+        }
+        return ipAddress;
+    }
+
+    /**
+     * Returns interface MAC by index.
+     *
+     * @param interfaceIndex interface index
+     * @return interface IP by index
+     */
+    private String getInterfaceMask(int interfaceIndex) {
+        String subnetMask = null;
+        try {
+            Ip4Address ipAddress = getInterfaceIp(interfaceIndex);
+            NetworkInterface networkInterface = NetworkInterface.getByInetAddress(
+                    InetAddress.getByName(ipAddress.toString()));
+            Enumeration ipAddresses = networkInterface.getInetAddresses();
+            int index = 0;
+            while (ipAddresses.hasMoreElements()) {
+                InetAddress address = (InetAddress) ipAddresses.nextElement();
+                if (!address.isLinkLocalAddress()) {
+                    break;
+                }
+                index++;
+            }
+            int prfLen = networkInterface.getInterfaceAddresses().get(index).getNetworkPrefixLength();
+            int shft = 0xffffffff << (32 - prfLen);
+            int oct1 = ((byte) ((shft & 0xff000000) >> 24)) & 0xff;
+            int oct2 = ((byte) ((shft & 0x00ff0000) >> 16)) & 0xff;
+            int oct3 = ((byte) ((shft & 0x0000ff00) >> 8)) & 0xff;
+            int oct4 = ((byte) (shft & 0x000000ff)) & 0xff;
+            subnetMask = oct1 + "." + oct2 + "." + oct3 + "." + oct4;
+        } catch (Exception e) {
+            log.debug("Error while getting Interface network mask by index");
+            return subnetMask;
+        }
+        return subnetMask;
+    }
+
+    /**
+     * Checks if primitive or not.
+     *
+     * @param value input value
+     * @return true if number else false
+     */
+    private boolean isPrimitive(String value) {
+        boolean status = true;
+        value = value.trim();
+        if (value.length() < 1) {
+            return false;
+        }
+        for (int i = 0; i < value.length(); i++) {
+            char c = value.charAt(i);
+            if (!Character.isDigit(c)) {
+                status = false;
+                break;
+            }
+        }
+
+        return status;
+    }
+
+    /**
+     * Checks if system id is valid or not.
+     *
+     * @param value input value
+     * @return true if valid else false
+     */
+    private boolean isValidSystemId(String value) {
+        value = value.trim();
+        boolean status = true;
+        if (value.length() != 14) {
+            return false;
+        }
+        for (int i = 0; i < value.length(); i++) {
+            char c = value.charAt(i);
+            if (!Character.isDigit(c)) {
+                if (!((i == 4 || i == 9) && c == '.')) {
+                    status = false;
+                    break;
+                }
+            }
+        }
+
+        return status;
+    }
+
+    /**
+     * Disconnects the executor.
+     */
+    public void disconnectExecutor() {
+        if (connectExecutor != null) {
+            connectExecutor.shutdown();
+            connectExecutor = null;
+        }
+    }
+
+    /**
+     * Connects to peer.
+     */
+    public void connectPeer() {
+        scheduleConnectionRetry(this.connectRetryTime);
+    }
+
+    /**
+     * Retry connection with exponential back-off mechanism.
+     *
+     * @param retryDelay retry delay
+     */
+    private void scheduleConnectionRetry(long retryDelay) {
+        if (this.connectExecutor == null) {
+            this.connectExecutor = Executors.newSingleThreadScheduledExecutor();
+        }
+        this.connectExecutor.schedule(new ConnectionRetry(), retryDelay, TimeUnit.MINUTES);
+    }
+
+    /**
+     * Implements ISIS connection and manages connection to peer with back-off mechanism in case of failure.
+     */
+    class ConnectionRetry implements Runnable {
+        @Override
+        public void run() {
+            log.debug("Connect to peer {}", IsisConstants.SHOST);
+            initConnection();
+            InetSocketAddress connectToSocket = new InetSocketAddress(IsisConstants.SHOST, isisPort.toInt());
+            try {
+                peerBootstrap.connect(connectToSocket).addListener(new ChannelFutureListener() {
+                    @Override
+                    public void operationComplete(ChannelFuture future) throws Exception {
+                        if (!future.isSuccess()) {
+                            connectRetryCounter++;
+                            log.error("Connection failed, ConnectRetryCounter {} remote host {}", connectRetryCounter,
+                                      IsisConstants.SHOST);
+                            /*
+                             * Reconnect to peer on failure is exponential till 4 mins, later on retry after every 4
+                             * mins.
+                             */
+                            if (connectRetryTime < RETRY_INTERVAL) {
+                                connectRetryTime = (connectRetryTime != 0) ? connectRetryTime * 2 : 1;
+                            }
+                            scheduleConnectionRetry(connectRetryTime);
+                        } else {
+                            connectRetryCounter++;
+                            log.info("Connected to remote host {}, Connect Counter {}", IsisConstants.SHOST,
+                                     connectRetryCounter);
+                            disconnectExecutor();
+                            isisChannelHandler.initializeInterfaceMap();
+                            //Send the config packet
+                            isisChannelHandler.sentConfigPacket(configPacket);
+                            return;
+                        }
+                    }
+                });
+            } catch (Exception e) {
+                log.info("Connect peer exception : " + e.toString());
+                disconnectExecutor();
+            }
+        }
+    }
 }
\ No newline at end of file