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; ...@@ -48,12 +48,15 @@ import org.slf4j.LoggerFactory;
48 48
49 import java.util.Collection; 49 import java.util.Collection;
50 import java.util.Collections; 50 import java.util.Collections;
51 +import java.util.Date;
51 import java.util.List; 52 import java.util.List;
52 import java.util.Set; 53 import java.util.Set;
53 import java.util.concurrent.ConcurrentMap; 54 import java.util.concurrent.ConcurrentMap;
54 import java.util.concurrent.ExecutionException; 55 import java.util.concurrent.ExecutionException;
55 import java.util.concurrent.TimeUnit; 56 import java.util.concurrent.TimeUnit;
56 57
58 +import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
59 +
57 /** 60 /**
58 * Flow rule programmable device behaviour implementation for BMv2. 61 * Flow rule programmable device behaviour implementation for BMv2.
59 */ 62 */
...@@ -65,7 +68,7 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour ...@@ -65,7 +68,7 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
65 68
66 // There's no Bmv2 client method to poll flow entries from the device. Use a local store. 69 // There's no Bmv2 client method to poll flow entries from the device. Use a local store.
67 // FIXME: this information should be distributed across instances of the cluster. 70 // FIXME: this information should be distributed across instances of the cluster.
68 - private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, FlowEntry>> 71 + private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, TimestampedFlowRule>>
69 ENTRIES_MAP = Maps.newConcurrentMap(); 72 ENTRIES_MAP = Maps.newConcurrentMap();
70 73
71 // Cache model objects instead of parsing the JSON each time. 74 // Cache model objects instead of parsing the JSON each time.
...@@ -106,10 +109,28 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour ...@@ -106,10 +109,28 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
106 ENTRIES_MAP.forEach((key, value) -> { 109 ENTRIES_MAP.forEach((key, value) -> {
107 if (key.getLeft() == deviceId && key.getMiddle() == table.name() 110 if (key.getLeft() == deviceId && key.getMiddle() == table.name()
108 && value != null) { 111 && value != null) {
112 + long entryId = value.getKey();
109 // Filter entries_map for this device and table. 113 // Filter entries_map for this device and table.
110 - if (installedEntryIds.contains(value.getKey())) { 114 + if (installedEntryIds.contains(entryId)) {
111 // Entry is installed. 115 // Entry is installed.
112 - entryList.add(value.getRight()); 116 + long bytes = 0L;
117 + long packets = 0L;
118 + if (table.hasCounters()) {
119 + // Read counter values from device.
120 + try {
121 + Pair<Long, Long> counterValue = deviceClient.readTableEntryCounter(table.name(),
122 + entryId);
123 + bytes = counterValue.getLeft();
124 + packets = counterValue.getRight();
125 + } catch (Bmv2RuntimeException e) {
126 + LOG.warn("Unable to get counter values for entry {} of table {} of device {}: {}",
127 + entryId, table.name(), deviceId, e.toString());
128 + }
129 + }
130 + TimestampedFlowRule tsRule = value.getRight();
131 + FlowEntry entry = new DefaultFlowEntry(tsRule.rule(), ADDED,
132 + tsRule.lifeInSeconds(), packets, bytes);
133 + entryList.add(entry);
113 } else { 134 } else {
114 // No such entry on device, can remove from local store. 135 // No such entry on device, can remove from local store.
115 ENTRIES_MAP.remove(key); 136 ENTRIES_MAP.remove(key);
...@@ -183,16 +204,14 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour ...@@ -183,16 +204,14 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
183 // Tentatively delete entry before re-adding. 204 // Tentatively delete entry before re-adding.
184 // It might not exist on device due to inconsistencies. 205 // It might not exist on device due to inconsistencies.
185 deviceClient.deleteTableEntry(bmv2Entry.tableName(), entryId); 206 deviceClient.deleteTableEntry(bmv2Entry.tableName(), entryId);
207 + value = null;
186 } catch (Bmv2RuntimeException e) { 208 } catch (Bmv2RuntimeException e) {
187 // Silently drop exception as we can probably fix this by re-adding the entry. 209 // Silently drop exception as we can probably fix this by re-adding the entry.
188 } 210 }
189 } 211 }
190 // Add entry. 212 // Add entry.
191 entryId = deviceClient.addTableEntry(bmv2Entry); 213 entryId = deviceClient.addTableEntry(bmv2Entry);
192 - // TODO: evaluate flow entry life, bytes and packets 214 + value = Pair.of(entryId, new TimestampedFlowRule(rule));
193 - FlowEntry flowEntry = new DefaultFlowEntry(
194 - rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0);
195 - value = Pair.of(entryId, flowEntry);
196 } else { 215 } else {
197 // Remove entry 216 // Remove entry
198 if (value == null) { 217 if (value == null) {
...@@ -258,4 +277,22 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour ...@@ -258,4 +277,22 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
258 private enum Operation { 277 private enum Operation {
259 APPLY, REMOVE 278 APPLY, REMOVE
260 } 279 }
280 +
281 + private class TimestampedFlowRule {
282 + private final FlowRule rule;
283 + private final Date addedDate;
284 +
285 + public TimestampedFlowRule(FlowRule rule) {
286 + this.rule = rule;
287 + this.addedDate = new Date();
288 + }
289 +
290 + public FlowRule rule() {
291 + return rule;
292 + }
293 +
294 + public long lifeInSeconds() {
295 + return (new Date().getTime() - addedDate.getTime()) / 1000;
296 + }
297 + }
261 } 298 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -115,7 +115,7 @@ public final class Bmv2ModelTable { ...@@ -115,7 +115,7 @@ public final class Bmv2ModelTable {
115 * 115 *
116 * @return a boolean value 116 * @return a boolean value
117 */ 117 */
118 - public boolean hasCunters() { 118 + public boolean hasCounters() {
119 return hasCounters; 119 return hasCounters;
120 } 120 }
121 121
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 16
17 package org.onosproject.bmv2.api.runtime; 17 package org.onosproject.bmv2.api.runtime;
18 18
19 +import org.apache.commons.lang3.tuple.Pair;
19 import org.onlab.util.ImmutableByteSequence; 20 import org.onlab.util.ImmutableByteSequence;
20 21
21 import java.util.Collection; 22 import java.util.Collection;
...@@ -132,4 +133,14 @@ public interface Bmv2Client { ...@@ -132,4 +133,14 @@ public interface Bmv2Client {
132 * @throws Bmv2RuntimeException if any error occurs 133 * @throws Bmv2RuntimeException if any error occurs
133 */ 134 */
134 String getJsonConfigMd5() throws Bmv2RuntimeException; 135 String getJsonConfigMd5() throws Bmv2RuntimeException;
136 +
137 + /**
138 + * Returns the counter values for a given table and entry.
139 + *
140 + * @param tableName a table name
141 + * @param entryId an entry id
142 + * @return a pair of long values, where the left value is the number of bytes and the right the number of packets
143 + * @throws Bmv2RuntimeException if any error occurs
144 + */
145 + Pair<Long, Long> readTableEntryCounter(String tableName, long entryId) throws Bmv2RuntimeException;
135 } 146 }
......
...@@ -44,6 +44,7 @@ import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam; ...@@ -44,6 +44,7 @@ import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam;
44 import org.onosproject.bmv2.api.runtime.Bmv2ValidMatchParam; 44 import org.onosproject.bmv2.api.runtime.Bmv2ValidMatchParam;
45 import org.onosproject.net.DeviceId; 45 import org.onosproject.net.DeviceId;
46 import org.p4.bmv2.thrift.BmAddEntryOptions; 46 import org.p4.bmv2.thrift.BmAddEntryOptions;
47 +import org.p4.bmv2.thrift.BmCounterValue;
47 import org.p4.bmv2.thrift.BmMatchParam; 48 import org.p4.bmv2.thrift.BmMatchParam;
48 import org.p4.bmv2.thrift.BmMatchParamExact; 49 import org.p4.bmv2.thrift.BmMatchParamExact;
49 import org.p4.bmv2.thrift.BmMatchParamLPM; 50 import org.p4.bmv2.thrift.BmMatchParamLPM;
...@@ -477,6 +478,24 @@ public final class Bmv2ThriftClient implements Bmv2Client { ...@@ -477,6 +478,24 @@ public final class Bmv2ThriftClient implements Bmv2Client {
477 } 478 }
478 479
479 @Override 480 @Override
481 + public Pair<Long, Long> readTableEntryCounter(String tableName, long entryId) throws Bmv2RuntimeException {
482 +
483 + LOG.debug("Reading table entry counters... > deviceId={}, tableName={}, entryId={}",
484 + deviceId, tableName, entryId);
485 +
486 + try {
487 + BmCounterValue counterValue = standardClient.bm_mt_read_counter(CONTEXT_ID, tableName, entryId);
488 + LOG.debug("Table entry counters retrieved! > deviceId={}, tableName={}, entryId={}, bytes={}, packets={}",
489 + deviceId, tableName, entryId, counterValue.bytes, counterValue.packets);
490 + return Pair.of(counterValue.bytes, counterValue.packets);
491 + } catch (TException e) {
492 + LOG.debug("Exception while reading table counters: {} > deviceId={}, tableName={}, entryId={}",
493 + e.toString(), deviceId);
494 + throw new Bmv2RuntimeException(e.getMessage(), e);
495 + }
496 + }
497 +
498 + @Override
480 public String getJsonConfigMd5() throws Bmv2RuntimeException { 499 public String getJsonConfigMd5() throws Bmv2RuntimeException {
481 500
482 LOG.debug("Getting device config md5... > deviceId={}", deviceId); 501 LOG.debug("Getting device config md5... > deviceId={}", deviceId);
......
...@@ -52,7 +52,7 @@ public class Bmv2ModelTest { ...@@ -52,7 +52,7 @@ public class Bmv2ModelTest {
52 @Test 52 @Test
53 public void testParse() throws Exception { 53 public void testParse() throws Exception {
54 Bmv2Model model = Bmv2Model.parse(json); 54 Bmv2Model model = Bmv2Model.parse(json);
55 - Bmv2Model model2 = Bmv2Model.parse(json); 55 + Bmv2Model model2 = Bmv2Model.parse(json2);
56 56
57 new EqualsTester() 57 new EqualsTester()
58 .addEqualityGroup(model, model2) 58 .addEqualityGroup(model, model2)
......