Carmelo Cascone
Committed by Ray Milkey

Added ability to poll flow counters in BMv2

Also fixed few minor things here and there.

Change-Id: Ib5e6a92de46870f52510cd6fad0cef8da022bb62
......@@ -48,12 +48,15 @@ import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
/**
* Flow rule programmable device behaviour implementation for BMv2.
*/
......@@ -65,7 +68,7 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
// There's no Bmv2 client method to poll flow entries from the device. Use a local store.
// FIXME: this information should be distributed across instances of the cluster.
private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, FlowEntry>>
private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, TimestampedFlowRule>>
ENTRIES_MAP = Maps.newConcurrentMap();
// Cache model objects instead of parsing the JSON each time.
......@@ -106,10 +109,28 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
ENTRIES_MAP.forEach((key, value) -> {
if (key.getLeft() == deviceId && key.getMiddle() == table.name()
&& value != null) {
long entryId = value.getKey();
// Filter entries_map for this device and table.
if (installedEntryIds.contains(value.getKey())) {
if (installedEntryIds.contains(entryId)) {
// Entry is installed.
entryList.add(value.getRight());
long bytes = 0L;
long packets = 0L;
if (table.hasCounters()) {
// Read counter values from device.
try {
Pair<Long, Long> counterValue = deviceClient.readTableEntryCounter(table.name(),
entryId);
bytes = counterValue.getLeft();
packets = counterValue.getRight();
} catch (Bmv2RuntimeException e) {
LOG.warn("Unable to get counter values for entry {} of table {} of device {}: {}",
entryId, table.name(), deviceId, e.toString());
}
}
TimestampedFlowRule tsRule = value.getRight();
FlowEntry entry = new DefaultFlowEntry(tsRule.rule(), ADDED,
tsRule.lifeInSeconds(), packets, bytes);
entryList.add(entry);
} else {
// No such entry on device, can remove from local store.
ENTRIES_MAP.remove(key);
......@@ -183,16 +204,14 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
// Tentatively delete entry before re-adding.
// It might not exist on device due to inconsistencies.
deviceClient.deleteTableEntry(bmv2Entry.tableName(), entryId);
value = null;
} catch (Bmv2RuntimeException e) {
// Silently drop exception as we can probably fix this by re-adding the entry.
}
}
// Add entry.
entryId = deviceClient.addTableEntry(bmv2Entry);
// TODO: evaluate flow entry life, bytes and packets
FlowEntry flowEntry = new DefaultFlowEntry(
rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0);
value = Pair.of(entryId, flowEntry);
value = Pair.of(entryId, new TimestampedFlowRule(rule));
} else {
// Remove entry
if (value == null) {
......@@ -258,4 +277,22 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
private enum Operation {
APPLY, REMOVE
}
private class TimestampedFlowRule {
private final FlowRule rule;
private final Date addedDate;
public TimestampedFlowRule(FlowRule rule) {
this.rule = rule;
this.addedDate = new Date();
}
public FlowRule rule() {
return rule;
}
public long lifeInSeconds() {
return (new Date().getTime() - addedDate.getTime()) / 1000;
}
}
}
\ No newline at end of file
......
......@@ -115,7 +115,7 @@ public final class Bmv2ModelTable {
*
* @return a boolean value
*/
public boolean hasCunters() {
public boolean hasCounters() {
return hasCounters;
}
......
......@@ -16,6 +16,7 @@
package org.onosproject.bmv2.api.runtime;
import org.apache.commons.lang3.tuple.Pair;
import org.onlab.util.ImmutableByteSequence;
import java.util.Collection;
......@@ -132,4 +133,14 @@ public interface Bmv2Client {
* @throws Bmv2RuntimeException if any error occurs
*/
String getJsonConfigMd5() throws Bmv2RuntimeException;
/**
* Returns the counter values for a given table and entry.
*
* @param tableName a table name
* @param entryId an entry id
* @return a pair of long values, where the left value is the number of bytes and the right the number of packets
* @throws Bmv2RuntimeException if any error occurs
*/
Pair<Long, Long> readTableEntryCounter(String tableName, long entryId) throws Bmv2RuntimeException;
}
......
......@@ -44,6 +44,7 @@ import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam;
import org.onosproject.bmv2.api.runtime.Bmv2ValidMatchParam;
import org.onosproject.net.DeviceId;
import org.p4.bmv2.thrift.BmAddEntryOptions;
import org.p4.bmv2.thrift.BmCounterValue;
import org.p4.bmv2.thrift.BmMatchParam;
import org.p4.bmv2.thrift.BmMatchParamExact;
import org.p4.bmv2.thrift.BmMatchParamLPM;
......@@ -477,6 +478,24 @@ public final class Bmv2ThriftClient implements Bmv2Client {
}
@Override
public Pair<Long, Long> readTableEntryCounter(String tableName, long entryId) throws Bmv2RuntimeException {
LOG.debug("Reading table entry counters... > deviceId={}, tableName={}, entryId={}",
deviceId, tableName, entryId);
try {
BmCounterValue counterValue = standardClient.bm_mt_read_counter(CONTEXT_ID, tableName, entryId);
LOG.debug("Table entry counters retrieved! > deviceId={}, tableName={}, entryId={}, bytes={}, packets={}",
deviceId, tableName, entryId, counterValue.bytes, counterValue.packets);
return Pair.of(counterValue.bytes, counterValue.packets);
} catch (TException e) {
LOG.debug("Exception while reading table counters: {} > deviceId={}, tableName={}, entryId={}",
e.toString(), deviceId);
throw new Bmv2RuntimeException(e.getMessage(), e);
}
}
@Override
public String getJsonConfigMd5() throws Bmv2RuntimeException {
LOG.debug("Getting device config md5... > deviceId={}", deviceId);
......
......@@ -52,7 +52,7 @@ public class Bmv2ModelTest {
@Test
public void testParse() throws Exception {
Bmv2Model model = Bmv2Model.parse(json);
Bmv2Model model2 = Bmv2Model.parse(json);
Bmv2Model model2 = Bmv2Model.parse(json2);
new EqualsTester()
.addEqualityGroup(model, model2)
......