blob: f0e68f7efb8ec32ac5058527ad8a608490adcafd [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.
Carmelo Cascone5fa651e2016-04-27 17:35:57 -070083 private static final int NUM_CONNECTION_RETRIES = 3;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070084 // Time between retries in milliseconds.
Carmelo Cascone5fa651e2016-04-27 17:35:57 -070085 private static final int TIME_BETWEEN_RETRIES = 300;
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -070086
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 /**
Carmelo Cascone5fa651e2016-04-27 17:35:57 -0700128 * Force a close of the transport session (if one is open) with the given device.
129 *
130 * @param deviceId device id
131 */
132 public static void forceDisconnectOf(DeviceId deviceId) {
133 CLIENT_CACHE.invalidate(deviceId);
134 }
135
136 /**
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800137 * Pings the device. Returns true if the device is reachable,
138 * false otherwise.
139 *
140 * @param deviceId device id
141 * @return true if reachable, false otherwise
142 */
143 public static boolean ping(DeviceId deviceId) {
144 // poll ports status as workaround to assess device reachability
145 try {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700146 LOG.debug("Pinging device... > deviceId={}", deviceId);
147 Bmv2ThriftClient client = of(deviceId);
Carmelo Casconee4da9092016-04-26 12:14:08 -0700148 boolean result = client.simpleSwitchClient.ping();
149 LOG.debug("Device pinged! > deviceId={}, state={}", deviceId, result);
150 return result;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700151 } catch (TException | Bmv2RuntimeException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700152 LOG.debug("Device NOT reachable! > deviceId={}", deviceId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800153 return false;
154 }
155 }
156
157 /**
158 * Parse device ID into host and port.
159 *
160 * @param did device ID
161 * @return a pair of host and port
162 */
163 private static Pair<String, Integer> parseDeviceId(DeviceId did) {
164 String[] info = did.toString().split(":");
165 if (info.length == 3) {
166 String host = info[1];
167 int port = Integer.parseInt(info[2]);
168 return ImmutablePair.of(host, port);
169 } else {
170 throw new IllegalArgumentException(
171 "Unable to parse BMv2 device ID "
172 + did.toString()
173 + ", expected format is scheme:host:port");
174 }
175 }
176
177 /**
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700178 * Builds a list of Bmv2/Thrift compatible match parameters.
179 *
180 * @param matchKey a bmv2 matchKey
181 * @return list of thrift-compatible bm match parameters
182 */
183 private static List<BmMatchParam> buildMatchParamsList(Bmv2MatchKey matchKey) {
184 List<BmMatchParam> paramsList = Lists.newArrayList();
185 matchKey.matchParams().forEach(x -> {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700186 ByteBuffer value;
187 ByteBuffer mask;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700188 switch (x.type()) {
189 case EXACT:
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700190 value = ByteBuffer.wrap(((Bmv2ExactMatchParam) x).value().asArray());
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700191 paramsList.add(
192 new BmMatchParam(BmMatchParamType.EXACT)
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700193 .setExact(new BmMatchParamExact(value)));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700194 break;
195 case TERNARY:
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700196 value = ByteBuffer.wrap(((Bmv2TernaryMatchParam) x).value().asArray());
197 mask = ByteBuffer.wrap(((Bmv2TernaryMatchParam) x).mask().asArray());
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700198 paramsList.add(
199 new BmMatchParam(BmMatchParamType.TERNARY)
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700200 .setTernary(new BmMatchParamTernary(value, mask)));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700201 break;
202 case LPM:
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700203 value = ByteBuffer.wrap(((Bmv2LpmMatchParam) x).value().asArray());
204 int prefixLength = ((Bmv2LpmMatchParam) x).prefixLength();
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700205 paramsList.add(
206 new BmMatchParam(BmMatchParamType.LPM)
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700207 .setLpm(new BmMatchParamLPM(value, prefixLength)));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700208 break;
209 case VALID:
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700210 boolean flag = ((Bmv2ValidMatchParam) x).flag();
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700211 paramsList.add(
212 new BmMatchParam(BmMatchParamType.VALID)
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700213 .setValid(new BmMatchParamValid(flag)));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700214 break;
215 default:
216 // should never be here
217 throw new RuntimeException("Unknown match param type " + x.type().name());
218 }
219 });
220 return paramsList;
221 }
222
223 /**
224 * Build a list of Bmv2/Thrift compatible action parameters.
225 *
226 * @param action an action object
227 * @return list of ByteBuffers
228 */
229 private static List<ByteBuffer> buildActionParamsList(Bmv2Action action) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700230 List<ByteBuffer> buffers = Lists.newArrayList();
231 action.parameters().forEach(p -> buffers.add(ByteBuffer.wrap(p.asArray())));
232 return buffers;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700233 }
234
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700235 private void closeTransport() {
236 LOG.debug("Closing transport session... > deviceId={}", deviceId);
237 if (this.transport.isOpen()) {
238 this.transport.close();
239 LOG.debug("Transport session closed! > deviceId={}", deviceId);
240 } else {
241 LOG.debug("Transport session was already closed! deviceId={}", deviceId);
242 }
243 }
244
245 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700246 public final long addTableEntry(Bmv2TableEntry entry) throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800247
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700248 LOG.debug("Adding table entry... > deviceId={}, entry={}", deviceId, entry);
249
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800250 long entryId = -1;
251
252 try {
253 BmAddEntryOptions options = new BmAddEntryOptions();
254
255 if (entry.hasPriority()) {
256 options.setPriority(entry.priority());
257 }
258
Carmelo Casconee4da9092016-04-26 12:14:08 -0700259 entryId = standardClient.bm_mt_add_entry(
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800260 CONTEXT_ID,
261 entry.tableName(),
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700262 buildMatchParamsList(entry.matchKey()),
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800263 entry.action().name(),
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700264 buildActionParamsList(entry.action()),
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800265 options);
266
267 if (entry.hasTimeout()) {
268 /* bmv2 accepts timeouts in milliseconds */
269 int msTimeout = (int) Math.round(entry.timeout() * 1_000);
Carmelo Casconee4da9092016-04-26 12:14:08 -0700270 standardClient.bm_mt_set_entry_ttl(
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800271 CONTEXT_ID, entry.tableName(), entryId, msTimeout);
272 }
273
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700274 LOG.debug("Table entry added! > deviceId={}, entryId={}/{}", deviceId, entry.tableName(), entryId);
275
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800276 return entryId;
277
278 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700279 LOG.debug("Exception while adding table entry: {} > deviceId={}, tableName={}",
280 e, deviceId, entry.tableName());
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800281 if (entryId != -1) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700282 // entry is in inconsistent state (unable to add timeout), remove it
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800283 try {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700284 deleteTableEntry(entry.tableName(), entryId);
285 } catch (Bmv2RuntimeException e1) {
286 LOG.debug("Unable to remove failed table entry: {} > deviceId={}, tableName={}",
287 e1, deviceId, entry.tableName());
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800288 }
289 }
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700290 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800291 }
292 }
293
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700294 @Override
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800295 public final void modifyTableEntry(String tableName,
296 long entryId, Bmv2Action action)
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700297 throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800298
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700299 LOG.debug("Modifying table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
300
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800301 try {
Carmelo Casconee4da9092016-04-26 12:14:08 -0700302 standardClient.bm_mt_modify_entry(
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800303 CONTEXT_ID,
304 tableName,
305 entryId,
306 action.name(),
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700307 buildActionParamsList(action));
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700308 LOG.debug("Table entry modified! > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800309 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700310 LOG.debug("Exception while modifying table entry: {} > deviceId={}, entryId={}/{}",
311 e, deviceId, tableName, entryId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700312 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800313 }
314 }
315
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700316 @Override
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800317 public final void deleteTableEntry(String tableName,
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700318 long entryId) throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800319
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700320 LOG.debug("Deleting table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
321
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800322 try {
Carmelo Casconee4da9092016-04-26 12:14:08 -0700323 standardClient.bm_mt_delete_entry(CONTEXT_ID, tableName, entryId);
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700324 LOG.debug("Table entry deleted! > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800325 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700326 LOG.debug("Exception while deleting table entry: {} > deviceId={}, entryId={}/{}",
327 e, deviceId, tableName, entryId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700328 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800329 }
330 }
331
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700332 @Override
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800333 public final void setTableDefaultAction(String tableName, Bmv2Action action)
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700334 throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800335
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700336 LOG.debug("Setting table default... > deviceId={}, tableName={}, action={}", deviceId, tableName, action);
337
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800338 try {
Carmelo Casconee4da9092016-04-26 12:14:08 -0700339 standardClient.bm_mt_set_default_action(
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800340 CONTEXT_ID,
341 tableName,
342 action.name(),
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700343 buildActionParamsList(action));
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700344 LOG.debug("Table default set! > deviceId={}, tableName={}, action={}", deviceId, tableName, action);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800345 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700346 LOG.debug("Exception while setting table default : {} > deviceId={}, tableName={}, action={}",
347 e, deviceId, tableName, action);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700348 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800349 }
350 }
351
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700352 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700353 public Collection<Bmv2PortInfo> getPortsInfo() throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800354
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700355 LOG.debug("Retrieving port info... > deviceId={}", deviceId);
356
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800357 try {
Carmelo Casconee4da9092016-04-26 12:14:08 -0700358 List<DevMgrPortInfo> portInfos = standardClient.bm_dev_mgr_show_ports();
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800359
360 Collection<Bmv2PortInfo> bmv2PortInfos = Lists.newArrayList();
361
362 bmv2PortInfos.addAll(
363 portInfos.stream()
364 .map(Bmv2PortInfo::new)
365 .collect(Collectors.toList()));
366
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700367 LOG.debug("Port info retrieved! > deviceId={}, portInfos={}", deviceId, bmv2PortInfos);
368
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800369 return bmv2PortInfos;
370
371 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700372 LOG.debug("Exception while retrieving port info: {} > deviceId={}", e, deviceId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700373 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800374 }
375 }
376
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700377 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700378 public String dumpTable(String tableName) throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800379
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700380 LOG.debug("Retrieving table dump... > deviceId={}, tableName={}", deviceId, tableName);
381
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800382 try {
Carmelo Casconee4da9092016-04-26 12:14:08 -0700383 String dump = standardClient.bm_dump_table(CONTEXT_ID, tableName);
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700384 LOG.debug("Table dump retrieved! > deviceId={}, tableName={}", deviceId, tableName);
385 return dump;
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800386 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700387 LOG.debug("Exception while retrieving table dump: {} > deviceId={}, tableName={}",
388 e, deviceId, tableName);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700389 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800390 }
391 }
392
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700393 @Override
Carmelo Casconee4da9092016-04-26 12:14:08 -0700394 public void transmitPacket(int portNumber, ImmutableByteSequence packet) throws Bmv2RuntimeException {
395
396 LOG.debug("Requesting packet transmission... > portNumber={}, packet={}", portNumber, packet);
397
398 try {
399
400 simpleSwitchClient.push_packet(portNumber, ByteBuffer.wrap(packet.asArray()));
401 LOG.debug("Packet transmission requested! > portNumber={}, packet={}", portNumber, packet);
402 } catch (TException e) {
403 LOG.debug("Exception while requesting packet transmission: {} > portNumber={}, packet={}",
Carmelo Cascone5fa651e2016-04-27 17:35:57 -0700404 e, portNumber, packet);
Carmelo Casconee4da9092016-04-26 12:14:08 -0700405 throw new Bmv2RuntimeException(e.getMessage(), e);
406 }
407 }
408
409 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700410 public void resetState() throws Bmv2RuntimeException {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800411
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700412 LOG.debug("Resetting device state... > deviceId={}", deviceId);
413
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800414 try {
Carmelo Casconee4da9092016-04-26 12:14:08 -0700415 standardClient.bm_reset_state();
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700416 LOG.debug("Device state reset! > deviceId={}", deviceId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800417 } catch (TException e) {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700418 LOG.debug("Exception while resetting device state: {} > deviceId={}", e, deviceId);
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700419 throw new Bmv2RuntimeException(e.getMessage(), e);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800420 }
421 }
422
423 /**
424 * Transport/client cache loader.
425 */
426 private static class ClientLoader
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700427 extends CacheLoader<DeviceId, Bmv2ThriftClient> {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800428
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700429 private static final Options RECONN_OPTIONS = new Options(NUM_CONNECTION_RETRIES, TIME_BETWEEN_RETRIES);
430
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800431 @Override
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700432 public Bmv2ThriftClient load(DeviceId deviceId)
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800433 throws TTransportException {
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700434 LOG.debug("Creating new client in cache... > deviceId={}", deviceId);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800435 Pair<String, Integer> info = parseDeviceId(deviceId);
436 //make the expensive call
437 TTransport transport = new TSocket(
438 info.getLeft(), info.getRight());
439 TProtocol protocol = new TBinaryProtocol(transport);
Carmelo Casconee4da9092016-04-26 12:14:08 -0700440 // Our BMv2 device implements multiple Thrift services, create a client for each one.
441 Standard.Client standardClient = new Standard.Client(
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800442 new TMultiplexedProtocol(protocol, "standard"));
Carmelo Casconee4da9092016-04-26 12:14:08 -0700443 SimpleSwitch.Client simpleSwitch = new SimpleSwitch.Client(
444 new TMultiplexedProtocol(protocol, "simple_switch"));
445 // Wrap clients so to automatically have synchronization and resiliency to connectivity errors
446 Standard.Iface safeStandardClient = SafeThriftClient.wrap(standardClient,
447 Standard.Iface.class,
448 RECONN_OPTIONS);
449 SimpleSwitch.Iface safeSimpleSwitchClient = SafeThriftClient.wrap(simpleSwitch,
450 SimpleSwitch.Iface.class,
451 RECONN_OPTIONS);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800452
Carmelo Casconee4da9092016-04-26 12:14:08 -0700453 return new Bmv2ThriftClient(deviceId, transport, safeStandardClient, safeSimpleSwitchClient);
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800454 }
455 }
456
457 /**
458 * Client cache removal listener. Close the connection on cache removal.
459 */
460 private static class ClientRemovalListener implements
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700461 RemovalListener<DeviceId, Bmv2ThriftClient> {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800462
463 @Override
464 public void onRemoval(
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700465 RemovalNotification<DeviceId, Bmv2ThriftClient> notification) {
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800466 // close the transport connection
Carmelo Cascone37d5dbf2016-04-18 15:15:48 -0700467 LOG.debug("Removing client from cache... > deviceId={}", notification.getKey());
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700468 notification.getValue().closeTransport();
Carmelo Cascone2ea177b2016-02-25 18:38:42 -0800469 }
470 }
471}