Ayaka Koshibe
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
...@@ -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 //************************
......