Marc De Leenheer
Committed by Gerrit Code Review

Support for OTN using optical circuit intents.

Refined DeviceResourceService.

Change-Id: I489f368a0fac5f4a8d0a1a1cb716f845558db35e
......@@ -29,7 +29,9 @@ import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.Host;
import org.onosproject.net.Link;
import org.onosproject.net.OchPort;
import org.onosproject.net.OduCltPort;
import org.onosproject.net.OduSignalType;
import org.onosproject.net.Path;
import org.onosproject.net.Port;
import org.onosproject.net.device.DeviceService;
......@@ -40,6 +42,7 @@ 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.resource.device.DeviceResourceService;
......@@ -307,17 +310,33 @@ public class OpticalPathProvisioner {
Port srcPort = deviceService.getPort(src.deviceId(), src.port());
Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
// TODO: Check availability of ports
// Create OTN circuit
Intent circuitIntent = OpticalCircuitIntent.builder()
.appId(appId)
.src(src)
.dst(dst)
.signalType(OduCltPort.SignalType.CLT_10GBE)
.build();
intents.add(circuitIntent);
continue;
} else if (srcPort instanceof OchPort && dstPort instanceof OchPort) {
// Create lightpath
// TODO: Ensure src & dst are of type OchPort
// FIXME: hardcoded ODU signal type
Intent opticalIntent = OpticalConnectivityIntent.builder()
.appId(appId)
.src(src)
.dst(dst)
.signalType(OduSignalType.ODU4)
.build();
intents.add(opticalIntent);
if (srcPort instanceof OduCltPort && dstPort instanceof OduCltPort) {
continue;
// also create OTN service
} else {
log.warn("Unsupported cross connect point types {} {}", srcPort.type(), dstPort.type());
return Collections.emptyList();
}
}
......
......@@ -18,6 +18,7 @@ package org.onosproject.cli.net;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.OduSignalType;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.OpticalConnectivityIntent;
......@@ -47,11 +48,13 @@ public class AddOpticalIntentCommand extends ConnectivityIntentCommand {
ConnectPoint egress = ConnectPoint.deviceConnectPoint(egressDeviceString);
// FIXME: Hardcoded ODU signal type
Intent intent = OpticalConnectivityIntent.builder()
.appId(appId())
.key(key())
.src(ingress)
.dst(egress)
.signalType(OduSignalType.ODU4)
.build();
service.submit(intent);
print("Optical intent submitted:\n%s", intent.toString());
......
......@@ -23,7 +23,7 @@ import java.util.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Implementation of OCh (Optical Channel) singal type criterion.
* Implementation of OCh (Optical Channel) signal type criterion.
*/
public class OchSignalTypeCriterion implements Criterion {
......
/*
* Copyright 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.net.intent;
import com.google.common.base.MoreObjects;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.OduCltPort;
import java.util.Collections;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* An optical layer intent for circuits between two OduClt ports.
* No traffic selector or traffic treatment are needed.
*/
public class OpticalCircuitIntent extends Intent {
private final ConnectPoint src;
private final ConnectPoint dst;
private final OduCltPort.SignalType signalType;
/**
* Creates an optical circuit intent between the specified
* connection points.
*
* @param appId application identification
* @param key intent key
* @param src the source transponder port
* @param dst the destination transponder port
* @param signalType ODU signal type
* @param priority priority to use for flows from this intent
*/
protected OpticalCircuitIntent(ApplicationId appId, Key key, ConnectPoint src, ConnectPoint dst,
OduCltPort.SignalType signalType, int priority) {
super(appId, key, Collections.emptyList(), priority);
this.src = checkNotNull(src);
this.dst = checkNotNull(dst);
this.signalType = checkNotNull(signalType);
}
/**
* Returns a new optical circuit intent builder.
*
* @return host to host intent builder
*/
public static Builder builder() {
return new Builder();
}
/**
* Builder for optical circuit intents.
*/
public static class Builder extends Intent.Builder {
private ConnectPoint src;
private ConnectPoint dst;
private OduCltPort.SignalType signalType;
@Override
public Builder appId(ApplicationId appId) {
return (Builder) super.appId(appId);
}
@Override
public Builder key(Key key) {
return (Builder) super.key(key);
}
@Override
public Builder priority(int priority) {
return (Builder) super.priority(priority);
}
/**
* Sets the source for the intent that will be built.
*
* @param src source to use for built intent
* @return this builder
*/
public Builder src(ConnectPoint src) {
this.src = src;
return this;
}
/**
* Sets the destination for the intent that will be built.
*
* @param dst dest to use for built intent
* @return this builder
*/
public Builder dst(ConnectPoint dst) {
this.dst = dst;
return this;
}
/**
* Sets the ODU signal type for the intent that will be built.
*
* @param signalType signal type to use for built intent
* @return this builder
*/
public Builder signalType(OduCltPort.SignalType signalType) {
this.signalType = signalType;
return this;
}
/**
* Builds an optical circuit intent from the accumulated parameters.
*
* @return point to point intent
*/
public OpticalCircuitIntent build() {
return new OpticalCircuitIntent(
appId,
key,
src,
dst,
signalType,
priority
);
}
}
/**
* Constructor for serializer.
*/
protected OpticalCircuitIntent() {
super();
this.src = null;
this.dst = null;
this.signalType = null;
}
/**
* Returns the source transponder port.
*
* @return source transponder port
*/
public ConnectPoint getSrc() {
return src;
}
/**
* Returns the destination transponder port.
*
* @return source transponder port
*/
public ConnectPoint getDst() {
return dst;
}
/**
* Returns the ODU signal type.
*
* @return ODU signal type
*/
public OduCltPort.SignalType getSignalType() {
return signalType;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("id", id())
.add("key", key())
.add("appId", appId())
.add("priority", priority())
.add("resources", resources())
.add("src", src)
.add("dst", dst)
.add("signalType", signalType)
.toString();
}
}
......@@ -18,6 +18,7 @@ package org.onosproject.net.intent;
import com.google.common.base.MoreObjects;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.OduSignalType;
import java.util.Collections;
......@@ -30,6 +31,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
public final class OpticalConnectivityIntent extends Intent {
private final ConnectPoint src;
private final ConnectPoint dst;
private final OduSignalType signalType;
/**
* Creates an optical connectivity intent between the specified
......@@ -45,10 +47,12 @@ public final class OpticalConnectivityIntent extends Intent {
Key key,
ConnectPoint src,
ConnectPoint dst,
OduSignalType signalType,
int priority) {
super(appId, key, Collections.emptyList(), priority);
this.src = checkNotNull(src);
this.dst = checkNotNull(dst);
this.signalType = checkNotNull(signalType);
}
/**
......@@ -67,6 +71,7 @@ public final class OpticalConnectivityIntent extends Intent {
public static class Builder extends Intent.Builder {
private ConnectPoint src;
private ConnectPoint dst;
private OduSignalType signalType;
@Override
public Builder appId(ApplicationId appId) {
......@@ -106,6 +111,17 @@ public final class OpticalConnectivityIntent extends Intent {
}
/**
* Sets the ODU signal type for the intent that will be built.
*
* @param signalType ODU signal type
* @return this builder
*/
public Builder signalType(OduSignalType signalType) {
this.signalType = signalType;
return this;
}
/**
* Builds an optical connectivity intent from the accumulated parameters.
*
* @return point to point intent
......@@ -117,6 +133,7 @@ public final class OpticalConnectivityIntent extends Intent {
key,
src,
dst,
signalType,
priority
);
}
......@@ -129,6 +146,7 @@ public final class OpticalConnectivityIntent extends Intent {
super();
this.src = null;
this.dst = null;
this.signalType = null;
}
/**
......@@ -149,6 +167,15 @@ public final class OpticalConnectivityIntent extends Intent {
return dst;
}
/**
* Returns the ODU signal type.
*
* @return ODU signal type
*/
public OduSignalType getSignalType() {
return signalType;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
......@@ -159,6 +186,7 @@ public final class OpticalConnectivityIntent extends Intent {
.add("resources", resources())
.add("src", src)
.add("dst", dst)
.add("signalType", signalType)
.toString();
}
}
......
......@@ -28,10 +28,46 @@ public interface DeviceResourceService {
/**
* Request a set of ports needed to satisfy the intent.
*
* @param ports set of ports to allocate
* @param intent the intent
* @return set of ports
* @return true if ports were successfully allocated, false otherwise
*/
Set<Port> requestPorts(Intent intent);
boolean requestPorts(Set<Port> ports, Intent intent);
/**
* Returns the set of ports allocated for an intent.
*
* @param intentId the intent ID
* @return set of allocated ports
*/
Set<Port> getAllocations(IntentId intentId);
/**
* Returns the intent allocated to a port.
*
* @param port the port
* @return intent ID allocated to the port
*/
IntentId getAllocations(Port port);
/**
* Request a mapping between the given intents.
*
* @param keyIntentId the key intent ID
* @param valIntentId the value intent ID
* @return true if mapping was successful, false otherwise
*/
boolean requestMapping(IntentId keyIntentId, IntentId valIntentId);
/**
* Returns the intents mapped to a lower intent.
*
* @param intentId the intent ID
* @return the set of intent IDs
*/
Set<IntentId> getMapping(IntentId intentId);
void releaseMapping(IntentId keyIntentId, IntentId valIntentId);
/**
* Release ports associated with given intent ID.
......
......@@ -24,7 +24,60 @@ import java.util.Set;
public interface DeviceResourceStore {
Set<Port> getFreePorts(DeviceId deviceId);
void allocatePorts(Set<Port> port, IntentId intent);
/**
* Allocates the given ports to the given intent.
* @param ports set of ports to allocate
* @param intentId intent ID
* @return true if allocation was successful, false otherwise
*/
boolean allocatePorts(Set<Port> ports, IntentId intentId);
/**
* Returns set of ports allocated for an intent.
*
* @param intentId the intent ID
* @return set of allocated ports
*/
Set<Port> getAllocations(IntentId intentId);
/**
* Returns intent allocated to a port.
*
* @param port the port
* @return intent ID allocated to the port
*/
IntentId getAllocations(Port port);
/**
* Allocates the mapping between the given intents.
*
* @param keyIntentId key intent ID
* @param valIntentId value intent ID
* @return true if mapping was successful, false otherwise
*/
boolean allocateMapping(IntentId keyIntentId, IntentId valIntentId);
void releasePorts(IntentId intent);
/**
* Returns the set of intents mapped to a lower intent.
*
* @param intentId intent ID
* @return set of intent IDs
*/
Set<IntentId> getMapping(IntentId intentId);
/**
* Releases the mapping between the given intents.
*
* @param keyIntentId key intent ID
* @param valIntentId value intent ID
*/
void releaseMapping(IntentId keyIntentId, IntentId valIntentId);
/**
* Releases the ports allocated to the given intent.
*
* @param intentId intent ID
* @return true if release was successful, false otherwise
*/
boolean releasePorts(IntentId intentId);
}
......
......@@ -15,7 +15,8 @@
*/
package org.onosproject.net.intent.impl.compiler;
import java.util.Collections;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
......@@ -41,6 +42,7 @@ import org.onosproject.net.intent.IntentCompiler;
import org.onosproject.net.intent.IntentExtensionService;
import org.onosproject.net.intent.OpticalConnectivityIntent;
import org.onosproject.net.intent.OpticalPathIntent;
import org.onosproject.net.intent.impl.IntentCompilationException;
import org.onosproject.net.resource.link.DefaultLinkResourceRequest;
import org.onosproject.net.resource.device.DeviceResourceService;
import org.onosproject.net.resource.link.LambdaResource;
......@@ -56,6 +58,8 @@ import org.onosproject.net.topology.TopologyEdge;
import org.onosproject.net.topology.TopologyService;
import com.google.common.collect.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkArgument;
......@@ -65,6 +69,8 @@ import static com.google.common.base.Preconditions.checkArgument;
@Component(immediate = true)
public class OpticalConnectivityIntentCompiler implements IntentCompiler<OpticalConnectivityIntent> {
protected static final Logger log = LoggerFactory.getLogger(OpticalConnectivityIntentCompiler.class);
private static final GridType DEFAULT_OCH_GRIDTYPE = GridType.DWDM;
private static final ChannelSpacing DEFAULT_CHANNEL_SPACING = ChannelSpacing.CHL_50GHZ;
......@@ -105,6 +111,13 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
checkArgument(srcPort instanceof OchPort);
checkArgument(dstPort instanceof OchPort);
log.debug("Compiling optical connectivity intent between {} and {}", src, dst);
// Reserve OCh ports
if (!deviceResourceService.requestPorts(new HashSet(Arrays.asList(srcPort, dstPort)), intent)) {
throw new IntentCompilationException("Unable to reserve ports for intent " + intent);
}
// Calculate available light paths
Set<Path> paths = getOpticalPaths(intent);
......@@ -118,13 +131,6 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
OmsPort omsPort = (OmsPort) deviceService.getPort(path.src().deviceId(), path.src().port());
// Try to reserve port resources, roll back if unsuccessful
Set<Port> portAllocs = deviceResourceService.requestPorts(intent);
if (portAllocs == null) {
linkResourceService.releaseResources(linkAllocs);
continue;
}
// Create installable optical path intent
LambdaResourceAllocation lambdaAlloc = getWavelength(path, linkAllocs);
OchSignal ochSignal = getOchSignal(lambdaAlloc, omsPort.minFrequency(), omsPort.grid());
......@@ -143,7 +149,10 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
return ImmutableList.of(newIntent);
}
return Collections.emptyList();
// Release port allocations if unsuccessful
deviceResourceService.releasePorts(intent.id());
throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent);
}
/**
......
......@@ -39,6 +39,8 @@ import org.onosproject.net.intent.IntentExtensionService;
import org.onosproject.net.intent.OpticalPathIntent;
import org.onosproject.net.resource.link.LinkResourceAllocations;
import org.onosproject.net.resource.link.LinkResourceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.LinkedList;
......@@ -48,6 +50,8 @@ import java.util.Set;
@Component(immediate = true)
public class OpticalPathIntentCompiler implements IntentCompiler<OpticalPathIntent> {
private static final Logger log = LoggerFactory.getLogger(OpticalPathIntentCompiler.class);
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentExtensionService intentManager;
......@@ -73,6 +77,8 @@ public class OpticalPathIntentCompiler implements IntentCompiler<OpticalPathInte
@Override
public List<Intent> compile(OpticalPathIntent intent, List<Intent> installable,
Set<LinkResourceAllocations> resources) {
log.debug("Compiling optical path intent between {} and {}", intent.src(), intent.dst());
return Collections.singletonList(
new FlowRuleIntent(appId, createRules(intent), intent.resources()));
}
......
......@@ -24,13 +24,10 @@ import org.apache.felix.scr.annotations.Service;
import org.onosproject.net.Port;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentId;
import org.onosproject.net.intent.OpticalConnectivityIntent;
import org.onosproject.net.resource.device.DeviceResourceService;
import org.onosproject.net.resource.device.DeviceResourceStore;
import org.slf4j.Logger;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
......@@ -59,28 +56,35 @@ public class DeviceResourceManager implements DeviceResourceService {
}
@Override
public Set<Port> requestPorts(Intent intent) {
public boolean requestPorts(Set<Port> ports, Intent intent) {
checkNotNull(intent);
if (intent instanceof OpticalConnectivityIntent) {
OpticalConnectivityIntent opticalIntent = (OpticalConnectivityIntent) intent;
Set<Port> srcPorts = store.getFreePorts(opticalIntent.getSrc().deviceId());
Set<Port> dstPorts = store.getFreePorts(opticalIntent.getDst().deviceId());
Port srcPort = getTypedPort(srcPorts, Port.Type.OCH);
Port dstPort = getTypedPort(dstPorts, Port.Type.OCH);
return store.allocatePorts(ports, intent.id());
}
if (srcPort == null || dstPort == null) {
return null;
@Override
public Set<Port> getAllocations(IntentId intentId) {
return store.getAllocations(intentId);
}
Set<Port> allocPorts = new HashSet(Arrays.asList(srcPort, dstPort));
@Override
public IntentId getAllocations(Port port) {
return store.getAllocations(port);
}
store.allocatePorts(allocPorts, intent.id());
@Override
public void releaseMapping(IntentId keyIntentId, IntentId valIntentId) {
store.releaseMapping(keyIntentId, valIntentId);
}
return allocPorts;
@Override
public boolean requestMapping(IntentId keyIntentId, IntentId valIntentId) {
return store.allocateMapping(keyIntentId, valIntentId);
}
return null;
@Override
public Set<IntentId> getMapping(IntentId intentId) {
return store.getMapping(intentId);
}
@Override
......
......@@ -35,6 +35,7 @@ import org.onosproject.store.service.TransactionContext;
import org.onosproject.store.service.TransactionalMap;
import org.slf4j.Logger;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
......@@ -52,12 +53,14 @@ public class ConsistentDeviceResourceStore implements DeviceResourceStore {
private static final String PORT_ALLOCATIONS = "PortAllocations";
private static final String INTENT_ALLOCATIONS = "IntentAllocations";
private static final String INTENT_MAPPING = "IntentMapping";
private static final Serializer SERIALIZER = Serializer.using(
new KryoNamespace.Builder().register(KryoNamespaces.API).build());
private ConsistentMap<Port, IntentId> portAllocMap;
private ConsistentMap<IntentId, Set<Port>> intentAllocMap;
private ConsistentMap<IntentId, Set<IntentId>> intentMapping;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService storageService;
......@@ -75,6 +78,10 @@ public class ConsistentDeviceResourceStore implements DeviceResourceStore {
.withName(INTENT_ALLOCATIONS)
.withSerializer(SERIALIZER)
.build();
intentMapping = storageService.<IntentId, Set<IntentId>>consistentMapBuilder()
.withName(INTENT_MAPPING)
.withSerializer(SERIALIZER)
.build();
log.info("Started");
}
......@@ -110,7 +117,7 @@ public class ConsistentDeviceResourceStore implements DeviceResourceStore {
}
@Override
public void allocatePorts(Set<Port> ports, IntentId intentId) {
public boolean allocatePorts(Set<Port> ports, IntentId intentId) {
checkNotNull(ports);
checkArgument(ports.size() > 0);
checkNotNull(intentId);
......@@ -120,20 +127,78 @@ public class ConsistentDeviceResourceStore implements DeviceResourceStore {
try {
TransactionalMap<Port, IntentId> portAllocs = getPortAllocs(tx);
for (Port port : ports) {
portAllocs.put(port, intentId);
if (portAllocs.putIfAbsent(port, intentId) != null) {
throw new Exception("Port already allocated " + port.toString());
}
}
TransactionalMap<IntentId, Set<Port>> intentAllocs = getIntentAllocs(tx);
intentAllocs.put(intentId, ports);
tx.commit();
} catch (Exception e) {
log.error("Exception thrown, rolling back", e);
tx.abort();
throw e;
return false;
}
return true;
}
@Override
public Set<Port> getAllocations(IntentId intentId) {
if (!intentAllocMap.containsKey(intentId)) {
Collections.emptySet();
}
return intentAllocMap.get(intentId).value();
}
@Override
public IntentId getAllocations(Port port) {
if (!portAllocMap.containsKey(port)) {
return null;
}
return portAllocMap.get(port).value();
}
@Override
public Set<IntentId> getMapping(IntentId intentId) {
return intentMapping.get(intentId).value();
}
@Override
public void releaseMapping(IntentId keyIntentId, IntentId valIntentId) {
if (!intentMapping.containsKey(keyIntentId)) {
return;
}
Set<IntentId> intents = intentMapping.get(keyIntentId).value();
try {
intents.remove(valIntentId);
} catch (Exception e) {
log.error("Trying to remove non-existing mapping {} {}", keyIntentId, valIntentId);
}
}
@Override
public void releasePorts(IntentId intentId) {
public boolean allocateMapping(IntentId keyIntentId, IntentId valIntentId) {
Set<IntentId> intents = intentMapping.get(keyIntentId).value();
if (intents == null) {
intents = Collections.singleton(valIntentId);
} else {
intents.add(valIntentId);
}
intentMapping.put(keyIntentId, intents);
return true;
}
@Override
public boolean releasePorts(IntentId intentId) {
checkNotNull(intentId);
TransactionContext tx = getTxContext();
......@@ -150,7 +215,9 @@ public class ConsistentDeviceResourceStore implements DeviceResourceStore {
} catch (Exception e) {
log.error("Exception thrown, rolling back", e);
tx.abort();
throw e;
return false;
}
return true;
}
}
......
......@@ -136,6 +136,7 @@ import org.onosproject.net.intent.LinkCollectionIntent;
import org.onosproject.net.intent.MplsIntent;
import org.onosproject.net.intent.MplsPathIntent;
import org.onosproject.net.intent.MultiPointToSinglePointIntent;
import org.onosproject.net.intent.OpticalCircuitIntent;
import org.onosproject.net.intent.OpticalConnectivityIntent;
import org.onosproject.net.intent.OpticalPathIntent;
import org.onosproject.net.intent.PathIntent;
......@@ -361,6 +362,7 @@ public final class KryoNamespaces {
LinkCollectionIntent.class,
OpticalConnectivityIntent.class,
OpticalPathIntent.class,
OpticalCircuitIntent.class,
LinkResourceRequest.class,
DefaultLinkResourceRequest.class,
BandwidthResourceRequest.class,
......