blob: c5ec5605df35f9e900daad4184d2d64f5e332ce2 [file] [log] [blame]
/*
* Copyright 2014-2015 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 com.google.common.collect.Lists;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
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.ConnectPoint;
import org.onosproject.net.Host;
import org.onosproject.net.Link;
import org.onosproject.net.Path;
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.OpticalConnectivityIntent;
import org.onosproject.net.intent.PointToPointIntent;
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.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static org.onosproject.net.intent.IntentState.INSTALLED;
/**
* OpticalPathProvisioner listens 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.
*
*/
@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;
private ApplicationId appId;
// TODO use a shared map for distributed operation
protected final Map<ConnectPoint, OpticalConnectivityIntent> inStatusTportMap =
new ConcurrentHashMap<>();
protected final Map<ConnectPoint, OpticalConnectivityIntent> outStatusTportMap =
new ConcurrentHashMap<>();
protected final Map<ConnectPoint, Map<ConnectPoint, Intent>> intentMap =
new ConcurrentHashMap<>();
private final InternalOpticalPathProvisioner pathProvisioner = new InternalOpticalPathProvisioner();
@Activate
protected void activate() {
intentService.addListener(pathProvisioner);
appId = coreService.registerApplication("org.onosproject.optical");
initTport();
log.info("Starting optical path provisioning...");
}
protected void initTport() {
inStatusTportMap.clear();
outStatusTportMap.clear();
for (Intent intent : intentService.getIntents()) {
if (intentService.getIntentState(intent.key()) == INSTALLED) {
if (intent instanceof OpticalConnectivityIntent) {
inStatusTportMap.put(((OpticalConnectivityIntent) intent).getSrc(),
(OpticalConnectivityIntent) intent);
outStatusTportMap.put(((OpticalConnectivityIntent) intent).getDst(),
(OpticalConnectivityIntent) intent);
}
}
}
}
protected void deactivate() {
intentService.removeListener(pathProvisioner);
}
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("packet intent {} failed, calling optical path provisioning APP.", event.subject());
setupLightpath(event.subject());
break;
case WITHDRAWN:
log.info("intent {} withdrawn.", event.subject());
//FIXME
//teardownLightpath(event.subject());
break;
default:
break;
}
}
private void reserveTport(Intent intent) {
// TODO move to resourceManager
if (intent instanceof OpticalConnectivityIntent) {
OpticalConnectivityIntent opticalIntent =
(OpticalConnectivityIntent) intent;
if (inStatusTportMap.containsKey(opticalIntent.getSrc()) ||
outStatusTportMap.containsKey(opticalIntent.getDst())) {
//TODO throw an exception, perhaps
log.warn("Overlapping reservation: {}", opticalIntent);
}
inStatusTportMap.put(opticalIntent.getSrc(), opticalIntent);
outStatusTportMap.put(opticalIntent.getDst(), opticalIntent);
}
}
/**
* Registers an intent from src to dst.
* @param src source point
* @param dst destination point
* @param intent intent to be registered
* @return true if intent has not been previously added, false otherwise
*/
private boolean addIntent(ConnectPoint src, ConnectPoint dst, Intent intent) {
Map<ConnectPoint, Intent> srcMap = intentMap.get(src);
if (srcMap == null) {
srcMap = new ConcurrentHashMap<>();
intentMap.put(src, srcMap);
}
if (srcMap.containsKey(dst)) {
return false;
} else {
srcMap.put(dst, intent);
return true;
}
}
private void setupLightpath(Intent intent) {
// TODO change the coordination approach between packet intents and optical intents
// Low speed LLDP may cause multiple calls which are not expected
if (!IntentState.FAILED.equals(intentService.getIntentState(intent.key()))) {
return;
}
NodeId localNode = clusterService.getLocalNode().id();
List<Intent> intents = Lists.newArrayList();
if (intent instanceof HostToHostIntent) {
HostToHostIntent hostToHostIntent = (HostToHostIntent) intent;
Host one = hostService.getHost(hostToHostIntent.one());
Host two = hostService.getHost(hostToHostIntent.two());
if (one == null || two == null) {
return; //FIXME
}
// Ignore if we're not the master for the intent's origin device
NodeId sourceMaster = mastershipService.getMasterFor(one.location().deviceId());
if (!localNode.equals(sourceMaster)) {
return;
}
// provision both directions
intents.addAll(getOpticalPath(one.location(), two.location()));
// note: bi-directional intent is set up
// HostToHost Intent requires symmetric path!
//intents.addAll(getOpticalPath(two.location(), one.location()));
} else if (intent instanceof PointToPointIntent) {
PointToPointIntent p2pIntent = (PointToPointIntent) intent;
// Ignore if we're not the master for the intent's origin device
NodeId sourceMaster = mastershipService.getMasterFor(p2pIntent.ingressPoint().deviceId());
if (!localNode.equals(sourceMaster)) {
return;
}
intents.addAll(getOpticalPath(p2pIntent.ingressPoint(), p2pIntent.egressPoint()));
} else {
log.info("Unsupported intent type: {}", intent.getClass());
}
// Create the intents
for (Intent i : intents) {
// TODO: don't allow duplicate intents between the same points for now
// we may want to allow this carefully in future to increase capacity
if (i instanceof OpticalConnectivityIntent) {
OpticalConnectivityIntent oi = (OpticalConnectivityIntent) i;
if (addIntent(oi.getSrc(), oi.getDst(), oi)) {
intentService.submit(i);
reserveTport(i);
}
} else {
log.warn("Invalid intent type: {} for {}", i.getClass(), i);
}
}
}
private List<Intent> getOpticalPath(ConnectPoint ingress, ConnectPoint egress) {
Set<Path> paths = pathService.getPaths(ingress.deviceId(),
egress.deviceId(),
new OpticalLinkWeight());
if (paths.isEmpty()) {
return Lists.newArrayList();
}
List<Intent> connectionList = Lists.newArrayList();
Iterator<Path> itrPath = paths.iterator();
while (itrPath.hasNext()) {
boolean usedTportFound = false;
Path nextPath = itrPath.next();
log.info(nextPath.links().toString()); // TODO drop log level
Iterator<Link> itrLink = nextPath.links().iterator();
while (itrLink.hasNext()) {
ConnectPoint srcWdmPoint, dstWdmPoint;
Link link1 = itrLink.next();
if (!isOpticalLink(link1)) {
continue;
} else {
srcWdmPoint = link1.dst();
dstWdmPoint = srcWdmPoint;
}
while (itrLink.hasNext()) {
Link link2 = itrLink.next();
if (isOpticalLink(link2)) {
dstWdmPoint = link2.src();
} else {
break;
}
}
if (inStatusTportMap.get(srcWdmPoint) != null ||
outStatusTportMap.get(dstWdmPoint) != null) {
usedTportFound = true;
// log.info("used ConnectPoint {} to {} were found", srcWdmPoint, dstWdmPoint);
break;
}
Intent opticalIntent = OpticalConnectivityIntent.builder()
.appId(appId)
.src(srcWdmPoint)
.dst(dstWdmPoint)
.build();
Intent opticalIntent2 = OpticalConnectivityIntent.builder()
.appId(appId)
.src(dstWdmPoint)
.dst(srcWdmPoint)
.build();
log.info("Creating optical intent from {} to {}", srcWdmPoint, dstWdmPoint);
log.info("Creating optical intent from {} to {}", dstWdmPoint, srcWdmPoint);
connectionList.add(opticalIntent);
connectionList.add(opticalIntent2);
break;
}
if (!usedTportFound) {
break;
} else {
// reset the connection list
connectionList = Lists.newArrayList();
}
}
return connectionList;
}
private void teardownLightpath(Intent intent) {
/* FIXME this command doesn't make much sense. we need to define the semantics
// TODO move to resourceManager
if (intent instanceof OpticalConnectivityIntent) {
inStatusTportMap.remove(((OpticalConnectivityIntent) intent).getSrc());
outStatusTportMap.remove(((OpticalConnectivityIntent) intent).getDst());
// TODO tear down the idle lightpath if the utilization is zero.
}
*/ //end-FIXME
}
}
private static boolean isOpticalLink(Link link) {
boolean isOptical = false;
Link.Type lt = link.type();
if (lt == Link.Type.OPTICAL) {
isOptical = true;
}
return isOptical;
}
private static class OpticalLinkWeight implements LinkWeight {
@Override
public double weight(TopologyEdge edge) {
if (edge.link().state() == Link.State.INACTIVE) {
return -1; // ignore inactive links
}
if (isOpticalLink(edge.link())) {
return 1000; // optical links
} else {
return 1; // packet links
}
}
}
}