ONOS-7080 and ONOS-7070:
- added support for user-defined signal in add-optical-intent CLI
- implemented LambdaQuery behavior for restCiena driver
- added port based filtering of paths in OpticalIntentCompiler
Change-Id: Ibb61cc3722d5b3a52859d5585decf82a50ef5be0
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/AddOpticalIntentCommand.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/AddOpticalIntentCommand.java
index 905585f..f1d686d 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/AddOpticalIntentCommand.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/cli/AddOpticalIntentCommand.java
@@ -18,7 +18,6 @@
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
-import org.onlab.util.Spectrum;
import org.onosproject.cli.app.AllApplicationNamesCompleter;
import org.onosproject.cli.net.ConnectPointCompleter;
import org.onosproject.cli.net.ConnectivityIntentCommand;
@@ -40,18 +39,35 @@
import org.onosproject.net.optical.OchPort;
import org.onosproject.net.optical.OduCltPort;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.AbstractMap.SimpleEntry;
import java.util.List;
+import java.util.stream.Stream;
+import java.util.stream.Collectors;
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;
/**
* Installs optical connectivity or circuit intents, depending on given port types.
*/
@Command(scope = "onos", name = "add-optical-intent",
- description = "Installs optical connectivity intent")
+ description = "Installs optical connectivity intent")
public class AddOpticalIntentCommand extends ConnectivityIntentCommand {
-
+ private static final String SIGNAL_FORMAT = "slotGranularity/channelSpacing(in GHz e.g 6.25,12.5,25,50,100)/" +
+ "spaceMultiplier/gridType(cwdm, flex, dwdm) " + "e.g 1/6.25/1/flex";
+ private static final String FLEX = "FLEX";
+ private static final String DWDM = "DWDM";
+ private static final String CWDM = "CWDM";
+ private static final String CH_6P25 = "6.25";
+ private static final String CH_12P5 = "12.5";
+ private static final String CH_25 = "25";
+ private static final String CH_50 = "50";
+ private static final String CH_100 = "100";
+ private static final Map<String, ChannelSpacing> CHANNEL_SPACING_MAP = createChannelSpacingMap();
+ private static final Map<String, GridType> GRID_TYPE_MAP = createGridTypeMap();
// OSGi workaround
@SuppressWarnings("unused")
private ConnectPointCompleter cpCompleter;
@@ -61,33 +77,51 @@
private AllApplicationNamesCompleter appCompleter;
@Argument(index = 0, name = "ingress",
- description = "Ingress Device/Port Description",
- required = true, multiValued = false)
+ description = "Ingress Device/Port Description",
+ required = true, multiValued = false)
String ingressString = "";
@Argument(index = 1, name = "egress",
- description = "Egress Device/Port Description",
- required = true, multiValued = false)
+ description = "Egress Device/Port Description",
+ required = true, multiValued = false)
String egressString = "";
@Option(name = "-b", aliases = "--bidirectional",
description = "If this argument is passed the optical link created will be bidirectional, " +
- "else the link will be unidirectional.",
+ "else the link will be unidirectional.",
required = false, multiValued = false)
private boolean bidirectional = false;
- @Option(name = "-c", aliases = "--channel",
- description = "Optical channel in GHz to use for the intent (e.g., 193.1). " +
- "Uses 50 GHz spaced DWDM channel plan by default.",
+ @Option(name = "-s", aliases = "--signal",
+ description = "Optical Signal. Format = " + SIGNAL_FORMAT,
required = false, multiValued = false)
- private Double channel;
+ private String signal;
+ private static final Map<String, ChannelSpacing> createChannelSpacingMap() {
+ return new HashMap(Stream.of(
+ new SimpleEntry(CH_6P25, ChannelSpacing.CHL_6P25GHZ),
+ new SimpleEntry(CH_12P5, ChannelSpacing.CHL_12P5GHZ),
+ new SimpleEntry(CH_25, ChannelSpacing.CHL_25GHZ),
+ new SimpleEntry(CH_50, ChannelSpacing.CHL_50GHZ),
+ new SimpleEntry(CH_100, ChannelSpacing.CHL_100GHZ))
+ .collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue())));
+
+ }
+
+ private static final Map<String, GridType> createGridTypeMap() {
+ return new HashMap(Stream.of(
+ new SimpleEntry(FLEX, GridType.FLEX),
+ new SimpleEntry(DWDM, GridType.DWDM),
+ new SimpleEntry(CWDM, GridType.CWDM))
+ .collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue())));
+
+ }
private ConnectPoint createConnectPoint(String devicePortString) {
String[] splitted = devicePortString.split("/");
checkArgument(splitted.length == 2,
- "Connect point must be in \"deviceUri/portNumber\" format");
+ "Connect point must be in \"deviceUri/portNumber\" format");
DeviceId deviceId = DeviceId.deviceId(splitted[0]);
DeviceService deviceService = get(DeviceService.class);
@@ -103,16 +137,36 @@
return null;
}
- private OchSignal createOchSignal(Double channel) {
- if (channel == null) {
+ private OchSignal createOchSignal() throws IllegalArgumentException {
+ if (signal == null) {
return null;
}
-
- ChannelSpacing spacing = ChannelSpacing.CHL_50GHZ;
- int multiplier = (int) (Math.round(channel - Spectrum.CENTER_FREQUENCY.asHz() / spacing.frequency().asHz()));
- return new OchSignal(GridType.DWDM, spacing, multiplier, 4);
+ try {
+ String[] splitted = signal.split("/");
+ checkArgument(splitted.length == 4,
+ "signal requires 4 parameters: " + SIGNAL_FORMAT);
+ int slotGranularity = Integer.parseInt(splitted[0]);
+ String chSpacing = splitted[1];
+ ChannelSpacing channelSpacing = checkNotNull(CHANNEL_SPACING_MAP.get(chSpacing),
+ String.format("invalid channel spacing: %s", chSpacing));
+ int multiplier = Integer.parseInt(splitted[2]);
+ String gdType = splitted[3].toUpperCase();
+ GridType gridType = checkNotNull(GRID_TYPE_MAP.get(gdType),
+ String.format("invalid grid type: %s", gdType));
+ return new OchSignal(gridType, channelSpacing, multiplier, slotGranularity);
+ } catch (RuntimeException e) {
+ /* catching RuntimeException as both NullPointerException (thrown by
+ * checkNotNull) and IllegalArgumentException (thrown by checkArgument)
+ * are subclasses of RuntimeException.
+ */
+ String msg = String.format("Invalid signal format: %s, expected format is %s.",
+ signal, SIGNAL_FORMAT);
+ print(msg);
+ throw new IllegalArgumentException(msg, e);
+ }
}
+
@Override
protected void execute() {
IntentService service = get(IntentService.class);
@@ -175,7 +229,7 @@
.dst(egress)
.signalType(signalType)
.bidirectional(bidirectional)
- .ochSignal(createOchSignal(channel))
+ .ochSignal(createOchSignal())
.build();
} else {
print("Unable to create optical intent between connect points %s and %s", ingress, egress);
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
index 11b78c4..9a2b6bf 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
@@ -29,6 +29,7 @@
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultOchSignalComparator;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.GridType;
import org.onosproject.net.Link;
import org.onosproject.net.OchSignal;
import org.onosproject.net.OchSignalType;
@@ -63,6 +64,7 @@
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import java.util.stream.IntStream;
import static com.google.common.base.Preconditions.checkArgument;
import static org.onosproject.net.optical.device.OpticalDeviceServiceView.opticalView;
@@ -132,19 +134,16 @@
// Find first path that has the required resources
Stream<Path> paths = getOpticalPaths(intent);
Optional<Map.Entry<Path, List<OchSignal>>> found = paths
- .map(path ->
- Maps.immutableEntry(path, findFirstAvailableLambda(intent, path)))
+ .map(path -> Maps.immutableEntry(path, findFirstAvailableLambda(intent, path)))
.filter(entry -> !entry.getValue().isEmpty())
.filter(entry -> convertToResources(entry.getKey(),
- entry.getValue()).stream().allMatch(resourceService::isAvailable))
+ entry.getValue()).stream().allMatch(resourceService::isAvailable))
.findFirst();
// Allocate resources and create optical path intent
if (found.isPresent()) {
resources.addAll(convertToResources(found.get().getKey(), found.get().getValue()));
-
allocateResources(intent, resources);
-
OchSignal ochSignal = OchSignal.toFixedGrid(found.get().getValue(), ChannelSpacing.CHL_50GHZ);
return ImmutableList.of(createIntent(intent, found.get().getKey(), ochSignal));
} else {
@@ -207,8 +206,8 @@
log.error("Resource allocation for {} failed (resource request: {})", intent.key(), resources);
if (log.isDebugEnabled()) {
log.debug("requested resources:\n\t{}", resources.stream()
- .map(Resource::toString)
- .collect(Collectors.joining("\n\t")));
+ .map(Resource::toString)
+ .collect(Collectors.joining("\n\t")));
}
throw new OpticalIntentCompilationException("Unable to allocate resources: " + resources);
}
@@ -222,7 +221,25 @@
*/
private List<OchSignal> findFirstAvailableLambda(OpticalConnectivityIntent intent, Path path) {
if (intent.ochSignal().isPresent()) {
- return Collections.singletonList(intent.ochSignal().get());
+ //create lambdas w.r.t. slotGanularity/slotWidth
+ OchSignal ochSignal = intent.ochSignal().get();
+ if (ochSignal.gridType() == GridType.FLEX) {
+ // multiplier sits in the middle of slots
+ int startMultiplier = ochSignal.spacingMultiplier() - (ochSignal.slotGranularity() / 2);
+ return IntStream.range(0, ochSignal.slotGranularity())
+ .mapToObj(x -> OchSignal.newFlexGridSlot(startMultiplier + (2 * x)))
+ .collect(Collectors.toList());
+ } else if (ochSignal.gridType() == GridType.DWDM) {
+ int startMultiplier = (int) (1 - ochSignal.slotGranularity() +
+ ochSignal.spacingMultiplier() * ochSignal.channelSpacing().frequency().asHz() /
+ ChannelSpacing.CHL_6P25GHZ.frequency().asHz());
+ return IntStream.range(0, ochSignal.slotGranularity())
+ .mapToObj(x -> OchSignal.newFlexGridSlot(startMultiplier + (2 * x)))
+ .collect(Collectors.toList());
+ }
+ //TODO: add support for other gridTypes
+ log.error("Grid type: {} not supported for user defined signal intents", ochSignal.gridType());
+ return Collections.emptyList();
}
Set<OchSignal> lambdas = findCommonLambdas(path);
@@ -312,6 +329,7 @@
private Stream<Path> getOpticalPaths(OpticalConnectivityIntent intent) {
// Route in WDM topology
Topology topology = topologyService.currentTopology();
+ //TODO: refactor with LinkWeigher class Implementation
LinkWeight weight = new LinkWeight() {
@Override
@@ -345,18 +363,22 @@
ConnectPoint start = intent.getSrc();
ConnectPoint end = intent.getDst();
+ //head link's src port should be same as intent src port and tail link dst port
+ //should be same as intent dst port in the path.
Stream<Path> paths = topologyService.getKShortestPaths(topology,
- start.deviceId(),
- end.deviceId(),
- AdapterLinkWeigher.adapt(weight));
+ start.deviceId(),
+ end.deviceId(),
+ AdapterLinkWeigher.adapt(weight))
+ .filter(p -> p.links().get(0).src().port().equals(start.port()) &&
+ p.links().get(p.links().size() - 1).dst().port().equals(end.port()));
if (log.isDebugEnabled()) {
return paths
.map(path -> {
// no-op map stage to add debug logging
log.debug("Candidate path: {}",
path.links().stream()
- .map(lk -> lk.src() + "-" + lk.dst())
- .collect(Collectors.toList()));
+ .map(lk -> lk.src() + "-" + lk.dst())
+ .collect(Collectors.toList()));
return path;
});
}
diff --git a/core/api/src/main/java/org/onosproject/net/OchSignal.java b/core/api/src/main/java/org/onosproject/net/OchSignal.java
index d29b3fc..7f67df3 100644
--- a/core/api/src/main/java/org/onosproject/net/OchSignal.java
+++ b/core/api/src/main/java/org/onosproject/net/OchSignal.java
@@ -203,10 +203,6 @@
IntStream.range(1, lambdas.size())
.forEach(i -> checkArgument(
lambdas.get(i).spacingMultiplier() == lambdas.get(i - 1).spacingMultiplier() + 2));
- // Is center frequency compatible with requested spacing
- Frequency center = lambdas.get(ratio / 2).centralFrequency().subtract(ChannelSpacing.CHL_6P25GHZ.frequency());
- checkArgument(Spectrum.CENTER_FREQUENCY.subtract(center).asHz() % spacing.frequency().asHz() == 0);
-
// Multiplier sits in middle of given lambdas, then convert from 6.25 to requested spacing
int spacingMultiplier = lambdas.stream()
.mapToInt(OchSignal::spacingMultiplier)
diff --git a/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaFlowRuleProgrammable.java b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaFlowRuleProgrammable.java
index e514188..36eb087 100644
--- a/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaFlowRuleProgrammable.java
+++ b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaFlowRuleProgrammable.java
@@ -42,6 +42,7 @@
public Collection<FlowEntry> getFlowEntries() {
DeviceId deviceId = handler().data().deviceId();
log.debug("getting flow entries for device {}", deviceId);
+ //TODO: implement getFlowEntries
log.debug("getFlowEntries not supported for device {}", deviceId);
return Collections.EMPTY_LIST;
}
@@ -60,11 +61,9 @@
@Override
public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
log.debug("removing flow rules: {}", rules);
- Collection<FlowRule> removed = rules.stream()
- .map(r -> createCrossConnectFlowRule(r))
- .filter(xc -> removeCrossConnect(xc))
- .collect(Collectors.toList());
- return removed;
+ //TODO: implement remove rule
+ log.debug("ignoring remove rule request");
+ return rules;
}
private CrossConnectFlowRule createCrossConnectFlowRule(FlowRule r) {
@@ -100,8 +99,7 @@
// only handling lineside rule
if (xc.isAddRule()) {
PortNumber outPort = xc.addDrop();
- OchSignal signal = OchSignal.newDwdmSlot(xc.ochSignal().channelSpacing(),
- -CienaRestDevice.getMultiplierOffset());
+ OchSignal signal = OchSignal.newDwdmSlot(xc.ochSignal().channelSpacing(), 0);
return install(outPort, signal);
}
return false;
diff --git a/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaRestDevice.java b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaRestDevice.java
index 7627959..ebe362f 100644
--- a/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaRestDevice.java
+++ b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaRestDevice.java
@@ -40,7 +40,6 @@
private static final String ENABLED = "enabled";
private static final String DISABLED = "disabled";
private static final Frequency BASE_FREQUENCY = Frequency.ofGHz(193_950);
- private static final int MULTIPLIER_OFFSET = 80;
//URIs
private static final String PORT_URI = "ws-ptps/ptps/%s";
@@ -74,10 +73,10 @@
}
- private String genFrequencyChangeRequest(long wavelength) {
+ private String genFrequencyChangeRequest(long frequency) {
String request = "{\n" +
"\"ciena-ws-ptp-modem:frequency\": {\n" +
- "\"value\": " + Long.toString(wavelength) + "\n" +
+ "\"value\": " + Long.toString(frequency) + "\n" +
"}\n" +
"}";
log.debug("request:\n{}", request);
@@ -123,7 +122,7 @@
public final boolean changeChannel(OchSignal signal, PortNumber outPort) {
String uri = genUri(CHANNEL_URI, outPort);
- int channel = signal.spacingMultiplier() + MULTIPLIER_OFFSET;
+ int channel = signal.spacingMultiplier();
log.debug("channel is {} for port {} on device {}", channel, outPort.name(), deviceId);
String request = genChannelChangeRequest(channel);
return putNoReply(uri, request);
@@ -135,10 +134,6 @@
return Double.valueOf(frequency).longValue();
}
- public static int getMultiplierOffset() {
- return MULTIPLIER_OFFSET;
- }
-
private int put(String uri, String request) {
InputStream payload = new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8));
int response = controller.put(deviceId, uri, payload, MediaType.valueOf(MediaType.APPLICATION_JSON));
diff --git a/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveServerLambdaQuery.java b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveServerLambdaQuery.java
new file mode 100644
index 0000000..0698287
--- /dev/null
+++ b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveServerLambdaQuery.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.drivers.ciena;
+
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.LambdaQuery;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+
+
+import com.google.common.collect.ImmutableSet;
+
+import java.util.Set;
+import java.util.stream.IntStream;
+
+/**
+ * Ciena WaveServer Lambda query.
+ * 88 50GHz flex grid channels with 12.5 slot width, starting from 0 to 87.
+ */
+public class CienaWaveServerLambdaQuery extends AbstractHandlerBehaviour implements LambdaQuery {
+
+ @Override
+ public Set<OchSignal> queryLambdas(PortNumber port) {
+ //88 channels of 50 GHz with 12.5 GHz slothWidth
+ int slots = (int) (ChannelSpacing.CHL_50GHZ.frequency().asHz() /
+ ChannelSpacing.CHL_12P5GHZ.frequency().asHz());
+ int channels = 88;
+ //total lambdas are equal to: channels * slots
+ return IntStream.rangeClosed(0, channels * slots)
+ .mapToObj(x -> OchSignal.newFlexGridSlot(2 * x))
+ .collect(ImmutableSet.toImmutableSet());
+ }
+
+}
+
+
diff --git a/drivers/ciena/src/main/resources/ciena-drivers.xml b/drivers/ciena/src/main/resources/ciena-drivers.xml
index 57ed4cf..02f7a97 100644
--- a/drivers/ciena/src/main/resources/ciena-drivers.xml
+++ b/drivers/ciena/src/main/resources/ciena-drivers.xml
@@ -26,7 +26,7 @@
<behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
impl="org.onosproject.drivers.ciena.CienaFlowRuleProgrammable"/>
<behaviour api="org.onosproject.net.behaviour.LambdaQuery"
- impl="org.onosproject.driver.optical.query.CBand50LambdaQuery"/>
+ impl="org.onosproject.drivers.ciena.CienaWaveServerLambdaQuery"/>
</driver>
</drivers>