ONOS-7043:
- Added FlowRuleProgramable behavior for restCiena driver
- Added CienaRestDevice which provides cleaner implementation
of behaviors
Change-Id: I94ab7afdc5a2cda82cc5d5ed794af512cb80adc6
diff --git a/drivers/ciena/BUCK b/drivers/ciena/BUCK
index f6b7fa7..be8746e 100644
--- a/drivers/ciena/BUCK
+++ b/drivers/ciena/BUCK
@@ -6,6 +6,7 @@
'//protocols/rest/api:onos-protocols-rest-api',
'//apps/optical-model:onos-apps-optical-model',
'//lib:javax.ws.rs-api',
+ '//drivers/optical:onos-drivers-optical',
]
TEST_DEPS = [
diff --git a/drivers/ciena/pom.xml b/drivers/ciena/pom.xml
index 671148a..9c6df7d 100644
--- a/drivers/ciena/pom.xml
+++ b/drivers/ciena/pom.xml
@@ -59,6 +59,11 @@
<artifactId>onos-restsb-api</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-drivers-optical</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
</project>
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
new file mode 100644
index 0000000..e514188
--- /dev/null
+++ b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaFlowRuleProgrammable.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2016-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.DeviceId;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.driver.optical.flowrule.CrossConnectFlowRule;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleProgrammable;
+
+import org.slf4j.Logger;
+
+
+import java.util.List;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.stream.Collectors;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+public class CienaFlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
+ private CienaRestDevice restCiena;
+ private final Logger log = getLogger(getClass());
+
+ @Override
+ public Collection<FlowEntry> getFlowEntries() {
+ DeviceId deviceId = handler().data().deviceId();
+ log.debug("getting flow entries for device {}", deviceId);
+ log.debug("getFlowEntries not supported for device {}", deviceId);
+ return Collections.EMPTY_LIST;
+ }
+
+ @Override
+ public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
+ log.debug("installing flow rules: {}", rules);
+ // Apply the valid rules on the device
+ Collection<FlowRule> added = rules.stream()
+ .map(r -> createCrossConnectFlowRule(r))
+ .filter(xc -> installCrossConnect(xc))
+ .collect(Collectors.toList());
+ return added;
+ }
+
+ @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;
+ }
+
+ private CrossConnectFlowRule createCrossConnectFlowRule(FlowRule r) {
+ List<PortNumber> linePorts = CienaWaveserverDeviceDescription.getLinesidePortId().stream()
+ .map(p -> PortNumber.portNumber(p))
+ .collect(Collectors.toList());
+ try {
+ return new CrossConnectFlowRule(r, linePorts);
+ } catch (IllegalArgumentException e) {
+ log.debug("unable to create cross connect for rule:\n{}", r);
+ }
+ return null;
+ }
+
+ private boolean installCrossConnect(CrossConnectFlowRule xc) {
+ if (xc == null) {
+ return false;
+ }
+ // only handling lineside rule
+ if (xc.isAddRule()) {
+ PortNumber outPort = xc.addDrop();
+ OchSignal signal = xc.ochSignal();
+ return install(outPort, signal);
+ }
+ return false;
+ }
+
+ private boolean removeCrossConnect(CrossConnectFlowRule xc) {
+ //for now setting channel to 0 for remove rule
+ if (xc == null) {
+ return false;
+ }
+ // only handling lineside rule
+ if (xc.isAddRule()) {
+ PortNumber outPort = xc.addDrop();
+ OchSignal signal = OchSignal.newDwdmSlot(xc.ochSignal().channelSpacing(),
+ -CienaRestDevice.getMultiplierOffset());
+ return install(outPort, signal);
+ }
+ return false;
+ }
+
+ private boolean install(PortNumber outPort, OchSignal signal) {
+ /*
+ * rule is installed in three steps
+ * 1- disable port
+ * 2- change channel
+ * 3- enable port
+ */
+ try {
+ restCiena = new CienaRestDevice(handler());
+ } catch (NullPointerException e) {
+ log.error("unable to create CienaRestDevice, {}", e);
+ return false;
+ }
+ //1- disable port
+ //blindly disabling port
+ if (!restCiena.disablePort(outPort)) {
+ log.error("unable to disable port {}", outPort);
+ return false;
+ }
+ //2- change channel
+ if (!restCiena.changeChannel(signal, outPort)) {
+ log.error("unable to change the channel for port {}", outPort);
+ return false;
+ }
+ //3- enable port
+ if (!restCiena.enablePort(outPort)) {
+ log.error("unable to enable port {}", outPort);
+ return false;
+ }
+ return true;
+ }
+}
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
new file mode 100644
index 0000000..7627959
--- /dev/null
+++ b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaRestDevice.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2016-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.onlab.util.Frequency;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.protocol.rest.RestSBController;
+import org.slf4j.Logger;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+public class CienaRestDevice {
+ private DeviceId deviceId;
+ private RestSBController controller;
+
+ private final Logger log = getLogger(getClass());
+ 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";
+ private static final String TRANSMITTER_URI = PORT_URI + "/properties/transmitter";
+ private static final String PORT_STATE_URI = PORT_URI + "/state";
+ private static final String WAVELENGTH_URI = TRANSMITTER_URI + "/wavelength";
+ private static final String FREQUENCY_URI = TRANSMITTER_URI + "/ciena-ws-ptp-modem:frequency";
+ private static final String CHANNEL_URI = TRANSMITTER_URI + "/ciena-ws-ptp-modem:line-system-channel-number";
+
+ public CienaRestDevice(DriverHandler handler) throws NullPointerException {
+ deviceId = handler.data().deviceId();
+ controller = checkNotNull(handler.get(RestSBController.class));
+ }
+
+ private final String genPortStateRequest(String state) {
+ String request = "{\n" +
+ "\"state\": {\n" +
+ "\"admin-state\": \"" + state + "\"\n}\n}";
+ log.debug("generated request: \n{}", request);
+ return request;
+ }
+
+ private String genWavelengthChangeRequest(String wavelength) {
+ String request = "{\n" +
+ "\"wavelength\": {\n" +
+ "\"value\": " + wavelength + "\n" +
+ "}\n" +
+ "}";
+ log.debug("request:\n{}", request);
+ return request;
+
+ }
+
+ private String genFrequencyChangeRequest(long wavelength) {
+ String request = "{\n" +
+ "\"ciena-ws-ptp-modem:frequency\": {\n" +
+ "\"value\": " + Long.toString(wavelength) + "\n" +
+ "}\n" +
+ "}";
+ log.debug("request:\n{}", request);
+ return request;
+
+ }
+
+ private String genChannelChangeRequest(int channel) {
+ String request = "{\n" +
+ "\"ciena-ws-ptp-modem:line-system-channel-number\": " +
+ Integer.toString(channel) + "\n}";
+ log.debug("request:\n{}", request);
+ return request;
+
+ }
+
+
+ private final String genUri(String uriFormat, PortNumber port) {
+ return String.format(uriFormat, port.name());
+ }
+
+ private boolean changePortState(PortNumber number, String state) {
+ log.debug("changing the port {} state to {}", number, state);
+ String uri = genUri(PORT_STATE_URI, number);
+ String request = genPortStateRequest(state);
+ return putNoReply(uri, request);
+ }
+
+ public boolean disablePort(PortNumber number) {
+ return changePortState(number, DISABLED);
+ }
+
+ public boolean enablePort(PortNumber number) {
+ return changePortState(number, ENABLED);
+ }
+
+ public final boolean changeFrequency(OchSignal signal, PortNumber outPort) {
+ String uri = genUri(FREQUENCY_URI, outPort);
+ long frequency = toFrequency(signal);
+ String request = genFrequencyChangeRequest(frequency);
+ return putNoReply(uri, request);
+ }
+
+ public final boolean changeChannel(OchSignal signal, PortNumber outPort) {
+ String uri = genUri(CHANNEL_URI, outPort);
+ int channel = signal.spacingMultiplier() + MULTIPLIER_OFFSET;
+ log.debug("channel is {} for port {} on device {}", channel, outPort.name(), deviceId);
+ String request = genChannelChangeRequest(channel);
+ return putNoReply(uri, request);
+ }
+
+ private final long toFrequency(OchSignal signal) {
+ double frequency = BASE_FREQUENCY.asGHz() +
+ (signal.channelSpacing().frequency().asGHz() * (double) signal.slotGranularity());
+ 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));
+ log.debug("response: {}", response);
+ return response;
+ }
+
+ private boolean putNoReply(String uri, String request) {
+ return put(uri, request) == Response.Status.NO_CONTENT.getStatusCode();
+ }
+
+}
diff --git a/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveserverDeviceDescription.java b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveserverDeviceDescription.java
index 31de548..92aa0f5 100644
--- a/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveserverDeviceDescription.java
+++ b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveserverDeviceDescription.java
@@ -51,6 +51,7 @@
/**
* Discovers the ports from a Ciena WaveServer Rest device.
*/
+//TODO: Use CienaRestDevice
public class CienaWaveserverDeviceDescription extends AbstractHandlerBehaviour
implements DeviceDescriptionDiscovery {
@@ -128,21 +129,15 @@
String portId = sub.getString(PORT_ID);
DefaultAnnotations.Builder annotations = DefaultAnnotations.builder();
if (LINESIDE_PORT_ID.contains(portId)) {
- // TX/OUT port
annotations.set(AnnotationKeys.CHANNEL_ID, sub.getString(CHANNEL_ID));
- annotations.set(AnnotationKeys.PORT_NAME, portId + " TX");
+ // TX/OUT and RX/IN ports
+ annotations.set(AnnotationKeys.PORT_OUT, sub.getString(PORT_OUT));
+ annotations.set(AnnotationKeys.PORT_IN, sub.getString(PORT_IN));
ports.add(parseWaveServerCienaOchPorts(
- sub.getLong(PORT_OUT),
+ Long.valueOf(portId),
sub,
annotations.build()));
- // RX/IN port
- annotations.set(AnnotationKeys.PORT_NAME, portId + " RX");
- annotations.set(AnnotationKeys.CHANNEL_ID, sub.getString(CHANNEL_ID));
- ports.add(parseWaveServerCienaOchPorts(
- sub.getLong(PORT_IN),
- sub,
- annotations.build()));
} else if (!portId.equals("5") && !portId.equals("49")) {
DefaultAnnotations.builder()
.set(AnnotationKeys.PORT_NAME, portId);
@@ -184,6 +179,10 @@
new OchSignal(gridType, chSpacing, spacingMult, 1), annotations);
}
+ public static ArrayList<String> getLinesidePortId() {
+ return LINESIDE_PORT_ID;
+ }
+
//FIXME remove when all optical types have two way information methods, see jira tickets
private static long toGbps(long speed) {
return speed * 1000;
diff --git a/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveserverPortAdmin.java b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveserverPortAdmin.java
index 0f3b6d7..a943f7a 100644
--- a/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveserverPortAdmin.java
+++ b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveserverPortAdmin.java
@@ -17,110 +17,41 @@
package org.onosproject.drivers.ciena;
import org.onlab.util.Tools;
-import org.onosproject.net.AnnotationKeys;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.behaviour.PortAdmin;
-import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
-import org.onosproject.protocol.rest.RestSBController;
import org.slf4j.Logger;
-import javax.ws.rs.core.MediaType;
import java.util.concurrent.CompletableFuture;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import javax.ws.rs.core.Response.Status;
-import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
public class CienaWaveserverPortAdmin extends AbstractHandlerBehaviour
implements PortAdmin {
+ private CienaRestDevice restCiena;
private final Logger log = getLogger(getClass());
- private static final String APP_JSON = "application/json";
- private static final String ENABLE = "enabled";
- private static final String DISABLE = "disabled";
-
- private final String generateUri(long number) {
- return String.format("ws-ptps/ptps/%d/state", number);
- }
-
- private final String generateRequest(String state) {
- String request = "{\n" +
- "\"state\": {\n" +
- "\"admin-state\": \"" + state + "\"\n}\n}";
- log.debug("generated request: \n{}", request);
- return request;
- }
-
- private final boolean put(long number, String state) {
- String uri = generateUri(number);
- String request = generateRequest(state);
- DeviceId deviceId = handler().data().deviceId();
- RestSBController controller =
- checkNotNull(handler().get(RestSBController.class));
- InputStream payload =
- new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8));
- int response = controller.put(deviceId, uri, payload,
- MediaType.valueOf(APP_JSON));
- log.debug("response: {}", response);
- // expecting 204/NO_CONTENT_RESPONSE as successful response
- return response == Status.NO_CONTENT.getStatusCode();
- }
-
- // returns null if specified port number was not a line side port
- private Long getLineSidePort(PortNumber number) {
- DeviceService deviceService = checkNotNull(handler().get(DeviceService.class));
- DeviceId deviceId = handler().data().deviceId();
- Port port = deviceService.getPort(deviceId, number);
- if (port != null) {
- String channelId = port.annotations().value(AnnotationKeys.CHANNEL_ID);
- // any port that has channel is lineSidePort and will have TX and RX
- if (channelId != null) {
- String portName = port.annotations().value(AnnotationKeys.PORT_NAME);
- // last three characters of portName will always be " TX" or " RX"
- portName = portName.substring(0, portName.length() - 3);
- log.debug("port number {} is mapped to {} lineside port",
- number, portName);
- return new Long(portName);
- }
- }
- // not a line-side port
- return null;
- }
@Override
public CompletableFuture<Boolean> disable(PortNumber number) {
- log.debug("disabling port {}", number);
- Long lineSidePort = getLineSidePort(number);
- long devicePortNum;
- if (lineSidePort != null) {
- devicePortNum = lineSidePort.longValue();
- } else {
- devicePortNum = number.toLong();
+ try {
+ restCiena = new CienaRestDevice(handler());
+ } catch (NullPointerException e) {
+ log.error("unable to create CienaRestDevice, {}", e);
+ return CompletableFuture.completedFuture(false);
}
- CompletableFuture<Boolean> result =
- CompletableFuture.completedFuture(put(devicePortNum, DISABLE));
- return result;
+ return CompletableFuture.completedFuture(restCiena.disablePort(number));
}
@Override
public CompletableFuture<Boolean> enable(PortNumber number) {
- log.debug("enabling port {}", number);
- Long lineSidePort = getLineSidePort(number);
- long devicePortNum;
- if (lineSidePort != null) {
- devicePortNum = lineSidePort.longValue();
- } else {
- devicePortNum = number.toLong();
+ try {
+ restCiena = new CienaRestDevice(handler());
+ } catch (NullPointerException e) {
+ log.error("unable to create CienaRestDevice, {}", e);
+ return CompletableFuture.completedFuture(false);
}
- CompletableFuture<Boolean> result =
- CompletableFuture.completedFuture(put(devicePortNum, ENABLE));
- return result;
+ return CompletableFuture.completedFuture(restCiena.enablePort(number));
}
@Override
diff --git a/drivers/ciena/src/main/resources/ciena-drivers.xml b/drivers/ciena/src/main/resources/ciena-drivers.xml
index 6ff5d75..57ed4cf 100644
--- a/drivers/ciena/src/main/resources/ciena-drivers.xml
+++ b/drivers/ciena/src/main/resources/ciena-drivers.xml
@@ -23,6 +23,8 @@
impl="org.onosproject.drivers.ciena.CienaWaveserverDeviceDescription"/>
<behaviour api="org.onosproject.net.behaviour.PortAdmin"
impl="org.onosproject.drivers.ciena.CienaWaveserverPortAdmin"/>
+ <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"/>
</driver>