blob: f1a86fcda0fbf83b1e1d9631d9345e6a482e3488 [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 Casconeaa8b6292016-04-13 14:27:06 -070034import org.onosproject.bmv2.api.runtime.Bmv2Action;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070035import org.onosproject.bmv2.api.runtime.Bmv2Client;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070036import org.onosproject.bmv2.api.runtime.Bmv2ExactMatchParam;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070037import org.onosproject.bmv2.api.runtime.Bmv2LpmMatchParam;
38import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
39import org.onosproject.bmv2.api.runtime.Bmv2PortInfo;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070040import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070041import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
42import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam;
43import org.onosproject.bmv2.api.runtime.Bmv2ValidMatchParam;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080044import org.onosproject.net.DeviceId;
45import org.p4.bmv2.thrift.BmAddEntryOptions;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070046import org.p4.bmv2.thrift.BmMatchParam;
47import org.p4.bmv2.thrift.BmMatchParamExact;
48import org.p4.bmv2.thrift.BmMatchParamLPM;
49import org.p4.bmv2.thrift.BmMatchParamTernary;
50import org.p4.bmv2.thrift.BmMatchParamType;
51import org.p4.bmv2.thrift.BmMatchParamValid;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080052import org.p4.bmv2.thrift.DevMgrPortInfo;
53import org.p4.bmv2.thrift.Standard;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070054import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080056
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070057import java.nio.ByteBuffer;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080058import java.util.Collection;
59import java.util.List;
60import java.util.concurrent.ExecutionException;
61import java.util.concurrent.TimeUnit;
62import java.util.stream.Collectors;
63
64import static com.google.common.base.Preconditions.checkNotNull;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070065import static org.onosproject.bmv2.ctl.SafeThriftClient.Options;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080066
67/**
68 * Implementation of a Thrift client to control the Bmv2 switch.
69 */
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070070public final class Bmv2ThriftClient implements Bmv2Client {
71
72 private static final Logger LOG =
73 LoggerFactory.getLogger(Bmv2ThriftClient.class);
74
75 // FIXME: make context_id arbitrary for each call
76 // See: https://github.com/p4lang/behavioral-model/blob/master/modules/bm_sim/include/bm_sim/context.h
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080077 private static final int CONTEXT_ID = 0;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070078 // Seconds after a client is expired (and connection closed) in the cache.
79 private static final int CLIENT_CACHE_TIMEOUT = 60;
80 // Number of connection retries after a network error.
81 private static final int NUM_CONNECTION_RETRIES = 10;
82 // Time between retries in milliseconds.
83 private static final int TIME_BETWEEN_RETRIES = 200;
84
85 // Static client cache where clients are removed after a predefined timeout.
86 private static final LoadingCache<DeviceId, Bmv2ThriftClient>
87 CLIENT_CACHE = CacheBuilder.newBuilder()
88 .expireAfterAccess(CLIENT_CACHE_TIMEOUT, TimeUnit.SECONDS)
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080089 .removalListener(new ClientRemovalListener())
90 .build(new ClientLoader());
91 private final Standard.Iface stdClient;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070092 private final TTransport transport;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070093 private final DeviceId deviceId;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080094
95 // ban constructor
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070096 private Bmv2ThriftClient(DeviceId deviceId, TTransport transport, Standard.Iface stdClient) {
97 this.deviceId = deviceId;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070098 this.transport = transport;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -080099 this.stdClient = stdClient;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800100
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700101 LOG.debug("New client created! > deviceId={}", deviceId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700102 }
103
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800104 /**
105 * Returns a client object to control the passed device.
106 *
107 * @param deviceId device id
108 * @return bmv2 client object
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700109 * @throws Bmv2RuntimeException if a connection to the device cannot be established
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800110 */
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700111 public static Bmv2ThriftClient of(DeviceId deviceId) throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800112 try {
113 checkNotNull(deviceId, "deviceId cannot be null");
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700114 LOG.debug("Getting a client from cache... > deviceId{}", deviceId);
115 return CLIENT_CACHE.get(deviceId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800116 } catch (ExecutionException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700117 LOG.debug("Exception while getting a client from cache: {} > ", e, deviceId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700118 throw new Bmv2RuntimeException(e.getMessage(), e.getCause());
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800119 }
120 }
121
122 /**
123 * Pings the device. Returns true if the device is reachable,
124 * false otherwise.
125 *
126 * @param deviceId device id
127 * @return true if reachable, false otherwise
128 */
129 public static boolean ping(DeviceId deviceId) {
130 // poll ports status as workaround to assess device reachability
131 try {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700132 LOG.debug("Pinging device... > deviceId={}", deviceId);
133 Bmv2ThriftClient client = of(deviceId);
134 client.stdClient.bm_dev_mgr_show_ports();
135 LOG.debug("Device reachable! > deviceId={}", deviceId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800136 return true;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700137 } catch (TException | Bmv2RuntimeException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700138 LOG.debug("Device NOT reachable! > deviceId={}", deviceId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800139 return false;
140 }
141 }
142
143 /**
144 * Parse device ID into host and port.
145 *
146 * @param did device ID
147 * @return a pair of host and port
148 */
149 private static Pair<String, Integer> parseDeviceId(DeviceId did) {
150 String[] info = did.toString().split(":");
151 if (info.length == 3) {
152 String host = info[1];
153 int port = Integer.parseInt(info[2]);
154 return ImmutablePair.of(host, port);
155 } else {
156 throw new IllegalArgumentException(
157 "Unable to parse BMv2 device ID "
158 + did.toString()
159 + ", expected format is scheme:host:port");
160 }
161 }
162
163 /**
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700164 * Builds a list of Bmv2/Thrift compatible match parameters.
165 *
166 * @param matchKey a bmv2 matchKey
167 * @return list of thrift-compatible bm match parameters
168 */
169 private static List<BmMatchParam> buildMatchParamsList(Bmv2MatchKey matchKey) {
170 List<BmMatchParam> paramsList = Lists.newArrayList();
171 matchKey.matchParams().forEach(x -> {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700172 ByteBuffer value;
173 ByteBuffer mask;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700174 switch (x.type()) {
175 case EXACT:
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700176 value = ByteBuffer.wrap(((Bmv2ExactMatchParam) x).value().asArray());
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700177 paramsList.add(
178 new BmMatchParam(BmMatchParamType.EXACT)
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700179 .setExact(new BmMatchParamExact(value)));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700180 break;
181 case TERNARY:
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700182 value = ByteBuffer.wrap(((Bmv2TernaryMatchParam) x).value().asArray());
183 mask = ByteBuffer.wrap(((Bmv2TernaryMatchParam) x).mask().asArray());
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700184 paramsList.add(
185 new BmMatchParam(BmMatchParamType.TERNARY)
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700186 .setTernary(new BmMatchParamTernary(value, mask)));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700187 break;
188 case LPM:
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700189 value = ByteBuffer.wrap(((Bmv2LpmMatchParam) x).value().asArray());
190 int prefixLength = ((Bmv2LpmMatchParam) x).prefixLength();
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700191 paramsList.add(
192 new BmMatchParam(BmMatchParamType.LPM)
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700193 .setLpm(new BmMatchParamLPM(value, prefixLength)));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700194 break;
195 case VALID:
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700196 boolean flag = ((Bmv2ValidMatchParam) x).flag();
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700197 paramsList.add(
198 new BmMatchParam(BmMatchParamType.VALID)
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700199 .setValid(new BmMatchParamValid(flag)));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700200 break;
201 default:
202 // should never be here
203 throw new RuntimeException("Unknown match param type " + x.type().name());
204 }
205 });
206 return paramsList;
207 }
208
209 /**
210 * Build a list of Bmv2/Thrift compatible action parameters.
211 *
212 * @param action an action object
213 * @return list of ByteBuffers
214 */
215 private static List<ByteBuffer> buildActionParamsList(Bmv2Action action) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700216 List<ByteBuffer> buffers = Lists.newArrayList();
217 action.parameters().forEach(p -> buffers.add(ByteBuffer.wrap(p.asArray())));
218 return buffers;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700219 }
220
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700221 private void closeTransport() {
222 LOG.debug("Closing transport session... > deviceId={}", deviceId);
223 if (this.transport.isOpen()) {
224 this.transport.close();
225 LOG.debug("Transport session closed! > deviceId={}", deviceId);
226 } else {
227 LOG.debug("Transport session was already closed! deviceId={}", deviceId);
228 }
229 }
230
231 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700232 public final long addTableEntry(Bmv2TableEntry entry) throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800233
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700234 LOG.debug("Adding table entry... > deviceId={}, entry={}", deviceId, entry);
235
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800236 long entryId = -1;
237
238 try {
239 BmAddEntryOptions options = new BmAddEntryOptions();
240
241 if (entry.hasPriority()) {
242 options.setPriority(entry.priority());
243 }
244
245 entryId = stdClient.bm_mt_add_entry(
246 CONTEXT_ID,
247 entry.tableName(),
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700248 buildMatchParamsList(entry.matchKey()),
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800249 entry.action().name(),
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700250 buildActionParamsList(entry.action()),
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800251 options);
252
253 if (entry.hasTimeout()) {
254 /* bmv2 accepts timeouts in milliseconds */
255 int msTimeout = (int) Math.round(entry.timeout() * 1_000);
256 stdClient.bm_mt_set_entry_ttl(
257 CONTEXT_ID, entry.tableName(), entryId, msTimeout);
258 }
259
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700260 LOG.debug("Table entry added! > deviceId={}, entryId={}/{}", deviceId, entry.tableName(), entryId);
261
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800262 return entryId;
263
264 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700265 LOG.debug("Exception while adding table entry: {} > deviceId={}, tableName={}",
266 e, deviceId, entry.tableName());
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800267 if (entryId != -1) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700268 // entry is in inconsistent state (unable to add timeout), remove it
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800269 try {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700270 deleteTableEntry(entry.tableName(), entryId);
271 } catch (Bmv2RuntimeException e1) {
272 LOG.debug("Unable to remove failed table entry: {} > deviceId={}, tableName={}",
273 e1, deviceId, entry.tableName());
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800274 }
275 }
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700276 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800277 }
278 }
279
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700280 @Override
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800281 public final void modifyTableEntry(String tableName,
282 long entryId, Bmv2Action action)
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700283 throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800284
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700285 LOG.debug("Modifying table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
286
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800287 try {
288 stdClient.bm_mt_modify_entry(
289 CONTEXT_ID,
290 tableName,
291 entryId,
292 action.name(),
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700293 buildActionParamsList(action));
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700294 LOG.debug("Table entry modified! > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800295 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700296 LOG.debug("Exception while modifying table entry: {} > deviceId={}, entryId={}/{}",
297 e, deviceId, tableName, entryId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700298 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800299 }
300 }
301
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700302 @Override
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800303 public final void deleteTableEntry(String tableName,
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700304 long entryId) throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800305
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700306 LOG.debug("Deleting table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
307
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800308 try {
309 stdClient.bm_mt_delete_entry(CONTEXT_ID, tableName, entryId);
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700310 LOG.debug("Table entry deleted! > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800311 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700312 LOG.debug("Exception while deleting table entry: {} > deviceId={}, entryId={}/{}",
313 e, deviceId, tableName, entryId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700314 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800315 }
316 }
317
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700318 @Override
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800319 public final void setTableDefaultAction(String tableName, Bmv2Action action)
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700320 throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800321
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700322 LOG.debug("Setting table default... > deviceId={}, tableName={}, action={}", deviceId, tableName, action);
323
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800324 try {
325 stdClient.bm_mt_set_default_action(
326 CONTEXT_ID,
327 tableName,
328 action.name(),
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700329 buildActionParamsList(action));
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700330 LOG.debug("Table default set! > deviceId={}, tableName={}, action={}", deviceId, tableName, action);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800331 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700332 LOG.debug("Exception while setting table default : {} > deviceId={}, tableName={}, action={}",
333 e, deviceId, tableName, action);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700334 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800335 }
336 }
337
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700338 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700339 public Collection<Bmv2PortInfo> getPortsInfo() throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800340
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700341 LOG.debug("Retrieving port info... > deviceId={}", deviceId);
342
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800343 try {
344 List<DevMgrPortInfo> portInfos = stdClient.bm_dev_mgr_show_ports();
345
346 Collection<Bmv2PortInfo> bmv2PortInfos = Lists.newArrayList();
347
348 bmv2PortInfos.addAll(
349 portInfos.stream()
350 .map(Bmv2PortInfo::new)
351 .collect(Collectors.toList()));
352
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700353 LOG.debug("Port info retrieved! > deviceId={}, portInfos={}", deviceId, bmv2PortInfos);
354
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800355 return bmv2PortInfos;
356
357 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700358 LOG.debug("Exception while retrieving port info: {} > deviceId={}", e, deviceId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700359 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800360 }
361 }
362
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700363 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700364 public String dumpTable(String tableName) throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800365
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700366 LOG.debug("Retrieving table dump... > deviceId={}, tableName={}", deviceId, tableName);
367
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800368 try {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700369 String dump = stdClient.bm_dump_table(CONTEXT_ID, tableName);
370 LOG.debug("Table dump retrieved! > deviceId={}, tableName={}", deviceId, tableName);
371 return dump;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800372 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700373 LOG.debug("Exception while retrieving table dump: {} > deviceId={}, tableName={}",
374 e, deviceId, tableName);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700375 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800376 }
377 }
378
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700379 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700380 public void resetState() throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800381
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700382 LOG.debug("Resetting device state... > deviceId={}", deviceId);
383
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800384 try {
385 stdClient.bm_reset_state();
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700386 LOG.debug("Device state reset! > deviceId={}", deviceId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800387 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700388 LOG.debug("Exception while resetting device state: {} > deviceId={}", e, deviceId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700389 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800390 }
391 }
392
393 /**
394 * Transport/client cache loader.
395 */
396 private static class ClientLoader
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700397 extends CacheLoader<DeviceId, Bmv2ThriftClient> {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800398
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700399 // Connection retries options: max 10 retries each 200 ms
400 private static final Options RECONN_OPTIONS = new Options(NUM_CONNECTION_RETRIES, TIME_BETWEEN_RETRIES);
401
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800402 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700403 public Bmv2ThriftClient load(DeviceId deviceId)
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800404 throws TTransportException {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700405 LOG.debug("Creating new client in cache... > deviceId={}", deviceId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800406 Pair<String, Integer> info = parseDeviceId(deviceId);
407 //make the expensive call
408 TTransport transport = new TSocket(
409 info.getLeft(), info.getRight());
410 TProtocol protocol = new TBinaryProtocol(transport);
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700411 Standard.Client stdClient = new Standard.Client(
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800412 new TMultiplexedProtocol(protocol, "standard"));
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700413 // Wrap the client so to automatically have synchronization and resiliency to connectivity problems
414 Standard.Iface reconnStdIface = SafeThriftClient.wrap(stdClient,
415 Standard.Iface.class,
416 RECONN_OPTIONS);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800417
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700418 return new Bmv2ThriftClient(deviceId, transport, reconnStdIface);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800419 }
420 }
421
422 /**
423 * Client cache removal listener. Close the connection on cache removal.
424 */
425 private static class ClientRemovalListener implements
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700426 RemovalListener<DeviceId, Bmv2ThriftClient> {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800427
428 @Override
429 public void onRemoval(
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700430 RemovalNotification<DeviceId, Bmv2ThriftClient> notification) {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800431 // close the transport connection
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700432 LOG.debug("Removing client from cache... > deviceId={}", notification.getKey());
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700433 notification.getValue().closeTransport();
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800434 }
435 }
436}