OFOpticalSwitchImplLINC13.java
13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.driver.handshaker;
import org.onosproject.openflow.controller.OpenFlowOpticalSwitch;
import org.onosproject.openflow.controller.PortDescPropertyType;
import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted;
import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeCompleted;
import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted;
import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply;
import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest;
import org.projectfloodlight.openflow.protocol.OFFactories;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.OFFlowMod;
import org.projectfloodlight.openflow.protocol.OFFlowModCommand;
import org.projectfloodlight.openflow.protocol.OFInstructionType;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFObject;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFStatsReply;
import org.projectfloodlight.openflow.protocol.OFStatsType;
import org.projectfloodlight.openflow.protocol.OFType;
import org.projectfloodlight.openflow.protocol.OFVersion;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.action.OFActionCircuit;
import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
import org.projectfloodlight.openflow.protocol.match.Match;
import org.projectfloodlight.openflow.protocol.match.MatchField;
import org.projectfloodlight.openflow.protocol.OFActionType;
import org.projectfloodlight.openflow.types.CircuitSignalID;
import org.projectfloodlight.openflow.types.OFPort;
import org.projectfloodlight.openflow.types.U8;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.Set;
import java.util.BitSet;
import java.util.stream.Collectors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;
import static org.projectfloodlight.openflow.protocol.OFFlowMod.Builder;
/**
* LINC-OE Optical Emulator switch class.
*/
public class OFOpticalSwitchImplLINC13
extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch {
// default number of lambdas, assuming 50GHz channels.
private static final int NUM_CHLS = 80;
private final OFFactory factory = OFFactories.getFactory(OFVersion.OF_13);
private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false);
private long barrierXidToWaitFor = -1;
private OFCircuitPortsReply wPorts;
// book-keeping maps for allocated Linc-OE lambdas
protected final ConcurrentMap<OFPort, BitSet> portChannelMap = new ConcurrentHashMap<>();
protected final ConcurrentMap<Match, Integer> matchMap = new ConcurrentHashMap<>();
@Override
public void startDriverHandshake() {
log.warn("Starting driver handshake for sw {}", getStringId());
if (startDriverHandshakeCalled) {
throw new SwitchDriverSubHandshakeAlreadyStarted();
}
startDriverHandshakeCalled = true;
try {
sendHandshakeOFExperimenterPortDescRequest();
} catch (IOException e) {
log.error("LINC-OE exception while sending experimenter port desc:",
e.getMessage());
e.printStackTrace();
}
}
@Override
public boolean isDriverHandshakeComplete() {
return driverHandshakeComplete.get();
}
@Override
public void processDriverHandshakeMessage(OFMessage m) {
if (!startDriverHandshakeCalled) {
throw new SwitchDriverSubHandshakeNotStarted();
}
if (driverHandshakeComplete.get()) {
throw new SwitchDriverSubHandshakeCompleted(m);
}
switch (m.getType()) {
case BARRIER_REPLY:
if (m.getXid() == barrierXidToWaitFor) {
log.debug("LINC-OE Received barrier response");
}
break;
case ERROR:
log.error("Switch {} Error {}", getStringId(), m);
break;
case FEATURES_REPLY:
break;
case FLOW_REMOVED:
break;
case GET_ASYNC_REPLY:
break;
case PACKET_IN:
break;
case PORT_STATUS:
log.warn("****LINC-OE Port Status {} {}", getStringId(), m);
processOFPortStatus((OFCircuitPortStatus) m);
break;
case QUEUE_GET_CONFIG_REPLY:
break;
case ROLE_REPLY:
break;
case STATS_REPLY:
OFStatsReply stats = (OFStatsReply) m;
if (stats.getStatsType() == OFStatsType.EXPERIMENTER) {
log.warn("LINC-OE : Received stats reply message {}", m);
wPorts = (OFCircuitPortsReply) m;
driverHandshakeComplete.set(true);
}
break;
default:
log.warn("Received message {} during switch-driver " +
"subhandshake " + "from switch {} ... " +
"Ignoring message", m,
getStringId());
}
}
public void processOFPortStatus(OFCircuitPortStatus ps) {
log.debug("LINC-OE ..OF Port Status :", ps);
}
private void sendHandshakeOFExperimenterPortDescRequest() throws
IOException {
// send multi part message for port description for optical switches
OFCircuitPortsRequest circuitPortsRequest = factory()
.buildCircuitPortsRequest().setXid(getNextTransactionId())
.build();
log.warn("LINC-OE : Sending experimented circuit port stats " +
"message " +
"{}",
circuitPortsRequest.toString());
this.sendHandshakeMessage(circuitPortsRequest);
}
@Override
/**
* Returns a list of standard (Ethernet) ports.
*
* @return List of ports
*/
public List<OFPortDesc> getPorts() {
return ImmutableList.copyOf(super.getPorts());
}
@Override
public Boolean supportNxRole() {
return false;
}
@Override
public boolean isOptical() {
return true;
}
@Override
public List<? extends OFObject> getPortsOf(PortDescPropertyType type) {
return ImmutableList.copyOf(wPorts.getEntries());
}
@Override
public Set<PortDescPropertyType> getPortTypes() {
return ImmutableSet.of(PortDescPropertyType.OPTICAL_TRANSPORT);
}
@Override
public OFMessage prepareMessage(OFMessage msg) {
if (OFVersion.OF_13 != msg.getVersion() || msg.getType() != OFType.FLOW_MOD) {
return msg;
}
OFFlowMod fm = (OFFlowMod) msg;
Match match = fm.getMatch();
// Don't touch FlowMods that aren't Optical-related.
if (match.get(MatchField.OCH_SIGTYPE) == null) {
return msg;
}
OFMessage newFM;
Builder builder = null;
List<OFAction> actions = new ArrayList<>();
if (fm.getCommand() == OFFlowModCommand.ADD) {
builder = factory.buildFlowAdd();
int lambda = allocateLambda(match.get(MatchField.IN_PORT), match);
CircuitSignalID sigid = new CircuitSignalID((byte) 1, (byte) 2, (short) lambda, (short) 1);
List<OFInstruction> instructions = fm.getInstructions();
newFM = buildFlowMod(builder, fm, buildMatch(match, sigid), buildActions(instructions, sigid));
} else if (fm.getCommand() == OFFlowModCommand.DELETE) {
builder = factory.buildFlowDelete();
int lambda = freeLambda(match.get(MatchField.IN_PORT), match);
CircuitSignalID sigid = new CircuitSignalID((byte) 1, (byte) 2, (short) lambda, (short) 1);
newFM = buildFlowMod(builder, fm, buildMatch(match, sigid), actions);
} else {
newFM = msg;
}
log.debug("new FM = {}", newFM);
return newFM;
}
// fetch the next available channel as the flat lambda value, or the lambda
// associated with a port/match combination
private int allocateLambda(OFPort port, Match match) {
Integer lambda = null;
synchronized (this) {
BitSet channels = portChannelMap.getOrDefault(port, new BitSet(NUM_CHLS + 1));
lambda = matchMap.get(match);
if (lambda == null) {
// TODO : double check behavior when bitset is full
// Linc lambdas start at 1.
lambda = channels.nextClearBit(1);
channels.set(lambda);
portChannelMap.put(port, channels);
matchMap.put(match, lambda);
}
}
return lambda;
}
// free lambda that was mapped to Port/Match combination and return its
// value to caller.
private int freeLambda(OFPort port, Match match) {
synchronized (this) {
Integer lambda = matchMap.get(match);
if (lambda != null) {
portChannelMap.get(port).clear(lambda);
return lambda;
}
// 1 is a sane-ish default for Linc.
return 1;
}
}
// build matches - *tons of assumptions are made here based on Linc-OE's behavior.*
// gridType = 1 (DWDM)
// channelSpacing = 2 (50GHz)
// spectralWidth = 1 (fixed grid default value)
private Match buildMatch(Match original, CircuitSignalID sigid) {
Match.Builder mBuilder = factory.buildMatch();
original.getMatchFields().forEach(mf -> {
String name = mf.getName();
if (MatchField.OCH_SIGID.getName().equals(name)) {
mBuilder.setExact(MatchField.OCH_SIGID, sigid);
} else if (MatchField.OCH_SIGTYPE.getName().equals(name)) {
mBuilder.setExact(MatchField.OCH_SIGTYPE, U8.of((short) 1));
} else if (MatchField.IN_PORT.getName().equals(name)) {
mBuilder.setExact(MatchField.IN_PORT, original.get(MatchField.IN_PORT));
}
});
return mBuilder.build();
}
private List<OFAction> buildActions(List<OFInstruction> iList, CircuitSignalID sigid) {
Map<OFInstructionType, OFInstruction> instructions = iList.stream()
.collect(Collectors.toMap(OFInstruction::getType, inst -> inst));
OFInstruction inst = instructions.get(OFInstructionType.APPLY_ACTIONS);
if (inst == null) {
return Collections.emptyList();
}
List<OFAction> actions = new ArrayList<>();
OFInstructionApplyActions iaa = (OFInstructionApplyActions) inst;
if (iaa.getActions() == null) {
return actions;
}
iaa.getActions().forEach(action -> {
if (OFActionType.EXPERIMENTER == action.getType()) {
OFActionCircuit.Builder cBuilder = factory.actions().buildCircuit()
.setField(factory.oxms()
.buildOchSigid()
.setValue(sigid)
.build());
actions.add(cBuilder.build());
} else {
actions.add(action);
}
});
return actions;
}
private OFMessage buildFlowMod(Builder builder, OFFlowMod fm, Match m, List<OFAction> act) {
return builder
.setXid(fm.getXid())
.setCookie(fm.getCookie())
.setCookieMask(fm.getCookieMask())
.setTableId(fm.getTableId())
.setIdleTimeout(fm.getIdleTimeout())
.setHardTimeout(fm.getHardTimeout())
.setBufferId(fm.getBufferId())
.setOutPort(fm.getOutPort())
.setOutGroup(fm.getOutGroup())
.setFlags(fm.getFlags())
.setMatch(m)
.setActions(act)
.build();
}
}