Committed by
Brian O'Connor
BMv2 performance improvements
- Implemented a non-blocking Thrift server for the controller (before it was limiting the number of active connections) - Improved configuration swap times by forcing it - Minor bugfixes and polishing - Update onos-bmv2 repo URL in thrift-api pom.xml Change-Id: I13b61f5aa22558c395768e3b445f302b20c5bd33
Showing
7 changed files
with
316 additions
and
134 deletions
| ... | @@ -53,9 +53,14 @@ public interface Bmv2DeviceContextService { | ... | @@ -53,9 +53,14 @@ public interface Bmv2DeviceContextService { |
| 53 | void registerInterpreterClassLoader(Class<? extends Bmv2Interpreter> interpreterClass, ClassLoader loader); | 53 | void registerInterpreterClassLoader(Class<? extends Bmv2Interpreter> interpreterClass, ClassLoader loader); |
| 54 | 54 | ||
| 55 | /** | 55 | /** |
| 56 | - * Returns a default context. | 56 | + * Returns the default context. |
| 57 | * | 57 | * |
| 58 | * @return a BMv2 device context | 58 | * @return a BMv2 device context |
| 59 | */ | 59 | */ |
| 60 | Bmv2DeviceContext defaultContext(); | 60 | Bmv2DeviceContext defaultContext(); |
| 61 | + | ||
| 62 | + /** | ||
| 63 | + * Sets the default context for the given device. | ||
| 64 | + */ | ||
| 65 | + void setDefaultContext(DeviceId deviceId); | ||
| 61 | } | 66 | } | ... | ... |
protocols/bmv2/ctl/src/main/java/org/onosproject/bmv2/ctl/Bmv2ControlPlaneThriftServer.java
0 → 100644
| 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 | + | ||
| 17 | +package org.onosproject.bmv2.ctl; | ||
| 18 | + | ||
| 19 | +import com.google.common.collect.Maps; | ||
| 20 | +import com.google.common.collect.Sets; | ||
| 21 | +import org.apache.thrift.TProcessor; | ||
| 22 | +import org.apache.thrift.server.TThreadedSelectorServer; | ||
| 23 | +import org.apache.thrift.transport.TFramedTransport; | ||
| 24 | +import org.apache.thrift.transport.TNonblockingServerSocket; | ||
| 25 | +import org.apache.thrift.transport.TNonblockingServerTransport; | ||
| 26 | +import org.apache.thrift.transport.TNonblockingSocket; | ||
| 27 | +import org.apache.thrift.transport.TNonblockingTransport; | ||
| 28 | +import org.apache.thrift.transport.TTransport; | ||
| 29 | +import org.apache.thrift.transport.TTransportException; | ||
| 30 | +import org.slf4j.Logger; | ||
| 31 | +import org.slf4j.LoggerFactory; | ||
| 32 | + | ||
| 33 | +import java.io.IOException; | ||
| 34 | +import java.net.InetAddress; | ||
| 35 | +import java.net.InetSocketAddress; | ||
| 36 | +import java.nio.channels.SelectionKey; | ||
| 37 | +import java.nio.channels.SocketChannel; | ||
| 38 | +import java.util.Map; | ||
| 39 | +import java.util.Set; | ||
| 40 | +import java.util.concurrent.ExecutorService; | ||
| 41 | + | ||
| 42 | +/** | ||
| 43 | + * A Thrift TThreadedSelectorServer that keeps track of the clients' IP address. | ||
| 44 | + */ | ||
| 45 | +final class Bmv2ControlPlaneThriftServer extends TThreadedSelectorServer { | ||
| 46 | + | ||
| 47 | + private static final int MAX_WORKER_THREADS = 20; | ||
| 48 | + private static final int MAX_SELECTOR_THREADS = 4; | ||
| 49 | + private static final int ACCEPT_QUEUE_LEN = 8; | ||
| 50 | + | ||
| 51 | + private final Map<TTransport, InetAddress> clientAddresses = Maps.newConcurrentMap(); | ||
| 52 | + private final Set<TrackingSelectorThread> selectorThreads = Sets.newHashSet(); | ||
| 53 | + | ||
| 54 | + private AcceptThread acceptThread; | ||
| 55 | + | ||
| 56 | + private final Logger log = LoggerFactory.getLogger(this.getClass()); | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * Creates a new server. | ||
| 60 | + * | ||
| 61 | + * @param port a listening port | ||
| 62 | + * @param processor a processor | ||
| 63 | + * @param executorService an executor service | ||
| 64 | + * @throws TTransportException | ||
| 65 | + */ | ||
| 66 | + public Bmv2ControlPlaneThriftServer(int port, TProcessor processor, ExecutorService executorService) | ||
| 67 | + throws TTransportException { | ||
| 68 | + super(new TThreadedSelectorServer.Args(new TNonblockingServerSocket(port)) | ||
| 69 | + .workerThreads(MAX_WORKER_THREADS) | ||
| 70 | + .selectorThreads(MAX_SELECTOR_THREADS) | ||
| 71 | + .acceptQueueSizePerThread(ACCEPT_QUEUE_LEN) | ||
| 72 | + .executorService(executorService) | ||
| 73 | + .processor(processor)); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + /** | ||
| 77 | + * Returns the IP address of the client associated with the given input framed transport. | ||
| 78 | + * | ||
| 79 | + * @param inputTransport a framed transport instance | ||
| 80 | + * @return the IP address of the client or null | ||
| 81 | + */ | ||
| 82 | + InetAddress getClientAddress(TFramedTransport inputTransport) { | ||
| 83 | + return clientAddresses.get(inputTransport); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + @Override | ||
| 87 | + protected boolean startThreads() { | ||
| 88 | + try { | ||
| 89 | + for (int i = 0; i < MAX_SELECTOR_THREADS; ++i) { | ||
| 90 | + selectorThreads.add(new TrackingSelectorThread(ACCEPT_QUEUE_LEN)); | ||
| 91 | + } | ||
| 92 | + acceptThread = new AcceptThread((TNonblockingServerTransport) serverTransport_, | ||
| 93 | + createSelectorThreadLoadBalancer(selectorThreads)); | ||
| 94 | + selectorThreads.forEach(Thread::start); | ||
| 95 | + acceptThread.start(); | ||
| 96 | + return true; | ||
| 97 | + } catch (IOException e) { | ||
| 98 | + log.error("Failed to start threads!", e); | ||
| 99 | + return false; | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + @Override | ||
| 104 | + protected void joinThreads() throws InterruptedException { | ||
| 105 | + // Wait until the io threads exit. | ||
| 106 | + acceptThread.join(); | ||
| 107 | + for (TThreadedSelectorServer.SelectorThread thread : selectorThreads) { | ||
| 108 | + thread.join(); | ||
| 109 | + } | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + @Override | ||
| 113 | + public void stop() { | ||
| 114 | + stopped_ = true; | ||
| 115 | + // Stop queuing connect attempts asap. | ||
| 116 | + stopListening(); | ||
| 117 | + if (acceptThread != null) { | ||
| 118 | + acceptThread.wakeupSelector(); | ||
| 119 | + } | ||
| 120 | + if (selectorThreads != null) { | ||
| 121 | + selectorThreads.stream() | ||
| 122 | + .filter(thread -> thread != null) | ||
| 123 | + .forEach(TrackingSelectorThread::wakeupSelector); | ||
| 124 | + } | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + private class TrackingSelectorThread extends TThreadedSelectorServer.SelectorThread { | ||
| 128 | + | ||
| 129 | + TrackingSelectorThread(int maxPendingAccepts) throws IOException { | ||
| 130 | + super(maxPendingAccepts); | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + @Override | ||
| 134 | + protected FrameBuffer createFrameBuffer(TNonblockingTransport trans, SelectionKey selectionKey, | ||
| 135 | + AbstractSelectThread selectThread) { | ||
| 136 | + TrackingFrameBuffer frameBuffer = new TrackingFrameBuffer(trans, selectionKey, selectThread); | ||
| 137 | + if (trans instanceof TNonblockingSocket) { | ||
| 138 | + try { | ||
| 139 | + SocketChannel socketChannel = ((TNonblockingSocket) trans).getSocketChannel(); | ||
| 140 | + InetAddress addr = ((InetSocketAddress) socketChannel.getRemoteAddress()).getAddress(); | ||
| 141 | + clientAddresses.put(frameBuffer.getInputFramedTransport(), addr); | ||
| 142 | + } catch (IOException e) { | ||
| 143 | + log.warn("Exception while tracking client address", e); | ||
| 144 | + clientAddresses.remove(frameBuffer.getInputFramedTransport()); | ||
| 145 | + } | ||
| 146 | + } else { | ||
| 147 | + log.warn("Unknown TNonblockingTransport instance: {}", trans.getClass().getName()); | ||
| 148 | + clientAddresses.remove(frameBuffer.getInputFramedTransport()); | ||
| 149 | + } | ||
| 150 | + return frameBuffer; | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + private class TrackingFrameBuffer extends FrameBuffer { | ||
| 155 | + | ||
| 156 | + TrackingFrameBuffer(TNonblockingTransport trans, SelectionKey selectionKey, | ||
| 157 | + AbstractSelectThread selectThread) { | ||
| 158 | + super(trans, selectionKey, selectThread); | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + TTransport getInputFramedTransport() { | ||
| 162 | + return this.inTrans_; | ||
| 163 | + } | ||
| 164 | + } | ||
| 165 | +} |
| ... | @@ -34,19 +34,18 @@ import org.apache.thrift.TProcessor; | ... | @@ -34,19 +34,18 @@ import org.apache.thrift.TProcessor; |
| 34 | import org.apache.thrift.protocol.TBinaryProtocol; | 34 | import org.apache.thrift.protocol.TBinaryProtocol; |
| 35 | import org.apache.thrift.protocol.TMultiplexedProtocol; | 35 | import org.apache.thrift.protocol.TMultiplexedProtocol; |
| 36 | import org.apache.thrift.protocol.TProtocol; | 36 | import org.apache.thrift.protocol.TProtocol; |
| 37 | -import org.apache.thrift.server.TThreadPoolServer; | 37 | +import org.apache.thrift.transport.TFramedTransport; |
| 38 | -import org.apache.thrift.transport.TServerSocket; | ||
| 39 | -import org.apache.thrift.transport.TServerTransport; | ||
| 40 | import org.apache.thrift.transport.TSocket; | 38 | import org.apache.thrift.transport.TSocket; |
| 41 | import org.apache.thrift.transport.TTransport; | 39 | import org.apache.thrift.transport.TTransport; |
| 42 | import org.apache.thrift.transport.TTransportException; | 40 | import org.apache.thrift.transport.TTransportException; |
| 43 | import org.onlab.util.ImmutableByteSequence; | 41 | import org.onlab.util.ImmutableByteSequence; |
| 44 | -import org.onosproject.bmv2.api.service.Bmv2Controller; | ||
| 45 | import org.onosproject.bmv2.api.runtime.Bmv2Device; | 42 | import org.onosproject.bmv2.api.runtime.Bmv2Device; |
| 46 | import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent; | 43 | import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent; |
| 44 | +import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException; | ||
| 45 | +import org.onosproject.bmv2.api.service.Bmv2Controller; | ||
| 47 | import org.onosproject.bmv2.api.service.Bmv2DeviceListener; | 46 | import org.onosproject.bmv2.api.service.Bmv2DeviceListener; |
| 48 | import org.onosproject.bmv2.api.service.Bmv2PacketListener; | 47 | import org.onosproject.bmv2.api.service.Bmv2PacketListener; |
| 49 | -import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException; | 48 | +import org.onosproject.bmv2.thriftapi.BmConfig; |
| 50 | import org.onosproject.bmv2.thriftapi.ControlPlaneService; | 49 | import org.onosproject.bmv2.thriftapi.ControlPlaneService; |
| 51 | import org.onosproject.bmv2.thriftapi.SimpleSwitch; | 50 | import org.onosproject.bmv2.thriftapi.SimpleSwitch; |
| 52 | import org.onosproject.bmv2.thriftapi.Standard; | 51 | import org.onosproject.bmv2.thriftapi.Standard; |
| ... | @@ -55,6 +54,7 @@ import org.onosproject.net.DeviceId; | ... | @@ -55,6 +54,7 @@ import org.onosproject.net.DeviceId; |
| 55 | import org.slf4j.Logger; | 54 | import org.slf4j.Logger; |
| 56 | import org.slf4j.LoggerFactory; | 55 | import org.slf4j.LoggerFactory; |
| 57 | 56 | ||
| 57 | +import java.net.InetAddress; | ||
| 58 | import java.nio.ByteBuffer; | 58 | import java.nio.ByteBuffer; |
| 59 | import java.util.List; | 59 | import java.util.List; |
| 60 | import java.util.Set; | 60 | import java.util.Set; |
| ... | @@ -67,6 +67,7 @@ import java.util.concurrent.TimeUnit; | ... | @@ -67,6 +67,7 @@ import java.util.concurrent.TimeUnit; |
| 67 | 67 | ||
| 68 | import static com.google.common.base.Preconditions.checkNotNull; | 68 | import static com.google.common.base.Preconditions.checkNotNull; |
| 69 | import static org.onlab.util.Tools.groupedThreads; | 69 | import static org.onlab.util.Tools.groupedThreads; |
| 70 | +import static org.onosproject.bmv2.thriftapi.ControlPlaneService.Processor; | ||
| 70 | 71 | ||
| 71 | /** | 72 | /** |
| 72 | * Default implementation of a BMv2 controller. | 73 | * Default implementation of a BMv2 controller. |
| ... | @@ -93,10 +94,10 @@ public class Bmv2ControllerImpl implements Bmv2Controller { | ... | @@ -93,10 +94,10 @@ public class Bmv2ControllerImpl implements Bmv2Controller { |
| 93 | .removalListener(new ClientRemovalListener()) | 94 | .removalListener(new ClientRemovalListener()) |
| 94 | .build(new ClientLoader()); | 95 | .build(new ClientLoader()); |
| 95 | 96 | ||
| 96 | - private final InternalTrackingProcessor trackingProcessor = new InternalTrackingProcessor(); | 97 | + private final TProcessor trackingProcessor = new TrackingProcessor(); |
| 97 | 98 | ||
| 98 | private final ExecutorService executorService = Executors | 99 | private final ExecutorService executorService = Executors |
| 99 | - .newFixedThreadPool(16, groupedThreads("onos/bmv2", "controller", log)); | 100 | + .newFixedThreadPool(32, groupedThreads("onos/bmv2", "controller", log)); |
| 100 | 101 | ||
| 101 | private final Set<Bmv2DeviceListener> deviceListeners = new CopyOnWriteArraySet<>(); | 102 | private final Set<Bmv2DeviceListener> deviceListeners = new CopyOnWriteArraySet<>(); |
| 102 | private final Set<Bmv2PacketListener> packetListeners = new CopyOnWriteArraySet<>(); | 103 | private final Set<Bmv2PacketListener> packetListeners = new CopyOnWriteArraySet<>(); |
| ... | @@ -104,7 +105,7 @@ public class Bmv2ControllerImpl implements Bmv2Controller { | ... | @@ -104,7 +105,7 @@ public class Bmv2ControllerImpl implements Bmv2Controller { |
| 104 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 105 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 105 | protected CoreService coreService; | 106 | protected CoreService coreService; |
| 106 | 107 | ||
| 107 | - private TThreadPoolServer thriftServer; | 108 | + private Bmv2ControlPlaneThriftServer server; |
| 108 | 109 | ||
| 109 | // TODO: make it configurable trough component config | 110 | // TODO: make it configurable trough component config |
| 110 | private int serverPort = DEFAULT_PORT; | 111 | private int serverPort = DEFAULT_PORT; |
| ... | @@ -124,12 +125,9 @@ public class Bmv2ControllerImpl implements Bmv2Controller { | ... | @@ -124,12 +125,9 @@ public class Bmv2ControllerImpl implements Bmv2Controller { |
| 124 | 125 | ||
| 125 | private void startServer(int port) { | 126 | private void startServer(int port) { |
| 126 | try { | 127 | try { |
| 127 | - TServerTransport transport = new TServerSocket(port); | ||
| 128 | log.info("Starting server on port {}...", port); | 128 | log.info("Starting server on port {}...", port); |
| 129 | - this.thriftServer = new TThreadPoolServer(new TThreadPoolServer.Args(transport) | 129 | + this.server = new Bmv2ControlPlaneThriftServer(port, trackingProcessor, executorService); |
| 130 | - .processor(trackingProcessor) | 130 | + executorService.execute(server::serve); |
| 131 | - .executorService(executorService)); | ||
| 132 | - executorService.execute(thriftServer::serve); | ||
| 133 | } catch (TTransportException e) { | 131 | } catch (TTransportException e) { |
| 134 | log.error("Unable to start server", e); | 132 | log.error("Unable to start server", e); |
| 135 | } | 133 | } |
| ... | @@ -137,8 +135,9 @@ public class Bmv2ControllerImpl implements Bmv2Controller { | ... | @@ -137,8 +135,9 @@ public class Bmv2ControllerImpl implements Bmv2Controller { |
| 137 | 135 | ||
| 138 | private void stopServer() { | 136 | private void stopServer() { |
| 139 | // Stop the server if running... | 137 | // Stop the server if running... |
| 140 | - if (thriftServer != null && !thriftServer.isServing()) { | 138 | + if (server != null && !server.isServing()) { |
| 141 | - thriftServer.stop(); | 139 | + server.setShouldStop(true); |
| 140 | + server.stop(); | ||
| 142 | } | 141 | } |
| 143 | try { | 142 | try { |
| 144 | executorService.shutdown(); | 143 | executorService.shutdown(); |
| ... | @@ -162,8 +161,11 @@ public class Bmv2ControllerImpl implements Bmv2Controller { | ... | @@ -162,8 +161,11 @@ public class Bmv2ControllerImpl implements Bmv2Controller { |
| 162 | @Override | 161 | @Override |
| 163 | public boolean isReacheable(DeviceId deviceId) { | 162 | public boolean isReacheable(DeviceId deviceId) { |
| 164 | try { | 163 | try { |
| 165 | - return getAgent(deviceId).ping(); | 164 | + Bmv2DeviceThriftClient client = (Bmv2DeviceThriftClient) getAgent(deviceId); |
| 166 | - } catch (Bmv2RuntimeException e) { | 165 | + BmConfig config = client.standardClient.bm_mgmt_get_info(); |
| 166 | + // The BMv2 instance running at this thrift IP and port might have a different BMv2 internal ID. | ||
| 167 | + return config.getDevice_id() == Integer.valueOf(deviceId.uri().getFragment()); | ||
| 168 | + } catch (Bmv2RuntimeException | TException e) { | ||
| 167 | return false; | 169 | return false; |
| 168 | } | 170 | } |
| 169 | } | 171 | } |
| ... | @@ -213,15 +215,15 @@ public class Bmv2ControllerImpl implements Bmv2Controller { | ... | @@ -213,15 +215,15 @@ public class Bmv2ControllerImpl implements Bmv2Controller { |
| 213 | } | 215 | } |
| 214 | 216 | ||
| 215 | /** | 217 | /** |
| 216 | - * Handles Thrift calls from BMv2 devices using registered listeners. | 218 | + * Handles requests from BMv2 devices using the registered listeners. |
| 217 | */ | 219 | */ |
| 218 | - private final class InternalServiceHandler implements ControlPlaneService.Iface { | 220 | + private final class ServiceHandler implements ControlPlaneService.Iface { |
| 219 | 221 | ||
| 220 | - private final TSocket socket; | 222 | + private final InetAddress clientAddress; |
| 221 | private Bmv2Device remoteDevice; | 223 | private Bmv2Device remoteDevice; |
| 222 | 224 | ||
| 223 | - private InternalServiceHandler(TSocket socket) { | 225 | + ServiceHandler(InetAddress clientAddress) { |
| 224 | - this.socket = socket; | 226 | + this.clientAddress = clientAddress; |
| 225 | } | 227 | } |
| 226 | 228 | ||
| 227 | @Override | 229 | @Override |
| ... | @@ -231,20 +233,19 @@ public class Bmv2ControllerImpl implements Bmv2Controller { | ... | @@ -231,20 +233,19 @@ public class Bmv2ControllerImpl implements Bmv2Controller { |
| 231 | 233 | ||
| 232 | @Override | 234 | @Override |
| 233 | public void hello(int thriftServerPort, int deviceId, int instanceId, String jsonConfigMd5) { | 235 | public void hello(int thriftServerPort, int deviceId, int instanceId, String jsonConfigMd5) { |
| 234 | - // Locally note the remote device for future uses. | 236 | + // Store a reference to the remote device for future uses. |
| 235 | - String host = socket.getSocket().getInetAddress().getHostAddress(); | 237 | + String host = clientAddress.getHostAddress(); |
| 236 | remoteDevice = new Bmv2Device(host, thriftServerPort, deviceId); | 238 | remoteDevice = new Bmv2Device(host, thriftServerPort, deviceId); |
| 237 | 239 | ||
| 238 | if (deviceListeners.size() == 0) { | 240 | if (deviceListeners.size() == 0) { |
| 239 | log.debug("Received hello, but there's no listener registered."); | 241 | log.debug("Received hello, but there's no listener registered."); |
| 240 | } else { | 242 | } else { |
| 241 | - deviceListeners.forEach( | 243 | + deviceListeners.forEach(l -> l.handleHello(remoteDevice, instanceId, jsonConfigMd5)); |
| 242 | - l -> executorService.execute(() -> l.handleHello(remoteDevice, instanceId, jsonConfigMd5))); | ||
| 243 | } | 244 | } |
| 244 | } | 245 | } |
| 245 | 246 | ||
| 246 | @Override | 247 | @Override |
| 247 | - public void packet_in(int port, ByteBuffer packet) { | 248 | + public void packet_in(int port, ByteBuffer data, int dataLength) { |
| 248 | if (remoteDevice == null) { | 249 | if (remoteDevice == null) { |
| 249 | log.debug("Received packet-in, but the remote device is still unknown. Need a hello first..."); | 250 | log.debug("Received packet-in, but the remote device is still unknown. Need a hello first..."); |
| 250 | return; | 251 | return; |
| ... | @@ -253,41 +254,42 @@ public class Bmv2ControllerImpl implements Bmv2Controller { | ... | @@ -253,41 +254,42 @@ public class Bmv2ControllerImpl implements Bmv2Controller { |
| 253 | if (packetListeners.size() == 0) { | 254 | if (packetListeners.size() == 0) { |
| 254 | log.debug("Received packet-in, but there's no listener registered."); | 255 | log.debug("Received packet-in, but there's no listener registered."); |
| 255 | } else { | 256 | } else { |
| 256 | - packetListeners.forEach( | 257 | + byte[] bytes = new byte[dataLength]; |
| 257 | - l -> executorService.execute(() -> l.handlePacketIn(remoteDevice, | 258 | + data.get(bytes); |
| 258 | - port, | 259 | + ImmutableByteSequence pkt = ImmutableByteSequence.copyFrom(bytes); |
| 259 | - ImmutableByteSequence.copyFrom(packet)))); | 260 | + packetListeners.forEach(l -> l.handlePacketIn(remoteDevice, port, pkt)); |
| 260 | } | 261 | } |
| 261 | } | 262 | } |
| 262 | } | 263 | } |
| 263 | 264 | ||
| 264 | /** | 265 | /** |
| 265 | - * Thrift Processor decorator. This class is needed in order to have access to the socket when handling a call. | 266 | + * Decorator of a Thrift processor needed in order to keep track of the client's IP address that originated the |
| 266 | - * Socket is needed to get the IP address of the client originating the call (see InternalServiceHandler.hello()) | 267 | + * request. |
| 267 | */ | 268 | */ |
| 268 | - private final class InternalTrackingProcessor implements TProcessor { | 269 | + private final class TrackingProcessor implements TProcessor { |
| 269 | 270 | ||
| 270 | - // Map sockets to processors. | 271 | + // Map transports to processors. |
| 271 | - // TODO: implement it as a cache so unused sockets are expired automatically | 272 | + private final ConcurrentMap<TTransport, Processor<ServiceHandler>> processors = Maps.newConcurrentMap(); |
| 272 | - private final ConcurrentMap<TSocket, ControlPlaneService.Processor<InternalServiceHandler>> processors = | ||
| 273 | - Maps.newConcurrentMap(); | ||
| 274 | 273 | ||
| 275 | @Override | 274 | @Override |
| 276 | public boolean process(final TProtocol in, final TProtocol out) throws TException { | 275 | public boolean process(final TProtocol in, final TProtocol out) throws TException { |
| 277 | - // Get the socket for this request. | 276 | + // Get the client address for this request. |
| 278 | - TSocket socket = (TSocket) in.getTransport(); | 277 | + InetAddress clientAddress = server.getClientAddress((TFramedTransport) in.getTransport()); |
| 279 | - // Get or create a processor for this socket | 278 | + if (clientAddress != null) { |
| 280 | - ControlPlaneService.Processor<InternalServiceHandler> processor = processors.computeIfAbsent(socket, s -> { | 279 | + // Get or create a processor for this input transport, i.e. the client on the other side. |
| 281 | - InternalServiceHandler handler = new InternalServiceHandler(s); | 280 | + Processor<ServiceHandler> processor = processors.computeIfAbsent( |
| 282 | - return new ControlPlaneService.Processor<>(handler); | 281 | + in.getTransport(), t -> new Processor<>(new ServiceHandler(clientAddress))); |
| 283 | - }); | 282 | + // Delegate to the processor we are decorating. |
| 284 | - // Delegate to the processor we are decorating. | 283 | + return processor.process(in, out); |
| 285 | - return processor.process(in, out); | 284 | + } else { |
| 285 | + log.warn("Unable to retrieve client IP address of incoming request"); | ||
| 286 | + return false; | ||
| 287 | + } | ||
| 286 | } | 288 | } |
| 287 | } | 289 | } |
| 288 | 290 | ||
| 289 | /** | 291 | /** |
| 290 | - * Transport/client cache loader. | 292 | + * Cache loader of BMv2 Thrift clients. |
| 291 | */ | 293 | */ |
| 292 | private class ClientLoader extends CacheLoader<DeviceId, Pair<TTransport, Bmv2DeviceThriftClient>> { | 294 | private class ClientLoader extends CacheLoader<DeviceId, Pair<TTransport, Bmv2DeviceThriftClient>> { |
| 293 | 295 | ... | ... |
| ... | @@ -171,6 +171,17 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService { | ... | @@ -171,6 +171,17 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService { |
| 171 | return defaultContext; | 171 | return defaultContext; |
| 172 | } | 172 | } |
| 173 | 173 | ||
| 174 | + @Override | ||
| 175 | + public void setDefaultContext(DeviceId deviceId) { | ||
| 176 | + Versioned<Bmv2DeviceContext> previous = contexts.put(deviceId, defaultContext); | ||
| 177 | + if (mastershipService.getMasterFor(deviceId) == null) { | ||
| 178 | + // Checking for who is the master here is ugly but necessary, as this method is called by Bmv2DeviceProvider | ||
| 179 | + // prior to master election. A solution could be to use a separate leadership contest instead of the | ||
| 180 | + // mastership service. | ||
| 181 | + triggerConfigCheck(deviceId, defaultContext); | ||
| 182 | + } | ||
| 183 | + } | ||
| 184 | + | ||
| 174 | private void configCheck(DeviceId deviceId, Bmv2DeviceContext storedContext) { | 185 | private void configCheck(DeviceId deviceId, Bmv2DeviceContext storedContext) { |
| 175 | if (storedContext == null) { | 186 | if (storedContext == null) { |
| 176 | return; | 187 | return; |
| ... | @@ -206,14 +217,14 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService { | ... | @@ -206,14 +217,14 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService { |
| 206 | } | 217 | } |
| 207 | 218 | ||
| 208 | private void triggerConfigCheck(DeviceId deviceId, Bmv2DeviceContext context) { | 219 | private void triggerConfigCheck(DeviceId deviceId, Bmv2DeviceContext context) { |
| 209 | - if (mastershipService.isLocalMaster(deviceId)) { | ||
| 210 | scheduledExecutor.schedule(() -> configCheck(deviceId, context), 0, TimeUnit.SECONDS); | 220 | scheduledExecutor.schedule(() -> configCheck(deviceId, context), 0, TimeUnit.SECONDS); |
| 211 | - } | ||
| 212 | } | 221 | } |
| 213 | 222 | ||
| 214 | private void checkDevices() { | 223 | private void checkDevices() { |
| 215 | deviceService.getAvailableDevices().forEach(device -> { | 224 | deviceService.getAvailableDevices().forEach(device -> { |
| 216 | - triggerConfigCheck(device.id(), getContext(device.id())); | 225 | + if (mastershipService.isLocalMaster(device.id())) { |
| 226 | + triggerConfigCheck(device.id(), getContext(device.id())); | ||
| 227 | + } | ||
| 217 | }); | 228 | }); |
| 218 | } | 229 | } |
| 219 | 230 | ||
| ... | @@ -235,8 +246,10 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService { | ... | @@ -235,8 +246,10 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService { |
| 235 | public void event(MapEvent<DeviceId, Bmv2DeviceContext> event) { | 246 | public void event(MapEvent<DeviceId, Bmv2DeviceContext> event) { |
| 236 | DeviceId deviceId = event.key(); | 247 | DeviceId deviceId = event.key(); |
| 237 | if (event.type().equals(INSERT) || event.type().equals(UPDATE)) { | 248 | if (event.type().equals(INSERT) || event.type().equals(UPDATE)) { |
| 238 | - log.trace("Context {} for {}", event.type().name(), deviceId); | 249 | + if (mastershipService.isLocalMaster(deviceId)) { |
| 239 | - triggerConfigCheck(deviceId, event.newValue().value()); | 250 | + log.trace("Context {} for {}", event.type().name(), deviceId); |
| 251 | + triggerConfigCheck(deviceId, event.newValue().value()); | ||
| 252 | + } | ||
| 240 | } | 253 | } |
| 241 | } | 254 | } |
| 242 | } | 255 | } | ... | ... |
| ... | @@ -68,7 +68,7 @@ public final class Bmv2DeviceThriftClient implements Bmv2DeviceAgent { | ... | @@ -68,7 +68,7 @@ public final class Bmv2DeviceThriftClient implements Bmv2DeviceAgent { |
| 68 | // See: https://github.com/p4lang/behavioral-model/blob/master/modules/bm_sim/include/bm_sim/context.h | 68 | // See: https://github.com/p4lang/behavioral-model/blob/master/modules/bm_sim/include/bm_sim/context.h |
| 69 | private static final int CONTEXT_ID = 0; | 69 | private static final int CONTEXT_ID = 0; |
| 70 | 70 | ||
| 71 | - private final Standard.Iface standardClient; | 71 | + protected final Standard.Iface standardClient; |
| 72 | private final SimpleSwitch.Iface simpleSwitchClient; | 72 | private final SimpleSwitch.Iface simpleSwitchClient; |
| 73 | private final TTransport transport; | 73 | private final TTransport transport; |
| 74 | private final DeviceId deviceId; | 74 | private final DeviceId deviceId; |
| ... | @@ -418,6 +418,7 @@ public final class Bmv2DeviceThriftClient implements Bmv2DeviceAgent { | ... | @@ -418,6 +418,7 @@ public final class Bmv2DeviceThriftClient implements Bmv2DeviceAgent { |
| 418 | 418 | ||
| 419 | try { | 419 | try { |
| 420 | standardClient.bm_swap_configs(); | 420 | standardClient.bm_swap_configs(); |
| 421 | + simpleSwitchClient.force_swap(); | ||
| 421 | log.debug("JSON config swapped! > deviceId={}", deviceId); | 422 | log.debug("JSON config swapped! > deviceId={}", deviceId); |
| 422 | } catch (TException e) { | 423 | } catch (TException e) { |
| 423 | log.debug("Exception while swapping JSON config: {} > deviceId={}", e, deviceId); | 424 | log.debug("Exception while swapping JSON config: {} > deviceId={}", e, deviceId); | ... | ... |
| ... | @@ -32,9 +32,9 @@ | ... | @@ -32,9 +32,9 @@ |
| 32 | 32 | ||
| 33 | <properties> | 33 | <properties> |
| 34 | <!-- BMv2 Commit ID and Thrift version --> | 34 | <!-- BMv2 Commit ID and Thrift version --> |
| 35 | - <bmv2.commit>024aa03e3b52f8d32c26774511e8e5b1dc11ec65</bmv2.commit> | 35 | + <bmv2.commit>8f675d0284e9e014f1b8ed502ba54e61d68108cf</bmv2.commit> |
| 36 | <bmv2.thrift.version>0.9.3</bmv2.thrift.version> | 36 | <bmv2.thrift.version>0.9.3</bmv2.thrift.version> |
| 37 | - <bmv2.baseurl>https://cdn.rawgit.com/opennetworkinglab/behavioral-model/${bmv2.commit}</bmv2.baseurl> | 37 | + <bmv2.baseurl>https://cdn.rawgit.com/opennetworkinglab/onos-bmv2/${bmv2.commit}</bmv2.baseurl> |
| 38 | <bmv2.thrift.javanamespace>org.onosproject.bmv2.thriftapi</bmv2.thrift.javanamespace> | 38 | <bmv2.thrift.javanamespace>org.onosproject.bmv2.thriftapi</bmv2.thrift.javanamespace> |
| 39 | <bmv2.thrift.srcdir>${project.build.directory}/thrift-sources/${bmv2.commit}/</bmv2.thrift.srcdir> | 39 | <bmv2.thrift.srcdir>${project.build.directory}/thrift-sources/${bmv2.commit}/</bmv2.thrift.srcdir> |
| 40 | <thrift.exedir>${project.build.directory}/thrift-compiler/</thrift.exedir> | 40 | <thrift.exedir>${project.build.directory}/thrift-compiler/</thrift.exedir> | ... | ... |
| ... | @@ -20,10 +20,7 @@ import com.google.common.collect.Maps; | ... | @@ -20,10 +20,7 @@ import com.google.common.collect.Maps; |
| 20 | import org.apache.felix.scr.annotations.Component; | 20 | import org.apache.felix.scr.annotations.Component; |
| 21 | import org.apache.felix.scr.annotations.Reference; | 21 | import org.apache.felix.scr.annotations.Reference; |
| 22 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 22 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
| 23 | -import org.jboss.netty.util.HashedWheelTimer; | 23 | +import org.onlab.util.SharedScheduledExecutors; |
| 24 | -import org.jboss.netty.util.Timeout; | ||
| 25 | -import org.jboss.netty.util.TimerTask; | ||
| 26 | -import org.onlab.util.Timer; | ||
| 27 | import org.onosproject.bmv2.api.runtime.Bmv2Device; | 24 | import org.onosproject.bmv2.api.runtime.Bmv2Device; |
| 28 | import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException; | 25 | import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException; |
| 29 | import org.onosproject.bmv2.api.service.Bmv2Controller; | 26 | import org.onosproject.bmv2.api.service.Bmv2Controller; |
| ... | @@ -34,6 +31,7 @@ import org.onosproject.common.net.AbstractDeviceProvider; | ... | @@ -34,6 +31,7 @@ import org.onosproject.common.net.AbstractDeviceProvider; |
| 34 | import org.onosproject.core.ApplicationId; | 31 | import org.onosproject.core.ApplicationId; |
| 35 | import org.onosproject.core.CoreService; | 32 | import org.onosproject.core.CoreService; |
| 36 | import org.onosproject.incubator.net.config.basics.ConfigException; | 33 | import org.onosproject.incubator.net.config.basics.ConfigException; |
| 34 | +import org.onosproject.mastership.MastershipService; | ||
| 37 | import org.onosproject.net.Device; | 35 | import org.onosproject.net.Device; |
| 38 | import org.onosproject.net.DeviceId; | 36 | import org.onosproject.net.DeviceId; |
| 39 | import org.onosproject.net.MastershipRole; | 37 | import org.onosproject.net.MastershipRole; |
| ... | @@ -55,14 +53,20 @@ import org.slf4j.Logger; | ... | @@ -55,14 +53,20 @@ import org.slf4j.Logger; |
| 55 | 53 | ||
| 56 | import java.util.Collection; | 54 | import java.util.Collection; |
| 57 | import java.util.List; | 55 | import java.util.List; |
| 56 | +import java.util.Map; | ||
| 58 | import java.util.Objects; | 57 | import java.util.Objects; |
| 59 | import java.util.concurrent.ConcurrentMap; | 58 | import java.util.concurrent.ConcurrentMap; |
| 60 | import java.util.concurrent.ExecutorService; | 59 | import java.util.concurrent.ExecutorService; |
| 61 | import java.util.concurrent.Executors; | 60 | import java.util.concurrent.Executors; |
| 62 | -import java.util.concurrent.TimeUnit; | 61 | +import java.util.concurrent.ScheduledExecutorService; |
| 62 | +import java.util.concurrent.ScheduledFuture; | ||
| 63 | +import java.util.concurrent.locks.Lock; | ||
| 64 | +import java.util.concurrent.locks.ReentrantLock; | ||
| 63 | 65 | ||
| 66 | +import static java.util.concurrent.TimeUnit.MILLISECONDS; | ||
| 64 | import static org.onlab.util.Tools.groupedThreads; | 67 | import static org.onlab.util.Tools.groupedThreads; |
| 65 | import static org.onosproject.bmv2.api.runtime.Bmv2Device.*; | 68 | import static org.onosproject.bmv2.api.runtime.Bmv2Device.*; |
| 69 | +import static org.onosproject.net.Device.Type.SWITCH; | ||
| 66 | import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY; | 70 | import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY; |
| 67 | import static org.onosproject.provider.bmv2.device.impl.Bmv2PortStatisticsGetter.getPortStatistics; | 71 | import static org.onosproject.provider.bmv2.device.impl.Bmv2PortStatisticsGetter.getPortStatistics; |
| 68 | import static org.onosproject.provider.bmv2.device.impl.Bmv2PortStatisticsGetter.initCounters; | 72 | import static org.onosproject.provider.bmv2.device.impl.Bmv2PortStatisticsGetter.initCounters; |
| ... | @@ -76,20 +80,22 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { | ... | @@ -76,20 +80,22 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { |
| 76 | 80 | ||
| 77 | private static final String APP_NAME = "org.onosproject.bmv2"; | 81 | private static final String APP_NAME = "org.onosproject.bmv2"; |
| 78 | 82 | ||
| 79 | - private static final int POLL_INTERVAL = 5_000; // milliseconds | 83 | + private static final int POLL_PERIOD = 5_000; // milliseconds |
| 80 | 84 | ||
| 81 | private final Logger log = getLogger(this.getClass()); | 85 | private final Logger log = getLogger(this.getClass()); |
| 82 | 86 | ||
| 83 | private final ExecutorService executorService = Executors | 87 | private final ExecutorService executorService = Executors |
| 84 | .newFixedThreadPool(16, groupedThreads("onos/bmv2", "device-discovery", log)); | 88 | .newFixedThreadPool(16, groupedThreads("onos/bmv2", "device-discovery", log)); |
| 85 | 89 | ||
| 90 | + private final ScheduledExecutorService scheduledExecutorService = SharedScheduledExecutors.getPoolThreadExecutor(); | ||
| 91 | + | ||
| 86 | private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener(); | 92 | private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener(); |
| 87 | 93 | ||
| 88 | private final ConfigFactory cfgFactory = new InternalConfigFactory(); | 94 | private final ConfigFactory cfgFactory = new InternalConfigFactory(); |
| 89 | 95 | ||
| 90 | - private final ConcurrentMap<DeviceId, DeviceDescription> activeDevices = Maps.newConcurrentMap(); | 96 | + private final Map<DeviceId, DeviceDescription> lastDescriptions = Maps.newHashMap(); |
| 91 | 97 | ||
| 92 | - private final DevicePoller devicePoller = new DevicePoller(); | 98 | + private final ConcurrentMap<DeviceId, Lock> deviceLocks = Maps.newConcurrentMap(); |
| 93 | 99 | ||
| 94 | private final InternalDeviceListener deviceListener = new InternalDeviceListener(); | 100 | private final InternalDeviceListener deviceListener = new InternalDeviceListener(); |
| 95 | 101 | ||
| ... | @@ -103,6 +109,9 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { | ... | @@ -103,6 +109,9 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { |
| 103 | protected DeviceService deviceService; | 109 | protected DeviceService deviceService; |
| 104 | 110 | ||
| 105 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 111 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 112 | + protected MastershipService mastershipService; | ||
| 113 | + | ||
| 114 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 106 | protected Bmv2Controller controller; | 115 | protected Bmv2Controller controller; |
| 107 | 116 | ||
| 108 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 117 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| ... | @@ -112,6 +121,7 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { | ... | @@ -112,6 +121,7 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { |
| 112 | protected Bmv2TableEntryService tableEntryService; | 121 | protected Bmv2TableEntryService tableEntryService; |
| 113 | 122 | ||
| 114 | private ApplicationId appId; | 123 | private ApplicationId appId; |
| 124 | + private ScheduledFuture<?> poller; | ||
| 115 | 125 | ||
| 116 | /** | 126 | /** |
| 117 | * Creates a Bmv2 device provider with the supplied identifier. | 127 | * Creates a Bmv2 device provider with the supplied identifier. |
| ... | @@ -126,19 +136,24 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { | ... | @@ -126,19 +136,24 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { |
| 126 | netCfgService.registerConfigFactory(cfgFactory); | 136 | netCfgService.registerConfigFactory(cfgFactory); |
| 127 | netCfgService.addListener(cfgListener); | 137 | netCfgService.addListener(cfgListener); |
| 128 | controller.addDeviceListener(deviceListener); | 138 | controller.addDeviceListener(deviceListener); |
| 129 | - devicePoller.start(); | 139 | + if (poller != null) { |
| 140 | + poller.cancel(false); | ||
| 141 | + } | ||
| 142 | + poller = scheduledExecutorService.scheduleAtFixedRate(this::pollDevices, 1_000, POLL_PERIOD, MILLISECONDS); | ||
| 130 | super.activate(); | 143 | super.activate(); |
| 131 | } | 144 | } |
| 132 | 145 | ||
| 133 | @Override | 146 | @Override |
| 134 | protected void deactivate() { | 147 | protected void deactivate() { |
| 135 | - devicePoller.stop(); | 148 | + if (poller != null) { |
| 149 | + poller.cancel(false); | ||
| 150 | + } | ||
| 136 | controller.removeDeviceListener(deviceListener); | 151 | controller.removeDeviceListener(deviceListener); |
| 137 | try { | 152 | try { |
| 138 | - activeDevices.forEach((did, value) -> { | 153 | + lastDescriptions.forEach((did, value) -> { |
| 139 | executorService.execute(() -> disconnectDevice(did)); | 154 | executorService.execute(() -> disconnectDevice(did)); |
| 140 | }); | 155 | }); |
| 141 | - executorService.awaitTermination(1000, TimeUnit.MILLISECONDS); | 156 | + executorService.awaitTermination(1000, MILLISECONDS); |
| 142 | } catch (InterruptedException e) { | 157 | } catch (InterruptedException e) { |
| 143 | log.error("Device discovery threads did not terminate"); | 158 | log.error("Device discovery threads did not terminate"); |
| 144 | } | 159 | } |
| ... | @@ -181,32 +196,43 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { | ... | @@ -181,32 +196,43 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { |
| 181 | } | 196 | } |
| 182 | 197 | ||
| 183 | private void discoverDevice(DeviceId did) { | 198 | private void discoverDevice(DeviceId did) { |
| 184 | - log.debug("Starting device discovery... deviceId={}", did); | 199 | + // Serialize discovery for the same device. |
| 185 | - activeDevices.compute(did, (k, lastDescription) -> { | 200 | + Lock lock = deviceLocks.computeIfAbsent(did, k -> new ReentrantLock()); |
| 201 | + lock.lock(); | ||
| 202 | + try { | ||
| 203 | + log.debug("Starting device discovery... deviceId={}", did); | ||
| 204 | + | ||
| 205 | + if (contextService.getContext(did) == null) { | ||
| 206 | + // Device is a first timer. | ||
| 207 | + log.info("Setting DEFAULT context for {}", did); | ||
| 208 | + // It is important to do this before creating the device in the core | ||
| 209 | + // so other services won't find a null context. | ||
| 210 | + contextService.setDefaultContext(did); | ||
| 211 | + // Abort discovery, we'll receive a new hello once the swap has been performed. | ||
| 212 | + return; | ||
| 213 | + } | ||
| 214 | + | ||
| 215 | + DeviceDescription lastDescription = lastDescriptions.get(did); | ||
| 186 | DeviceDescription thisDescription = getDeviceDescription(did); | 216 | DeviceDescription thisDescription = getDeviceDescription(did); |
| 217 | + | ||
| 187 | if (thisDescription != null) { | 218 | if (thisDescription != null) { |
| 188 | - boolean descriptionChanged = lastDescription != null && | 219 | + boolean descriptionChanged = lastDescription == null || |
| 189 | - (!Objects.equals(thisDescription, lastDescription) || | 220 | + (!Objects.equals(thisDescription, lastDescription) || |
| 190 | - !Objects.equals(thisDescription.annotations(), lastDescription.annotations())); | 221 | + !Objects.equals(thisDescription.annotations(), lastDescription.annotations())); |
| 191 | if (descriptionChanged || !deviceService.isAvailable(did)) { | 222 | if (descriptionChanged || !deviceService.isAvailable(did)) { |
| 192 | - if (deviceService.getDevice(did) == null) { | ||
| 193 | - // Device is a first timer. | ||
| 194 | - log.info("Setting DEFAULT context for {}", did); | ||
| 195 | - // It is important to do this before connecting the device so other | ||
| 196 | - // services won't find a null context. | ||
| 197 | - contextService.setContext(did, contextService.defaultContext()); | ||
| 198 | - } | ||
| 199 | resetDeviceState(did); | 223 | resetDeviceState(did); |
| 200 | initPortCounters(did); | 224 | initPortCounters(did); |
| 201 | providerService.deviceConnected(did, thisDescription); | 225 | providerService.deviceConnected(did, thisDescription); |
| 202 | updatePortsAndStats(did); | 226 | updatePortsAndStats(did); |
| 203 | } | 227 | } |
| 204 | - return thisDescription; | 228 | + lastDescriptions.put(did, thisDescription); |
| 205 | } else { | 229 | } else { |
| 206 | log.warn("Unable to get device description for {}", did); | 230 | log.warn("Unable to get device description for {}", did); |
| 207 | - return lastDescription; | 231 | + lastDescriptions.put(did, lastDescription); |
| 208 | } | 232 | } |
| 209 | - }); | 233 | + } finally { |
| 234 | + lock.unlock(); | ||
| 235 | + } | ||
| 210 | } | 236 | } |
| 211 | 237 | ||
| 212 | private DeviceDescription getDeviceDescription(DeviceId did) { | 238 | private DeviceDescription getDeviceDescription(DeviceId did) { |
| ... | @@ -269,11 +295,27 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { | ... | @@ -269,11 +295,27 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { |
| 269 | } | 295 | } |
| 270 | 296 | ||
| 271 | private void disconnectDevice(DeviceId did) { | 297 | private void disconnectDevice(DeviceId did) { |
| 272 | - log.debug("Trying to disconnect device from core... deviceId={}", did); | 298 | + log.debug("Disconnecting device from core... deviceId={}", did); |
| 273 | - if (deviceService.isAvailable(did)) { | 299 | + providerService.deviceDisconnected(did); |
| 274 | - providerService.deviceDisconnected(did); | 300 | + lastDescriptions.remove(did); |
| 301 | + } | ||
| 302 | + | ||
| 303 | + private void pollDevices() { | ||
| 304 | + for (Device device: deviceService.getAvailableDevices(SWITCH)) { | ||
| 305 | + if (device.id().uri().getScheme().equals(SCHEME) && | ||
| 306 | + mastershipService.isLocalMaster(device.id())) { | ||
| 307 | + executorService.execute(() -> pollingTask(device.id())); | ||
| 308 | + } | ||
| 309 | + } | ||
| 310 | + } | ||
| 311 | + | ||
| 312 | + private void pollingTask(DeviceId deviceId) { | ||
| 313 | + log.debug("Polling device {}...", deviceId); | ||
| 314 | + if (isReachable(deviceId)) { | ||
| 315 | + updatePortsAndStats(deviceId); | ||
| 316 | + } else { | ||
| 317 | + disconnectDevice(deviceId); | ||
| 275 | } | 318 | } |
| 276 | - activeDevices.remove(did); | ||
| 277 | } | 319 | } |
| 278 | 320 | ||
| 279 | /** | 321 | /** |
| ... | @@ -332,50 +374,4 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { | ... | @@ -332,50 +374,4 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { |
| 332 | triggerProbe(device.asDeviceId()); | 374 | triggerProbe(device.asDeviceId()); |
| 333 | } | 375 | } |
| 334 | } | 376 | } |
| 335 | - | ||
| 336 | - /** | ||
| 337 | - * Task that periodically trigger device probes to check for device status and update port information. | ||
| 338 | - */ | ||
| 339 | - private class DevicePoller implements TimerTask { | ||
| 340 | - | ||
| 341 | - private final HashedWheelTimer timer = Timer.getTimer(); | ||
| 342 | - private Timeout timeout; | ||
| 343 | - | ||
| 344 | - @Override | ||
| 345 | - public void run(Timeout tout) throws Exception { | ||
| 346 | - if (tout.isCancelled()) { | ||
| 347 | - return; | ||
| 348 | - } | ||
| 349 | - activeDevices.keySet() | ||
| 350 | - .stream() | ||
| 351 | - // Filter out devices not yet created in the core. | ||
| 352 | - .filter(did -> deviceService.getDevice(did) != null) | ||
| 353 | - .forEach(did -> executorService.execute(() -> pollingTask(did))); | ||
| 354 | - tout.getTimer().newTimeout(this, POLL_INTERVAL, TimeUnit.MILLISECONDS); | ||
| 355 | - } | ||
| 356 | - | ||
| 357 | - private void pollingTask(DeviceId deviceId) { | ||
| 358 | - if (isReachable(deviceId)) { | ||
| 359 | - updatePortsAndStats(deviceId); | ||
| 360 | - } else { | ||
| 361 | - disconnectDevice(deviceId); | ||
| 362 | - } | ||
| 363 | - } | ||
| 364 | - | ||
| 365 | - /** | ||
| 366 | - * Starts the collector. | ||
| 367 | - */ | ||
| 368 | - synchronized void start() { | ||
| 369 | - log.info("Starting device poller..."); | ||
| 370 | - timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS); | ||
| 371 | - } | ||
| 372 | - | ||
| 373 | - /** | ||
| 374 | - * Stops the collector. | ||
| 375 | - */ | ||
| 376 | - synchronized void stop() { | ||
| 377 | - log.info("Stopping device poller..."); | ||
| 378 | - timeout.cancel(); | ||
| 379 | - } | ||
| 380 | - } | ||
| 381 | } | 377 | } | ... | ... |
-
Please register or login to post a comment