/* * Copyright 2016-present 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.optical.handshaker; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Set; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.onosproject.drivers.optical.OpticalAdjacencyLinkService; import org.onosproject.net.ConnectPoint; import org.onosproject.net.Annotations; import org.onosproject.net.DefaultAnnotations; import org.onosproject.net.DeviceId; import org.onosproject.net.Link; import org.onosproject.net.Port; import org.onosproject.net.PortNumber; import org.onosproject.net.device.DefaultPortDescription; import org.onosproject.net.device.DeviceService; import org.onosproject.net.device.PortDescription; import org.onosproject.net.link.DefaultLinkDescription; import org.onosproject.net.link.LinkService; import org.onosproject.net.optical.OpticalAnnotations; import org.onosproject.openflow.controller.Dpid; import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver; import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest; import org.projectfloodlight.openflow.protocol.OFExpExtAdId; import org.projectfloodlight.openflow.protocol.OFExpPortAdidOtn; import org.projectfloodlight.openflow.protocol.OFExpPortAdjacency; import org.projectfloodlight.openflow.protocol.OFExpPortAdjacencyId; import org.projectfloodlight.openflow.protocol.OFExpPortAdjacencyRequest; import org.projectfloodlight.openflow.protocol.OFOplinkPortPower; import org.projectfloodlight.openflow.protocol.OFOplinkPortPowerRequest; import org.slf4j.Logger; import static org.slf4j.LoggerFactory.getLogger; /** * Oplink handshaker utility. */ public class OplinkHandshakerUtil { // Parent driver instance private OpenFlowSwitchDriver driver; // Total count of opspec in OFExpPortAdidOtn private static final int OPSPEC_BYTES = 32; // Bit count of id in opspec private static final int OPSPEC_ID_BITS = 4; // Start byte position of mac info private static final int OPSPEC_MAC_POS = 18; // Bit offset for mac private static final int OPSPEC_MAC_BIT_OFF = 16; // Start byte position of port info private static final int OPSPEC_PORT_POS = 24; // Right bit offset for mac private static final int OPSPEC_PORT_BIT_OFF = 32; // Log private final Logger log = getLogger(getClass()); /** * Create a new OplinkHandshakerUtil. * @param driver parent driver instance */ public OplinkHandshakerUtil(OpenFlowSwitchDriver driver) { this.driver = driver; } /** * Creates an oplink port power request OF message. * * @return OF message of oplink port power request */ public OFOplinkPortPowerRequest buildPortPowerRequest() { OFOplinkPortPowerRequest request = driver.factory().buildOplinkPortPowerRequest() .setXid(driver.getNextTransactionId()) .build(); return request; } /** * Creates port adjacency request OF message. * * @return OF message of oplink port adjacency request */ public OFExpPortAdjacencyRequest buildPortAdjacencyRequest() { OFExpPortAdjacencyRequest request = driver.factory().buildExpPortAdjacencyRequest() .setXid(driver.getNextTransactionId()) .build(); return request; } /** * Creates an oplink port description request OF message. * * @return OF message of oplink port description request */ public OFCircuitPortsRequest buildCircuitPortsRequest() { OFCircuitPortsRequest request = driver.factory().buildCircuitPortsRequest() .setXid(driver.getNextTransactionId()) .build(); return request; } /** * Creates port descriptions with current power. * * @param portPowers current power * @return port descriptions */ public List<PortDescription> buildPortPowerDescriptions(List<OFOplinkPortPower> portPowers) { DeviceService deviceService = driver.handler().get(DeviceService.class); List<Port> ports = deviceService.getPorts(driver.data().deviceId()); HashMap<Long, OFOplinkPortPower> powerMap = new HashMap<>(portPowers.size()); // Get each port power value portPowers.forEach(power -> powerMap.put((long) power.getPort(), power)); final List<PortDescription> portDescs = new ArrayList<>(); for (Port port : ports) { DefaultAnnotations.Builder builder = DefaultAnnotations.builder(); builder.putAll(port.annotations()); OFOplinkPortPower power = powerMap.get(port.number().toLong()); if (power != null) { // power value is actually signed-short value, down casting to recover sign bit. builder.set(OpticalAnnotations.CURRENT_POWER, Short.toString((short) power.getPowerValue())); } portDescs.add(new DefaultPortDescription(port.number(), port.isEnabled(), port.type(), port.portSpeed(), builder.build())); } return portDescs; } /** * Creates port descriptions with adjacency. * * @param portAds adjacency information * @return port descriptions */ public List<PortDescription> buildPortAdjacencyDescriptions(List<OFExpPortAdjacency> portAds) { DeviceService deviceService = driver.handler().get(DeviceService.class); List<Port> ports = deviceService.getPorts(driver.data().deviceId()); // Map port's number with port's adjacency HashMap<Long, OFExpPortAdjacency> adMap = new HashMap<>(portAds.size()); portAds.forEach(ad -> adMap.put((long) ad.getPortNo().getPortNumber(), ad)); List<PortDescription> portDescs = new ArrayList<>(); for (Port port : ports) { DefaultAnnotations.Builder builder = DefaultAnnotations.builder(); Annotations oldAnnotations = port.annotations(); builder.putAll(oldAnnotations); OFExpPortAdjacency ad = adMap.get(port.number().toLong()); OplinkPortAdjacency neighbor = getNeighbor(ad); if (!linkValidation(deviceService, neighbor)) { // no neighbors found builder.remove(OpticalAnnotations.NEIGHBOR_ID); builder.remove(OpticalAnnotations.NEIGHBOR_PORT); removeLink(port.number()); } else { // neighbor discovered, add to port descriptions String newId = neighbor.getDeviceId().toString(); String newPort = neighbor.getPort().toString(); // Check if annotation already exists if (!newId.equals(oldAnnotations.value(OpticalAnnotations.NEIGHBOR_ID)) || !newPort.equals(oldAnnotations.value(OpticalAnnotations.NEIGHBOR_PORT))) { builder.set(OpticalAnnotations.NEIGHBOR_ID, newId); builder.set(OpticalAnnotations.NEIGHBOR_PORT, newPort); } addLink(port.number(), neighbor); } portDescs.add(new DefaultPortDescription(port.number(), port.isEnabled(), port.type(), port.portSpeed(), builder.build())); } return portDescs; } private OplinkPortAdjacency getNeighbor(OFExpPortAdjacency ad) { // Check input parameter if (ad == null) { return null; } // Get adjacency properties for (OFExpPortAdjacencyId adid : ad.getProperties()) { List<OFExpExtAdId> otns = adid.getAdId(); if (otns != null && otns.size() > 0) { OFExpPortAdidOtn otn = (OFExpPortAdidOtn) otns.get(0); // ITU-T G.7714 ETH MAC Format (in second 16 bytes of the following) // |---------------------------------------------------------------------------| // | Other format (16 bytes) | // |---------------------------------------------------------------------------| // | Header (2 bytes) | ID (4 BITS) | MAC (6 bytes) | Port (4 bytes) | Unused | // |---------------------------------------------------------------------------| ChannelBuffer buffer = ChannelBuffers.buffer(OPSPEC_BYTES); otn.getOpspec().write32Bytes(buffer); long mac = buffer.getLong(OPSPEC_MAC_POS) << OPSPEC_ID_BITS >>> OPSPEC_MAC_BIT_OFF; int port = (int) (buffer.getLong(OPSPEC_PORT_POS) << OPSPEC_ID_BITS >>> OPSPEC_PORT_BIT_OFF); // Oplink does not use the 4 most significant bytes of Dpid so Dpid can be // constructed from MAC address return new OplinkPortAdjacency(DeviceId.deviceId(Dpid.uri(new Dpid(mac))), PortNumber.portNumber(port)); } } // Returns null if no properties found return null; } private boolean linkValidation(DeviceService deviceService, OplinkPortAdjacency neighbor) { // check neighbor object if (neighbor == null) { return false; } // check src device is validate or not if (!deviceService.isAvailable(neighbor.getDeviceId())) { log.debug("Invalid adjacency device. devId = {}", neighbor.getDeviceId()); return false; } // check src port is validate or not if (deviceService.getPort(neighbor.getDeviceId(), neighbor.getPort()) == null) { log.debug("Invalid adjacency port. devId = {}, port = {}", neighbor.getDeviceId(), neighbor.getPort()); return false; } // validate link return true; } // Add incoming link with port private void addLink(PortNumber portNumber, OplinkPortAdjacency neighbor) { ConnectPoint dst = new ConnectPoint(driver.handler().data().deviceId(), portNumber); Set<Link> links = driver.handler().get(LinkService.class).getIngressLinks(dst); // find out if the new report link is the same as before for (Link link : links) { if (link.src().port().equals(neighbor.getPort())) { return; } } OpticalAdjacencyLinkService adService = driver.handler().get(OpticalAdjacencyLinkService.class); // remove the old link of destination connect point if (!links.isEmpty()) { log.debug("Remove link of destination {}.", dst); adService.linksVanished(dst); } // add the new link ConnectPoint src = new ConnectPoint(neighbor.getDeviceId(), neighbor.getPort()); log.debug("Add link from {} to {}.", src, dst); adService.linkDetected(new DefaultLinkDescription(src, dst, Link.Type.OPTICAL)); } // Remove incoming link with port if there are any. private void removeLink(PortNumber portNumber) { ConnectPoint dst = new ConnectPoint(driver.handler().data().deviceId(), portNumber); // Check so only incoming links are removed Set<Link> links = driver.handler().get(LinkService.class).getIngressLinks(dst); // If link exists, remove it, otherwise return if (links.isEmpty()) { return; } log.debug("Remove link for {}.", dst); driver.handler().get(OpticalAdjacencyLinkService.class).linksVanished(dst); } private class OplinkPortAdjacency { private DeviceId deviceId; private PortNumber portNumber; public OplinkPortAdjacency(DeviceId deviceId, PortNumber portNumber) { this.deviceId = deviceId; this.portNumber = portNumber; } public DeviceId getDeviceId() { return deviceId; } public PortNumber getPort() { return portNumber; } } }