Carmelo Cascone
Committed by Gerrit Code Review

ONOS-4278 Implemented BMv2 control plane server and packet-out support

Change-Id: I4d9027b232dea31d1091c980fb040ec93da9473d
...@@ -34,11 +34,11 @@ ...@@ -34,11 +34,11 @@
34 34
35 <properties> 35 <properties>
36 <!-- BMv2 Commit ID and Thrift version --> 36 <!-- BMv2 Commit ID and Thrift version -->
37 - <bmv2.commit>a012ee4124c1892a91a359660824d311d5d7fe88</bmv2.commit> 37 + <bmv2.commit>4421bafd6d26740b0bbf802c2e9f9f54c1211b13</bmv2.commit>
38 <bmv2.thrift.version>0.9.3</bmv2.thrift.version> 38 <bmv2.thrift.version>0.9.3</bmv2.thrift.version>
39 <!-- Do not change below --> 39 <!-- Do not change below -->
40 <bmv2.baseurl> 40 <bmv2.baseurl>
41 - https://raw.githubusercontent.com/p4lang/behavioral-model/${bmv2.commit} 41 + https://raw.githubusercontent.com/ccascone/behavioral-model/${bmv2.commit}
42 </bmv2.baseurl> 42 </bmv2.baseurl>
43 <bmv2.thrift.srcdir>${project.basedir}/src/main/thrift</bmv2.thrift.srcdir> 43 <bmv2.thrift.srcdir>${project.basedir}/src/main/thrift</bmv2.thrift.srcdir>
44 <thrift.path>${project.build.directory}/thrift-compiler/</thrift.path> 44 <thrift.path>${project.build.directory}/thrift-compiler/</thrift.path>
...@@ -56,6 +56,10 @@ ...@@ -56,6 +56,10 @@
56 <artifactId>onos-api</artifactId> 56 <artifactId>onos-api</artifactId>
57 <version>${project.version}</version> 57 <version>${project.version}</version>
58 </dependency> 58 </dependency>
59 + <dependency>
60 + <groupId>org.apache.felix</groupId>
61 + <artifactId>org.apache.felix.scr.annotations</artifactId>
62 + </dependency>
59 </dependencies> 63 </dependencies>
60 64
61 <repositories> 65 <repositories>
...@@ -84,7 +88,7 @@ ...@@ -84,7 +88,7 @@
84 <executions> 88 <executions>
85 <execution> 89 <execution>
86 <id>download-bmv2-thrift-standard</id> 90 <id>download-bmv2-thrift-standard</id>
87 - <phase>validate</phase> 91 + <phase>initialize</phase>
88 <goals> 92 <goals>
89 <goal>download-single</goal> 93 <goal>download-single</goal>
90 </goals> 94 </goals>
...@@ -133,6 +137,20 @@ ...@@ -133,6 +137,20 @@
133 <toDir>${bmv2.thrift.srcdir}</toDir> 137 <toDir>${bmv2.thrift.srcdir}</toDir>
134 </configuration> 138 </configuration>
135 </execution> 139 </execution>
140 + <execution>
141 + <id>download-bmv2-thrift-simple_switch-cpservice</id>
142 + <phase>initialize</phase>
143 + <goals>
144 + <goal>download-single</goal>
145 + </goals>
146 + <configuration>
147 + <url>${bmv2.baseurl}</url>
148 + <fromFile>
149 + targets/simple_switch/thrift/control_plane.thrift
150 + </fromFile>
151 + <toDir>${bmv2.thrift.srcdir}</toDir>
152 + </configuration>
153 + </execution>
136 </executions> 154 </executions>
137 </plugin> 155 </plugin>
138 <!-- Extract Thrift compiler --> 156 <!-- Extract Thrift compiler -->
...@@ -201,11 +219,12 @@ ...@@ -201,11 +219,12 @@
201 <version>0.1.11</version> 219 <version>0.1.11</version>
202 <configuration> 220 <configuration>
203 <thriftExecutable>${thrift.path}/${thrift.filename}</thriftExecutable> 221 <thriftExecutable>${thrift.path}/${thrift.filename}</thriftExecutable>
222 + <outputDirectory>${project.build.directory}/generated-sources</outputDirectory>
204 </configuration> 223 </configuration>
205 <executions> 224 <executions>
206 <execution> 225 <execution>
207 <id>thrift-sources</id> 226 <id>thrift-sources</id>
208 - <phase>generate-sources</phase> 227 + <phase>initialize</phase>
209 <goals> 228 <goals>
210 <goal>compile</goal> 229 <goal>compile</goal>
211 </goals> 230 </goals>
...@@ -227,13 +246,22 @@ ...@@ -227,13 +246,22 @@
227 <configuration> 246 <configuration>
228 <sources> 247 <sources>
229 <source> 248 <source>
230 - ${project.build.directory}/generated-sources/thrift 249 + ${project.build.directory}/generated-sources
231 </source> 250 </source>
232 </sources> 251 </sources>
233 </configuration> 252 </configuration>
234 </execution> 253 </execution>
235 </executions> 254 </executions>
236 </plugin> 255 </plugin>
256 + <!-- OSGi -->
257 + <plugin>
258 + <groupId>org.apache.felix</groupId>
259 + <artifactId>maven-scr-plugin</artifactId>
260 + </plugin>
261 + <plugin>
262 + <groupId>org.onosproject</groupId>
263 + <artifactId>onos-maven-plugin</artifactId>
264 + </plugin>
237 </plugins> 265 </plugins>
238 </build> 266 </build>
239 267
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
16 16
17 package org.onosproject.bmv2.api.runtime; 17 package org.onosproject.bmv2.api.runtime;
18 18
19 +import org.onlab.util.ImmutableByteSequence;
20 +
19 import java.util.Collection; 21 import java.util.Collection;
20 22
21 /** 23 /**
...@@ -81,6 +83,15 @@ public interface Bmv2Client { ...@@ -81,6 +83,15 @@ public interface Bmv2Client {
81 String dumpTable(String tableName) throws Bmv2RuntimeException; 83 String dumpTable(String tableName) throws Bmv2RuntimeException;
82 84
83 /** 85 /**
86 + * Requests the device to transmit a given byte sequence over the given port.
87 + *
88 + * @param portNumber a port number
89 + * @param packet a byte sequence
90 + * @throws Bmv2RuntimeException
91 + */
92 + void transmitPacket(int portNumber, ImmutableByteSequence packet) throws Bmv2RuntimeException;
93 +
94 + /**
84 * Reset the state of the switch (e.g. delete all entries, etc.). 95 * Reset the state of the switch (e.g. delete all entries, etc.).
85 * 96 *
86 * @throws Bmv2RuntimeException if any error occurs 97 * @throws Bmv2RuntimeException if any error occurs
......
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.api.runtime;
18 +
19 +import org.onlab.util.ImmutableByteSequence;
20 +
21 +/**
22 + * A server that listens for requests from a BMv2 device.
23 + */
24 +public interface Bmv2ControlPlaneServer {
25 + /**
26 + * Default listening port.
27 + */
28 + int DEFAULT_PORT = 40123;
29 +
30 + /**
31 + * Register the given hello listener, to be called each time a hello message is received from a BMv2 device.
32 + *
33 + * @param listener a hello listener
34 + */
35 + void addHelloListener(HelloListener listener);
36 +
37 + /**
38 + * Unregister the given hello listener.
39 + *
40 + * @param listener a hello listener
41 + */
42 + void removeHelloListener(HelloListener listener);
43 +
44 + /**
45 + * Register the given packet listener, to be called each time a packet-in message is received from a BMv2 device.
46 + *
47 + * @param listener a packet listener
48 + */
49 + void addPacketListener(PacketListener listener);
50 +
51 + /**
52 + * Unregister the given packet listener.
53 + *
54 + * @param listener a packet listener
55 + */
56 + void removePacketListener(PacketListener listener);
57 +
58 + interface HelloListener {
59 +
60 + /**
61 + * Handles a hello message.
62 + *
63 + * @param device the BMv2 device that originated the message
64 + */
65 + void handleHello(Bmv2Device device);
66 + }
67 +
68 + interface PacketListener {
69 +
70 + /**
71 + * Handles a packet-in message.
72 + *
73 + * @param device the BMv2 device that originated the message
74 + * @param inputPort the device port where the packet was received
75 + * @param reason a reason code
76 + * @param tableId the table id that originated this packet-in
77 + * @param contextId the context id where the packet-in was originated
78 + * @param packet the packet body
79 + */
80 + void handlePacketIn(Bmv2Device device, int inputPort, long reason, int tableId, int contextId,
81 + ImmutableByteSequence packet);
82 + }
83 +}
...\ No newline at end of file ...\ No newline at end of file
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.api.runtime;
18 +
19 +import com.google.common.base.Objects;
20 +
21 +import static com.google.common.base.Preconditions.checkNotNull;
22 +
23 +/**
24 + * Representation of a BMv2 device.
25 + */
26 +public final class Bmv2Device {
27 +
28 + private final String thriftServerHost;
29 + private final int thriftServerPort;
30 + private final int internalDeviceId;
31 +
32 + /**
33 + * Creates a new Bmv2 device object.
34 + *
35 + * @param thriftServerHost the host of the Thrift runtime server running inside the device
36 + * @param thriftServerPort the port of the Thrift runtime server running inside the device
37 + * @param internalDeviceId the internal device id
38 + */
39 + public Bmv2Device(String thriftServerHost, int thriftServerPort, int internalDeviceId) {
40 + this.thriftServerHost = checkNotNull(thriftServerHost, "host cannot be null");
41 + this.thriftServerPort = checkNotNull(thriftServerPort, "port cannot be null");
42 + this.internalDeviceId = internalDeviceId;
43 + }
44 +
45 + /**
46 + * Returns the hostname (or IP address) of the Thrift runtime server running inside the device.
47 + *
48 + * @return a string value
49 + */
50 + public String thriftServerHost() {
51 + return thriftServerHost;
52 + }
53 +
54 + /**
55 + * Returns the port of the Thrift runtime server running inside the device.
56 + *
57 + * @return an integer value
58 + */
59 + public int thriftServerPort() {
60 + return thriftServerPort;
61 + }
62 +
63 + /**
64 + * Returns the BMv2-internal device ID, which is an integer arbitrary chosen at device boot.
65 + * Such an ID must not be confused with the ONOS-internal {@link org.onosproject.net.DeviceId}.
66 + *
67 + * @return an integer value
68 + */
69 + public int getInternalDeviceId() {
70 + return internalDeviceId;
71 + }
72 +
73 + @Override
74 + public int hashCode() {
75 + return Objects.hashCode(thriftServerHost, thriftServerPort, internalDeviceId);
76 + }
77 +
78 + @Override
79 + public boolean equals(Object obj) {
80 + if (this == obj) {
81 + return true;
82 + }
83 + if (obj == null || getClass() != obj.getClass()) {
84 + return false;
85 + }
86 + final Bmv2Device other = (Bmv2Device) obj;
87 + return Objects.equal(this.thriftServerHost, other.thriftServerHost)
88 + && Objects.equal(this.thriftServerPort, other.thriftServerPort)
89 + && Objects.equal(this.internalDeviceId, other.internalDeviceId);
90 + }
91 +
92 + @Override
93 + public String toString() {
94 + return thriftServerHost + ":" + thriftServerPort + "/" + internalDeviceId;
95 + }
96 +}
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 org.apache.felix.scr.annotations.Activate;
21 +import org.apache.felix.scr.annotations.Component;
22 +import org.apache.felix.scr.annotations.Deactivate;
23 +import org.apache.felix.scr.annotations.Reference;
24 +import org.apache.felix.scr.annotations.ReferenceCardinality;
25 +import org.apache.felix.scr.annotations.Service;
26 +import org.apache.thrift.TException;
27 +import org.apache.thrift.TProcessor;
28 +import org.apache.thrift.protocol.TProtocol;
29 +import org.apache.thrift.server.TThreadPoolServer;
30 +import org.apache.thrift.transport.TServerSocket;
31 +import org.apache.thrift.transport.TServerTransport;
32 +import org.apache.thrift.transport.TSocket;
33 +import org.apache.thrift.transport.TTransportException;
34 +import org.onlab.util.ImmutableByteSequence;
35 +import org.onosproject.bmv2.api.runtime.Bmv2ControlPlaneServer;
36 +import org.onosproject.bmv2.api.runtime.Bmv2Device;
37 +import org.onosproject.core.CoreService;
38 +import org.p4.bmv2.thrift.ControlPlaneService;
39 +import org.slf4j.Logger;
40 +import org.slf4j.LoggerFactory;
41 +
42 +import java.nio.ByteBuffer;
43 +import java.util.Set;
44 +import java.util.concurrent.ConcurrentMap;
45 +import java.util.concurrent.CopyOnWriteArraySet;
46 +import java.util.concurrent.ExecutorService;
47 +import java.util.concurrent.Executors;
48 +import java.util.concurrent.TimeUnit;
49 +
50 +import static org.onlab.util.Tools.groupedThreads;
51 +import static org.p4.bmv2.thrift.ControlPlaneService.Processor;
52 +
53 +@Component(immediate = true)
54 +@Service
55 +public class Bmv2ControlPlaneThriftServer implements Bmv2ControlPlaneServer {
56 +
57 + private static final String APP_ID = "org.onosproject.bmv2";
58 + private static final Logger LOG = LoggerFactory.getLogger(Bmv2ControlPlaneThriftServer.class);
59 +
60 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
61 + protected CoreService coreService;
62 +
63 + private final InternalTrackingProcessor trackingProcessor = new InternalTrackingProcessor();
64 + private final ExecutorService executorService = Executors
65 + .newFixedThreadPool(16, groupedThreads("onos/bmv2", "control-plane-server", LOG));
66 +
67 + private final Set<HelloListener> helloListeners = new CopyOnWriteArraySet<>();
68 + private final Set<PacketListener> packetListeners = new CopyOnWriteArraySet<>();
69 +
70 + private TThreadPoolServer thriftServer;
71 + private int serverPort = DEFAULT_PORT;
72 +
73 + @Activate
74 + public void activate() {
75 + coreService.registerApplication(APP_ID);
76 + try {
77 + TServerTransport transport = new TServerSocket(serverPort);
78 + LOG.info("Starting server on port {}...", serverPort);
79 + this.thriftServer = new TThreadPoolServer(new TThreadPoolServer.Args(transport)
80 + .processor(trackingProcessor)
81 + .executorService(executorService));
82 + executorService.execute(thriftServer::serve);
83 + } catch (TTransportException e) {
84 + LOG.error("Unable to start server", e);
85 + }
86 + LOG.info("Activated");
87 + }
88 +
89 + @Deactivate
90 + public void deactivate() {
91 + // Stop the server if running...
92 + if (thriftServer != null && !thriftServer.isServing()) {
93 + thriftServer.stop();
94 + }
95 + try {
96 + executorService.awaitTermination(1, TimeUnit.SECONDS);
97 + } catch (InterruptedException e) {
98 + LOG.error("Server threads did not terminate");
99 + }
100 + executorService.shutdownNow();
101 + LOG.info("Deactivated");
102 + }
103 +
104 + @Override
105 + public void addHelloListener(HelloListener listener) {
106 + if (!helloListeners.contains(listener)) {
107 + helloListeners.add(listener);
108 + }
109 + }
110 +
111 + @Override
112 + public void removeHelloListener(HelloListener listener) {
113 + helloListeners.remove(listener);
114 + }
115 +
116 + @Override
117 + public void addPacketListener(PacketListener listener) {
118 + if (!packetListeners.contains(listener)) {
119 + packetListeners.add(listener);
120 + }
121 + }
122 +
123 + @Override
124 + public void removePacketListener(PacketListener listener) {
125 + packetListeners.remove(listener);
126 + }
127 +
128 + /**
129 + * Handles service calls using registered listeners.
130 + */
131 + private final class InternalServiceHandler implements ControlPlaneService.Iface {
132 +
133 + private final TSocket socket;
134 + private Bmv2Device remoteDevice;
135 +
136 + private InternalServiceHandler(TSocket socket) {
137 + this.socket = socket;
138 + }
139 +
140 + @Override
141 + public boolean ping() {
142 + return true;
143 + }
144 +
145 + @Override
146 + public void hello(int thriftServerPort, int deviceId) {
147 + // Locally note the remote device for future uses.
148 + String host = socket.getSocket().getInetAddress().getHostAddress();
149 + remoteDevice = new Bmv2Device(host, thriftServerPort, deviceId);
150 +
151 + if (helloListeners.size() == 0) {
152 + LOG.debug("Received hello, but there's no listener registered.");
153 + } else {
154 + helloListeners.forEach(listener -> listener.handleHello(remoteDevice));
155 + }
156 + }
157 +
158 + @Override
159 + public void packetIn(int port, long reason, int tableId, int contextId, ByteBuffer packet) {
160 + if (remoteDevice == null) {
161 + LOG.debug("Received packet-in, but the remote device is still unknown. Need a hello first...");
162 + return;
163 + }
164 +
165 + if (packetListeners.size() == 0) {
166 + LOG.debug("Received packet-in, but there's no listener registered.");
167 + } else {
168 + packetListeners.forEach(listener -> listener.handlePacketIn(remoteDevice,
169 + port,
170 + reason,
171 + tableId,
172 + contextId,
173 + ImmutableByteSequence.copyFrom(packet)));
174 + }
175 + }
176 + }
177 +
178 + /**
179 + * Thrift Processor decorator. This class is needed in order to have access to the socket when handling a call.
180 + * Socket is needed to get the IP address of the client originating the call (see InternalServiceHandler.hello())
181 + */
182 + private final class InternalTrackingProcessor implements TProcessor {
183 +
184 + // Map sockets to processors.
185 + // TODO: implement it as a cache so unused sockets are expired automatically
186 + private final ConcurrentMap<TSocket, Processor<InternalServiceHandler>> processors = Maps.newConcurrentMap();
187 +
188 + @Override
189 + public boolean process(final TProtocol in, final TProtocol out) throws TException {
190 + // Get the socket for this request.
191 + TSocket socket = (TSocket) in.getTransport();
192 + // Get or create a processor for this socket
193 + Processor<InternalServiceHandler> processor = processors.computeIfAbsent(socket, s -> {
194 + InternalServiceHandler handler = new InternalServiceHandler(s);
195 + return new Processor<>(handler);
196 + });
197 + // Delegate to the processor we are decorating.
198 + return processor.process(in, out);
199 + }
200 + }
201 +}
...@@ -31,6 +31,7 @@ import org.apache.thrift.protocol.TProtocol; ...@@ -31,6 +31,7 @@ import org.apache.thrift.protocol.TProtocol;
31 import org.apache.thrift.transport.TSocket; 31 import org.apache.thrift.transport.TSocket;
32 import org.apache.thrift.transport.TTransport; 32 import org.apache.thrift.transport.TTransport;
33 import org.apache.thrift.transport.TTransportException; 33 import org.apache.thrift.transport.TTransportException;
34 +import org.onlab.util.ImmutableByteSequence;
34 import org.onosproject.bmv2.api.runtime.Bmv2Action; 35 import org.onosproject.bmv2.api.runtime.Bmv2Action;
35 import org.onosproject.bmv2.api.runtime.Bmv2Client; 36 import org.onosproject.bmv2.api.runtime.Bmv2Client;
36 import org.onosproject.bmv2.api.runtime.Bmv2ExactMatchParam; 37 import org.onosproject.bmv2.api.runtime.Bmv2ExactMatchParam;
...@@ -50,6 +51,7 @@ import org.p4.bmv2.thrift.BmMatchParamTernary; ...@@ -50,6 +51,7 @@ import org.p4.bmv2.thrift.BmMatchParamTernary;
50 import org.p4.bmv2.thrift.BmMatchParamType; 51 import org.p4.bmv2.thrift.BmMatchParamType;
51 import org.p4.bmv2.thrift.BmMatchParamValid; 52 import org.p4.bmv2.thrift.BmMatchParamValid;
52 import org.p4.bmv2.thrift.DevMgrPortInfo; 53 import org.p4.bmv2.thrift.DevMgrPortInfo;
54 +import org.p4.bmv2.thrift.SimpleSwitch;
53 import org.p4.bmv2.thrift.Standard; 55 import org.p4.bmv2.thrift.Standard;
54 import org.slf4j.Logger; 56 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory; 57 import org.slf4j.LoggerFactory;
...@@ -88,15 +90,18 @@ public final class Bmv2ThriftClient implements Bmv2Client { ...@@ -88,15 +90,18 @@ public final class Bmv2ThriftClient implements Bmv2Client {
88 .expireAfterAccess(CLIENT_CACHE_TIMEOUT, TimeUnit.SECONDS) 90 .expireAfterAccess(CLIENT_CACHE_TIMEOUT, TimeUnit.SECONDS)
89 .removalListener(new ClientRemovalListener()) 91 .removalListener(new ClientRemovalListener())
90 .build(new ClientLoader()); 92 .build(new ClientLoader());
91 - private final Standard.Iface stdClient; 93 + private final Standard.Iface standardClient;
94 + private final SimpleSwitch.Iface simpleSwitchClient;
92 private final TTransport transport; 95 private final TTransport transport;
93 private final DeviceId deviceId; 96 private final DeviceId deviceId;
94 97
95 // ban constructor 98 // ban constructor
96 - private Bmv2ThriftClient(DeviceId deviceId, TTransport transport, Standard.Iface stdClient) { 99 + private Bmv2ThriftClient(DeviceId deviceId, TTransport transport, Standard.Iface standardClient,
100 + SimpleSwitch.Iface simpleSwitchClient) {
97 this.deviceId = deviceId; 101 this.deviceId = deviceId;
98 this.transport = transport; 102 this.transport = transport;
99 - this.stdClient = stdClient; 103 + this.standardClient = standardClient;
104 + this.simpleSwitchClient = simpleSwitchClient;
100 105
101 LOG.debug("New client created! > deviceId={}", deviceId); 106 LOG.debug("New client created! > deviceId={}", deviceId);
102 } 107 }
...@@ -131,9 +136,9 @@ public final class Bmv2ThriftClient implements Bmv2Client { ...@@ -131,9 +136,9 @@ public final class Bmv2ThriftClient implements Bmv2Client {
131 try { 136 try {
132 LOG.debug("Pinging device... > deviceId={}", deviceId); 137 LOG.debug("Pinging device... > deviceId={}", deviceId);
133 Bmv2ThriftClient client = of(deviceId); 138 Bmv2ThriftClient client = of(deviceId);
134 - client.stdClient.bm_dev_mgr_show_ports(); 139 + boolean result = client.simpleSwitchClient.ping();
135 - LOG.debug("Device reachable! > deviceId={}", deviceId); 140 + LOG.debug("Device pinged! > deviceId={}, state={}", deviceId, result);
136 - return true; 141 + return result;
137 } catch (TException | Bmv2RuntimeException e) { 142 } catch (TException | Bmv2RuntimeException e) {
138 LOG.debug("Device NOT reachable! > deviceId={}", deviceId); 143 LOG.debug("Device NOT reachable! > deviceId={}", deviceId);
139 return false; 144 return false;
...@@ -242,7 +247,7 @@ public final class Bmv2ThriftClient implements Bmv2Client { ...@@ -242,7 +247,7 @@ public final class Bmv2ThriftClient implements Bmv2Client {
242 options.setPriority(entry.priority()); 247 options.setPriority(entry.priority());
243 } 248 }
244 249
245 - entryId = stdClient.bm_mt_add_entry( 250 + entryId = standardClient.bm_mt_add_entry(
246 CONTEXT_ID, 251 CONTEXT_ID,
247 entry.tableName(), 252 entry.tableName(),
248 buildMatchParamsList(entry.matchKey()), 253 buildMatchParamsList(entry.matchKey()),
...@@ -253,7 +258,7 @@ public final class Bmv2ThriftClient implements Bmv2Client { ...@@ -253,7 +258,7 @@ public final class Bmv2ThriftClient implements Bmv2Client {
253 if (entry.hasTimeout()) { 258 if (entry.hasTimeout()) {
254 /* bmv2 accepts timeouts in milliseconds */ 259 /* bmv2 accepts timeouts in milliseconds */
255 int msTimeout = (int) Math.round(entry.timeout() * 1_000); 260 int msTimeout = (int) Math.round(entry.timeout() * 1_000);
256 - stdClient.bm_mt_set_entry_ttl( 261 + standardClient.bm_mt_set_entry_ttl(
257 CONTEXT_ID, entry.tableName(), entryId, msTimeout); 262 CONTEXT_ID, entry.tableName(), entryId, msTimeout);
258 } 263 }
259 264
...@@ -285,7 +290,7 @@ public final class Bmv2ThriftClient implements Bmv2Client { ...@@ -285,7 +290,7 @@ public final class Bmv2ThriftClient implements Bmv2Client {
285 LOG.debug("Modifying table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId); 290 LOG.debug("Modifying table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
286 291
287 try { 292 try {
288 - stdClient.bm_mt_modify_entry( 293 + standardClient.bm_mt_modify_entry(
289 CONTEXT_ID, 294 CONTEXT_ID,
290 tableName, 295 tableName,
291 entryId, 296 entryId,
...@@ -306,7 +311,7 @@ public final class Bmv2ThriftClient implements Bmv2Client { ...@@ -306,7 +311,7 @@ public final class Bmv2ThriftClient implements Bmv2Client {
306 LOG.debug("Deleting table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId); 311 LOG.debug("Deleting table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
307 312
308 try { 313 try {
309 - stdClient.bm_mt_delete_entry(CONTEXT_ID, tableName, entryId); 314 + standardClient.bm_mt_delete_entry(CONTEXT_ID, tableName, entryId);
310 LOG.debug("Table entry deleted! > deviceId={}, entryId={}/{}", deviceId, tableName, entryId); 315 LOG.debug("Table entry deleted! > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
311 } catch (TException e) { 316 } catch (TException e) {
312 LOG.debug("Exception while deleting table entry: {} > deviceId={}, entryId={}/{}", 317 LOG.debug("Exception while deleting table entry: {} > deviceId={}, entryId={}/{}",
...@@ -322,7 +327,7 @@ public final class Bmv2ThriftClient implements Bmv2Client { ...@@ -322,7 +327,7 @@ public final class Bmv2ThriftClient implements Bmv2Client {
322 LOG.debug("Setting table default... > deviceId={}, tableName={}, action={}", deviceId, tableName, action); 327 LOG.debug("Setting table default... > deviceId={}, tableName={}, action={}", deviceId, tableName, action);
323 328
324 try { 329 try {
325 - stdClient.bm_mt_set_default_action( 330 + standardClient.bm_mt_set_default_action(
326 CONTEXT_ID, 331 CONTEXT_ID,
327 tableName, 332 tableName,
328 action.name(), 333 action.name(),
...@@ -341,7 +346,7 @@ public final class Bmv2ThriftClient implements Bmv2Client { ...@@ -341,7 +346,7 @@ public final class Bmv2ThriftClient implements Bmv2Client {
341 LOG.debug("Retrieving port info... > deviceId={}", deviceId); 346 LOG.debug("Retrieving port info... > deviceId={}", deviceId);
342 347
343 try { 348 try {
344 - List<DevMgrPortInfo> portInfos = stdClient.bm_dev_mgr_show_ports(); 349 + List<DevMgrPortInfo> portInfos = standardClient.bm_dev_mgr_show_ports();
345 350
346 Collection<Bmv2PortInfo> bmv2PortInfos = Lists.newArrayList(); 351 Collection<Bmv2PortInfo> bmv2PortInfos = Lists.newArrayList();
347 352
...@@ -366,7 +371,7 @@ public final class Bmv2ThriftClient implements Bmv2Client { ...@@ -366,7 +371,7 @@ public final class Bmv2ThriftClient implements Bmv2Client {
366 LOG.debug("Retrieving table dump... > deviceId={}, tableName={}", deviceId, tableName); 371 LOG.debug("Retrieving table dump... > deviceId={}, tableName={}", deviceId, tableName);
367 372
368 try { 373 try {
369 - String dump = stdClient.bm_dump_table(CONTEXT_ID, tableName); 374 + String dump = standardClient.bm_dump_table(CONTEXT_ID, tableName);
370 LOG.debug("Table dump retrieved! > deviceId={}, tableName={}", deviceId, tableName); 375 LOG.debug("Table dump retrieved! > deviceId={}, tableName={}", deviceId, tableName);
371 return dump; 376 return dump;
372 } catch (TException e) { 377 } catch (TException e) {
...@@ -377,12 +382,28 @@ public final class Bmv2ThriftClient implements Bmv2Client { ...@@ -377,12 +382,28 @@ public final class Bmv2ThriftClient implements Bmv2Client {
377 } 382 }
378 383
379 @Override 384 @Override
385 + public void transmitPacket(int portNumber, ImmutableByteSequence packet) throws Bmv2RuntimeException {
386 +
387 + LOG.debug("Requesting packet transmission... > portNumber={}, packet={}", portNumber, packet);
388 +
389 + try {
390 +
391 + simpleSwitchClient.push_packet(portNumber, ByteBuffer.wrap(packet.asArray()));
392 + LOG.debug("Packet transmission requested! > portNumber={}, packet={}", portNumber, packet);
393 + } catch (TException e) {
394 + LOG.debug("Exception while requesting packet transmission: {} > portNumber={}, packet={}",
395 + portNumber, packet);
396 + throw new Bmv2RuntimeException(e.getMessage(), e);
397 + }
398 + }
399 +
400 + @Override
380 public void resetState() throws Bmv2RuntimeException { 401 public void resetState() throws Bmv2RuntimeException {
381 402
382 LOG.debug("Resetting device state... > deviceId={}", deviceId); 403 LOG.debug("Resetting device state... > deviceId={}", deviceId);
383 404
384 try { 405 try {
385 - stdClient.bm_reset_state(); 406 + standardClient.bm_reset_state();
386 LOG.debug("Device state reset! > deviceId={}", deviceId); 407 LOG.debug("Device state reset! > deviceId={}", deviceId);
387 } catch (TException e) { 408 } catch (TException e) {
388 LOG.debug("Exception while resetting device state: {} > deviceId={}", e, deviceId); 409 LOG.debug("Exception while resetting device state: {} > deviceId={}", e, deviceId);
...@@ -396,7 +417,6 @@ public final class Bmv2ThriftClient implements Bmv2Client { ...@@ -396,7 +417,6 @@ public final class Bmv2ThriftClient implements Bmv2Client {
396 private static class ClientLoader 417 private static class ClientLoader
397 extends CacheLoader<DeviceId, Bmv2ThriftClient> { 418 extends CacheLoader<DeviceId, Bmv2ThriftClient> {
398 419
399 - // Connection retries options: max 10 retries each 200 ms
400 private static final Options RECONN_OPTIONS = new Options(NUM_CONNECTION_RETRIES, TIME_BETWEEN_RETRIES); 420 private static final Options RECONN_OPTIONS = new Options(NUM_CONNECTION_RETRIES, TIME_BETWEEN_RETRIES);
401 421
402 @Override 422 @Override
...@@ -408,14 +428,20 @@ public final class Bmv2ThriftClient implements Bmv2Client { ...@@ -408,14 +428,20 @@ public final class Bmv2ThriftClient implements Bmv2Client {
408 TTransport transport = new TSocket( 428 TTransport transport = new TSocket(
409 info.getLeft(), info.getRight()); 429 info.getLeft(), info.getRight());
410 TProtocol protocol = new TBinaryProtocol(transport); 430 TProtocol protocol = new TBinaryProtocol(transport);
411 - Standard.Client stdClient = new Standard.Client( 431 + // Our BMv2 device implements multiple Thrift services, create a client for each one.
432 + Standard.Client standardClient = new Standard.Client(
412 new TMultiplexedProtocol(protocol, "standard")); 433 new TMultiplexedProtocol(protocol, "standard"));
413 - // Wrap the client so to automatically have synchronization and resiliency to connectivity problems 434 + SimpleSwitch.Client simpleSwitch = new SimpleSwitch.Client(
414 - Standard.Iface reconnStdIface = SafeThriftClient.wrap(stdClient, 435 + new TMultiplexedProtocol(protocol, "simple_switch"));
415 - Standard.Iface.class, 436 + // Wrap clients so to automatically have synchronization and resiliency to connectivity errors
416 - RECONN_OPTIONS); 437 + Standard.Iface safeStandardClient = SafeThriftClient.wrap(standardClient,
417 - 438 + Standard.Iface.class,
418 - return new Bmv2ThriftClient(deviceId, transport, reconnStdIface); 439 + RECONN_OPTIONS);
440 + SimpleSwitch.Iface safeSimpleSwitchClient = SafeThriftClient.wrap(simpleSwitch,
441 + SimpleSwitch.Iface.class,
442 + RECONN_OPTIONS);
443 +
444 + return new Bmv2ThriftClient(deviceId, transport, safeStandardClient, safeSimpleSwitchClient);
419 } 445 }
420 } 446 }
421 447
......