blob: 5ea98c8a2d582c45bd332498e509c7e648469337 [file] [log] [blame]
MaoLuc201ae42017-02-06 17:57:01 -08001/*
2 * Copyright 2016-present 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 java.util.ArrayList;
19import java.util.HashMap;
20import java.util.List;
21import java.util.Set;
22
Jimmy Jine9b7a022016-08-12 16:56:48 -070023import io.netty.buffer.ByteBuf;
24import io.netty.buffer.Unpooled;
MaoLuc201ae42017-02-06 17:57:01 -080025import org.onosproject.drivers.optical.OpticalAdjacencyLinkService;
26import org.onosproject.net.ConnectPoint;
27import org.onosproject.net.Annotations;
28import org.onosproject.net.DefaultAnnotations;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.Link;
31import org.onosproject.net.Port;
32import org.onosproject.net.PortNumber;
33import org.onosproject.net.device.DefaultPortDescription;
34import org.onosproject.net.device.DeviceService;
35import org.onosproject.net.device.PortDescription;
36import org.onosproject.net.link.DefaultLinkDescription;
37import org.onosproject.net.link.LinkService;
38import org.onosproject.net.optical.OpticalAnnotations;
39import org.onosproject.openflow.controller.Dpid;
40import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
41import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest;
42import org.projectfloodlight.openflow.protocol.OFExpExtAdId;
43import org.projectfloodlight.openflow.protocol.OFExpPortAdidOtn;
44import org.projectfloodlight.openflow.protocol.OFExpPortAdjacency;
45import org.projectfloodlight.openflow.protocol.OFExpPortAdjacencyId;
46import org.projectfloodlight.openflow.protocol.OFExpPortAdjacencyRequest;
47import org.projectfloodlight.openflow.protocol.OFOplinkPortPower;
48import org.projectfloodlight.openflow.protocol.OFOplinkPortPowerRequest;
49
MaoLu93858982017-03-29 17:10:35 -070050import org.slf4j.Logger;
51
52import static org.slf4j.LoggerFactory.getLogger;
53
MaoLuc201ae42017-02-06 17:57:01 -080054/**
55 * Oplink handshaker utility.
56 */
57public class OplinkHandshakerUtil {
58
59 // Parent driver instance
60 private OpenFlowSwitchDriver driver;
61 // Total count of opspec in OFExpPortAdidOtn
62 private static final int OPSPEC_BYTES = 32;
63 // Bit count of id in opspec
64 private static final int OPSPEC_ID_BITS = 4;
65 // Start byte position of mac info
66 private static final int OPSPEC_MAC_POS = 18;
67 // Bit offset for mac
68 private static final int OPSPEC_MAC_BIT_OFF = 16;
69 // Start byte position of port info
70 private static final int OPSPEC_PORT_POS = 24;
71 // Right bit offset for mac
72 private static final int OPSPEC_PORT_BIT_OFF = 32;
73
MaoLu93858982017-03-29 17:10:35 -070074 // Log
75 private final Logger log = getLogger(getClass());
76
MaoLuc201ae42017-02-06 17:57:01 -080077 /**
78 * Create a new OplinkHandshakerUtil.
79 * @param driver parent driver instance
80 */
81 public OplinkHandshakerUtil(OpenFlowSwitchDriver driver) {
82 this.driver = driver;
83 }
84
85 /**
86 * Creates an oplink port power request OF message.
87 *
88 * @return OF message of oplink port power request
89 */
90 public OFOplinkPortPowerRequest buildPortPowerRequest() {
91 OFOplinkPortPowerRequest request = driver.factory().buildOplinkPortPowerRequest()
92 .setXid(driver.getNextTransactionId())
93 .build();
94 return request;
95 }
96
97 /**
98 * Creates port adjacency request OF message.
99 *
100 * @return OF message of oplink port adjacency request
101 */
102 public OFExpPortAdjacencyRequest buildPortAdjacencyRequest() {
103 OFExpPortAdjacencyRequest request = driver.factory().buildExpPortAdjacencyRequest()
104 .setXid(driver.getNextTransactionId())
105 .build();
106 return request;
107 }
108
109 /**
110 * Creates an oplink port description request OF message.
111 *
112 * @return OF message of oplink port description request
113 */
114 public OFCircuitPortsRequest buildCircuitPortsRequest() {
115 OFCircuitPortsRequest request = driver.factory().buildCircuitPortsRequest()
116 .setXid(driver.getNextTransactionId())
117 .build();
118 return request;
119 }
120
121 /**
122 * Creates port descriptions with current power.
123 *
124 * @param portPowers current power
125 * @return port descriptions
126 */
127 public List<PortDescription> buildPortPowerDescriptions(List<OFOplinkPortPower> portPowers) {
128 DeviceService deviceService = driver.handler().get(DeviceService.class);
129 List<Port> ports = deviceService.getPorts(driver.data().deviceId());
130 HashMap<Long, OFOplinkPortPower> powerMap = new HashMap<>(portPowers.size());
131 // Get each port power value
132 portPowers.forEach(power -> powerMap.put((long) power.getPort(), power));
133 final List<PortDescription> portDescs = new ArrayList<>();
134 for (Port port : ports) {
135 DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
136 builder.putAll(port.annotations());
137 OFOplinkPortPower power = powerMap.get(port.number().toLong());
138 if (power != null) {
139 // power value is actually signed-short value, down casting to recover sign bit.
140 builder.set(OpticalAnnotations.CURRENT_POWER, Short.toString((short) power.getPowerValue()));
141 }
142 portDescs.add(new DefaultPortDescription(port.number(), port.isEnabled(),
143 port.type(), port.portSpeed(), builder.build()));
144 }
145 return portDescs;
146 }
147
148 /**
149 * Creates port descriptions with adjacency.
150 *
151 * @param portAds adjacency information
152 * @return port descriptions
153 */
154 public List<PortDescription> buildPortAdjacencyDescriptions(List<OFExpPortAdjacency> portAds) {
155 DeviceService deviceService = driver.handler().get(DeviceService.class);
156 List<Port> ports = deviceService.getPorts(driver.data().deviceId());
157 // Map port's number with port's adjacency
158 HashMap<Long, OFExpPortAdjacency> adMap = new HashMap<>(portAds.size());
159 portAds.forEach(ad -> adMap.put((long) ad.getPortNo().getPortNumber(), ad));
160 List<PortDescription> portDescs = new ArrayList<>();
161 for (Port port : ports) {
162 DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
163 Annotations oldAnnotations = port.annotations();
164 builder.putAll(oldAnnotations);
165 OFExpPortAdjacency ad = adMap.get(port.number().toLong());
166 OplinkPortAdjacency neighbor = getNeighbor(ad);
MaoLu93858982017-03-29 17:10:35 -0700167 if (!linkValidation(deviceService, neighbor)) {
MaoLuc201ae42017-02-06 17:57:01 -0800168 // no neighbors found
169 builder.remove(OpticalAnnotations.NEIGHBOR_ID);
170 builder.remove(OpticalAnnotations.NEIGHBOR_PORT);
171 removeLink(port.number());
172 } else {
173 // neighbor discovered, add to port descriptions
174 String newId = neighbor.getDeviceId().toString();
175 String newPort = neighbor.getPort().toString();
176 // Check if annotation already exists
177 if (!newId.equals(oldAnnotations.value(OpticalAnnotations.NEIGHBOR_ID)) ||
MaoLu93858982017-03-29 17:10:35 -0700178 !newPort.equals(oldAnnotations.value(OpticalAnnotations.NEIGHBOR_PORT))) {
MaoLuc201ae42017-02-06 17:57:01 -0800179 builder.set(OpticalAnnotations.NEIGHBOR_ID, newId);
180 builder.set(OpticalAnnotations.NEIGHBOR_PORT, newPort);
181 }
182 addLink(port.number(), neighbor);
183 }
184 portDescs.add(new DefaultPortDescription(port.number(), port.isEnabled(),
MaoLu93858982017-03-29 17:10:35 -0700185 port.type(), port.portSpeed(), builder.build()));
MaoLuc201ae42017-02-06 17:57:01 -0800186 }
187 return portDescs;
188 }
189
190 private OplinkPortAdjacency getNeighbor(OFExpPortAdjacency ad) {
191 // Check input parameter
192 if (ad == null) {
193 return null;
194 }
195 // Get adjacency properties
196 for (OFExpPortAdjacencyId adid : ad.getProperties()) {
197 List<OFExpExtAdId> otns = adid.getAdId();
198 if (otns != null && otns.size() > 0) {
199 OFExpPortAdidOtn otn = (OFExpPortAdidOtn) otns.get(0);
200 // ITU-T G.7714 ETH MAC Format (in second 16 bytes of the following)
201 // |---------------------------------------------------------------------------|
202 // | Other format (16 bytes) |
203 // |---------------------------------------------------------------------------|
204 // | Header (2 bytes) | ID (4 BITS) | MAC (6 bytes) | Port (4 bytes) | Unused |
205 // |---------------------------------------------------------------------------|
Jimmy Jine9b7a022016-08-12 16:56:48 -0700206 ByteBuf buffer = Unpooled.buffer(OPSPEC_BYTES);
MaoLuc201ae42017-02-06 17:57:01 -0800207 otn.getOpspec().write32Bytes(buffer);
208 long mac = buffer.getLong(OPSPEC_MAC_POS) << OPSPEC_ID_BITS >>> OPSPEC_MAC_BIT_OFF;
209 int port = (int) (buffer.getLong(OPSPEC_PORT_POS) << OPSPEC_ID_BITS >>> OPSPEC_PORT_BIT_OFF);
210 // Oplink does not use the 4 most significant bytes of Dpid so Dpid can be
211 // constructed from MAC address
212 return new OplinkPortAdjacency(DeviceId.deviceId(Dpid.uri(new Dpid(mac))),
213 PortNumber.portNumber(port));
214 }
215 }
216 // Returns null if no properties found
217 return null;
218 }
219
MaoLu93858982017-03-29 17:10:35 -0700220 private boolean linkValidation(DeviceService deviceService, OplinkPortAdjacency neighbor) {
221 // check neighbor object
222 if (neighbor == null) {
223 return false;
224 }
225 // check src device is validate or not
226 if (!deviceService.isAvailable(neighbor.getDeviceId())) {
227 log.debug("Invalid adjacency device. devId = {}", neighbor.getDeviceId());
228 return false;
229 }
230 // check src port is validate or not
231 if (deviceService.getPort(neighbor.getDeviceId(), neighbor.getPort()) == null) {
232 log.debug("Invalid adjacency port. devId = {}, port = {}",
233 neighbor.getDeviceId(), neighbor.getPort());
234 return false;
235 }
236 // validate link
237 return true;
238 }
239
MaoLuc201ae42017-02-06 17:57:01 -0800240 // Add incoming link with port
241 private void addLink(PortNumber portNumber, OplinkPortAdjacency neighbor) {
242 ConnectPoint dst = new ConnectPoint(driver.handler().data().deviceId(), portNumber);
MaoLu93858982017-03-29 17:10:35 -0700243 Set<Link> links = driver.handler().get(LinkService.class).getIngressLinks(dst);
244 // find out if the new report link is the same as before
245 for (Link link : links) {
246 if (link.src().port().equals(neighbor.getPort())) {
247 return;
248 }
249 }
MaoLuc201ae42017-02-06 17:57:01 -0800250 OpticalAdjacencyLinkService adService = driver.handler().get(OpticalAdjacencyLinkService.class);
MaoLu93858982017-03-29 17:10:35 -0700251 // remove the old link of destination connect point
252 if (!links.isEmpty()) {
253 log.debug("Remove link of destination {}.", dst);
254 adService.linksVanished(dst);
255 }
256 // add the new link
257 ConnectPoint src = new ConnectPoint(neighbor.getDeviceId(), neighbor.getPort());
258 log.debug("Add link from {} to {}.", src, dst);
MaoLuc201ae42017-02-06 17:57:01 -0800259 adService.linkDetected(new DefaultLinkDescription(src, dst, Link.Type.OPTICAL));
260 }
261
262 // Remove incoming link with port if there are any.
263 private void removeLink(PortNumber portNumber) {
264 ConnectPoint dst = new ConnectPoint(driver.handler().data().deviceId(), portNumber);
265 // Check so only incoming links are removed
266 Set<Link> links = driver.handler().get(LinkService.class).getIngressLinks(dst);
MaoLu93858982017-03-29 17:10:35 -0700267 // If link exists, remove it, otherwise return
MaoLuc201ae42017-02-06 17:57:01 -0800268 if (links.isEmpty()) {
269 return;
270 }
MaoLu93858982017-03-29 17:10:35 -0700271 log.debug("Remove link for {}.", dst);
272 driver.handler().get(OpticalAdjacencyLinkService.class).linksVanished(dst);
MaoLuc201ae42017-02-06 17:57:01 -0800273 }
274
275 private class OplinkPortAdjacency {
276 private DeviceId deviceId;
277 private PortNumber portNumber;
278
279 public OplinkPortAdjacency(DeviceId deviceId, PortNumber portNumber) {
280 this.deviceId = deviceId;
281 this.portNumber = portNumber;
282 }
283
284 public DeviceId getDeviceId() {
285 return deviceId;
286 }
287
288 public PortNumber getPort() {
289 return portNumber;
290 }
291 }
292}