f91e2a7ed7af641fda32c8512c27273eaed7458f
[onosfw.git] /
1 /*
2  * Copyright 2015 Open Networking Laboratory
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.onosproject.driver.handshaker;
17
18 import com.google.common.collect.ImmutableSet;
19 import org.onosproject.net.Device;
20 import org.onosproject.openflow.controller.OpenFlowOpticalSwitch;
21 import org.onosproject.openflow.controller.PortDescPropertyType;
22 import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
23 import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted;
24 import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeCompleted;
25 import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted;
26 import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
27 import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply;
28 import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest;
29 import org.projectfloodlight.openflow.protocol.OFFlowMod;
30 import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest;
31 import org.projectfloodlight.openflow.protocol.OFMessage;
32 import org.projectfloodlight.openflow.protocol.OFObject;
33 import org.projectfloodlight.openflow.protocol.OFPortDesc;
34 import org.projectfloodlight.openflow.protocol.OFPortDescPropOpticalTransport;
35 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
36 import org.projectfloodlight.openflow.protocol.OFPortOptical;
37 import org.projectfloodlight.openflow.protocol.OFStatsReply;
38 import org.projectfloodlight.openflow.protocol.OFStatsType;
39 import org.projectfloodlight.openflow.protocol.action.OFAction;
40 import org.projectfloodlight.openflow.protocol.action.OFActionSetField;
41 import org.projectfloodlight.openflow.protocol.match.Match;
42 import org.projectfloodlight.openflow.protocol.match.MatchField;
43 import org.projectfloodlight.openflow.protocol.oxm.OFOxmExpOchSigId;
44 import org.projectfloodlight.openflow.types.CircuitSignalID;
45 import org.projectfloodlight.openflow.types.OFPort;
46 import org.projectfloodlight.openflow.types.U8;
47
48 import java.io.IOException;
49 import java.util.ArrayList;
50 import java.util.Collections;
51 import java.util.LinkedList;
52 import java.util.List;
53 import java.util.Set;
54 import java.util.concurrent.atomic.AtomicBoolean;
55
56 /**
57  * LINC-OE Optical Emulator switch class.
58  *
59  * The LINC ROADM emulator exposes two types of ports: OCh ports connect to ports in the packet layer,
60  * while OMS ports connect to an OMS port on a neighbouring ROADM.
61  *
62  * LINC sends the tap ports (OCh for our purposes) in the regular port desc stats reply,
63  * while it sends *all* ports (both tap and WDM ports, i.e., OCh and OMS) in the experimenter port desc stats reply.
64  *
65  * As LINC implements custom OF optical extensions (in contrast to the final standard as specified in
66  * ONF TS-022 (March 15, 2015), we need to rewrite flow stat requests and flow mods in {@link #sendMsg(OFMessage)}.
67  *
68  */
69 public class OfOpticalSwitchImplLinc13
70  extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch {
71
72     private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false);
73     private long barrierXidToWaitFor = -1;
74
75     private List<OFPortOptical> opticalPorts;
76
77     @Override
78     public void startDriverHandshake() {
79         log.warn("Starting driver handshake for sw {}", getStringId());
80         if (startDriverHandshakeCalled) {
81             throw new SwitchDriverSubHandshakeAlreadyStarted();
82         }
83         startDriverHandshakeCalled = true;
84         try {
85             sendHandshakeOFExperimenterPortDescRequest();
86         } catch (IOException e) {
87             log.error("LINC-OE exception while sending experimenter port desc:",
88                      e.getMessage());
89             e.printStackTrace();
90         }
91     }
92
93     @Override
94     public boolean isDriverHandshakeComplete() {
95         return driverHandshakeComplete.get();
96     }
97
98     @Override
99     public void processDriverHandshakeMessage(OFMessage m) {
100         if (!startDriverHandshakeCalled) {
101             throw new SwitchDriverSubHandshakeNotStarted();
102         }
103         if (driverHandshakeComplete.get()) {
104             throw new SwitchDriverSubHandshakeCompleted(m);
105         }
106
107         switch (m.getType()) {
108             case BARRIER_REPLY:
109                 if (m.getXid() == barrierXidToWaitFor) {
110                     log.debug("LINC-OE Received barrier response");
111                 }
112                 break;
113             case ERROR:
114                 log.error("Switch {} Error {}", getStringId(), m);
115                 break;
116             case FEATURES_REPLY:
117                 break;
118             case FLOW_REMOVED:
119                 break;
120             case GET_ASYNC_REPLY:
121                 break;
122             case PACKET_IN:
123                 break;
124             case PORT_STATUS:
125                 log.warn("****LINC-OE Port Status {} {}", getStringId(), m);
126                 processOFPortStatus((OFCircuitPortStatus) m);
127                 break;
128             case QUEUE_GET_CONFIG_REPLY:
129                 break;
130             case ROLE_REPLY:
131                 break;
132             case STATS_REPLY:
133                 OFStatsReply stats = (OFStatsReply) m;
134                 if (stats.getStatsType() == OFStatsType.EXPERIMENTER) {
135                     log.warn("LINC-OE : Received stats reply message {}", m);
136                     createOpticalPortList((OFCircuitPortsReply) m);
137                     driverHandshakeComplete.set(true);
138                 }
139                 break;
140             default:
141                 log.warn("Received message {} during switch-driver " +
142                                  "subhandshake " + "from switch {} ... " +
143                                  "Ignoring message", m,
144                          getStringId());
145
146         }
147     }
148
149     public void processOFPortStatus(OFCircuitPortStatus ps) {
150         log.debug("LINC-OE ..OF Port Status :", ps);
151     }
152
153     private void sendHandshakeOFExperimenterPortDescRequest() throws
154             IOException {
155         // send multi part message for port description for optical switches
156         OFCircuitPortsRequest circuitPortsRequest = factory()
157                 .buildCircuitPortsRequest().setXid(getNextTransactionId())
158                 .build();
159         log.warn("LINC-OE : Sending experimented circuit port stats " +
160                          "message " +
161                          "{}",
162                  circuitPortsRequest.toString());
163         this.sendHandshakeMessage(circuitPortsRequest);
164     }
165
166     @Override
167     /**
168      * Returns a list of standard (Ethernet) ports.
169      *
170      * @return List of ports
171      */
172     public List<OFPortDesc> getPorts() {
173         return Collections.EMPTY_LIST;
174     }
175
176     /**
177      * Rewrite match object to use LINC OF optical extensions.
178      *
179      * @param match original match
180      * @return rewritten match
181      */
182     private Match rewriteMatch(Match match) {
183         Match.Builder mBuilder = factory().buildMatch();
184         for (MatchField mf : match.getMatchFields()) {
185             if (mf == MatchField.EXP_OCH_SIG_ID) {
186                 mBuilder.setExact(MatchField.OCH_SIGID, (CircuitSignalID) match.get(mf));
187                 continue;
188             }
189             if (mf == MatchField.EXP_OCH_SIGTYPE) {
190                 mBuilder.setExact(MatchField.OCH_SIGTYPE, (U8) match.get(mf));
191                 continue;
192             }
193             mBuilder.setExact(mf, match.get(mf));
194         }
195
196         return mBuilder.build();
197     }
198
199     /**
200      * Rewrite actions to use LINC OF optical extensions.
201      *
202      * @param actions original actions
203      * @return rewritten actions
204      */
205     private List<OFAction> rewriteActions(List<OFAction> actions) {
206         List<OFAction> newActions = new LinkedList<>();
207
208         for (OFAction action : actions) {
209             if (!(action instanceof OFActionSetField)) {
210                 newActions.add(action);
211                 continue;
212             }
213
214             OFActionSetField sf = (OFActionSetField) action;
215             if (!(sf instanceof OFOxmExpOchSigId)) {
216                 newActions.add(action);
217             }
218
219             OFOxmExpOchSigId oxm = (OFOxmExpOchSigId) sf.getField();
220             CircuitSignalID signalId = oxm.getValue();
221
222             newActions.add(
223                     factory().actions().circuit(factory().oxms().ochSigid(signalId)));
224         }
225
226         return newActions;
227     }
228
229     @Override
230     public void sendMsg(OFMessage msg) {
231         // Ignore everything but flow mods and stat requests
232         if (!(msg instanceof OFFlowMod || msg instanceof OFFlowStatsRequest)) {
233             super.sendMsg(msg);
234             return;
235         }
236
237         Match newMatch;
238         OFMessage newMsg = null;
239
240         if (msg instanceof OFFlowStatsRequest) {
241             // Rewrite match only
242             OFFlowStatsRequest fsr = (OFFlowStatsRequest) msg;
243             newMatch = rewriteMatch(fsr.getMatch());
244             newMsg = fsr.createBuilder().setMatch(newMatch).build();
245         } else if (msg instanceof OFFlowMod) {
246             // Rewrite match and actions
247             OFFlowMod fm = (OFFlowMod) msg;
248             newMatch = rewriteMatch(fm.getMatch());
249             List<OFAction> actions = rewriteActions(fm.getActions());
250
251             newMsg = fm.createBuilder().setMatch(newMatch).setActions(actions).build();
252         }
253
254         super.sendMsg(newMsg);
255     }
256
257     @Override
258     public Boolean supportNxRole() {
259         return false;
260     }
261
262     @Override
263     public Device.Type deviceType() {
264         return Device.Type.ROADM;
265     }
266
267     /**
268      * Checks if given port is also part of the regular port desc stats, i.e., is the port a tap port.
269      *
270      * @param port given OF port
271      * @return true if the port is a tap (OCh), false otherwise (OMS port)
272      */
273     private boolean hasPort(OFPort port) {
274         for (OFPortDescStatsReply reply : this.ports) {
275             for (OFPortDesc p : reply.getEntries()) {
276                 if (p.getPortNo().equals(port)) {
277                     return true;
278                 }
279             }
280         }
281
282         return false;
283     }
284
285     /**
286      * Creates an OpenFlow optical port based on the given port and transport type.
287      *
288      * @param port OpenFlow optical port
289      * @param type transport type
290      * @return OpenFlow optical port
291      */
292     private OFPortOptical createOpticalPort(OFPortOptical port, short type) {
293         List<OFPortDescPropOpticalTransport> descList = new ArrayList<>(port.getDesc().size());
294
295         for (OFPortDescPropOpticalTransport desc : port.getDesc()) {
296             OFPortDescPropOpticalTransport newDesc = desc.createBuilder()
297                     .setType(desc.getType())
298                     .setPortSignalType(type)
299                     .setPortType(desc.getPortType())
300                     .setReserved(desc.getReserved())
301                     .build();
302             descList.add(newDesc);
303         }
304
305         OFPortOptical newPort = port.createBuilder()
306                 .setConfig(port.getConfig())
307                 .setDesc(descList)
308                 .setHwAddr(port.getHwAddr())
309                 .setName(port.getName())
310                 .setPortNo(port.getPortNo())
311                 .setState(port.getState())
312                 .build();
313
314         return newPort;
315     }
316
317     /**
318      * Builds list of OFPortOptical ports based on the multi-part circuit ports reply.
319      *
320      * Ensure the optical transport port's signal type is configured correctly.
321      *
322      * @param wPorts OF reply with circuit ports
323      */
324     private void createOpticalPortList(OFCircuitPortsReply wPorts) {
325         opticalPorts = new ArrayList<>(wPorts.getEntries().size());
326
327         for (OFPortOptical p : wPorts.getEntries()) {
328             short signalType;
329
330             // FIXME: use constants once loxi has full optical extensions
331             if (hasPort(p.getPortNo())) {
332                 signalType = 5;      // OCH port
333             } else {
334                 signalType = 2;      // OMS port
335             }
336
337             opticalPorts.add(createOpticalPort(p, signalType));
338         }
339     }
340
341     @Override
342     public List<? extends OFObject> getPortsOf(PortDescPropertyType type) {
343         if (!type.equals(PortDescPropertyType.OPTICAL_TRANSPORT)) {
344             return Collections.EMPTY_LIST;
345         }
346
347         return opticalPorts;
348     }
349
350     @Override
351     public Set<PortDescPropertyType> getPortTypes() {
352         return ImmutableSet.of(PortDescPropertyType.OPTICAL_TRANSPORT);
353     }
354 }