tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2016-present Open Networking Laboratory |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | package org.onosproject.isis.controller.impl; |
| 17 | |
| 18 | import org.jboss.netty.channel.Channel; |
| 19 | import org.jboss.netty.channel.ChannelHandlerContext; |
| 20 | import org.jboss.netty.channel.ChannelStateEvent; |
| 21 | import org.jboss.netty.channel.ExceptionEvent; |
| 22 | import org.jboss.netty.channel.MessageEvent; |
| 23 | import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler; |
| 24 | import org.jboss.netty.handler.timeout.ReadTimeoutException; |
| 25 | import org.onlab.packet.Ip4Address; |
| 26 | import org.onosproject.isis.controller.IsisInterface; |
| 27 | import org.onosproject.isis.controller.IsisLsdb; |
| 28 | import org.onosproject.isis.controller.IsisMessage; |
| 29 | import org.onosproject.isis.controller.IsisProcess; |
| 30 | import org.onosproject.isis.controller.impl.lsdb.DefaultIsisLsdb; |
| 31 | import org.onosproject.isis.exceptions.IsisParseException; |
| 32 | import org.slf4j.Logger; |
| 33 | import org.slf4j.LoggerFactory; |
| 34 | |
| 35 | import java.io.IOException; |
| 36 | import java.nio.channels.ClosedChannelException; |
| 37 | import java.util.ArrayList; |
| 38 | import java.util.List; |
| 39 | import java.util.Map; |
| 40 | import java.util.Set; |
| 41 | import java.util.concurrent.ConcurrentHashMap; |
| 42 | import java.util.concurrent.RejectedExecutionException; |
| 43 | import java.util.concurrent.ScheduledExecutorService; |
| 44 | |
| 45 | /** |
| 46 | * Channel handler deals with the ISIS channel connection. |
| 47 | * Also it dispatches messages to the appropriate handlers for processing. |
| 48 | */ |
| 49 | public class IsisChannelHandler extends IdleStateAwareChannelHandler { |
| 50 | |
| 51 | private static final Logger log = LoggerFactory.getLogger(IsisChannelHandler.class); |
| 52 | private static Map<Integer, Object> isisDb = null; |
sunish vk | c3824e8 | 2016-05-11 19:38:24 +0530 | [diff] [blame] | 53 | private Channel channel = null; |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 54 | private Controller controller; |
| 55 | private List<IsisProcess> processes = null; |
| 56 | private List<ScheduledExecutorService> executorList = new ArrayList<>(); |
| 57 | private byte[] configPacket = null; |
| 58 | private Map<Integer, IsisInterface> isisInterfaceMap = new ConcurrentHashMap<>(); |
| 59 | private IsisLsdb isisLsdb = new DefaultIsisLsdb(); |
| 60 | private List<Ip4Address> interfaceIps = new ArrayList<>(); |
| 61 | |
| 62 | /** |
| 63 | * Creates an instance of ISIS channel handler. |
| 64 | * |
| 65 | * @param controller controller instance |
| 66 | * @param processes list of configured processes |
| 67 | */ |
| 68 | public IsisChannelHandler(Controller controller, List<IsisProcess> processes) { |
| 69 | this.controller = controller; |
| 70 | this.processes = processes; |
sunish vk | 7bdf4d4 | 2016-06-24 12:29:43 +0530 | [diff] [blame] | 71 | ((DefaultIsisLsdb) isisLsdb).setController(this.controller); |
| 72 | ((DefaultIsisLsdb) isisLsdb).setIsisInterface(isisInterfaceList()); |
| 73 | } |
| 74 | |
| 75 | private List<IsisInterface> isisInterfaceList() { |
| 76 | List<IsisInterface> isisInterfaceList = new ArrayList<>(); |
| 77 | for (Integer key : isisInterfaceMap.keySet()) { |
| 78 | isisInterfaceList.add(isisInterfaceMap.get(key)); |
| 79 | } |
| 80 | return isisInterfaceList; |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | /** |
| 84 | * Initializes the interface map with interface details. |
| 85 | */ |
| 86 | public void initializeInterfaceMap() { |
| 87 | for (IsisProcess process : processes) { |
| 88 | for (IsisInterface isisInterface : process.isisInterfaceList()) { |
sunish vk | c3824e8 | 2016-05-11 19:38:24 +0530 | [diff] [blame] | 89 | IsisInterface anInterface = isisInterfaceMap.get(isisInterface.interfaceIndex()); |
| 90 | if (anInterface == null) { |
| 91 | isisInterfaceMap.put(isisInterface.interfaceIndex(), isisInterface); |
| 92 | interfaceIps.add(isisInterface.interfaceIpAddress()); |
| 93 | } |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 94 | } |
| 95 | } |
| 96 | //Initializes the interface with all interface ip details - for ls pdu generation |
| 97 | initializeInterfaceIpList(); |
| 98 | } |
| 99 | |
| 100 | /** |
| 101 | * Updates the interface map with interface details. |
| 102 | * |
| 103 | * @param isisProcesses updated process instances |
| 104 | */ |
| 105 | public void updateInterfaceMap(List<IsisProcess> isisProcesses) { |
| 106 | for (IsisProcess isisUpdatedProcess : isisProcesses) { |
| 107 | for (IsisInterface isisUpdatedInterface : isisUpdatedProcess.isisInterfaceList()) { |
| 108 | IsisInterface isisInterface = isisInterfaceMap.get(isisUpdatedInterface.interfaceIndex()); |
| 109 | if (isisInterface == null) { |
sunish vk | 4b5ce00 | 2016-05-09 20:18:35 +0530 | [diff] [blame] | 110 | isisInterfaceMap.put(isisUpdatedInterface.interfaceIndex(), isisUpdatedInterface); |
| 111 | interfaceIps.add(isisUpdatedInterface.interfaceIpAddress()); |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 112 | } else { |
sunish vk | 4b5ce00 | 2016-05-09 20:18:35 +0530 | [diff] [blame] | 113 | if (isisInterface.intermediateSystemName() != isisUpdatedInterface.intermediateSystemName()) { |
| 114 | isisInterface.setIntermediateSystemName(isisUpdatedInterface.intermediateSystemName()); |
| 115 | } |
| 116 | if (isisInterface.reservedPacketCircuitType() != isisUpdatedInterface.reservedPacketCircuitType()) { |
| 117 | isisInterface.setReservedPacketCircuitType(isisUpdatedInterface.reservedPacketCircuitType()); |
| 118 | isisInterface.removeNeighbors(); |
| 119 | } |
| 120 | if (isisInterface.circuitId() != isisUpdatedInterface.circuitId()) { |
| 121 | isisInterface.setCircuitId(isisUpdatedInterface.circuitId()); |
| 122 | } |
| 123 | if (isisInterface.networkType() != isisUpdatedInterface.networkType()) { |
| 124 | isisInterface.setNetworkType(isisUpdatedInterface.networkType()); |
| 125 | isisInterface.removeNeighbors(); |
| 126 | } |
| 127 | if (isisInterface.areaAddress() != isisUpdatedInterface.areaAddress()) { |
| 128 | isisInterface.setAreaAddress(isisUpdatedInterface.areaAddress()); |
| 129 | } |
| 130 | if (isisInterface.holdingTime() != isisUpdatedInterface.holdingTime()) { |
| 131 | isisInterface.setHoldingTime(isisUpdatedInterface.holdingTime()); |
| 132 | } |
| 133 | if (isisInterface.helloInterval() != isisUpdatedInterface.helloInterval()) { |
| 134 | isisInterface.setHelloInterval(isisUpdatedInterface.helloInterval()); |
| 135 | isisInterface.stopHelloSender(); |
| 136 | isisInterface.startHelloSender(channel); |
| 137 | } |
| 138 | |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 139 | isisInterfaceMap.put(isisInterface.interfaceIndex(), isisInterface); |
| 140 | } |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | /** |
| 146 | * Initializes the interface with all interface ip details. |
| 147 | */ |
| 148 | public void initializeInterfaceIpList() { |
| 149 | for (IsisProcess process : processes) { |
| 150 | for (IsisInterface isisInterface : process.isisInterfaceList()) { |
| 151 | ((DefaultIsisInterface) isisInterface).setAllConfiguredInterfaceIps(interfaceIps); |
| 152 | } |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | /** |
| 157 | * Initialize channel, start hello sender and initialize LSDB. |
| 158 | */ |
| 159 | private void initialize() { |
| 160 | log.debug("IsisChannelHandler initialize..!!!"); |
| 161 | if (configPacket != null) { |
| 162 | log.debug("IsisChannelHandler initialize -> sentConfig packet of length ::" |
| 163 | + configPacket.length); |
| 164 | sentConfigPacket(configPacket); |
| 165 | } |
sunish vk | c3824e8 | 2016-05-11 19:38:24 +0530 | [diff] [blame] | 166 | initializeInterfaceMap(); |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 167 | //start the hello timer |
| 168 | startHelloSender(); |
| 169 | //Initialize Database |
| 170 | isisLsdb.initializeDb(); |
| 171 | } |
| 172 | |
| 173 | @Override |
| 174 | public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent evt) throws Exception { |
| 175 | log.info("ISIS channelConnected from {}", evt.getChannel().getRemoteAddress()); |
| 176 | this.channel = evt.getChannel(); |
| 177 | initialize(); |
| 178 | } |
| 179 | |
| 180 | @Override |
| 181 | public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent evt) { |
| 182 | log.debug("IsisChannelHandler::channelDisconnected...!!!"); |
sunish vk | 4b5ce00 | 2016-05-09 20:18:35 +0530 | [diff] [blame] | 183 | if (controller != null) { |
| 184 | controller.connectPeer(); |
sunish vk | c3824e8 | 2016-05-11 19:38:24 +0530 | [diff] [blame] | 185 | stopHelloSender(); |
sunish vk | 4b5ce00 | 2016-05-09 20:18:35 +0530 | [diff] [blame] | 186 | } |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 187 | } |
| 188 | |
| 189 | @Override |
| 190 | public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 191 | if (e.getCause() instanceof ReadTimeoutException) { |
sunish vk | 7bdf4d4 | 2016-06-24 12:29:43 +0530 | [diff] [blame] | 192 | log.debug("Disconnecting device {} due to read timeout", e.getChannel().getRemoteAddress()); |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 193 | return; |
| 194 | } else if (e.getCause() instanceof ClosedChannelException) { |
| 195 | log.debug("Channel for ISIS {} already closed", e.getChannel().getRemoteAddress()); |
| 196 | } else if (e.getCause() instanceof IOException) { |
sunish vk | 7bdf4d4 | 2016-06-24 12:29:43 +0530 | [diff] [blame] | 197 | log.debug("Disconnecting ISIS {} due to IO Error: {}", e.getChannel().getRemoteAddress(), |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 198 | e.getCause().getMessage()); |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 199 | } else if (e.getCause() instanceof IsisParseException) { |
| 200 | IsisParseException errMsg = (IsisParseException) e.getCause(); |
| 201 | byte errorCode = errMsg.errorCode(); |
| 202 | byte errorSubCode = errMsg.errorSubCode(); |
sunish vk | 7bdf4d4 | 2016-06-24 12:29:43 +0530 | [diff] [blame] | 203 | log.debug("Error while parsing message from ISIS {}, ErrorCode {}", |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 204 | e.getChannel().getRemoteAddress(), errorCode); |
| 205 | } else if (e.getCause() instanceof RejectedExecutionException) { |
sunish vk | 7bdf4d4 | 2016-06-24 12:29:43 +0530 | [diff] [blame] | 206 | log.debug("Could not process message: queue full"); |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 207 | } else { |
sunish vk | 7bdf4d4 | 2016-06-24 12:29:43 +0530 | [diff] [blame] | 208 | log.debug("Error while processing message from ISIS {}, {}", |
sunish vk | 4b5ce00 | 2016-05-09 20:18:35 +0530 | [diff] [blame] | 209 | e.getChannel().getRemoteAddress(), e.getCause().getMessage()); |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 210 | } |
| 211 | } |
| 212 | |
| 213 | @Override |
| 214 | public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { |
| 215 | log.debug("IsisChannelHandler::messageReceived...!!!"); |
| 216 | Object message = e.getMessage(); |
| 217 | if (message instanceof List) { |
| 218 | List<IsisMessage> isisMessageList = (List<IsisMessage>) message; |
| 219 | log.debug("IsisChannelHandler::List of IsisMessages Size {}", isisMessageList.size()); |
| 220 | if (isisMessageList != null) { |
| 221 | for (IsisMessage isisMessage : isisMessageList) { |
| 222 | processIsisMessage(isisMessage, ctx); |
| 223 | } |
| 224 | } else { |
| 225 | log.debug("IsisChannelHandler::IsisMessages Null List...!!"); |
| 226 | } |
| 227 | } |
| 228 | if (message instanceof IsisMessage) { |
| 229 | IsisMessage isisMessage = (IsisMessage) message; |
| 230 | log.debug("IsisChannelHandler::IsisMessages received...!!"); |
| 231 | processIsisMessage(isisMessage, ctx); |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | /** |
| 236 | * When an ISIS message received it is handed over to this method. |
| 237 | * Based on the type of the ISIS message received it will be handed over |
| 238 | * to corresponding message handler methods. |
| 239 | * |
| 240 | * @param isisMessage received ISIS message |
| 241 | * @param ctx channel handler context instance. |
| 242 | * @throws Exception might throws exception |
| 243 | */ |
| 244 | public void processIsisMessage(IsisMessage isisMessage, ChannelHandlerContext ctx) throws Exception { |
| 245 | log.debug("IsisChannelHandler::processIsisMessage...!!!"); |
| 246 | int interfaceIndex = isisMessage.interfaceIndex(); |
| 247 | IsisInterface isisInterface = isisInterfaceMap.get(interfaceIndex); |
| 248 | isisInterface.processIsisMessage(isisMessage, isisLsdb, channel); |
| 249 | } |
| 250 | |
| 251 | /** |
| 252 | * Starts the hello timer which sends hello packet every configured seconds. |
| 253 | */ |
| 254 | public void startHelloSender() { |
| 255 | log.debug("IsisController::startHelloSender"); |
| 256 | Set<Integer> interfaceIndexes = isisInterfaceMap.keySet(); |
| 257 | for (Integer interfaceIndex : interfaceIndexes) { |
| 258 | IsisInterface isisInterface = isisInterfaceMap.get(interfaceIndex); |
| 259 | isisInterface.startHelloSender(channel); |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | /** |
| 264 | * Stops the hello timer. |
| 265 | */ |
| 266 | public void stopHelloSender() { |
| 267 | log.debug("ISISChannelHandler::stopHelloTimer "); |
sunish vk | c3824e8 | 2016-05-11 19:38:24 +0530 | [diff] [blame] | 268 | log.debug("IsisController::startHelloSender"); |
| 269 | Set<Integer> interfaceIndexes = isisInterfaceMap.keySet(); |
| 270 | for (Integer interfaceIndex : interfaceIndexes) { |
| 271 | IsisInterface isisInterface = isisInterfaceMap.get(interfaceIndex); |
| 272 | isisInterface.stopHelloSender(); |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 273 | } |
| 274 | } |
| 275 | |
| 276 | /** |
| 277 | * Sends the interface configuration packet to server. |
| 278 | * |
| 279 | * @param configPacket interface configuration |
| 280 | */ |
| 281 | public void sentConfigPacket(byte[] configPacket) { |
sunish vk | 7bdf4d4 | 2016-06-24 12:29:43 +0530 | [diff] [blame] | 282 | if (channel != null && channel.isConnected() && channel.isOpen()) { |
tejeshwer degala | 3fe1ed5 | 2016-04-22 17:04:01 +0530 | [diff] [blame] | 283 | channel.write(configPacket); |
| 284 | log.debug("IsisChannelHandler sentConfigPacket packet sent..!!!"); |
| 285 | } else { |
| 286 | log.debug("IsisChannelHandler sentConfigPacket channel not connected - re try..!!!"); |
| 287 | this.configPacket = configPacket; |
| 288 | } |
| 289 | } |
| 290 | } |