Carmelo Cascone
Committed by Jonathan Hart

Improve flow rule polling consistency with bmv2

Change-Id: Iee5e7d7bee8f16505fe4d2acf48e65775bb2a524
...@@ -22,6 +22,7 @@ import com.google.common.cache.CacheLoader; ...@@ -22,6 +22,7 @@ import com.google.common.cache.CacheLoader;
22 import com.google.common.cache.LoadingCache; 22 import com.google.common.cache.LoadingCache;
23 import com.google.common.collect.Lists; 23 import com.google.common.collect.Lists;
24 import com.google.common.collect.Maps; 24 import com.google.common.collect.Maps;
25 +import com.google.common.collect.Sets;
25 import org.apache.commons.lang3.tuple.Pair; 26 import org.apache.commons.lang3.tuple.Pair;
26 import org.apache.commons.lang3.tuple.Triple; 27 import org.apache.commons.lang3.tuple.Triple;
27 import org.onosproject.bmv2.api.model.Bmv2Model; 28 import org.onosproject.bmv2.api.model.Bmv2Model;
...@@ -48,6 +49,7 @@ import org.slf4j.LoggerFactory; ...@@ -48,6 +49,7 @@ import org.slf4j.LoggerFactory;
48 import java.util.Collection; 49 import java.util.Collection;
49 import java.util.Collections; 50 import java.util.Collections;
50 import java.util.List; 51 import java.util.List;
52 +import java.util.Set;
51 import java.util.concurrent.ConcurrentMap; 53 import java.util.concurrent.ConcurrentMap;
52 import java.util.concurrent.ExecutionException; 54 import java.util.concurrent.ExecutionException;
53 import java.util.concurrent.TimeUnit; 55 import java.util.concurrent.TimeUnit;
...@@ -61,7 +63,8 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour ...@@ -61,7 +63,8 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
61 private static final Logger LOG = 63 private static final Logger LOG =
62 LoggerFactory.getLogger(Bmv2FlowRuleProgrammable.class); 64 LoggerFactory.getLogger(Bmv2FlowRuleProgrammable.class);
63 65
64 - // There's no Bmv2 client method to poll flow entries from the device device. Need a local store. 66 + // 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.
65 private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, FlowEntry>> 68 private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, FlowEntry>>
66 ENTRIES_MAP = Maps.newConcurrentMap(); 69 ENTRIES_MAP = Maps.newConcurrentMap();
67 70
...@@ -81,12 +84,41 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour ...@@ -81,12 +84,41 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
81 84
82 DeviceId deviceId = handler().data().deviceId(); 85 DeviceId deviceId = handler().data().deviceId();
83 86
87 + Bmv2Client deviceClient;
88 + try {
89 + deviceClient = Bmv2ThriftClient.of(deviceId);
90 + } catch (Bmv2RuntimeException e) {
91 + LOG.error("Failed to connect to Bmv2 device", e);
92 + return Collections.emptyList();
93 + }
94 +
95 + Bmv2Model model = getTranslator(deviceId).config().model();
96 +
84 List<FlowEntry> entryList = Lists.newArrayList(); 97 List<FlowEntry> entryList = Lists.newArrayList();
85 98
86 - // FIXME: improve this, e.g. might store a separate Map<DeviceId, Collection<FlowEntry>> 99 + model.tables().forEach(table -> {
87 - ENTRIES_MAP.forEach((key, value) -> { 100 + // For each table declared in the model for this device, do:
88 - if (key.getLeft() == deviceId && value != null) { 101 + try {
89 - entryList.add(value.getRight()); 102 + // Bmv2 doesn't support proper polling for table entries, but only a string based table dump.
103 + // The trick here is to first dump the entry ids currently installed in the device for a given table,
104 + // and then filter ENTRIES_MAP based on the retrieved values.
105 + Set<Long> installedEntryIds = Sets.newHashSet(deviceClient.getInstalledEntryIds(table.name()));
106 + ENTRIES_MAP.forEach((key, value) -> {
107 + if (key.getLeft() == deviceId && key.getMiddle() == table.name()
108 + && value != null) {
109 + // Filter entries_map for this device and table.
110 + if (installedEntryIds.contains(value.getKey())) {
111 + // Entry is installed.
112 + entryList.add(value.getRight());
113 + } else {
114 + // No such entry on device, can remove from local store.
115 + ENTRIES_MAP.remove(key);
116 + }
117 + }
118 + });
119 + } catch (Bmv2RuntimeException e) {
120 + LOG.error("Unable to get flow entries for table {} of device {}: {}",
121 + table.name(), deviceId, e.toString());
90 } 122 }
91 }); 123 });
92 124
...@@ -144,16 +176,19 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour ...@@ -144,16 +176,19 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
144 if (operation == Operation.APPLY) { 176 if (operation == Operation.APPLY) {
145 // Apply entry 177 // Apply entry
146 long entryId; 178 long entryId;
147 - if (value == null) { 179 + if (value != null) {
148 - // New entry 180 + // Existing entry.
149 - entryId = deviceClient.addTableEntry(bmv2Entry);
150 - } else {
151 - // Existing entry
152 entryId = value.getKey(); 181 entryId = value.getKey();
153 - // FIXME: check if priority or timeout changed 182 + try {
154 - // In this case we should to re-add the entry (not modify) 183 + // Tentatively delete entry before re-adding.
155 - deviceClient.modifyTableEntry(tableName, entryId, bmv2Entry.action()); 184 + // It might not exist on device due to inconsistencies.
185 + deviceClient.deleteTableEntry(bmv2Entry.tableName(), entryId);
186 + } catch (Bmv2RuntimeException e) {
187 + // Silently drop exception as we can probably fix this by re-adding the entry.
188 + }
156 } 189 }
190 + // Add entry.
191 + entryId = deviceClient.addTableEntry(bmv2Entry);
157 // TODO: evaluate flow entry life, bytes and packets 192 // TODO: evaluate flow entry life, bytes and packets
158 FlowEntry flowEntry = new DefaultFlowEntry( 193 FlowEntry flowEntry = new DefaultFlowEntry(
159 rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0); 194 rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0);
...@@ -171,9 +206,7 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour ...@@ -171,9 +206,7 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
171 // If here, no exceptions... things went well :) 206 // If here, no exceptions... things went well :)
172 processedFlowRules.add(rule); 207 processedFlowRules.add(rule);
173 } catch (Bmv2RuntimeException e) { 208 } catch (Bmv2RuntimeException e) {
174 - LOG.error("Unable to " + operation.name().toLowerCase() + " flow rule", e); 209 + LOG.warn("Unable to {} flow rule: {}", operation.name().toLowerCase(), e.toString());
175 - } catch (Exception e) {
176 - LOG.error("Uncaught exception while processing flow rule", e);
177 } 210 }
178 return value; 211 return value;
179 }); 212 });
......
...@@ -39,7 +39,7 @@ import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT; ...@@ -39,7 +39,7 @@ import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
39 public class Bmv2PacketProgrammable extends AbstractHandlerBehaviour implements PacketProgrammable { 39 public class Bmv2PacketProgrammable extends AbstractHandlerBehaviour implements PacketProgrammable {
40 40
41 private static final Logger LOG = 41 private static final Logger LOG =
42 - LoggerFactory.getLogger(Bmv2FlowRuleProgrammable.class); 42 + LoggerFactory.getLogger(Bmv2PacketProgrammable.class);
43 43
44 @Override 44 @Override
45 public void emit(OutboundPacket packet) { 45 public void emit(OutboundPacket packet) {
......
...@@ -182,6 +182,8 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -182,6 +182,8 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
182 DeviceDescription descr = new DefaultDeviceDescription( 182 DeviceDescription descr = new DefaultDeviceDescription(
183 did.uri(), Device.Type.SWITCH, MANUFACTURER, HW_VERSION, 183 did.uri(), Device.Type.SWITCH, MANUFACTURER, HW_VERSION,
184 UNKNOWN, UNKNOWN, new ChassisId(), annotationsBuilder.build()); 184 UNKNOWN, UNKNOWN, new ChassisId(), annotationsBuilder.build());
185 + // Reset device state (cleanup entries, etc.)
186 + resetDeviceState(did);
185 providerService.deviceConnected(did, descr); 187 providerService.deviceConnected(did, descr);
186 } 188 }
187 updatePorts(did); 189 updatePorts(did);
...@@ -199,7 +201,15 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -199,7 +201,15 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
199 builder.set("bmv2JsonConfigMd5", md5); 201 builder.set("bmv2JsonConfigMd5", md5);
200 builder.set("bmv2JsonConfigValue", jsonString); 202 builder.set("bmv2JsonConfigValue", jsonString);
201 } catch (Bmv2RuntimeException e) { 203 } catch (Bmv2RuntimeException e) {
202 - LOG.warn("Unable to dump device JSON config from device {}: {}", did, e); 204 + LOG.warn("Unable to dump device JSON config from device {}: {}", did, e.toString());
205 + }
206 + }
207 +
208 + private void resetDeviceState(DeviceId did) {
209 + try {
210 + Bmv2ThriftClient.of(did).resetState();
211 + } catch (Bmv2RuntimeException e) {
212 + LOG.warn("Unable to reset {}: {}", did, e.toString());
203 } 213 }
204 } 214 }
205 215
......