blob: db0d5e2c4d3e3479d2153b24b15d78b7a6a8dea3 [file] [log] [blame]
Carmelo Cascone2ea177b2016-02-25 18:38:42 -08001/*
2 * Copyright 2014-2016 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
17package org.onosproject.bmv2.ctl;
18
19import com.google.common.cache.CacheBuilder;
20import com.google.common.cache.CacheLoader;
21import com.google.common.cache.LoadingCache;
22import com.google.common.cache.RemovalListener;
23import com.google.common.cache.RemovalNotification;
24import com.google.common.collect.Lists;
25import org.apache.commons.lang3.tuple.ImmutablePair;
26import org.apache.commons.lang3.tuple.Pair;
27import org.apache.thrift.TException;
28import org.apache.thrift.protocol.TBinaryProtocol;
29import org.apache.thrift.protocol.TMultiplexedProtocol;
30import org.apache.thrift.protocol.TProtocol;
31import org.apache.thrift.transport.TSocket;
32import org.apache.thrift.transport.TTransport;
33import org.apache.thrift.transport.TTransportException;
Carmelo Casconee4da9092016-04-26 12:14:08 -070034import org.onlab.util.ImmutableByteSequence;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070035import org.onosproject.bmv2.api.runtime.Bmv2Action;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070036import org.onosproject.bmv2.api.runtime.Bmv2Client;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070037import org.onosproject.bmv2.api.runtime.Bmv2ExactMatchParam;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070038import org.onosproject.bmv2.api.runtime.Bmv2LpmMatchParam;
39import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
40import org.onosproject.bmv2.api.runtime.Bmv2PortInfo;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070041import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070042import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
43import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam;
44import org.onosproject.bmv2.api.runtime.Bmv2ValidMatchParam;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080045import org.onosproject.net.DeviceId;
46import org.p4.bmv2.thrift.BmAddEntryOptions;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070047import org.p4.bmv2.thrift.BmMatchParam;
48import org.p4.bmv2.thrift.BmMatchParamExact;
49import org.p4.bmv2.thrift.BmMatchParamLPM;
50import org.p4.bmv2.thrift.BmMatchParamTernary;
51import org.p4.bmv2.thrift.BmMatchParamType;
52import org.p4.bmv2.thrift.BmMatchParamValid;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080053import org.p4.bmv2.thrift.DevMgrPortInfo;
Carmelo Casconee4da9092016-04-26 12:14:08 -070054import org.p4.bmv2.thrift.SimpleSwitch;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080055import org.p4.bmv2.thrift.Standard;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070056import org.slf4j.Logger;
57import org.slf4j.LoggerFactory;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080058
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070059import java.nio.ByteBuffer;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080060import java.util.Collection;
61import java.util.List;
62import java.util.concurrent.ExecutionException;
63import java.util.concurrent.TimeUnit;
64import java.util.stream.Collectors;
65
66import static com.google.common.base.Preconditions.checkNotNull;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070067import static org.onosproject.bmv2.ctl.SafeThriftClient.Options;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080068
69/**
70 * Implementation of a Thrift client to control the Bmv2 switch.
71 */
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070072public final class Bmv2ThriftClient implements Bmv2Client {
73
74 private static final Logger LOG =
75 LoggerFactory.getLogger(Bmv2ThriftClient.class);
76
77 // FIXME: make context_id arbitrary for each call
78 // See: https://github.com/p4lang/behavioral-model/blob/master/modules/bm_sim/include/bm_sim/context.h
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080079 private static final int CONTEXT_ID = 0;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070080 // Seconds after a client is expired (and connection closed) in the cache.
81 private static final int CLIENT_CACHE_TIMEOUT = 60;
82 // Number of connection retries after a network error.
83 private static final int NUM_CONNECTION_RETRIES = 10;
84 // Time between retries in milliseconds.
85 private static final int TIME_BETWEEN_RETRIES = 200;
86
87 // Static client cache where clients are removed after a predefined timeout.
88 private static final LoadingCache<DeviceId, Bmv2ThriftClient>
89 CLIENT_CACHE = CacheBuilder.newBuilder()
90 .expireAfterAccess(CLIENT_CACHE_TIMEOUT, TimeUnit.SECONDS)
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080091 .removalListener(new ClientRemovalListener())
92 .build(new ClientLoader());
Carmelo Casconee4da9092016-04-26 12:14:08 -070093 private final Standard.Iface standardClient;
94 private final SimpleSwitch.Iface simpleSwitchClient;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070095 private final TTransport transport;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070096 private final DeviceId deviceId;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080097
98 // ban constructor
Carmelo Casconee4da9092016-04-26 12:14:08 -070099 private Bmv2ThriftClient(DeviceId deviceId, TTransport transport, Standard.Iface standardClient,
100 SimpleSwitch.Iface simpleSwitchClient) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700101 this.deviceId = deviceId;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700102 this.transport = transport;
Carmelo Casconee4da9092016-04-26 12:14:08 -0700103 this.standardClient = standardClient;
104 this.simpleSwitchClient = simpleSwitchClient;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800105
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700106 LOG.debug("New client created! > deviceId={}", deviceId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700107 }
108
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800109 /**
110 * Returns a client object to control the passed device.
111 *
112 * @param deviceId device id
113 * @return bmv2 client object
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700114 * @throws Bmv2RuntimeException if a connection to the device cannot be established
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800115 */
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700116 public static Bmv2ThriftClient of(DeviceId deviceId) throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800117 try {
118 checkNotNull(deviceId, "deviceId cannot be null");
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700119 LOG.debug("Getting a client from cache... > deviceId{}", deviceId);
120 return CLIENT_CACHE.get(deviceId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800121 } catch (ExecutionException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700122 LOG.debug("Exception while getting a client from cache: {} > ", e, deviceId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700123 throw new Bmv2RuntimeException(e.getMessage(), e.getCause());
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800124 }
125 }
126
127 /**
128 * Pings the device. Returns true if the device is reachable,
129 * false otherwise.
130 *
131 * @param deviceId device id
132 * @return true if reachable, false otherwise
133 */
134 public static boolean ping(DeviceId deviceId) {
135 // poll ports status as workaround to assess device reachability
136 try {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700137 LOG.debug("Pinging device... > deviceId={}", deviceId);
138 Bmv2ThriftClient client = of(deviceId);
Carmelo Casconee4da9092016-04-26 12:14:08 -0700139 boolean result = client.simpleSwitchClient.ping();
140 LOG.debug("Device pinged! > deviceId={}, state={}", deviceId, result);
141 return result;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700142 } catch (TException | Bmv2RuntimeException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700143 LOG.debug("Device NOT reachable! > deviceId={}", deviceId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800144 return false;
145 }
146 }
147
148 /**
149 * Parse device ID into host and port.
150 *
151 * @param did device ID
152 * @return a pair of host and port
153 */
154 private static Pair<String, Integer> parseDeviceId(DeviceId did) {
155 String[] info = did.toString().split(":");
156 if (info.length == 3) {
157 String host = info[1];
158 int port = Integer.parseInt(info[2]);
159 return ImmutablePair.of(host, port);
160 } else {
161 throw new IllegalArgumentException(
162 "Unable to parse BMv2 device ID "
163 + did.toString()
164 + ", expected format is scheme:host:port");
165 }
166 }
167
168 /**
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700169 * Builds a list of Bmv2/Thrift compatible match parameters.
170 *
171 * @param matchKey a bmv2 matchKey
172 * @return list of thrift-compatible bm match parameters
173 */
174 private static List<BmMatchParam> buildMatchParamsList(Bmv2MatchKey matchKey) {
175 List<BmMatchParam> paramsList = Lists.newArrayList();
176 matchKey.matchParams().forEach(x -> {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700177 ByteBuffer value;
178 ByteBuffer mask;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700179 switch (x.type()) {
180 case EXACT:
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700181 value = ByteBuffer.wrap(((Bmv2ExactMatchParam) x).value().asArray());
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700182 paramsList.add(
183 new BmMatchParam(BmMatchParamType.EXACT)
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700184 .setExact(new BmMatchParamExact(value)));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700185 break;
186 case TERNARY:
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700187 value = ByteBuffer.wrap(((Bmv2TernaryMatchParam) x).value().asArray());
188 mask = ByteBuffer.wrap(((Bmv2TernaryMatchParam) x).mask().asArray());
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700189 paramsList.add(
190 new BmMatchParam(BmMatchParamType.TERNARY)
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700191 .setTernary(new BmMatchParamTernary(value, mask)));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700192 break;
193 case LPM:
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700194 value = ByteBuffer.wrap(((Bmv2LpmMatchParam) x).value().asArray());
195 int prefixLength = ((Bmv2LpmMatchParam) x).prefixLength();
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700196 paramsList.add(
197 new BmMatchParam(BmMatchParamType.LPM)
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700198 .setLpm(new BmMatchParamLPM(value, prefixLength)));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700199 break;
200 case VALID:
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700201 boolean flag = ((Bmv2ValidMatchParam) x).flag();
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700202 paramsList.add(
203 new BmMatchParam(BmMatchParamType.VALID)
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700204 .setValid(new BmMatchParamValid(flag)));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700205 break;
206 default:
207 // should never be here
208 throw new RuntimeException("Unknown match param type " + x.type().name());
209 }
210 });
211 return paramsList;
212 }
213
214 /**
215 * Build a list of Bmv2/Thrift compatible action parameters.
216 *
217 * @param action an action object
218 * @return list of ByteBuffers
219 */
220 private static List<ByteBuffer> buildActionParamsList(Bmv2Action action) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700221 List<ByteBuffer> buffers = Lists.newArrayList();
222 action.parameters().forEach(p -> buffers.add(ByteBuffer.wrap(p.asArray())));
223 return buffers;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700224 }
225
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700226 private void closeTransport() {
227 LOG.debug("Closing transport session... > deviceId={}", deviceId);
228 if (this.transport.isOpen()) {
229 this.transport.close();
230 LOG.debug("Transport session closed! > deviceId={}", deviceId);
231 } else {
232 LOG.debug("Transport session was already closed! deviceId={}", deviceId);
233 }
234 }
235
236 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700237 public final long addTableEntry(Bmv2TableEntry entry) throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800238
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700239 LOG.debug("Adding table entry... > deviceId={}, entry={}", deviceId, entry);
240
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800241 long entryId = -1;
242
243 try {
244 BmAddEntryOptions options = new BmAddEntryOptions();
245
246 if (entry.hasPriority()) {
247 options.setPriority(entry.priority());
248 }
249
Carmelo Casconee4da9092016-04-26 12:14:08 -0700250 entryId = standardClient.bm_mt_add_entry(
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800251 CONTEXT_ID,
252 entry.tableName(),
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700253 buildMatchParamsList(entry.matchKey()),
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800254 entry.action().name(),
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700255 buildActionParamsList(entry.action()),
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800256 options);
257
258 if (entry.hasTimeout()) {
259 /* bmv2 accepts timeouts in milliseconds */
260 int msTimeout = (int) Math.round(entry.timeout() * 1_000);
Carmelo Casconee4da9092016-04-26 12:14:08 -0700261 standardClient.bm_mt_set_entry_ttl(
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800262 CONTEXT_ID, entry.tableName(), entryId, msTimeout);
263 }
264
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700265 LOG.debug("Table entry added! > deviceId={}, entryId={}/{}", deviceId, entry.tableName(), entryId);
266
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800267 return entryId;
268
269 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700270 LOG.debug("Exception while adding table entry: {} > deviceId={}, tableName={}",
271 e, deviceId, entry.tableName());
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800272 if (entryId != -1) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700273 // entry is in inconsistent state (unable to add timeout), remove it
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800274 try {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700275 deleteTableEntry(entry.tableName(), entryId);
276 } catch (Bmv2RuntimeException e1) {
277 LOG.debug("Unable to remove failed table entry: {} > deviceId={}, tableName={}",
278 e1, deviceId, entry.tableName());
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800279 }
280 }
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700281 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800282 }
283 }
284
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700285 @Override
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800286 public final void modifyTableEntry(String tableName,
287 long entryId, Bmv2Action action)
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700288 throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800289
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700290 LOG.debug("Modifying table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
291
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800292 try {
Carmelo Casconee4da9092016-04-26 12:14:08 -0700293 standardClient.bm_mt_modify_entry(
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800294 CONTEXT_ID,
295 tableName,
296 entryId,
297 action.name(),
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700298 buildActionParamsList(action));
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700299 LOG.debug("Table entry modified! > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800300 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700301 LOG.debug("Exception while modifying table entry: {} > deviceId={}, entryId={}/{}",
302 e, deviceId, tableName, entryId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700303 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800304 }
305 }
306
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700307 @Override
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800308 public final void deleteTableEntry(String tableName,
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700309 long entryId) throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800310
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700311 LOG.debug("Deleting table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
312
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800313 try {
Carmelo Casconee4da9092016-04-26 12:14:08 -0700314 standardClient.bm_mt_delete_entry(CONTEXT_ID, tableName, entryId);
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700315 LOG.debug("Table entry deleted! > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800316 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700317 LOG.debug("Exception while deleting table entry: {} > deviceId={}, entryId={}/{}",
318 e, deviceId, tableName, entryId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700319 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800320 }
321 }
322
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700323 @Override
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800324 public final void setTableDefaultAction(String tableName, Bmv2Action action)
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700325 throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800326
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700327 LOG.debug("Setting table default... > deviceId={}, tableName={}, action={}", deviceId, tableName, action);
328
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800329 try {
Carmelo Casconee4da9092016-04-26 12:14:08 -0700330 standardClient.bm_mt_set_default_action(
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800331 CONTEXT_ID,
332 tableName,
333 action.name(),
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700334 buildActionParamsList(action));
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700335 LOG.debug("Table default set! > deviceId={}, tableName={}, action={}", deviceId, tableName, action);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800336 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700337 LOG.debug("Exception while setting table default : {} > deviceId={}, tableName={}, action={}",
338 e, deviceId, tableName, action);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700339 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800340 }
341 }
342
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700343 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700344 public Collection<Bmv2PortInfo> getPortsInfo() throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800345
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700346 LOG.debug("Retrieving port info... > deviceId={}", deviceId);
347
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800348 try {
Carmelo Casconee4da9092016-04-26 12:14:08 -0700349 List<DevMgrPortInfo> portInfos = standardClient.bm_dev_mgr_show_ports();
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800350
351 Collection<Bmv2PortInfo> bmv2PortInfos = Lists.newArrayList();
352
353 bmv2PortInfos.addAll(
354 portInfos.stream()
355 .map(Bmv2PortInfo::new)
356 .collect(Collectors.toList()));
357
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700358 LOG.debug("Port info retrieved! > deviceId={}, portInfos={}", deviceId, bmv2PortInfos);
359
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800360 return bmv2PortInfos;
361
362 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700363 LOG.debug("Exception while retrieving port info: {} > deviceId={}", e, deviceId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700364 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800365 }
366 }
367
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700368 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700369 public String dumpTable(String tableName) throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800370
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700371 LOG.debug("Retrieving table dump... > deviceId={}, tableName={}", deviceId, tableName);
372
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800373 try {
Carmelo Casconee4da9092016-04-26 12:14:08 -0700374 String dump = standardClient.bm_dump_table(CONTEXT_ID, tableName);
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700375 LOG.debug("Table dump retrieved! > deviceId={}, tableName={}", deviceId, tableName);
376 return dump;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800377 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700378 LOG.debug("Exception while retrieving table dump: {} > deviceId={}, tableName={}",
379 e, deviceId, tableName);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700380 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800381 }
382 }
383
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700384 @Override
Carmelo Casconee4da9092016-04-26 12:14:08 -0700385 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
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700401 public void resetState() throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800402
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700403 LOG.debug("Resetting device state... > deviceId={}", deviceId);
404
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800405 try {
Carmelo Casconee4da9092016-04-26 12:14:08 -0700406 standardClient.bm_reset_state();
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700407 LOG.debug("Device state reset! > deviceId={}", deviceId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800408 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700409 LOG.debug("Exception while resetting device state: {} > deviceId={}", e, deviceId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700410 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800411 }
412 }
413
414 /**
415 * Transport/client cache loader.
416 */
417 private static class ClientLoader
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700418 extends CacheLoader<DeviceId, Bmv2ThriftClient> {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800419
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700420 private static final Options RECONN_OPTIONS = new Options(NUM_CONNECTION_RETRIES, TIME_BETWEEN_RETRIES);
421
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800422 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700423 public Bmv2ThriftClient load(DeviceId deviceId)
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800424 throws TTransportException {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700425 LOG.debug("Creating new client in cache... > deviceId={}", deviceId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800426 Pair<String, Integer> info = parseDeviceId(deviceId);
427 //make the expensive call
428 TTransport transport = new TSocket(
429 info.getLeft(), info.getRight());
430 TProtocol protocol = new TBinaryProtocol(transport);
Carmelo Casconee4da9092016-04-26 12:14:08 -0700431 // Our BMv2 device implements multiple Thrift services, create a client for each one.
432 Standard.Client standardClient = new Standard.Client(
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800433 new TMultiplexedProtocol(protocol, "standard"));
Carmelo Casconee4da9092016-04-26 12:14:08 -0700434 SimpleSwitch.Client simpleSwitch = new SimpleSwitch.Client(
435 new TMultiplexedProtocol(protocol, "simple_switch"));
436 // Wrap clients so to automatically have synchronization and resiliency to connectivity errors
437 Standard.Iface safeStandardClient = SafeThriftClient.wrap(standardClient,
438 Standard.Iface.class,
439 RECONN_OPTIONS);
440 SimpleSwitch.Iface safeSimpleSwitchClient = SafeThriftClient.wrap(simpleSwitch,
441 SimpleSwitch.Iface.class,
442 RECONN_OPTIONS);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800443
Carmelo Casconee4da9092016-04-26 12:14:08 -0700444 return new Bmv2ThriftClient(deviceId, transport, safeStandardClient, safeSimpleSwitchClient);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800445 }
446 }
447
448 /**
449 * Client cache removal listener. Close the connection on cache removal.
450 */
451 private static class ClientRemovalListener implements
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700452 RemovalListener<DeviceId, Bmv2ThriftClient> {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800453
454 @Override
455 public void onRemoval(
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700456 RemovalNotification<DeviceId, Bmv2ThriftClient> notification) {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800457 // close the transport connection
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700458 LOG.debug("Removing client from cache... > deviceId={}", notification.getKey());
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700459 notification.getValue().closeTransport();
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800460 }
461 }
462}