blob: 4ea954c706e7bb8d4b1fc35bf4653dbc33b71c44 [file] [log] [blame]
yjimmyyb94f93b2016-07-11 16:03:48 -07001/*
2 * Copyright 2016 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 */
16package org.onosproject.driver.optical.handshaker;
17
18import com.google.common.collect.ImmutableList;
19import com.google.common.collect.ImmutableSet;
20
21import java.io.IOException;
22import java.util.ArrayList;
23import java.util.Collections;
24import java.util.HashMap;
25import java.util.List;
26import java.util.Set;
27import java.util.concurrent.atomic.AtomicBoolean;
28
29import org.jboss.netty.buffer.ChannelBuffer;
30import org.jboss.netty.buffer.ChannelBuffers;
31import org.onosproject.drivers.optical.OpticalAdjacencyLinkService;
32import org.onosproject.net.Annotations;
33import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.DefaultAnnotations;
35import org.onosproject.net.Device;
36import org.onosproject.net.DeviceId;
37import org.onosproject.net.Link;
38import org.onosproject.net.Port;
39import org.onosproject.net.PortNumber;
40import org.onosproject.net.device.DefaultPortDescription;
41import org.onosproject.net.device.DeviceService;
42import org.onosproject.net.device.PortDescription;
43import org.onosproject.net.link.DefaultLinkDescription;
44import org.onosproject.net.optical.OpticalAnnotations;
45import org.onosproject.openflow.controller.Dpid;
46import org.onosproject.openflow.controller.OpenFlowOpticalSwitch;
47import org.onosproject.openflow.controller.PortDescPropertyType;
48import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
49import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted;
50import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeCompleted;
51import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted;
52import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
53import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply;
54import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest;
55import org.projectfloodlight.openflow.protocol.OFExpExtAdId;
56import org.projectfloodlight.openflow.protocol.OFExpPortAdidOtn;
57import org.projectfloodlight.openflow.protocol.OFExpPortAdjacency;
58import org.projectfloodlight.openflow.protocol.OFExpPortAdjacencyId;
59import org.projectfloodlight.openflow.protocol.OFExpPortAdjacencyReply;
60import org.projectfloodlight.openflow.protocol.OFExpPortAdjacencyRequest;
61import org.projectfloodlight.openflow.protocol.OFMessage;
62import org.projectfloodlight.openflow.protocol.OFObject;
63import org.projectfloodlight.openflow.protocol.OFOplinkPortPower;
64import org.projectfloodlight.openflow.protocol.OFPortDesc;
65import org.projectfloodlight.openflow.protocol.OFPortOptical;
66import org.projectfloodlight.openflow.protocol.OFStatsReply;
67import org.projectfloodlight.openflow.protocol.OFStatsRequest;
68import org.projectfloodlight.openflow.protocol.OFStatsType;
69import org.projectfloodlight.openflow.protocol.OFType;
70import org.projectfloodlight.openflow.protocol.OFOplinkPortPowerRequest;
71import org.projectfloodlight.openflow.protocol.OFOplinkPortPowerReply;
72
73
74/**
75 * Driver for Oplink single WSS 8D ROADM.
76 *
77 * Driver implements custom handshaker and supports for Optical channel Port based on OpenFlow OTN extension.
78 * The device consists of Och ports, and performances wavelength cross-connect among the ports.
79 */
80public class OplinkRoadm extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch {
81
82 private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false);
83 private List<OFPortOptical> opticalPorts;
84
85 @Override
86 public List<? extends OFObject> getPortsOf(PortDescPropertyType type) {
87 return ImmutableList.copyOf(opticalPorts);
88 }
89
90 @Override
91 /**
92 * Returns a list of standard (Ethernet) ports.
93 *
94 * @return List of ports
95 */
96 public List<OFPortDesc> getPorts() {
97 return Collections.EMPTY_LIST;
98 }
99
100 @Override
101 public Set<PortDescPropertyType> getPortTypes() {
102 return ImmutableSet.of(PortDescPropertyType.OPTICAL_TRANSPORT);
103 }
104
105 @Override
106 public Boolean supportNxRole() {
107 return false;
108 }
109
110 @Override
111 public void startDriverHandshake() {
112 log.warn("Starting driver handshake for sw {}", getStringId());
113 if (startDriverHandshakeCalled) {
114 throw new SwitchDriverSubHandshakeAlreadyStarted();
115 }
116 startDriverHandshakeCalled = true;
117 try {
118 sendHandshakeOFExperimenterPortDescRequest();
119 } catch (IOException e) {
120 log.error("OPLK ROADM exception while sending experimenter port desc:", e);
121 }
122 }
123
124 @Override
125 public boolean isDriverHandshakeComplete() {
126 return driverHandshakeComplete.get();
127 }
128
129 @Override
130 public void processDriverHandshakeMessage(OFMessage m) {
131
132 if (!startDriverHandshakeCalled) {
133 throw new SwitchDriverSubHandshakeNotStarted();
134 }
135
136 if (driverHandshakeComplete.get()) {
137 throw new SwitchDriverSubHandshakeCompleted(m);
138 }
139
140 switch (m.getType()) {
141 case BARRIER_REPLY:
142 log.debug("OPLK ROADM Received barrier response");
143 break;
144 case ERROR:
145 log.error("Switch {} Error {}", getStringId(), m);
146 break;
147 case FEATURES_REPLY:
148 break;
149 case FLOW_REMOVED:
150 break;
151 case GET_ASYNC_REPLY:
152 break;
153 case PACKET_IN:
154 break;
155 case PORT_STATUS:
156 processOFPortStatus((OFCircuitPortStatus) m);
157 break;
158 case QUEUE_GET_CONFIG_REPLY:
159 break;
160 case ROLE_REPLY:
161 break;
162 case STATS_REPLY:
163 OFStatsReply stats = (OFStatsReply) m;
164 if (stats.getStatsType() == OFStatsType.EXPERIMENTER) {
165 log.warn("OPLK ROADM : Received multipart (port desc) reply message {}", m);
166 //OTN Optical extension 1.0 port-desc
167 createOpticalPortList((OFCircuitPortsReply) m);
168 driverHandshakeComplete.set(true);
169 }
170 break;
171 default:
172 log.warn("Received message {} during switch-driver " +
173 "subhandshake " + "from switch {} ... " +
174 "Ignoring message", m,
175 getStringId());
176
177 }
178 }
179
180 private void processOFPortStatus(OFCircuitPortStatus ps) {
181 log.debug("OPLK ROADM ..OF Port Status :", ps);
182 }
183
184 @Override
185 public Device.Type deviceType() {
186 return Device.Type.ROADM;
187 }
188
189 @Override
190 public final void sendMsg(OFMessage m) {
191 List<OFMessage> messages = new ArrayList<>();
192 messages.add(m);
193 if (m.getType() == OFType.STATS_REQUEST) {
194 OFStatsRequest sr = (OFStatsRequest) m;
195 log.debug("OPLK ROADM rebuilding stats request type {}", sr.getStatsType());
196 switch (sr.getStatsType()) {
197 case PORT:
198 //replace with Oplink experiment stats message to get the port current power
199 OFOplinkPortPowerRequest powerRequest = this.factory().buildOplinkPortPowerRequest()
200 .setXid(sr.getXid())
201 .setFlags(sr.getFlags())
202 .build();
203 messages.add(powerRequest);
204 OFExpPortAdjacencyRequest adjacencyRequest = this.factory().buildExpPortAdjacencyRequest()
205 .setXid(sr.getXid())
206 .setFlags(sr.getFlags())
207 .build();
208 messages.add(adjacencyRequest);
209 break;
210 default:
211 break;
212 }
213 } else {
214 log.debug("OPLK ROADM sends msg:{}, as is", m.getType());
215 }
216
217 for (OFMessage message : messages) {
218 super.sendMsg(message);
219 }
220 }
221
222 private void sendHandshakeOFExperimenterPortDescRequest() throws IOException {
223 // send multi part message for port description for optical switches
224 OFCircuitPortsRequest circuitPortsRequest = factory()
225 .buildCircuitPortsRequest().setXid(getNextTransactionId())
226 .build();
227 log.info("OPLK ROADM : Sending experimented circuit port stats " +
228 "message " +
229 "{}",
230 circuitPortsRequest);
231 this.sendHandshakeMessage(circuitPortsRequest);
232 }
233
234 /**
235 * Builds list of OFPortOptical ports based on the multi-part circuit ports reply.
236 * Ensure the optical transport port's signal type is configured correctly.
237 *
238 * @param wPorts OF reply with circuit ports
239 */
240 private void createOpticalPortList(OFCircuitPortsReply wPorts) {
241 opticalPorts = new ArrayList<>();
242 opticalPorts.addAll(wPorts.getEntries());
243 }
244
245 @Override
246 public List<PortDescription> processExpPortStats(OFMessage msg) {
247 if (msg instanceof OFOplinkPortPowerReply) {
248 return buildPortPowerDescriptions(((OFOplinkPortPowerReply) msg).getEntries());
249 } else if (msg instanceof OFExpPortAdjacencyReply) {
250 return buildPortAdjacencyDescriptions(((OFExpPortAdjacencyReply) msg).getEntries());
251 }
252 return Collections.emptyList();
253 }
254
255 private List<PortDescription> buildPortPowerDescriptions(List<OFOplinkPortPower> portPowers) {
256 DeviceService deviceService = this.handler().get(DeviceService.class);
257 List<Port> ports = deviceService.getPorts(this.data().deviceId());
258 HashMap<Long, OFOplinkPortPower> powerMap = new HashMap<>(portPowers.size());
259 portPowers.forEach(power -> powerMap.put((long) power.getPort(), power));
260 final List<PortDescription> portDescs = new ArrayList<>();
261 for (Port port : ports) {
262 DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
263 builder.putAll(port.annotations());
264 OFOplinkPortPower power = powerMap.get(port.number().toLong());
265 if (power != null) {
266 builder.set(OpticalAnnotations.CURRENT_POWER, Long.toString(power.getPowerValue()));
267 }
268 portDescs.add(new DefaultPortDescription(port.number(), port.isEnabled(),
269 port.type(), port.portSpeed(), builder.build()));
270 }
271 return portDescs;
272 }
273
274 private OplinkPortAdjacency getNeighbor(OFExpPortAdjacency ad) {
275 for (OFExpPortAdjacencyId adid : ad.getProperties()) {
276 List<OFExpExtAdId> otns = adid.getAdId();
277 if (otns != null && otns.size() > 0) {
278 OFExpPortAdidOtn otn = (OFExpPortAdidOtn) otns.get(0);
279 // ITU-T G.7714 ETH MAC Format (in second 16 bytes of the following)
280 // |---------------------------------------------------------------------------|
281 // | Other format (16 bytes) |
282 // |---------------------------------------------------------------------------|
283 // | Header (2 bytes) | ID (4 bits) | MAC (6 bytes) | Port (4 bytes) | Unused |
284 // |---------------------------------------------------------------------------|
285 ChannelBuffer buffer = ChannelBuffers.buffer(32);
286 otn.getOpspec().write32Bytes(buffer);
287 long mac = buffer.getLong(18) << 4 >>> 16;
288 int port = (int) (buffer.getLong(24) << 4 >>> 32);
289 // Oplink does not use the 4 most significant bytes of Dpid so Dpid can be
290 // constructed from MAC address
291 return new OplinkPortAdjacency(DeviceId.deviceId(Dpid.uri(new Dpid(mac))),
292 PortNumber.portNumber(port));
293 }
294 }
295 return null;
296 }
297
298 private List<PortDescription> buildPortAdjacencyDescriptions(List<OFExpPortAdjacency> portAds) {
299 DeviceService deviceService = this.handler().get(DeviceService.class);
300 List<Port> ports = deviceService.getPorts(this.data().deviceId());
301
302 // Map port's number with port's adjacency
303 HashMap<Long, OFExpPortAdjacency> adMap = new HashMap<>(portAds.size());
304 portAds.forEach(ad -> adMap.put((long) ad.getPortNo().getPortNumber(), ad));
305
306 List<PortDescription> portDescs = new ArrayList<>();
307 for (Port port : ports) {
308 DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
309 Annotations oldAnnotations = port.annotations();
310 builder.putAll(oldAnnotations);
311 OFExpPortAdjacency ad = adMap.get(port.number().toLong());
312 if (ad != null) {
313 // neighbor discovered, add to port descriptions
314 OplinkPortAdjacency neighbor = getNeighbor(ad);
315 String newId = neighbor.getDeviceId().toString();
316 String newPort = neighbor.getPort().toString();
317 // Check if annotation already exists
318 if (!newId.equals(oldAnnotations.value(OpticalAnnotations.NEIGHBOR_ID)) ||
319 !newPort.equals(oldAnnotations.value(OpticalAnnotations.NEIGHBOR_PORT))) {
320 builder.set(OpticalAnnotations.NEIGHBOR_ID, newId);
321 builder.set(OpticalAnnotations.NEIGHBOR_PORT, newPort);
322 }
323 addLink(port.number(), neighbor);
324 } else {
325 // no neighbors found
326 builder.remove(OpticalAnnotations.NEIGHBOR_ID);
327 builder.remove(OpticalAnnotations.NEIGHBOR_PORT);
328 removeLink(port.number());
329 }
330 portDescs.add(new DefaultPortDescription(port.number(), port.isEnabled(),
331 port.type(), port.portSpeed(), builder.build()));
332 }
333 return portDescs;
334 }
335
336 private void addLink(PortNumber portNumber, OplinkPortAdjacency neighbor) {
337 ConnectPoint dst = new ConnectPoint(handler().data().deviceId(), portNumber);
338 ConnectPoint src = new ConnectPoint(neighbor.getDeviceId(), neighbor.portNumber);
339 OpticalAdjacencyLinkService adService =
340 this.handler().get(OpticalAdjacencyLinkService.class);
341 adService.linkDetected(new DefaultLinkDescription(src, dst, Link.Type.OPTICAL));
342 }
343
344 // Remove incoming link with port if there are any.
345 private void removeLink(PortNumber portNumber) {
346 ConnectPoint dst = new ConnectPoint(handler().data().deviceId(), portNumber);
347 OpticalAdjacencyLinkService adService =
348 this.handler().get(OpticalAdjacencyLinkService.class);
349 adService.linksVanished(dst);
350 }
351
352 private class OplinkPortAdjacency {
353 private DeviceId deviceId;
354 private PortNumber portNumber;
355
356 public OplinkPortAdjacency(DeviceId deviceId, PortNumber portNumber) {
357 this.deviceId = deviceId;
358 this.portNumber = portNumber;
359 }
360
361 public DeviceId getDeviceId() {
362 return deviceId;
363 }
364
365 public PortNumber getPort() {
366 return portNumber;
367 }
368 }
369}