ONOS-4278 Implemented BMv2 control plane server and packet-out support
Change-Id: I4d9027b232dea31d1091c980fb040ec93da9473d
diff --git a/protocols/bmv2/pom.xml b/protocols/bmv2/pom.xml
index 7176aef..1a67c76 100644
--- a/protocols/bmv2/pom.xml
+++ b/protocols/bmv2/pom.xml
@@ -34,11 +34,11 @@
<properties>
<!-- BMv2 Commit ID and Thrift version -->
- <bmv2.commit>a012ee4124c1892a91a359660824d311d5d7fe88</bmv2.commit>
+ <bmv2.commit>4421bafd6d26740b0bbf802c2e9f9f54c1211b13</bmv2.commit>
<bmv2.thrift.version>0.9.3</bmv2.thrift.version>
<!-- Do not change below -->
<bmv2.baseurl>
- https://raw.githubusercontent.com/p4lang/behavioral-model/${bmv2.commit}
+ https://raw.githubusercontent.com/ccascone/behavioral-model/${bmv2.commit}
</bmv2.baseurl>
<bmv2.thrift.srcdir>${project.basedir}/src/main/thrift</bmv2.thrift.srcdir>
<thrift.path>${project.build.directory}/thrift-compiler/</thrift.path>
@@ -56,6 +56,10 @@
<artifactId>onos-api</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ </dependency>
</dependencies>
<repositories>
@@ -84,7 +88,7 @@
<executions>
<execution>
<id>download-bmv2-thrift-standard</id>
- <phase>validate</phase>
+ <phase>initialize</phase>
<goals>
<goal>download-single</goal>
</goals>
@@ -133,6 +137,20 @@
<toDir>${bmv2.thrift.srcdir}</toDir>
</configuration>
</execution>
+ <execution>
+ <id>download-bmv2-thrift-simple_switch-cpservice</id>
+ <phase>initialize</phase>
+ <goals>
+ <goal>download-single</goal>
+ </goals>
+ <configuration>
+ <url>${bmv2.baseurl}</url>
+ <fromFile>
+ targets/simple_switch/thrift/control_plane.thrift
+ </fromFile>
+ <toDir>${bmv2.thrift.srcdir}</toDir>
+ </configuration>
+ </execution>
</executions>
</plugin>
<!-- Extract Thrift compiler -->
@@ -201,11 +219,12 @@
<version>0.1.11</version>
<configuration>
<thriftExecutable>${thrift.path}/${thrift.filename}</thriftExecutable>
+ <outputDirectory>${project.build.directory}/generated-sources</outputDirectory>
</configuration>
<executions>
<execution>
<id>thrift-sources</id>
- <phase>generate-sources</phase>
+ <phase>initialize</phase>
<goals>
<goal>compile</goal>
</goals>
@@ -227,13 +246,22 @@
<configuration>
<sources>
<source>
- ${project.build.directory}/generated-sources/thrift
+ ${project.build.directory}/generated-sources
</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
+ <!-- OSGi -->
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-maven-plugin</artifactId>
+ </plugin>
</plugins>
</build>
diff --git a/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Client.java b/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Client.java
index 252f59b..80d9c10 100644
--- a/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Client.java
+++ b/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Client.java
@@ -16,6 +16,8 @@
package org.onosproject.bmv2.api.runtime;
+import org.onlab.util.ImmutableByteSequence;
+
import java.util.Collection;
/**
@@ -81,6 +83,15 @@
String dumpTable(String tableName) throws Bmv2RuntimeException;
/**
+ * Requests the device to transmit a given byte sequence over the given port.
+ *
+ * @param portNumber a port number
+ * @param packet a byte sequence
+ * @throws Bmv2RuntimeException
+ */
+ void transmitPacket(int portNumber, ImmutableByteSequence packet) throws Bmv2RuntimeException;
+
+ /**
* Reset the state of the switch (e.g. delete all entries, etc.).
*
* @throws Bmv2RuntimeException if any error occurs
diff --git a/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ControlPlaneServer.java b/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ControlPlaneServer.java
new file mode 100644
index 0000000..672529e
--- /dev/null
+++ b/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2ControlPlaneServer.java
@@ -0,0 +1,83 @@
+/*
+ * 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.bmv2.api.runtime;
+
+import org.onlab.util.ImmutableByteSequence;
+
+/**
+ * A server that listens for requests from a BMv2 device.
+ */
+public interface Bmv2ControlPlaneServer {
+ /**
+ * Default listening port.
+ */
+ int DEFAULT_PORT = 40123;
+
+ /**
+ * Register the given hello listener, to be called each time a hello message is received from a BMv2 device.
+ *
+ * @param listener a hello listener
+ */
+ void addHelloListener(HelloListener listener);
+
+ /**
+ * Unregister the given hello listener.
+ *
+ * @param listener a hello listener
+ */
+ void removeHelloListener(HelloListener listener);
+
+ /**
+ * Register the given packet listener, to be called each time a packet-in message is received from a BMv2 device.
+ *
+ * @param listener a packet listener
+ */
+ void addPacketListener(PacketListener listener);
+
+ /**
+ * Unregister the given packet listener.
+ *
+ * @param listener a packet listener
+ */
+ void removePacketListener(PacketListener listener);
+
+ interface HelloListener {
+
+ /**
+ * Handles a hello message.
+ *
+ * @param device the BMv2 device that originated the message
+ */
+ void handleHello(Bmv2Device device);
+ }
+
+ interface PacketListener {
+
+ /**
+ * Handles a packet-in message.
+ *
+ * @param device the BMv2 device that originated the message
+ * @param inputPort the device port where the packet was received
+ * @param reason a reason code
+ * @param tableId the table id that originated this packet-in
+ * @param contextId the context id where the packet-in was originated
+ * @param packet the packet body
+ */
+ void handlePacketIn(Bmv2Device device, int inputPort, long reason, int tableId, int contextId,
+ ImmutableByteSequence packet);
+ }
+}
\ No newline at end of file
diff --git a/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Device.java b/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Device.java
new file mode 100644
index 0000000..446a514
--- /dev/null
+++ b/protocols/bmv2/src/main/java/org/onosproject/bmv2/api/runtime/Bmv2Device.java
@@ -0,0 +1,96 @@
+/*
+ * 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.bmv2.api.runtime;
+
+import com.google.common.base.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Representation of a BMv2 device.
+ */
+public final class Bmv2Device {
+
+ private final String thriftServerHost;
+ private final int thriftServerPort;
+ private final int internalDeviceId;
+
+ /**
+ * Creates a new Bmv2 device object.
+ *
+ * @param thriftServerHost the host of the Thrift runtime server running inside the device
+ * @param thriftServerPort the port of the Thrift runtime server running inside the device
+ * @param internalDeviceId the internal device id
+ */
+ public Bmv2Device(String thriftServerHost, int thriftServerPort, int internalDeviceId) {
+ this.thriftServerHost = checkNotNull(thriftServerHost, "host cannot be null");
+ this.thriftServerPort = checkNotNull(thriftServerPort, "port cannot be null");
+ this.internalDeviceId = internalDeviceId;
+ }
+
+ /**
+ * Returns the hostname (or IP address) of the Thrift runtime server running inside the device.
+ *
+ * @return a string value
+ */
+ public String thriftServerHost() {
+ return thriftServerHost;
+ }
+
+ /**
+ * Returns the port of the Thrift runtime server running inside the device.
+ *
+ * @return an integer value
+ */
+ public int thriftServerPort() {
+ return thriftServerPort;
+ }
+
+ /**
+ * Returns the BMv2-internal device ID, which is an integer arbitrary chosen at device boot.
+ * Such an ID must not be confused with the ONOS-internal {@link org.onosproject.net.DeviceId}.
+ *
+ * @return an integer value
+ */
+ public int getInternalDeviceId() {
+ return internalDeviceId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(thriftServerHost, thriftServerPort, internalDeviceId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final Bmv2Device other = (Bmv2Device) obj;
+ return Objects.equal(this.thriftServerHost, other.thriftServerHost)
+ && Objects.equal(this.thriftServerPort, other.thriftServerPort)
+ && Objects.equal(this.internalDeviceId, other.internalDeviceId);
+ }
+
+ @Override
+ public String toString() {
+ return thriftServerHost + ":" + thriftServerPort + "/" + internalDeviceId;
+ }
+}
diff --git a/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2ControlPlaneThriftServer.java b/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2ControlPlaneThriftServer.java
new file mode 100644
index 0000000..80ce0c2
--- /dev/null
+++ b/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2ControlPlaneThriftServer.java
@@ -0,0 +1,201 @@
+/*
+ * 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.bmv2.ctl;
+
+import com.google.common.collect.Maps;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.thrift.TException;
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.server.TThreadPoolServer;
+import org.apache.thrift.transport.TServerSocket;
+import org.apache.thrift.transport.TServerTransport;
+import org.apache.thrift.transport.TSocket;
+import org.apache.thrift.transport.TTransportException;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.bmv2.api.runtime.Bmv2ControlPlaneServer;
+import org.onosproject.bmv2.api.runtime.Bmv2Device;
+import org.onosproject.core.CoreService;
+import org.p4.bmv2.thrift.ControlPlaneService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import static org.onlab.util.Tools.groupedThreads;
+import static org.p4.bmv2.thrift.ControlPlaneService.Processor;
+
+@Component(immediate = true)
+@Service
+public class Bmv2ControlPlaneThriftServer implements Bmv2ControlPlaneServer {
+
+ private static final String APP_ID = "org.onosproject.bmv2";
+ private static final Logger LOG = LoggerFactory.getLogger(Bmv2ControlPlaneThriftServer.class);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ private final InternalTrackingProcessor trackingProcessor = new InternalTrackingProcessor();
+ private final ExecutorService executorService = Executors
+ .newFixedThreadPool(16, groupedThreads("onos/bmv2", "control-plane-server", LOG));
+
+ private final Set<HelloListener> helloListeners = new CopyOnWriteArraySet<>();
+ private final Set<PacketListener> packetListeners = new CopyOnWriteArraySet<>();
+
+ private TThreadPoolServer thriftServer;
+ private int serverPort = DEFAULT_PORT;
+
+ @Activate
+ public void activate() {
+ coreService.registerApplication(APP_ID);
+ try {
+ TServerTransport transport = new TServerSocket(serverPort);
+ LOG.info("Starting server on port {}...", serverPort);
+ this.thriftServer = new TThreadPoolServer(new TThreadPoolServer.Args(transport)
+ .processor(trackingProcessor)
+ .executorService(executorService));
+ executorService.execute(thriftServer::serve);
+ } catch (TTransportException e) {
+ LOG.error("Unable to start server", e);
+ }
+ LOG.info("Activated");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ // Stop the server if running...
+ if (thriftServer != null && !thriftServer.isServing()) {
+ thriftServer.stop();
+ }
+ try {
+ executorService.awaitTermination(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ LOG.error("Server threads did not terminate");
+ }
+ executorService.shutdownNow();
+ LOG.info("Deactivated");
+ }
+
+ @Override
+ public void addHelloListener(HelloListener listener) {
+ if (!helloListeners.contains(listener)) {
+ helloListeners.add(listener);
+ }
+ }
+
+ @Override
+ public void removeHelloListener(HelloListener listener) {
+ helloListeners.remove(listener);
+ }
+
+ @Override
+ public void addPacketListener(PacketListener listener) {
+ if (!packetListeners.contains(listener)) {
+ packetListeners.add(listener);
+ }
+ }
+
+ @Override
+ public void removePacketListener(PacketListener listener) {
+ packetListeners.remove(listener);
+ }
+
+ /**
+ * Handles service calls using registered listeners.
+ */
+ private final class InternalServiceHandler implements ControlPlaneService.Iface {
+
+ private final TSocket socket;
+ private Bmv2Device remoteDevice;
+
+ private InternalServiceHandler(TSocket socket) {
+ this.socket = socket;
+ }
+
+ @Override
+ public boolean ping() {
+ return true;
+ }
+
+ @Override
+ public void hello(int thriftServerPort, int deviceId) {
+ // Locally note the remote device for future uses.
+ String host = socket.getSocket().getInetAddress().getHostAddress();
+ remoteDevice = new Bmv2Device(host, thriftServerPort, deviceId);
+
+ if (helloListeners.size() == 0) {
+ LOG.debug("Received hello, but there's no listener registered.");
+ } else {
+ helloListeners.forEach(listener -> listener.handleHello(remoteDevice));
+ }
+ }
+
+ @Override
+ public void packetIn(int port, long reason, int tableId, int contextId, ByteBuffer packet) {
+ if (remoteDevice == null) {
+ LOG.debug("Received packet-in, but the remote device is still unknown. Need a hello first...");
+ return;
+ }
+
+ if (packetListeners.size() == 0) {
+ LOG.debug("Received packet-in, but there's no listener registered.");
+ } else {
+ packetListeners.forEach(listener -> listener.handlePacketIn(remoteDevice,
+ port,
+ reason,
+ tableId,
+ contextId,
+ ImmutableByteSequence.copyFrom(packet)));
+ }
+ }
+ }
+
+ /**
+ * Thrift Processor decorator. This class is needed in order to have access to the socket when handling a call.
+ * Socket is needed to get the IP address of the client originating the call (see InternalServiceHandler.hello())
+ */
+ private final class InternalTrackingProcessor implements TProcessor {
+
+ // Map sockets to processors.
+ // TODO: implement it as a cache so unused sockets are expired automatically
+ private final ConcurrentMap<TSocket, Processor<InternalServiceHandler>> processors = Maps.newConcurrentMap();
+
+ @Override
+ public boolean process(final TProtocol in, final TProtocol out) throws TException {
+ // Get the socket for this request.
+ TSocket socket = (TSocket) in.getTransport();
+ // Get or create a processor for this socket
+ Processor<InternalServiceHandler> processor = processors.computeIfAbsent(socket, s -> {
+ InternalServiceHandler handler = new InternalServiceHandler(s);
+ return new Processor<>(handler);
+ });
+ // Delegate to the processor we are decorating.
+ return processor.process(in, out);
+ }
+ }
+}
diff --git a/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2ThriftClient.java b/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2ThriftClient.java
index f1a86fc..db0d5e2 100644
--- a/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2ThriftClient.java
+++ b/protocols/bmv2/src/main/java/org/onosproject/bmv2/ctl/Bmv2ThriftClient.java
@@ -31,6 +31,7 @@
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
+import org.onlab.util.ImmutableByteSequence;
import org.onosproject.bmv2.api.runtime.Bmv2Action;
import org.onosproject.bmv2.api.runtime.Bmv2Client;
import org.onosproject.bmv2.api.runtime.Bmv2ExactMatchParam;
@@ -50,6 +51,7 @@
import org.p4.bmv2.thrift.BmMatchParamType;
import org.p4.bmv2.thrift.BmMatchParamValid;
import org.p4.bmv2.thrift.DevMgrPortInfo;
+import org.p4.bmv2.thrift.SimpleSwitch;
import org.p4.bmv2.thrift.Standard;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -88,15 +90,18 @@
.expireAfterAccess(CLIENT_CACHE_TIMEOUT, TimeUnit.SECONDS)
.removalListener(new ClientRemovalListener())
.build(new ClientLoader());
- private final Standard.Iface stdClient;
+ private final Standard.Iface standardClient;
+ private final SimpleSwitch.Iface simpleSwitchClient;
private final TTransport transport;
private final DeviceId deviceId;
// ban constructor
- private Bmv2ThriftClient(DeviceId deviceId, TTransport transport, Standard.Iface stdClient) {
+ private Bmv2ThriftClient(DeviceId deviceId, TTransport transport, Standard.Iface standardClient,
+ SimpleSwitch.Iface simpleSwitchClient) {
this.deviceId = deviceId;
this.transport = transport;
- this.stdClient = stdClient;
+ this.standardClient = standardClient;
+ this.simpleSwitchClient = simpleSwitchClient;
LOG.debug("New client created! > deviceId={}", deviceId);
}
@@ -131,9 +136,9 @@
try {
LOG.debug("Pinging device... > deviceId={}", deviceId);
Bmv2ThriftClient client = of(deviceId);
- client.stdClient.bm_dev_mgr_show_ports();
- LOG.debug("Device reachable! > deviceId={}", deviceId);
- return true;
+ boolean result = client.simpleSwitchClient.ping();
+ LOG.debug("Device pinged! > deviceId={}, state={}", deviceId, result);
+ return result;
} catch (TException | Bmv2RuntimeException e) {
LOG.debug("Device NOT reachable! > deviceId={}", deviceId);
return false;
@@ -242,7 +247,7 @@
options.setPriority(entry.priority());
}
- entryId = stdClient.bm_mt_add_entry(
+ entryId = standardClient.bm_mt_add_entry(
CONTEXT_ID,
entry.tableName(),
buildMatchParamsList(entry.matchKey()),
@@ -253,7 +258,7 @@
if (entry.hasTimeout()) {
/* bmv2 accepts timeouts in milliseconds */
int msTimeout = (int) Math.round(entry.timeout() * 1_000);
- stdClient.bm_mt_set_entry_ttl(
+ standardClient.bm_mt_set_entry_ttl(
CONTEXT_ID, entry.tableName(), entryId, msTimeout);
}
@@ -285,7 +290,7 @@
LOG.debug("Modifying table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
try {
- stdClient.bm_mt_modify_entry(
+ standardClient.bm_mt_modify_entry(
CONTEXT_ID,
tableName,
entryId,
@@ -306,7 +311,7 @@
LOG.debug("Deleting table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
try {
- stdClient.bm_mt_delete_entry(CONTEXT_ID, tableName, entryId);
+ standardClient.bm_mt_delete_entry(CONTEXT_ID, tableName, entryId);
LOG.debug("Table entry deleted! > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
} catch (TException e) {
LOG.debug("Exception while deleting table entry: {} > deviceId={}, entryId={}/{}",
@@ -322,7 +327,7 @@
LOG.debug("Setting table default... > deviceId={}, tableName={}, action={}", deviceId, tableName, action);
try {
- stdClient.bm_mt_set_default_action(
+ standardClient.bm_mt_set_default_action(
CONTEXT_ID,
tableName,
action.name(),
@@ -341,7 +346,7 @@
LOG.debug("Retrieving port info... > deviceId={}", deviceId);
try {
- List<DevMgrPortInfo> portInfos = stdClient.bm_dev_mgr_show_ports();
+ List<DevMgrPortInfo> portInfos = standardClient.bm_dev_mgr_show_ports();
Collection<Bmv2PortInfo> bmv2PortInfos = Lists.newArrayList();
@@ -366,7 +371,7 @@
LOG.debug("Retrieving table dump... > deviceId={}, tableName={}", deviceId, tableName);
try {
- String dump = stdClient.bm_dump_table(CONTEXT_ID, tableName);
+ String dump = standardClient.bm_dump_table(CONTEXT_ID, tableName);
LOG.debug("Table dump retrieved! > deviceId={}, tableName={}", deviceId, tableName);
return dump;
} catch (TException e) {
@@ -377,12 +382,28 @@
}
@Override
+ public void transmitPacket(int portNumber, ImmutableByteSequence packet) throws Bmv2RuntimeException {
+
+ LOG.debug("Requesting packet transmission... > portNumber={}, packet={}", portNumber, packet);
+
+ try {
+
+ simpleSwitchClient.push_packet(portNumber, ByteBuffer.wrap(packet.asArray()));
+ LOG.debug("Packet transmission requested! > portNumber={}, packet={}", portNumber, packet);
+ } catch (TException e) {
+ LOG.debug("Exception while requesting packet transmission: {} > portNumber={}, packet={}",
+ portNumber, packet);
+ throw new Bmv2RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ @Override
public void resetState() throws Bmv2RuntimeException {
LOG.debug("Resetting device state... > deviceId={}", deviceId);
try {
- stdClient.bm_reset_state();
+ standardClient.bm_reset_state();
LOG.debug("Device state reset! > deviceId={}", deviceId);
} catch (TException e) {
LOG.debug("Exception while resetting device state: {} > deviceId={}", e, deviceId);
@@ -396,7 +417,6 @@
private static class ClientLoader
extends CacheLoader<DeviceId, Bmv2ThriftClient> {
- // Connection retries options: max 10 retries each 200 ms
private static final Options RECONN_OPTIONS = new Options(NUM_CONNECTION_RETRIES, TIME_BETWEEN_RETRIES);
@Override
@@ -408,14 +428,20 @@
TTransport transport = new TSocket(
info.getLeft(), info.getRight());
TProtocol protocol = new TBinaryProtocol(transport);
- Standard.Client stdClient = new Standard.Client(
+ // Our BMv2 device implements multiple Thrift services, create a client for each one.
+ Standard.Client standardClient = new Standard.Client(
new TMultiplexedProtocol(protocol, "standard"));
- // Wrap the client so to automatically have synchronization and resiliency to connectivity problems
- Standard.Iface reconnStdIface = SafeThriftClient.wrap(stdClient,
- Standard.Iface.class,
- RECONN_OPTIONS);
+ SimpleSwitch.Client simpleSwitch = new SimpleSwitch.Client(
+ new TMultiplexedProtocol(protocol, "simple_switch"));
+ // Wrap clients so to automatically have synchronization and resiliency to connectivity errors
+ Standard.Iface safeStandardClient = SafeThriftClient.wrap(standardClient,
+ Standard.Iface.class,
+ RECONN_OPTIONS);
+ SimpleSwitch.Iface safeSimpleSwitchClient = SafeThriftClient.wrap(simpleSwitch,
+ SimpleSwitch.Iface.class,
+ RECONN_OPTIONS);
- return new Bmv2ThriftClient(deviceId, transport, reconnStdIface);
+ return new Bmv2ThriftClient(deviceId, transport, safeStandardClient, safeSimpleSwitchClient);
}
}