Committed by
Gerrit Code Review
Lambdas are reinterpreted before being sent to Linc-OE switches. This includes
adding ability to intercept messages at the switch driver for modification before being sent down. Reference: ONOS-1980 Change-Id: I405b89a0fc3844555c9efa0cd9fc887a90d00280
Showing
3 changed files
with
177 additions
and
2 deletions
... | @@ -24,30 +24,61 @@ import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotSta | ... | @@ -24,30 +24,61 @@ import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotSta |
24 | import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus; | 24 | import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus; |
25 | import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply; | 25 | import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply; |
26 | import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest; | 26 | import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest; |
27 | +import org.projectfloodlight.openflow.protocol.OFFactories; | ||
28 | +import org.projectfloodlight.openflow.protocol.OFFactory; | ||
29 | +import org.projectfloodlight.openflow.protocol.OFFlowMod; | ||
30 | +import org.projectfloodlight.openflow.protocol.OFFlowModCommand; | ||
31 | +import org.projectfloodlight.openflow.protocol.OFInstructionType; | ||
27 | import org.projectfloodlight.openflow.protocol.OFMessage; | 32 | import org.projectfloodlight.openflow.protocol.OFMessage; |
28 | import org.projectfloodlight.openflow.protocol.OFObject; | 33 | import org.projectfloodlight.openflow.protocol.OFObject; |
29 | import org.projectfloodlight.openflow.protocol.OFPortDesc; | 34 | import org.projectfloodlight.openflow.protocol.OFPortDesc; |
30 | import org.projectfloodlight.openflow.protocol.OFStatsReply; | 35 | import org.projectfloodlight.openflow.protocol.OFStatsReply; |
31 | import org.projectfloodlight.openflow.protocol.OFStatsType; | 36 | import org.projectfloodlight.openflow.protocol.OFStatsType; |
37 | +import org.projectfloodlight.openflow.protocol.OFType; | ||
38 | +import org.projectfloodlight.openflow.protocol.OFVersion; | ||
39 | +import org.projectfloodlight.openflow.protocol.action.OFAction; | ||
40 | +import org.projectfloodlight.openflow.protocol.action.OFActionCircuit; | ||
41 | +import org.projectfloodlight.openflow.protocol.instruction.OFInstruction; | ||
42 | +import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions; | ||
43 | +import org.projectfloodlight.openflow.protocol.match.Match; | ||
44 | +import org.projectfloodlight.openflow.protocol.match.MatchField; | ||
45 | +import org.projectfloodlight.openflow.protocol.OFActionType; | ||
46 | +import org.projectfloodlight.openflow.types.CircuitSignalID; | ||
47 | +import org.projectfloodlight.openflow.types.OFPort; | ||
48 | +import org.projectfloodlight.openflow.types.U8; | ||
32 | 49 | ||
33 | import com.google.common.collect.ImmutableList; | 50 | import com.google.common.collect.ImmutableList; |
34 | import com.google.common.collect.ImmutableSet; | 51 | import com.google.common.collect.ImmutableSet; |
35 | 52 | ||
36 | import java.io.IOException; | 53 | import java.io.IOException; |
37 | import java.util.List; | 54 | import java.util.List; |
55 | +import java.util.Map; | ||
56 | +import java.util.ArrayList; | ||
38 | import java.util.Set; | 57 | import java.util.Set; |
58 | +import java.util.BitSet; | ||
59 | +import java.util.stream.Collectors; | ||
39 | import java.util.concurrent.atomic.AtomicBoolean; | 60 | import java.util.concurrent.atomic.AtomicBoolean; |
61 | +import java.util.concurrent.ConcurrentMap; | ||
62 | +import java.util.concurrent.ConcurrentHashMap; | ||
63 | + | ||
64 | +import static org.projectfloodlight.openflow.protocol.OFFlowMod.Builder; | ||
40 | 65 | ||
41 | /** | 66 | /** |
42 | * LINC-OE Optical Emulator switch class. | 67 | * LINC-OE Optical Emulator switch class. |
43 | */ | 68 | */ |
44 | public class OFOpticalSwitchImplLINC13 | 69 | public class OFOpticalSwitchImplLINC13 |
45 | extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch { | 70 | extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch { |
71 | + // default number of lambdas, assuming 50GHz channels. | ||
72 | + private static final int NUM_CHLS = 80; | ||
73 | + private final OFFactory factory = OFFactories.getFactory(OFVersion.OF_13); | ||
46 | 74 | ||
47 | private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false); | 75 | private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false); |
48 | private long barrierXidToWaitFor = -1; | 76 | private long barrierXidToWaitFor = -1; |
49 | 77 | ||
50 | private OFCircuitPortsReply wPorts; | 78 | private OFCircuitPortsReply wPorts; |
79 | + // book-keeping maps for allocated Linc-OE lambdas | ||
80 | + protected final ConcurrentMap<OFPort, BitSet> portChannelMap = new ConcurrentHashMap<>(); | ||
81 | + protected final ConcurrentMap<Match, Integer> matchMap = new ConcurrentHashMap<>(); | ||
51 | 82 | ||
52 | @Override | 83 | @Override |
53 | public void startDriverHandshake() { | 84 | public void startDriverHandshake() { |
... | @@ -170,4 +201,136 @@ public class OFOpticalSwitchImplLINC13 | ... | @@ -170,4 +201,136 @@ public class OFOpticalSwitchImplLINC13 |
170 | return ImmutableSet.of(PortDescPropertyType.OPTICAL_TRANSPORT); | 201 | return ImmutableSet.of(PortDescPropertyType.OPTICAL_TRANSPORT); |
171 | } | 202 | } |
172 | 203 | ||
204 | + @Override | ||
205 | + public OFMessage prepareMessage(OFMessage msg) { | ||
206 | + if (OFVersion.OF_13 != msg.getVersion() || msg.getType() != OFType.FLOW_MOD) { | ||
207 | + return msg; | ||
208 | + } | ||
209 | + OFFlowMod fm = (OFFlowMod) msg; | ||
210 | + Match match = fm.getMatch(); | ||
211 | + // Don't touch FlowMods that aren't Optical-related. | ||
212 | + if (match.get(MatchField.OCH_SIGTYPE) == null) { | ||
213 | + return msg; | ||
214 | + } | ||
215 | + | ||
216 | + OFMessage newFM; | ||
217 | + Builder builder = null; | ||
218 | + List<OFAction> actions = new ArrayList<>(); | ||
219 | + if (fm.getCommand() == OFFlowModCommand.ADD) { | ||
220 | + builder = factory.buildFlowAdd(); | ||
221 | + int lambda = allocateLambda(match.get(MatchField.IN_PORT), match); | ||
222 | + CircuitSignalID sigid = new CircuitSignalID((byte) 1, (byte) 2, (short) lambda, (short) 1); | ||
223 | + List<OFInstruction> instructions = fm.getInstructions(); | ||
224 | + | ||
225 | + newFM = buildFlowMod(builder, fm, buildMatch(match, sigid), buildActions(instructions, sigid)); | ||
226 | + } else if (fm.getCommand() == OFFlowModCommand.DELETE) { | ||
227 | + builder = factory.buildFlowDelete(); | ||
228 | + int lambda = freeLambda(match.get(MatchField.IN_PORT), match); | ||
229 | + CircuitSignalID sigid = new CircuitSignalID((byte) 1, (byte) 2, (short) lambda, (short) 1); | ||
230 | + | ||
231 | + newFM = buildFlowMod(builder, fm, buildMatch(match, sigid), actions); | ||
232 | + } else { | ||
233 | + newFM = msg; | ||
234 | + } | ||
235 | + log.debug("new FM = {}", newFM); | ||
236 | + return newFM; | ||
237 | + } | ||
238 | + | ||
239 | + // fetch the next available channel as the flat lambda value, or the lambda | ||
240 | + // associated with a port/match combination | ||
241 | + private int allocateLambda(OFPort port, Match match) { | ||
242 | + Integer lambda = null; | ||
243 | + synchronized (this) { | ||
244 | + BitSet channels = portChannelMap.getOrDefault(port, new BitSet(NUM_CHLS + 1)); | ||
245 | + lambda = matchMap.get(match); | ||
246 | + if (lambda == null) { | ||
247 | + // TODO : double check behavior when bitset is full | ||
248 | + // Linc lambdas start at 1. | ||
249 | + lambda = channels.nextClearBit(1); | ||
250 | + channels.set(lambda); | ||
251 | + portChannelMap.put(port, channels); | ||
252 | + matchMap.put(match, lambda); | ||
253 | + } | ||
254 | + } | ||
255 | + return lambda; | ||
256 | + } | ||
257 | + | ||
258 | + // free lambda that was mapped to Port/Match combination and return its | ||
259 | + // value to caller. | ||
260 | + private int freeLambda(OFPort port, Match match) { | ||
261 | + synchronized (this) { | ||
262 | + Integer lambda = matchMap.get(match); | ||
263 | + if (lambda != null) { | ||
264 | + portChannelMap.get(port).clear(lambda); | ||
265 | + return lambda; | ||
266 | + } | ||
267 | + // 1 is a sane-ish default for Linc. | ||
268 | + return 1; | ||
269 | + } | ||
270 | + } | ||
271 | + | ||
272 | + // build matches - *tons of assumptions are made here based on Linc-OE's behavior.* | ||
273 | + // gridType = 1 (DWDM) | ||
274 | + // channelSpacing = 2 (50GHz) | ||
275 | + // spectralWidth = 1 (fixed grid default value) | ||
276 | + private Match buildMatch(Match original, CircuitSignalID sigid) { | ||
277 | + Match.Builder mBuilder = factory.buildMatch(); | ||
278 | + | ||
279 | + original.getMatchFields().forEach(mf -> { | ||
280 | + String name = mf.getName(); | ||
281 | + if (MatchField.OCH_SIGID.getName().equals(name)) { | ||
282 | + mBuilder.setExact(MatchField.OCH_SIGID, sigid); | ||
283 | + } else if (MatchField.OCH_SIGTYPE.getName().equals(name)) { | ||
284 | + mBuilder.setExact(MatchField.OCH_SIGTYPE, U8.of((short) 1)); | ||
285 | + } else if (MatchField.IN_PORT.getName().equals(name)) { | ||
286 | + mBuilder.setExact(MatchField.IN_PORT, original.get(MatchField.IN_PORT)); | ||
287 | + } | ||
288 | + }); | ||
289 | + | ||
290 | + return mBuilder.build(); | ||
291 | + } | ||
292 | + | ||
293 | + private List<OFAction> buildActions(List<OFInstruction> iList, CircuitSignalID sigid) { | ||
294 | + List<OFAction> actions = new ArrayList<>(); | ||
295 | + Map<OFInstructionType, OFInstruction> instructions = iList.stream() | ||
296 | + .collect(Collectors.toMap(OFInstruction::getType, inst -> inst)); | ||
297 | + | ||
298 | + OFInstruction inst = instructions.get(OFInstructionType.APPLY_ACTIONS); | ||
299 | + if (inst != null) { | ||
300 | + OFInstructionApplyActions iaa = (OFInstructionApplyActions) inst; | ||
301 | + if (iaa.getActions() == null) { | ||
302 | + return actions; | ||
303 | + } | ||
304 | + iaa.getActions().forEach(action -> { | ||
305 | + if (OFActionType.EXPERIMENTER == action.getType()) { | ||
306 | + OFActionCircuit.Builder cBuilder = factory.actions().buildCircuit() | ||
307 | + .setField(factory.oxms() | ||
308 | + .buildOchSigid() | ||
309 | + .setValue(sigid) | ||
310 | + .build()); | ||
311 | + actions.add(cBuilder.build()); | ||
312 | + } else { | ||
313 | + actions.add(action); | ||
314 | + } | ||
315 | + }); | ||
316 | + } | ||
317 | + return actions; | ||
318 | + } | ||
319 | + | ||
320 | + private OFMessage buildFlowMod(Builder builder, OFFlowMod fm, Match m, List<OFAction> act) { | ||
321 | + return builder | ||
322 | + .setXid(fm.getXid()) | ||
323 | + .setCookie(fm.getCookie()) | ||
324 | + .setCookieMask(fm.getCookieMask()) | ||
325 | + .setTableId(fm.getTableId()) | ||
326 | + .setIdleTimeout(fm.getIdleTimeout()) | ||
327 | + .setHardTimeout(fm.getHardTimeout()) | ||
328 | + .setBufferId(fm.getBufferId()) | ||
329 | + .setOutPort(fm.getOutPort()) | ||
330 | + .setOutGroup(fm.getOutGroup()) | ||
331 | + .setFlags(fm.getFlags()) | ||
332 | + .setMatch(m) | ||
333 | + .setActions(act) | ||
334 | + .build(); | ||
335 | + } | ||
173 | } | 336 | } | ... | ... |
... | @@ -148,4 +148,16 @@ public interface OpenFlowSwitch { | ... | @@ -148,4 +148,16 @@ public interface OpenFlowSwitch { |
148 | * @return string representation of the connection to the device | 148 | * @return string representation of the connection to the device |
149 | */ | 149 | */ |
150 | String channelId(); | 150 | String channelId(); |
151 | + | ||
152 | + /** | ||
153 | + * Prepares a message to be sent, if necessary. Default is to do nothing, | ||
154 | + * since most Devices do not need to pre-process a message that's about to | ||
155 | + * be sent. | ||
156 | + * | ||
157 | + * @param msg The message to prepare for sending | ||
158 | + * @return the prepared OFMessage | ||
159 | + */ | ||
160 | + default OFMessage prepareMessage(OFMessage msg) { | ||
161 | + return msg; | ||
162 | + } | ||
151 | } | 163 | } | ... | ... |
... | @@ -97,7 +97,7 @@ public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour | ... | @@ -97,7 +97,7 @@ public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour |
97 | @Override | 97 | @Override |
98 | public final void sendMsg(OFMessage m) { | 98 | public final void sendMsg(OFMessage m) { |
99 | if (role == RoleState.MASTER && channel.isWritable()) { | 99 | if (role == RoleState.MASTER && channel.isWritable()) { |
100 | - channel.write(Collections.singletonList(m)); | 100 | + channel.write(Collections.singletonList(prepareMessage(m))); |
101 | } | 101 | } |
102 | } | 102 | } |
103 | 103 | ||
... | @@ -119,6 +119,7 @@ public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour | ... | @@ -119,6 +119,7 @@ public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour |
119 | "a non role request message"); | 119 | "a non role request message"); |
120 | } | 120 | } |
121 | 121 | ||
122 | + @Override | ||
122 | public final void sendHandshakeMessage(OFMessage message) { | 123 | public final void sendHandshakeMessage(OFMessage message) { |
123 | if (!this.isDriverHandshakeComplete()) { | 124 | if (!this.isDriverHandshakeComplete()) { |
124 | channel.write(Collections.singletonList(message)); | 125 | channel.write(Collections.singletonList(message)); |
... | @@ -155,7 +156,6 @@ public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour | ... | @@ -155,7 +156,6 @@ public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour |
155 | return channelId; | 156 | return channelId; |
156 | } | 157 | } |
157 | 158 | ||
158 | - | ||
159 | //************************ | 159 | //************************ |
160 | // Switch features related | 160 | // Switch features related |
161 | //************************ | 161 | //************************ | ... | ... |
-
Please register or login to post a comment