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