/* * Copyright 2014-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.optical; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.onosproject.cluster.ClusterService; import org.onosproject.cluster.NodeId; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.mastership.MastershipService; import org.onosproject.net.CltSignalType; import org.onosproject.net.ConnectPoint; import org.onosproject.net.Device; import org.onosproject.net.Host; import org.onosproject.net.Link; import org.onosproject.net.OduSignalType; import org.onosproject.net.Path; import org.onosproject.net.Port; import org.onosproject.net.device.DeviceService; import org.onosproject.net.host.HostService; import org.onosproject.net.intent.HostToHostIntent; import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.IntentEvent; import org.onosproject.net.intent.IntentListener; import org.onosproject.net.intent.IntentService; import org.onosproject.net.intent.IntentState; import org.onosproject.net.intent.OpticalCircuitIntent; import org.onosproject.net.intent.OpticalConnectivityIntent; import org.onosproject.net.intent.PointToPointIntent; import org.onosproject.net.optical.OchPort; import org.onosproject.net.optical.OduCltPort; import org.onosproject.net.topology.LinkWeight; import org.onosproject.net.topology.PathService; import org.onosproject.net.topology.TopologyEdge; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static org.onosproject.net.optical.device.OpticalDeviceServiceView.opticalView; /** * OpticalPathProvisioner listens for event notifications from the Intent F/W. * It generates one or more opticalConnectivityIntent(s) and submits (or withdraws) to Intent F/W * for adding/releasing capacity at the packet layer. * * @deprecated in Goldeneye (1.6.0) */ @Deprecated @Component(immediate = true) public class OpticalPathProvisioner { protected static final Logger log = LoggerFactory .getLogger(OpticalPathProvisioner.class); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) private IntentService intentService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected PathService pathService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected CoreService coreService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected HostService hostService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected MastershipService mastershipService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ClusterService clusterService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected DeviceService deviceService; private ApplicationId appId; private final InternalOpticalPathProvisioner pathProvisioner = new InternalOpticalPathProvisioner(); @Activate protected void activate() { deviceService = opticalView(deviceService); intentService.addListener(pathProvisioner); appId = coreService.registerApplication("org.onosproject.optical"); initOpticalPorts(); log.info("Started"); } @Deactivate protected void deactivate() { intentService.removeListener(pathProvisioner); log.info("Stopped"); } /** * Initialize availability of optical ports. */ private void initOpticalPorts() { // TODO: check for existing optical intents return; } public class InternalOpticalPathProvisioner implements IntentListener { @Override public void event(IntentEvent event) { switch (event.type()) { case INSTALL_REQ: break; case INSTALLED: break; case FAILED: log.info("Intent {} failed, calling optical path provisioning app.", event.subject()); setupLightpath(event.subject()); break; default: break; } } private void setupLightpath(Intent intent) { checkNotNull(intent); // TODO change the coordination approach between packet intents and optical intents // Low speed LLDP may cause multiple calls which are not expected if (intentService.getIntentState(intent.key()) != IntentState.FAILED) { return; } // Get source and destination based on intent type ConnectPoint src; ConnectPoint dst; if (intent instanceof HostToHostIntent) { HostToHostIntent hostToHostIntent = (HostToHostIntent) intent; Host one = hostService.getHost(hostToHostIntent.one()); Host two = hostService.getHost(hostToHostIntent.two()); checkNotNull(one); checkNotNull(two); src = one.location(); dst = two.location(); } else if (intent instanceof PointToPointIntent) { PointToPointIntent p2pIntent = (PointToPointIntent) intent; src = p2pIntent.ingressPoint(); dst = p2pIntent.egressPoint(); } else { return; } if (src == null || dst == null) { return; } // Ignore if we're not the master for the intent's origin device NodeId localNode = clusterService.getLocalNode().id(); NodeId sourceMaster = mastershipService.getMasterFor(src.deviceId()); if (!localNode.equals(sourceMaster)) { return; } // Generate optical connectivity intents List<Intent> intents = getOpticalIntents(src, dst); // Submit the intents for (Intent i : intents) { intentService.submit(i); log.debug("Submitted an intent: {}", i); } } /** * Returns list of cross connection points of missing optical path sections. * * Scans the given multi-layer path and looks for sections that use cross connect links. * The ingress and egress points in the optical layer are returned in a list. * * @param path the multi-layer path * @return list of cross connection points on the optical layer */ private List<ConnectPoint> getCrossConnectPoints(Path path) { boolean scanning = false; List<ConnectPoint> connectPoints = new LinkedList<>(); for (Link link : path.links()) { if (!isCrossConnectLink(link)) { continue; } if (scanning) { connectPoints.add(checkNotNull(link.src())); scanning = false; } else { connectPoints.add(checkNotNull(link.dst())); scanning = true; } } return connectPoints; } /** * Checks if cross connect points are of same type. * * @param crossConnectPoints list of cross connection points * @return true if cross connect point pairs are of same type, false otherwise */ private boolean checkCrossConnectPoints(List<ConnectPoint> crossConnectPoints) { checkArgument(crossConnectPoints.size() % 2 == 0); Iterator<ConnectPoint> itr = crossConnectPoints.iterator(); while (itr.hasNext()) { // checkArgument at start ensures we'll always have pairs of connect points ConnectPoint src = itr.next(); ConnectPoint dst = itr.next(); Device.Type srcType = deviceService.getDevice(src.deviceId()).type(); Device.Type dstType = deviceService.getDevice(dst.deviceId()).type(); // Only support connections between identical port types if (srcType != dstType) { log.warn("Unsupported mix of cross connect points"); return false; } } return true; } /** * Scans the list of cross connection points and returns a list of optical connectivity intents. * * @param crossConnectPoints list of cross connection points * @return list of optical connectivity intents */ private List<Intent> getIntents(List<ConnectPoint> crossConnectPoints) { checkArgument(crossConnectPoints.size() % 2 == 0); List<Intent> intents = new LinkedList<>(); Iterator<ConnectPoint> itr = crossConnectPoints.iterator(); while (itr.hasNext()) { // checkArgument at start ensures we'll always have pairs of connect points ConnectPoint src = itr.next(); ConnectPoint dst = itr.next(); Port srcPort = deviceService.getPort(src.deviceId(), src.port()); Port dstPort = deviceService.getPort(dst.deviceId(), dst.port()); if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) { // Create OTN circuit Intent circuitIntent = OpticalCircuitIntent.builder() .appId(appId) .src(src) .dst(dst) .signalType(CltSignalType.CLT_10GBE) .bidirectional(true) .build(); intents.add(circuitIntent); } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) { // Create lightpath // FIXME: hardcoded ODU signal type Intent opticalIntent = OpticalConnectivityIntent.builder() .appId(appId) .src(src) .dst(dst) .signalType(OduSignalType.ODU4) .bidirectional(true) .build(); intents.add(opticalIntent); } else { log.warn("Unsupported cross connect point types {} {}", srcPort.type(), dstPort.type()); return Collections.emptyList(); } } return intents; } /** * Returns list of optical connectivity intents needed to create connectivity * between ingress and egress. * * @param ingress the ingress connect point * @param egress the egress connect point * @return list of optical connectivity intents, empty list if no path was found */ private List<Intent> getOpticalIntents(ConnectPoint ingress, ConnectPoint egress) { Set<Path> paths = pathService.getPaths(ingress.deviceId(), egress.deviceId(), new OpticalLinkWeight()); if (paths.isEmpty()) { return Collections.emptyList(); } // Search path with available cross connect points for (Path path : paths) { List<ConnectPoint> crossConnectPoints = getCrossConnectPoints(path); // Skip to next path if cross connect points are mismatched if (!checkCrossConnectPoints(crossConnectPoints)) { continue; } return getIntents(crossConnectPoints); } log.warn("Unable to find multi-layer path."); return Collections.emptyList(); } /** * Link weight function that emphasizes re-use of packet links. */ private class OpticalLinkWeight implements LinkWeight { @Override public double weight(TopologyEdge edge) { // Ignore inactive links if (edge.link().state() == Link.State.INACTIVE) { return -1; } // TODO: Ignore cross connect links with used ports // Transport links have highest weight if (edge.link().type() == Link.Type.OPTICAL) { return 1000; } // Packet links return 1; } } } /** * Verifies if given device type is in packet layer, i.e., ROADM, OTN or ROADM_OTN device. * * @param type device type * @return true if in packet layer, false otherwise */ private boolean isPacketLayer(Device.Type type) { return type == Device.Type.SWITCH || type == Device.Type.ROUTER || type == Device.Type.VIRTUAL; } /** * Verifies if given device type is in packet layer, i.e., switch or router device. * * @param type device type * @return true if in packet layer, false otherwise */ private boolean isTransportLayer(Device.Type type) { return type == Device.Type.ROADM || type == Device.Type.OTN || type == Device.Type.ROADM_OTN; } /** * Verifies if given link forms a cross-connection between packet and optical layer. * * @param link the link * @return true if the link is a cross-connect link, false otherwise */ private boolean isCrossConnectLink(Link link) { if (link.type() != Link.Type.OPTICAL) { return false; } Device.Type src = deviceService.getDevice(link.src().deviceId()).type(); Device.Type dst = deviceService.getDevice(link.dst().deviceId()).type(); return src != dst && ((isPacketLayer(src) && isTransportLayer(dst)) || (isPacketLayer(dst) && isTransportLayer(src))); } }