ONOS-4086 to ONOS-4091, ONOS-4098 to ONOS-4100:ISIS controller implementation

Change-Id: I7be52805652fe762baf808515401d6b5042b2aa5
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
new file mode 100755
index 0000000..c12c520
--- /dev/null
+++ b/protocols/isis/ctl/src/main/java/org/onosproject/isis/controller/impl/Controller.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2016-present 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.isis.controller.impl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.jboss.netty.bootstrap.ClientBootstrap;
+import org.jboss.netty.channel.AdaptiveReceiveBufferSizePredictor;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.FixedReceiveBufferSizePredictorFactory;
+import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.isis.controller.IsisInterface;
+import org.onosproject.isis.controller.IsisNetworkType;
+import org.onosproject.isis.controller.IsisProcess;
+import org.onosproject.isis.controller.IsisRouterType;
+import org.onosproject.isis.io.util.IsisConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+
+import static org.onlab.util.Tools.groupedThreads;
+
+/**
+ * Representation of an ISIS controller.
+ */
+public class Controller {
+    protected static final int BUFFER_SIZE = 4 * 1024 * 1024;
+    private static final Logger log = LoggerFactory.getLogger(Controller.class);
+    private final int peerWorkerThreads = 16;
+    private List<IsisProcess> processes = null;
+    private IsisChannelHandler isisChannelHandler;
+    private NioClientSocketChannelFactory peerExecFactory;
+    private ClientBootstrap peerBootstrap = null;
+    private TpPort isisPort = TpPort.tpPort(IsisConstants.SPORT);
+
+    /**
+     * Deactivates ISIS controller.
+     */
+    public void isisDeactivate() {
+        peerExecFactory.shutdown();
+    }
+
+    /**
+     * Updates the processes configuration.
+     *
+     * @param jsonNode json node instance
+     * @throws Exception might throws parse exception
+     */
+    public void updateConfig(JsonNode jsonNode) throws Exception {
+        log.debug("Controller::UpdateConfig called");
+        byte[] 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()) {
+                DefaultIsisInterface isisInterfaceImpl = (DefaultIsisInterface) isisInterface;
+                log.debug("IsisInterfaceDetails : " + isisInterface);
+                numberOfInterface++;
+                configPacket[2 * numberOfInterface] = (byte) isisInterfaceImpl.interfaceIndex();
+                if (isisInterface.networkType() == IsisNetworkType.BROADCAST &&
+                        isisInterfaceImpl.reservedPacketCircuitType() == IsisRouterType.L1.value()) {
+                    configPacket[(2 * numberOfInterface) + 1] = (byte) 0;
+                } else if (isisInterface.networkType() == IsisNetworkType.BROADCAST &&
+                        isisInterfaceImpl.reservedPacketCircuitType() == IsisRouterType.L2.value()) {
+                    configPacket[(2 * numberOfInterface) + 1] = (byte) 1;
+                } else if (isisInterface.networkType() == IsisNetworkType.P2P) {
+                    configPacket[(2 * numberOfInterface) + 1] = (byte) 2;
+                } else if (isisInterface.networkType() == IsisNetworkType.BROADCAST &&
+                        isisInterfaceImpl.reservedPacketCircuitType() == IsisRouterType.L1L2.value()) {
+                    configPacket[(2 * numberOfInterface) + 1] = (byte) 3;
+                }
+            }
+        }
+        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();
+        } else {
+            isisChannelHandler.updateInterfaceMap(isisProcesses);
+        }
+        //Send the config packet
+        isisChannelHandler.sentConfigPacket(configPacket);
+    }
+
+    /**
+     * Initializes the netty client channel connection.
+     */
+    private void initConnection() {
+        if (peerBootstrap != null) {
+            return;
+        }
+        peerBootstrap = createPeerBootStrap();
+
+        peerBootstrap.setOption("reuseAddress", true);
+        peerBootstrap.setOption("tcpNoDelay", true);
+        peerBootstrap.setOption("keepAlive", true);
+        peerBootstrap.setOption("receiveBufferSize", Controller.BUFFER_SIZE);
+        peerBootstrap.setOption("receiveBufferSizePredictorFactory",
+                                new FixedReceiveBufferSizePredictorFactory(
+                                        Controller.BUFFER_SIZE));
+        peerBootstrap.setOption("receiveBufferSizePredictor",
+                                new AdaptiveReceiveBufferSizePredictor(64, 1024, 65536));
+        peerBootstrap.setOption("child.keepAlive", true);
+        peerBootstrap.setOption("child.tcpNoDelay", true);
+        peerBootstrap.setOption("child.sendBufferSize", Controller.BUFFER_SIZE);
+        peerBootstrap.setOption("child.receiveBufferSize", Controller.BUFFER_SIZE);
+        peerBootstrap.setOption("child.receiveBufferSizePredictorFactory",
+                                new FixedReceiveBufferSizePredictorFactory(
+                                        Controller.BUFFER_SIZE));
+        peerBootstrap.setOption("child.reuseAddress", true);
+
+        isisChannelHandler = new IsisChannelHandler(this, processes);
+        ChannelPipelineFactory pfact = new IsisPipelineFactory(isisChannelHandler);
+        peerBootstrap.setPipelineFactory(pfact);
+        ChannelFuture connection = peerBootstrap.connect(new InetSocketAddress(IsisConstants.SHOST, isisPort.toInt()));
+    }
+
+    /**
+     * Creates peer boot strap.
+     *
+     * @return client bootstrap instance
+     */
+    private ClientBootstrap createPeerBootStrap() {
+
+        if (peerWorkerThreads == 0) {
+            peerExecFactory = new NioClientSocketChannelFactory(
+                    Executors.newCachedThreadPool(groupedThreads("onos/isis", "boss-%d")),
+                    Executors.newCachedThreadPool(groupedThreads("onos/isis", "worker-%d")));
+            return new ClientBootstrap(peerExecFactory);
+        } else {
+            peerExecFactory = new NioClientSocketChannelFactory(
+                    Executors.newCachedThreadPool(groupedThreads("onos/isis", "boss-%d")),
+                    Executors.newCachedThreadPool(groupedThreads("onos/isis", "worker-%d")),
+                    peerWorkerThreads);
+            return new ClientBootstrap(peerExecFactory);
+        }
+    }
+
+    /**
+     * Gets all configured processes.
+     *
+     * @return all configured processes
+     */
+    public List<IsisProcess> getAllConfiguredProcesses() {
+        return processes;
+    }
+
+    /**
+     * Gets the list of processes configured.
+     *
+     * @param json posted json
+     * @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;
+        }
+        jsonNodes.forEach(jsonNode -> {
+            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");
+                }
+                isisInterface.setInterfaceMacAddress(MacAddress.valueOf(jsonNode1
+                                                                                .path(IsisConstants.MACADDRESS)
+                                                                                .asText()));
+                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());
+                }
+                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());
+                interfaceList.add(isisInterface);
+            }
+            IsisProcess process = new DefaultIsisProcess();
+            process.setProcessId(jsonNode.path(IsisConstants.PROCESSESID).asText());
+            process.setIsisInterfaceList(interfaceList);
+            isisProcessesList.add(process);
+        });
+
+        return isisProcessesList;
+    }
+}
\ No newline at end of file