Drivers for Cassini box with IP Infusion OCNOS v5 supports CFP2-DCO and CFP2-ACO.
* patch 2: support of DP_8QAM
* patch 3: support of modulation change from roadm-gui
* patch 4, 5, 6, 7, 8: style
* patch 9: tested device EC_AS7716-24SC-OcNOS-5.0.187-OTN_IPBASE-S0-P0
* patch 10, 11, 12: style
* patch 13: comments by andrea
Change-Id: Ida475c61d5f48a79f8a56bff299dd708c9ad8eaf
diff --git a/apps/roadm/app/src/main/java/org/onosproject/roadm/RoadmManager.java b/apps/roadm/app/src/main/java/org/onosproject/roadm/RoadmManager.java
index 2cbc711..cd08337 100644
--- a/apps/roadm/app/src/main/java/org/onosproject/roadm/RoadmManager.java
+++ b/apps/roadm/app/src/main/java/org/onosproject/roadm/RoadmManager.java
@@ -328,13 +328,7 @@
Direction component = Direction.ALL;
if (device.is(ModulationConfig.class)) {
ModulationConfig<Object> modulationConfig = device.as(ModulationConfig.class);
- long bitRate = 0;
- if (modulation.equalsIgnoreCase(ModulationScheme.DP_QPSK.name())) {
- bitRate = 100;
- } else {
- bitRate = 200;
- }
- modulationConfig.setModulationScheme(portNumber, component, bitRate);
+ modulationConfig.setModulationScheme(portNumber, component, ModulationScheme.valueOf(modulation));
}
}
diff --git a/apps/roadm/app/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java b/apps/roadm/app/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java
index 7d5ceab..86c644f 100644
--- a/apps/roadm/app/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java
+++ b/apps/roadm/app/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java
@@ -237,11 +237,14 @@
// Returns the current input power as a string, Unknown if no value can be found.
private String getCurrentInputPower(DeviceId deviceId, PortNumber portNumber) {
- PowerConfig powerConfig = deviceService.getDevice(deviceId).as(PowerConfig.class);
- Optional<Double> currentInputPower = powerConfig.currentInputPower(portNumber, Direction.ALL);
Double inputPowerVal = null;
- if (currentInputPower.isPresent()) {
- inputPowerVal = currentInputPower.orElse(Double.MIN_VALUE);
+ if (deviceService.getDevice(deviceId).is(PowerConfig.class)) {
+ PowerConfig powerConfig = deviceService.getDevice(deviceId).as(PowerConfig.class);
+ Optional<Double> currentInputPower = powerConfig.currentInputPower(portNumber, Direction.ALL);
+
+ if (currentInputPower.isPresent()) {
+ inputPowerVal = currentInputPower.orElse(Double.MIN_VALUE);
+ }
}
return RoadmUtil.objectToString(inputPowerVal, RoadmUtil.UNKNOWN);
}
diff --git a/apps/roadm/web/roadm-gui/lib/port/port.component.html b/apps/roadm/web/roadm-gui/lib/port/port.component.html
index 5c82dc4..18f8577 100644
--- a/apps/roadm/web/roadm-gui/lib/port/port.component.html
+++ b/apps/roadm/web/roadm-gui/lib/port/port.component.html
@@ -108,8 +108,9 @@
<td [ngClass]="(isDelta() ? 'delta' : '')">
<form [formGroup]="modulationForm" (ngSubmit)="submitModulation(devId, port.id)" *ngIf="port.type=='OCH'">
<select [(ngModel)]="port.modulation" formControlName="newModulation">
- <option value="dp_qpsk">qpsk</option>
- <option value="dp_16qam">16qam</option>
+ <option value="DP_QPSK">DP_QPSK</option>
+ <option value="DP_16QAM">DP_16QAM</option>
+ <option value="DP_8QAM">DP_8QAM</option>
</select>
<button type="submit">Submit</button>
</form>
diff --git a/core/api/src/main/java/org/onosproject/net/behaviour/ModulationConfig.java b/core/api/src/main/java/org/onosproject/net/behaviour/ModulationConfig.java
index 8aa4dbe..d27b273 100644
--- a/core/api/src/main/java/org/onosproject/net/behaviour/ModulationConfig.java
+++ b/core/api/src/main/java/org/onosproject/net/behaviour/ModulationConfig.java
@@ -53,5 +53,14 @@
*/
void setModulationScheme(PortNumber port, T component, long bitRate);
+ /**
+ * Set the target Modulation Scheme on the component.
+ *
+ * @param port the port
+ * @param component the port component
+ * @param modulationScheme adopted modulation scheme
+ */
+ void setModulationScheme(PortNumber port, T component, ModulationScheme modulationScheme);
+
}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5BitErrorRate.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5BitErrorRate.java
new file mode 100644
index 0000000..de87bec
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5BitErrorRate.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2022-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.
+ *
+ * This work was partially supported by EC H2020 project B5G-OPEN (101016663).
+ */
+
+package org.onosproject.drivers.odtn;
+
+import org.apache.commons.configuration.XMLConfiguration;
+import org.onosproject.drivers.odtn.util.NetconfSessionUtility;
+import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.BitErrorRateState;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.odtn.behaviour.OdtnDeviceDescriptionDiscovery.OC_OPTICAL_CHANNEL_NAME;
+
+/**
+ * Implementation of BitErrorRateState interface for Cassini device running Ocnos v5.
+ */
+public class CassiniOcnos5BitErrorRate
+ extends AbstractHandlerBehaviour implements BitErrorRateState {
+
+ private static final Logger log = LoggerFactory.getLogger(CassiniOcnos5BitErrorRate.class);
+
+ private static final String RPC_TAG_NETCONF_BASE =
+ "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
+
+ private static final String RPC_CLOSE_TAG = "</rpc>";
+ private static final String PRE_FEC_BER_TAG = "current-pre-fec-ber";
+ private static final String POST_FEC_BER_TAG = "current-post-fec-ber";
+ private static final String PRE_FEC_BER_FILTER =
+ "data.terminal-device.coherent-module.network-interfaces.interface.ber.state." + PRE_FEC_BER_TAG;
+ private static final String POST_FEC_BER_FILTER =
+ "data..terminal-device.coherent-module.network-interfaces.interface.ber.state." + POST_FEC_BER_TAG;
+
+ /*
+ * This method returns the instance of NetconfController from DriverHandler.
+ */
+ private NetconfController getController() {
+ return handler().get(NetconfController.class);
+ }
+
+ /**
+ * Get the BER value pre FEC.
+ *
+ * @param deviceId the device identifier
+ * @param port the port identifier
+ * @return the decimal value of BER
+ */
+ @Override
+ public Optional<Double> getPreFecBer(DeviceId deviceId, PortNumber port) {
+ NetconfSession session = NetconfSessionUtility
+ .getNetconfSession(deviceId, getController());
+ checkNotNull(session);
+
+ String slotIndex = getOpticalChannel(port);
+
+ if (slotIndex != null) {
+
+ String reply;
+ try {
+ reply = session.get(getBerFilter(slotIndex, PRE_FEC_BER_TAG));
+ } catch (Exception e) {
+ throw new IllegalStateException(new NetconfException("Failed to retrieve getPreFecBer info.", e));
+ }
+
+ log.debug("REPLY from device: {}", reply);
+
+ XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+ if (xconf == null) {
+ log.error("Error in executing RPC");
+ return Optional.empty();
+ }
+
+ String powerString = xconf.getString((PRE_FEC_BER_FILTER));
+
+ log.debug("currentPreFecBer from device: {}", powerString);
+
+ if (powerString == null) {
+ return Optional.empty();
+ }
+
+ Double rational = 1e18;
+ return Optional.of(Double.valueOf(powerString) / rational);
+ }
+
+ return Optional.empty();
+ }
+
+ /**
+ * Get the OpenConfig component name for the OpticalChannel component.
+ *
+ * @param portNumber ONOS port number of the Line port ().
+ * @return the channel component name or null
+ */
+ protected String getOpticalChannel(PortNumber portNumber) {
+ Port clientPort = handler().get(DeviceService.class).getPort(did(), portNumber);
+ return clientPort.annotations().value(OC_OPTICAL_CHANNEL_NAME);
+ }
+
+ /**
+ * Get the deviceId for which the methods apply.
+ *
+ * @return The deviceId as contained in the handler data
+ */
+ private DeviceId did() {
+ return handler().data().deviceId();
+ }
+
+ /**
+ * Get the BER value post FEC.
+ *
+ * @param deviceId the device identifier
+ * @param port the port identifier
+ * @return the decimal value of BER
+ */
+ @Override
+ public Optional<Double> getPostFecBer(DeviceId deviceId, PortNumber port) {
+ NetconfSession session = NetconfSessionUtility
+ .getNetconfSession(deviceId, getController());
+ checkNotNull(session);
+
+ String slotIndex = getOpticalChannel(port);
+
+ if (slotIndex != null) {
+
+ String reply;
+ try {
+ reply = session.get(getBerFilter(slotIndex, POST_FEC_BER_TAG));
+ } catch (Exception e) {
+ throw new IllegalStateException(new NetconfException("Failed to retrieve getPostFecBer info.", e));
+ }
+
+ log.debug("REPLY from device: {}", reply);
+
+ XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+ if (xconf == null) {
+ log.error("Error in executing RPC");
+ return Optional.empty();
+ }
+
+ String powerString = xconf.getString(POST_FEC_BER_FILTER);
+
+ log.debug("currentPostFecBer from device: {}", powerString);
+
+ if (powerString == null) {
+ return Optional.empty();
+ }
+
+ Double rational = 1e18;
+ return Optional.of(Double.valueOf(powerString) / rational);
+ }
+
+ return Optional.empty();
+ }
+
+ private String getBerFilter(String slotNumber, String filterBer) {
+ StringBuilder filter = new StringBuilder();
+
+ filter.append("<terminal-device xmlns='http://www.ipinfusion.com/yang/ocnos/ipi-platform-terminal-device'>"
+ + "<coherent-module>"
+ + " <slot-index>" + slotNumber + "</slot-index>"
+ + "<network-interfaces>"
+ + "<interface>"
+ + " <net-index>0</net-index>"
+ + " <ber>"
+ + " <state>"
+ + " <" + filterBer + "/>"
+ + " </state>"
+ + " </ber>"
+ + "</interface>"
+ + "</network-interfaces>"
+ + "</coherent-module>"
+ + "</terminal-device>");
+
+ return filteredGetBuilder(filter.toString());
+ }
+
+ private String filteredGetBuilder(String filter) {
+ StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
+ rpc.append("<get>");
+ rpc.append("<filter type='subtree'>");
+ rpc.append(filter);
+ rpc.append("</filter>");
+ rpc.append("</get>");
+ rpc.append(RPC_CLOSE_TAG);
+ return rpc.toString();
+ }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5DeviceDiscovery.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5DeviceDiscovery.java
new file mode 100644
index 0000000..88e62bb
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5DeviceDiscovery.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright 2022-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.
+ *
+ * This work was partially supported by EC H2020 project B5G-OPEN (101016663).
+ */
+
+package org.onosproject.drivers.odtn;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
+import org.onlab.packet.ChassisId;
+import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.Device;
+import org.onosproject.net.Port;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.OduSignalType;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.device.DefaultPortDescription;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.device.DeviceDescriptionDiscovery;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.optical.device.OchPortHelper;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfDevice;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+import org.onosproject.odtn.behaviour.OdtnDeviceDescriptionDiscovery;
+import org.slf4j.Logger;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+Tested on Cassini running OcNOS version EC_AS7716-24SC-OcNOS-5.0.187-OTN_IPBASE-S0-P0.
+
+- Driver consider proprietary YANG model by IP Infusion
+- The device also support an OpenConfig-based model that can be activated using NETCONF translation
+- Cassini CLI command to activate NETCONF translation
+ --- OcNOS# cml netconf translation (disable|openconfig)
+ */
+/**
+ * Implementation of DeviceDescriptionDiscovery interface for Cassini device running Ocnos v5.
+ */
+public class CassiniOcnos5DeviceDiscovery
+ extends AbstractHandlerBehaviour
+ implements OdtnDeviceDescriptionDiscovery, DeviceDescriptionDiscovery {
+
+ private static final String RPC_TAG_NETCONF_BASE =
+ "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
+
+ private static final String RPC_CLOSE_TAG = "</rpc>";
+
+ private static final String OC_PLATFORM_TYPES_TRANSCEIVER =
+ "oc-platform-types:TRANSCEIVER";
+
+ private static final String OC_PLATFORM_TYPES_PORT =
+ "oc-platform-types:PORT";
+
+ private static final String OC_TRANSPORT_TYPES_OPTICAL_CHANNEL =
+ "oc-opt-types:OPTICAL_CHANNEL";
+
+ private static final Logger log = getLogger(CassiniOcnos5DeviceDiscovery.class);
+
+ /**
+ * Returns the NetconfSession with the device for which the method was called.
+ *
+ * @param deviceId device indetifier
+ *
+ * @return The netconf session or null
+ */
+ private NetconfSession getNetconfSession(DeviceId deviceId) {
+ NetconfController controller = handler().get(NetconfController.class);
+ NetconfDevice ncdev = controller.getDevicesMap().get(deviceId);
+ if (ncdev == null) {
+ log.trace("No netconf device, returning null session");
+ return null;
+ }
+ return ncdev.getSession();
+ }
+
+
+ /**
+ * Get the deviceId for which the methods apply.
+ *
+ * @return The deviceId as contained in the handler data
+ */
+ private DeviceId did() {
+ return handler().data().deviceId();
+ }
+
+
+ /**
+ * Get the device instance for which the methods apply.
+ *
+ * @return The device instance
+ */
+ private Device getDevice() {
+ DeviceService deviceService = checkNotNull(handler().get(DeviceService.class));
+ Device device = deviceService.getDevice(did());
+ return device;
+ }
+
+
+ /**
+ * Construct a String with a Netconf filtered get RPC Message.
+ *
+ * @param filter A valid XML tree with the filter to apply in the get
+ * @return a String containing the RPC XML Document
+ */
+ private String filteredGetBuilder(String filter) {
+ StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
+ rpc.append("<get>");
+ rpc.append("<filter type='subtree'>");
+ rpc.append(filter);
+ rpc.append("</filter>");
+ rpc.append("</get>");
+ rpc.append(RPC_CLOSE_TAG);
+ return rpc.toString();
+ }
+
+
+ /**
+ * Construct a String with a Netconf filtered get RPC Message.
+ *
+ * @param filter A valid XPath Expression with the filter to apply in the get
+ * @return a String containing the RPC XML Document
+ *
+ * Note: server must support xpath capability.
+
+ * <select=" /components/component[name='PORT-A-In-1']/properties/...
+ * ...property[name='onos-index']/config/value" type="xpath"/>
+ */
+ private String xpathFilteredGetBuilder(String filter) {
+ StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
+ rpc.append("<get>");
+ rpc.append("<filter type='xpath' select=\"");
+ rpc.append(filter);
+ rpc.append("\"/>");
+ rpc.append("</get>");
+ rpc.append(RPC_CLOSE_TAG);
+ return rpc.toString();
+ }
+
+
+ /**
+ * Builds a request to get Device details, operational data.
+ *
+ * @return A string with the Netconf RPC for a get with subtree rpcing based on
+ * /components/component/state/type being oc-platform-types:OPERATING_SYSTEM
+ */
+ private String getDeviceDetailsBuilder() {
+ StringBuilder filter = new StringBuilder();
+ filter.append("<components xmlns='http://openconfig.net/yang/platform'>");
+ filter.append(" <component>");
+ filter.append(" <state>");
+ filter.append(" <type xmlns:oc-platform-types='http://openconfig.net/");
+ filter.append("yang/platform-types'>oc-platform-types:OPERATING_SYSTEM</type>");
+ filter.append(" </state>");
+ filter.append(" </component>");
+ filter.append("</components>");
+ return filteredGetBuilder(filter.toString());
+ }
+
+ private String getCassiniDeviceDetailsBuilder() {
+ StringBuilder filter = new StringBuilder();
+ filter.append("<components xmlns='http://www.ipinfusion.com/yang/ocnos/ipi-platform'>");
+ filter.append(" <component>");
+ filter.append(" <name>CHASSIS</name>");
+ filter.append(" </component>");
+ filter.append("</components>");
+ return filteredGetBuilder(filter.toString());
+ }
+
+ private String getCassiniDeviceComponentsBuilder() {
+ StringBuilder filter = new StringBuilder();
+ filter.append("<components xmlns='http://www.ipinfusion.com/yang/ocnos/ipi-platform'>");
+ filter.append("</components>");
+ return filteredGetBuilder(filter.toString());
+ }
+
+ /**
+ * Returns a DeviceDescription with Device info.
+ *
+ * @return DeviceDescription or null
+ *
+ * //CHECKSTYLE:OFF
+ * <pre>{@code
+ * <data>
+ * <components xmlns="http://openconfig.net/yang/platform">
+ * <component>
+ * <state>
+ * <name>FIRMWARE</name>
+ * <type>oc-platform-types:OPERATING_SYSTEM</type>
+ * <description>CTTC METRO-HAUL Emulated OpenConfig TerminalDevice</description>
+ * <version>0.0.1</version>
+ * </state>
+ * </component>
+ * </components>
+ * </data>
+ *}</pre>
+ * //CHECKSTYLE:ON
+ */
+ @Override
+ public DeviceDescription discoverDeviceDetails() {
+ log.info("CassiniOcnos5DeviceDiscovery::discoverDeviceDetails device {}", did());
+ boolean defaultAvailable = true;
+ SparseAnnotations annotations = DefaultAnnotations.builder().build();
+
+ org.onosproject.net.Device.Type type = Device.Type.TERMINAL_DEVICE;
+
+ // Some defaults
+ String vendor = "Not loaded";
+ String hwVersion = "Not loaded";
+ String swVersion = "Not loaded";
+ String serialNumber = "Not loaded";
+ String chassisId = "12";
+
+ // Get the session,
+ NetconfSession session = getNetconfSession(did());
+ if (session != null) {
+ try {
+ String reply = session.get(getCassiniDeviceDetailsBuilder());
+ XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+
+ log.debug("REPLY from device: {}", xconf);
+
+ vendor = xconf.getString("data.components.component.chassis.state.vendor-name", vendor);
+ serialNumber = xconf.getString("data.components.component.state.serial-no", serialNumber);
+ // Requires OpenConfig >= 2018
+ swVersion = xconf.getString("data.components.component.state.software-version", swVersion);
+ hwVersion = xconf.getString("data.components.component.state.mfg-date", hwVersion);
+ } catch (Exception e) {
+ throw new IllegalStateException(new NetconfException("Failed to retrieve version info.", e));
+ }
+ } else {
+ log.info("CassiniOcnos5DeviceDiscovery::discoverDeviceDetails - No netconf session for {}", did());
+ }
+
+ log.info("VENDOR {}", vendor);
+ log.info("HWVERSION {}", hwVersion);
+ log.info("SWVERSION {}", swVersion);
+ log.info("SERIAL {}", serialNumber);
+ log.info("CHASSISID {}", chassisId);
+
+ ChassisId cid = new ChassisId(Long.valueOf(chassisId, 10));
+
+ return new DefaultDeviceDescription(did().uri(),
+ type, vendor, hwVersion, swVersion, serialNumber,
+ cid, defaultAvailable, annotations);
+ }
+
+ /**
+ * Returns a list of PortDescriptions for the device.
+ *
+ * @return a list of descriptions.
+ *
+ * The RPC reply follows the following pattern:
+ * //CHECKSTYLE:OFF
+ * <pre>{@code
+ * <?xml version="1.0" encoding="UTF-8"?>
+ * <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="7">
+ * <data>
+ * <components xmlns="http://openconfig.net/yang/platform">
+ * <component>....
+ * </component>
+ * <component>....
+ * </component>
+ * </components>
+ * </data>
+ * </rpc-reply>
+ * }</pre>
+ * //CHECKSTYLE:ON
+ */
+ @Override
+ public List<PortDescription> discoverPortDetails() {
+ try {
+ XPathExpressionEngine xpe = new XPathExpressionEngine();
+ NetconfSession session = getNetconfSession(did());
+ if (session == null) {
+ log.error("discoverPortDetails called with null session for {}", did());
+ return ImmutableList.of();
+ }
+
+ String reply = session.get(getCassiniDeviceComponentsBuilder());
+
+ XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+ xconf.setExpressionEngine(xpe);
+
+ HierarchicalConfiguration components = xconf.configurationAt("data/components");
+ return parsePorts(components);
+ } catch (Exception e) {
+ log.error("Exception discoverPortDetails() {}", did(), e);
+ return ImmutableList.of();
+ }
+ }
+
+ /**
+ * Parses port information from OpenConfig XML configuration.
+ *
+ * @param components the XML document with components root.
+ * @return List of ports
+ *
+ * //CHECKSTYLE:OFF
+ * <pre>{@code
+ * <components xmlns="http://openconfig.net/yang/platform">
+ * <component>....
+ * </component>
+ * <component>....
+ * </component>
+ * </components>
+ * }</pre>
+ * //CHECKSTYLE:ON
+ */
+ protected List<PortDescription> parsePorts(HierarchicalConfiguration components) {
+
+ return components.configurationsAt("component")
+ .stream()
+ .map(component -> {
+ try {
+ // Pass the root document for cross-reference
+ return parsePortComponent(component);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ )
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Parses a component XML doc into a PortDescription.
+ *
+ * @param component subtree to parse. It must be a component ot type PORT.
+ * case we need to check transceivers or optical channels.
+ *
+ * @return PortDescription or null if component does not have onos-index
+ */
+ private PortDescription parsePortComponent(HierarchicalConfiguration component) {
+ Map<String, String> annotations = new HashMap<>();
+ String name = component.getString("name");
+
+ log.info("Parsing Component {}", name);
+
+ // Build the port using heuristic
+ // NOTE: using portNumber(id, name) breaks things. Intent parsing, port resorce management, etc. There seems
+ // to be an issue with resource mapping
+
+ if (name.contains("QSFP")) {
+ annotations.put(PORT_TYPE, OdtnDeviceDescriptionDiscovery.OdtnPortType.CLIENT.value());
+
+ // Assing an ONOS port number
+ PortNumber portNum;
+ portNum = PortNumber.portNumber(fromPortNameToPortId(name));
+ log.info("--- CLIENT PORT {} assigned number {}", name, portNum);
+
+ DefaultPortDescription.Builder builder = DefaultPortDescription.builder();
+ builder.type(Port.Type.PACKET);
+ builder.withPortNumber(portNum);
+ builder.annotations(DefaultAnnotations.builder().putAll(annotations).build());
+
+ return builder.build();
+ }
+
+ //Could be improved checking if there is an OCH subcomponent or current <oper-status>
+ if (name.contains("PORT-coherent")) {
+ PortNumber portNum;
+ portNum = PortNumber.portNumber(fromPortNameToPortId(name));
+
+ annotations.put(PORT_TYPE, OdtnDeviceDescriptionDiscovery.OdtnPortType.LINE.value());
+
+ String state = component.configurationAt("state").getString("oper-status");
+
+ if (!state.contains("disabled")) {
+ String slotName = String.valueOf(portNum.toLong() - 100);
+
+ //Config annotations
+ annotations.put(PORT_TYPE, OdtnPortType.LINE.value());
+ annotations.put(OC_NAME, name);
+ annotations.put(OC_OPTICAL_CHANNEL_NAME, slotName);
+
+ log.info("--- LINE port {} assigned onos index {}", name, portNum);
+ log.info("--- LINE port {} associated OPTICAL_CHANNEL {}", name, slotName);
+
+ // TODO: To be configured
+ OchSignal signalId = OchSignal.newDwdmSlot(ChannelSpacing.CHL_50GHZ, 1);
+
+ return OchPortHelper.ochPortDescription(
+ portNum, true,
+ OduSignalType.ODU4, // TODO Client signal to be discovered
+ true,
+ signalId,
+ DefaultAnnotations.builder().putAll(annotations).build());
+ } else {
+ log.info("--- LINE port {} is disabled", name);
+ }
+ }
+
+ log.warn("Unknown port type");
+ return null;
+ }
+
+ //Client ports are reported in the model as QSFP-1, QSFP-2 and imported as 1, 2, ...
+ //Line ports are reported in the model as PORT-Coherent-1, PORT-Coherent-2 and imported as 101, 102
+ public int fromPortNameToPortId(String name) {
+ String[] portions;
+ if (name.contains("QSFP")) {
+ portions = name.split("-");
+ return Integer.parseInt(portions[1]);
+ }
+ if (name.contains("PORT")) {
+ portions = name.split("-");
+ return (100 + Integer.parseInt(portions[2]));
+ }
+ log.error("Port name not supported");
+ return 0;
+ }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5FlowRuleProgrammable.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5FlowRuleProgrammable.java
new file mode 100644
index 0000000..ac50a30
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5FlowRuleProgrammable.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2022-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.
+ *
+ * This work was partially supported by EC H2020 project B5G-OPEN (101016663).
+ */
+
+package org.onosproject.drivers.odtn;
+
+import com.google.common.collect.ImmutableList;
+import org.onosproject.drivers.odtn.impl.DeviceConnectionCache;
+import org.onosproject.drivers.odtn.impl.FlowRuleParser;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleProgrammable;
+import org.onosproject.netconf.DatastoreId;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.odtn.behaviour.OdtnDeviceDescriptionDiscovery.OC_OPTICAL_CHANNEL_NAME;
+
+/**
+ * Implementation of FlowRuleProgrammable interface for Cassini device running Ocnos v5.
+ */
+public class CassiniOcnos5FlowRuleProgrammable
+ extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
+
+ private static final Logger log =
+ LoggerFactory.getLogger(CassiniOcnos5FlowRuleProgrammable.class);
+
+ private static final String RPC_TAG_NETCONF_BASE =
+ "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
+
+ private static final String RPC_CLOSE_TAG = "</rpc>";
+
+
+ /**
+ * Apply the flow entries specified in the collection rules.
+ *
+ * @param rules A collection of Flow Rules to be applied
+ * @return The collection of added Flow Entries
+ */
+ @Override
+ public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
+ NetconfSession session = getNetconfSession();
+ if (session == null) {
+ ocnosError("null session");
+ return ImmutableList.of();
+ }
+ List<FlowRule> added = new ArrayList<>();
+ for (FlowRule r : rules) {
+ try {
+ String connectionId = applyFlowRule(session, r);
+ getConnectionCache().add(did(), connectionId, r);
+ added.add(r);
+ } catch (Exception e) {
+ ocnosError("Error {}", e);
+ continue;
+ }
+ }
+ ocnosLog("applyFlowRules added {}", added.size());
+ return added;
+ }
+
+ /**
+ * Get the flow entries that are present on the device.
+ *
+ * @return A collection of Flow Entries
+ */
+ @Override
+ public Collection<FlowEntry> getFlowEntries() {
+ DeviceConnectionCache cache = getConnectionCache();
+ if (cache.get(did()) == null) {
+ return ImmutableList.of();
+ }
+
+ List<FlowEntry> entries = new ArrayList<>();
+ for (FlowRule r : cache.get(did())) {
+ entries.add(
+ new DefaultFlowEntry(r, FlowEntry.FlowEntryState.ADDED, 0, 0, 0));
+ }
+ return entries;
+ }
+
+ /**
+ * Remove the specified flow rules.
+ *
+ * @param rules A collection of Flow Rules to be removed
+ * @return The collection of removed Flow Entries
+ */
+ @Override
+ public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
+ NetconfSession session = getNetconfSession();
+ if (session == null) {
+ ocnosError("null session");
+ return ImmutableList.of();
+ }
+ List<FlowRule> removed = new ArrayList<>();
+ for (FlowRule r : rules) {
+ try {
+ String connectionId = removeFlowRule(session, r);
+ getConnectionCache().remove(did(), connectionId);
+ removed.add(r);
+ } catch (Exception e) {
+ ocnosError("Error {}", e);
+ continue;
+ }
+ }
+ ocnosLog("removedFlowRules removed {}", removed.size());
+ return removed;
+ }
+
+ private DeviceConnectionCache getConnectionCache() {
+ return DeviceConnectionCache.init();
+ }
+
+ /**
+ * Helper method to get the device id.
+ */
+ private DeviceId did() {
+ return data().deviceId();
+ }
+
+ /**
+ * Helper method to log from this class adding DeviceId.
+ */
+ private void ocnosLog(String format, Object... arguments) {
+ log.info("OCNOS5 {}: " + format, did(), arguments);
+ }
+
+ /**
+ * Helper method to log an error from this class adding DeviceId.
+ */
+ private void ocnosError(String format, Object... arguments) {
+ log.error("OCNOS5 {}: " + format, did(), arguments);
+ }
+
+ /**
+ * Helper method to get the Netconf Session.
+ */
+ private NetconfSession getNetconfSession() {
+ NetconfController controller =
+ checkNotNull(handler().get(NetconfController.class));
+ return controller.getNetconfDevice(did()).getSession();
+ }
+
+ /**
+ * Get the OpenConfig component name for the OpticalChannel component.
+ *
+ * @param portNumber ONOS port number of the Line port ().
+ * @return the channel component name or null
+ */
+ protected String getOpticalChannel(PortNumber portNumber) {
+ Port clientPort = handler().get(DeviceService.class).getPort(did(), portNumber);
+ return clientPort.annotations().value(OC_OPTICAL_CHANNEL_NAME);
+ }
+
+ public void setOpticalChannelFrequency(NetconfSession session, FlowRuleParser r)
+ throws NetconfException {
+ StringBuilder sbSet = new StringBuilder();
+
+ String slot = getOpticalChannel(r.getPortNumber());
+
+ sbSet.append("<terminal-device xmlns='http://www.ipinfusion.com/yang/ocnos/ipi-platform-terminal-device'>"
+ + "<coherent-module>"
+ + " <slot-index>" + slot + "</slot-index>"
+ + " <config>"
+ + " <slot-index>" + slot + "</slot-index>"
+ + " </config>"
+ + "<network-interfaces>"
+ + "<interface>"
+ + " <net-index>0</net-index>"
+ + " <config>"
+ + " <net-index>0</net-index>"
+ + " <frequency>" + r.getCentralFrequency().asHz() + "Hz</frequency>"
+ + " </config>"
+ + "</interface>"
+ + "</network-interfaces>"
+ + "</coherent-module>"
+ + "</terminal-device>");
+
+ log.info("Configure optical channel {}", sbSet);
+
+ boolean set = session.editConfig(DatastoreId.CANDIDATE, null, sbSet.toString());
+ if (!set) {
+ throw new NetconfException("error editing channel frequency");
+ }
+
+ log.info("Commit configure optical channel");
+ boolean commit2 = session.commit();
+ if (!commit2) {
+ throw new NetconfException("error committing channel frequency");
+ }
+ };
+
+ public void deleteOpticalChannelFrequency(NetconfSession session, FlowRuleParser r)
+ throws NetconfException {
+ StringBuilder sb = new StringBuilder();
+
+ String slot = getOpticalChannel(r.getPortNumber());
+
+ sb.append("<terminal-device xmlns='http://www.ipinfusion.com/yang/ocnos/ipi-platform-terminal-device'>"
+ + "<coherent-module>"
+ + " <slot-index>" + slot + "</slot-index>"
+ + " <config>"
+ + " <slot-index>" + slot + "</slot-index>"
+ + " </config>"
+ + "<network-interfaces>"
+ + "<interface>"
+ + " <net-index>0</net-index>"
+ + " <config>"
+ + " <net-index>0</net-index>"
+ + " <frequency>" + r.getCentralFrequency().asHz() + "Hz</frequency>"
+ + " </config>"
+ + "</interface>"
+ + "</network-interfaces>"
+ + "</coherent-module>"
+ + "</terminal-device>");
+
+ log.info("Disable service and delete optical channel {}", sb);
+ boolean edit = session.editConfig(DatastoreId.CANDIDATE, null, sb.toString());
+ if (!edit) {
+ throw new NetconfException("error editing channel frequency");
+ }
+
+ log.info("Commit optical channel");
+ boolean commit = session.commit();
+ if (!commit) {
+ throw new NetconfException("error committing channel frequency");
+ }
+ };
+
+
+ /**
+ * Apply the flowrule.
+ *
+ * @param session The Netconf session.
+ * @param r Flow Rules to be applied.
+ * @return the optical channel + the frequency or just channel as identifier fo the config installed on the device
+ * @throws NetconfException if exchange goes wrong
+ */
+ protected String applyFlowRule(NetconfSession session, FlowRule r)
+ throws NetconfException {
+ FlowRuleParser frp = new FlowRuleParser(r);
+
+ setOpticalChannelFrequency(session, frp);
+ return frp.getPortNumber() + ":" + frp.getCentralFrequency().asGHz();
+ }
+
+
+ protected String removeFlowRule(NetconfSession session, FlowRule r)
+ throws NetconfException {
+ FlowRuleParser frp = new FlowRuleParser(r);
+
+ deleteOpticalChannelFrequency(session, frp);
+ return frp.getPortNumber() + ":" + frp.getCentralFrequency().asGHz();
+ }
+}
+
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5Modulation.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5Modulation.java
new file mode 100644
index 0000000..f73108d
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5Modulation.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright 2022-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.
+ *
+ * This work was partially supported by EC H2020 project B5G-OPEN (101016663).
+ */
+
+package org.onosproject.drivers.odtn;
+
+import org.apache.commons.configuration.XMLConfiguration;
+import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.ModulationScheme;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.ModulationConfig;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.netconf.DatastoreId;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfDevice;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.odtn.behaviour.OdtnDeviceDescriptionDiscovery.OC_OPTICAL_CHANNEL_NAME;
+
+/**
+ * Implementation of ModulationConfig interface for Cassini device running Ocnos v5.
+ */
+public class CassiniOcnos5Modulation<T> extends AbstractHandlerBehaviour
+ implements ModulationConfig<T> {
+
+ private static final String RPC_TAG_NETCONF_BASE =
+ "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
+
+ private static final String RPC_CLOSE_TAG = "</rpc>";
+
+ private static final Logger log = LoggerFactory.getLogger(CassiniOcnos5Modulation.class);
+
+ /**
+ * Returns the NetconfSession with the device for which the method was called.
+ *
+ * @param deviceId device indetifier
+ * @return The netconf session or null
+ */
+ private NetconfSession getNetconfSession(DeviceId deviceId) {
+ NetconfController controller = handler().get(NetconfController.class);
+ NetconfDevice ncdev = controller.getDevicesMap().get(deviceId);
+ if (ncdev == null) {
+ log.trace("No netconf device, returning null session");
+ return null;
+ }
+ return ncdev.getSession();
+ }
+
+ /**
+ * Get the OpenConfig component name for the OpticalChannel component.
+ *
+ * @param portNumber ONOS port number of the Line port ().
+ * @return the channel component name or null
+ */
+ protected String getOpticalChannel(PortNumber portNumber) {
+ Port clientPort = handler().get(DeviceService.class).getPort(did(), portNumber);
+ return clientPort.annotations().value(OC_OPTICAL_CHANNEL_NAME);
+ }
+
+ /*
+ *
+ * Get the deviceId for which the methods apply.
+ *
+ * @return The deviceId as contained in the handler data
+ */
+ private DeviceId did() {
+ return handler().data().deviceId();
+ }
+
+ private String getOpModeFilter(String slotName) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("<terminal-device xmlns='http://www.ipinfusion.com/yang/ocnos/ipi-platform-terminal-device'>"
+ + "<coherent-module>"
+ + " <slot-index>" + slotName + "</slot-index>"
+ + "<network-interfaces>"
+ + "<interface>"
+ + " <net-index>0</net-index>"
+ + " <state>"
+ + " <modulation-format/>"
+ + " </state>"
+ + "</interface>"
+ + "</network-interfaces>"
+ + "</coherent-module>"
+ + "</terminal-device>");
+
+ return sb.toString();
+ }
+
+ /*Parse filtering string from port and component.
+ *
+ * @param portNumber Port Number
+ * @param modulation
+ * @return filtering string in xml format
+ */
+ private String setOpModeFilter(PortNumber portNumber, ModulationScheme modulation) {
+
+ String operationalMode = getOperationalMode(modulation);
+ String slotName = getOpticalChannel(portNumber);
+
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("<terminal-device xmlns='http://www.ipinfusion.com/yang/ocnos/ipi-platform-terminal-device'>"
+ + "<coherent-module>"
+ + " <slot-index>" + slotName + "</slot-index>"
+ + " <config>"
+ + " <slot-index>" + slotName + "</slot-index>"
+ + " </config>"
+ + "<network-interfaces>"
+ + "<interface>"
+ + " <net-index>0</net-index>"
+ + " <config>"
+ + " <net-index>0</net-index>"
+ + " <modulation-format>" + operationalMode + "</modulation-format>"
+ + " </config>"
+ + "</interface>"
+ + "</network-interfaces>"
+ + "</coherent-module>"
+ + "</terminal-device>");
+
+ return sb.toString();
+ }
+
+ /**
+ * Get the target Modulation Scheme on the component.
+ *
+ * @param port the port
+ * @param component the port component
+ * @return ModulationScheme as per bitRate value
+ **/
+ @Override
+ public Optional<ModulationScheme> getModulationScheme(PortNumber port, T component) {
+ if (checkPortComponent(port, component)) {
+ return getOcnosModulationScheme(port, component);
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Set the target Modulation Scheme on the component.
+ *
+ * @param port the port
+ * @param component the port component
+ * @param bitRate bit rate in bps
+ **/
+ @Override
+ public void setModulationScheme(PortNumber port, T component, long bitRate) {
+ if (checkPortComponent(port, component)) {
+ setOcnosModulationScheme(port, component, bitRate);
+ }
+ }
+
+ /**
+ * Set the target Modulation Scheme on the component.
+ *
+ * @param port the port
+ * @param component the port component
+ * @param modulationScheme selecetd modulation
+ **/
+ @Override
+ public void setModulationScheme(PortNumber port, T component, ModulationScheme modulationScheme) {
+ if (checkPortComponent(port, component)) {
+ setOcnosModulationScheme(port, component, modulationScheme);
+ }
+ }
+
+ private String filteredEditConfigBuilder(String filterEditConfig) {
+ StringBuilder rpc = new StringBuilder();
+ rpc.append(RPC_TAG_NETCONF_BASE);
+ rpc.append("<edit-config>");
+ rpc.append("<target><" + DatastoreId.CANDIDATE + "/></target>");
+ rpc.append("<config>");
+ rpc.append(filterEditConfig);
+ rpc.append("</config>");
+ rpc.append("</edit-config>");
+ rpc.append(RPC_CLOSE_TAG);
+
+ return rpc.toString();
+ }
+
+ private String filteredGetBuilder(String filter) {
+ StringBuilder rpc = new StringBuilder();
+ rpc.append(RPC_TAG_NETCONF_BASE);
+ rpc.append("<get>");
+ rpc.append("<filter type='subtree'>");
+ rpc.append(filter);
+ rpc.append("</filter>");
+ rpc.append("</get>");
+ rpc.append(RPC_CLOSE_TAG);
+ return rpc.toString();
+ }
+
+ /**
+ * Set the ComponentType to invoke proper methods for different template T.
+ *
+ * @param port the component.
+ * @param component the component.
+ */
+ private Boolean checkPortComponent(PortNumber port, Object component) {
+
+ //Check componenet
+ String clsName = component.getClass().getName();
+ switch (clsName) {
+ case "org.onosproject.net.Direction":
+ break;
+ case "org.onosproject.net.OchSignal":
+ break;
+ default:
+ log.error("Cannot parse the component type {}.", clsName);
+ log.error("The component content is {}.", component.toString());
+ return false;
+ }
+
+ //Check that port has an associated optical channel
+ if (getOpticalChannel(port) == null) {
+ return false;
+ }
+
+ //Checks are ok
+ return true;
+ }
+
+ /*
+ * Set modulation scheme.
+ *
+ * @param port port
+ * @param component component
+ * @param power target value
+ */
+ void setOcnosModulationScheme(PortNumber port, Object component, ModulationScheme modulationScheme) {
+ NetconfSession session = getNetconfSession(did());
+ checkNotNull(session);
+
+ //log.info("Setting modulation scheme {}", modulationScheme);
+
+ String filter = setOpModeFilter(port, modulationScheme);
+ String rpcReq = filteredEditConfigBuilder(filter);
+
+ try {
+ session.rpc(rpcReq);
+ } catch (Exception e) {
+ log.error("Error writing operational mode on CANDIDATE", e);
+ }
+
+ //log.info("Modulation config sent {}", rpcReq);
+
+ try {
+ session.commit();
+ } catch (NetconfException e) {
+ log.error("Error committing operational mode", e);
+ }
+ }
+
+ /*
+ * Get modulation scheme.
+ *
+ * @param port port
+ * @param component component
+ * @return target modulation
+ */
+ Optional<ModulationScheme> getOcnosModulationScheme(PortNumber port, Object component) {
+ NetconfSession session = getNetconfSession(did());
+ checkNotNull(session);
+
+ String filter = getOpModeFilter(getOpticalChannel(port));
+
+ String reply;
+ try {
+ reply = session.get(filteredGetBuilder(filter));
+ } catch (Exception e) {
+ throw new IllegalStateException(new NetconfException("Failed to retrieve opMode.", e));
+ }
+
+ //log.info("REPLY from device: {}", reply);
+
+ XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+ if (xconf == null) {
+ log.error("Error in executing get");
+ return Optional.empty();
+ }
+
+ String opModeString = xconf.getString(("data." +
+ "terminal-device.coherent-module.network-interfaces.interface." +
+ "state.modulation-format"));
+
+ //log.info("Modulation format mode from device: {}", opModeString);
+
+ if (opModeString == null) {
+ return Optional.empty();
+ }
+
+ ModulationScheme modulation;
+ if (opModeString.equals("dp-8-qam")) {
+ modulation = ModulationScheme.DP_8QAM;
+ } else if (opModeString.equals("dp-16-qam")) {
+ modulation = ModulationScheme.DP_16QAM;
+ } else if (opModeString.equals("dp-qpsk")) {
+ modulation = ModulationScheme.DP_QPSK;
+ } else {
+ log.error("Current operational mode not supported by the driver");
+ return Optional.empty();
+ }
+ return Optional.of(modulation);
+ }
+
+ /*
+ * Set modulation scheme using bitrate.
+ *
+ * @param port port
+ * @param component component
+ * @param power target value
+ */
+ void setOcnosModulationScheme(PortNumber port, Object component, long bitRate) {
+ NetconfSession session = getNetconfSession(did());
+ checkNotNull(session);
+
+ ModulationScheme modulationScheme;
+ if (bitRate == 200) {
+ modulationScheme = ModulationScheme.DP_8QAM;
+ } else { // check if bitrate is greater than 100 Gig
+ modulationScheme = ModulationScheme.DP_QPSK;
+ }
+
+ String filter = setOpModeFilter(port, modulationScheme);
+ String rpcReq = filteredEditConfigBuilder(filter);
+
+ try {
+ session.rpc(rpcReq);
+ } catch (Exception e) {
+ log.error("Error writing operational mode on CANDIDATE", e);
+ }
+
+ log.info("Modulation config sent {}", rpcReq);
+
+ try {
+ session.commit();
+ } catch (NetconfException e) {
+ log.error("Error committing channel power", e);
+ }
+ }
+
+ private String getOperationalMode(ModulationScheme modulation) {
+ if (modulation.equals(ModulationScheme.DP_QPSK)) {
+ return "dp-qpsk";
+ }
+ if (modulation.equals(ModulationScheme.DP_16QAM)) {
+ return "dp-16-qam";
+ }
+ if (modulation.equals(ModulationScheme.DP_8QAM)) {
+ return "dp-8-qam";
+ }
+ log.error("Modulation scheme is not supported.");
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5PowerConfig.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5PowerConfig.java
new file mode 100644
index 0000000..70d4a4a
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/CassiniOcnos5PowerConfig.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright 2022-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.
+ *
+ * This work was partially supported by EC H2020 project B5G-OPEN (101016663).
+ */
+
+package org.onosproject.drivers.odtn;
+
+import com.google.common.collect.Range;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.PowerConfig;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.netconf.NetconfSession;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfDevice;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.DatastoreId;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.odtn.behaviour.OdtnDeviceDescriptionDiscovery.OC_OPTICAL_CHANNEL_NAME;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of PowerConfig interface for Cassini device running Ocnos v5.
+ */
+public class CassiniOcnos5PowerConfig<T>
+ extends AbstractHandlerBehaviour implements PowerConfig<T> {
+
+ public static final String RPC_TAG_NETCONF_BASE =
+ "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
+
+ private static final String RPC_CLOSE_TAG = "</rpc>";
+
+ private static final Logger log = getLogger(CassiniOcnos5PowerConfig.class);
+
+ /**
+ * Returns the NetconfSession with the device for which the method was called.
+ *
+ * @param deviceId device indetifier
+ * @return The netconf session or null
+ */
+
+ private NetconfSession getNetconfSession(DeviceId deviceId) {
+ NetconfController controller = handler().get(NetconfController.class);
+ NetconfDevice ncdev = controller.getDevicesMap().get(deviceId);
+ if (ncdev == null) {
+ log.trace("No netconf device, returning null session");
+ return null;
+ }
+ return ncdev.getSession();
+ }
+
+ /**
+ * Get the deviceId for which the methods apply.
+ *
+ * @return The deviceId as contained in the handler data
+ */
+ private DeviceId did() {
+ return handler().data().deviceId();
+ }
+
+ /**
+ * Get the OpenConfig component name for the OpticalChannel component.
+ *
+ * @param portNumber ONOS port number of the Line port ().
+ * @return the channel component name or null
+ */
+ protected String getOpticalChannel(PortNumber portNumber) {
+ Port clientPort = handler().get(DeviceService.class).getPort(did(), portNumber);
+ return clientPort.annotations().value(OC_OPTICAL_CHANNEL_NAME);
+ }
+
+ /**
+ * Get the target-output-power value on specific optical-channel.
+ *
+ * @param port the port
+ * @param component the port component. It should be 'oc-name' in the Annotations of Port.
+ * 'oc-name' could be mapped to '/component/name' in openconfig yang.
+ * @return target power value
+ */
+ @Override
+ public Optional<Double> getTargetPower(PortNumber port, T component) {
+ if (checkPortComponent(port, component)) {
+ return getOcnosTargetPower(port, component);
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public void setTargetPower(PortNumber port, T component, double power) {
+ if (checkPortComponent(port, component)) {
+ setOcnosTargetPower(port, component, power);
+ }
+ }
+
+ @Override
+ public Optional<Double> currentPower(PortNumber port, T component) {
+ if (checkPortComponent(port, component)) {
+ return getOcnosCurrentPower(port, component);
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<Double> currentInputPower(PortNumber port, T component) {
+ if (checkPortComponent(port, component)) {
+ return getOcnosCurrentInputPower(port, component);
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<Range<Double>> getTargetPowerRange(PortNumber port, T component) {
+ if (checkPortComponent(port, component)) {
+ return getOcnosTargetPowerRange(port, component);
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<Range<Double>> getInputPowerRange(PortNumber port, T component) {
+ //FIXME to be implemented
+ if (checkPortComponent(port, component)) {
+ //return getOcnosInputPowerRange(port, component);
+ return Optional.empty();
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public List<PortNumber> getPorts(T component) {
+ return getOcnosPorts(component);
+ }
+
+ /**
+ * Set the ComponentType to invoke proper methods for different template T.
+ *
+ * @param port the component.
+ * @param component the component.
+ */
+ private Boolean checkPortComponent(PortNumber port, Object component) {
+
+ //Check componenet
+ String clsName = component.getClass().getName();
+ switch (clsName) {
+ case "org.onosproject.net.Direction":
+ break;
+ case "org.onosproject.net.OchSignal":
+ break;
+ default:
+ log.error("Cannot parse the component type {}.", clsName);
+ log.error("The component content is {}.", component.toString());
+ return false;
+ }
+
+ //Check that port has an associated optical channel
+ if (getOpticalChannel(port) == null) {
+ return false;
+ }
+
+ //Checks are ok
+ return true;
+ }
+
+ private String filteredEditConfigBuilder(String filterEditConfig) {
+ StringBuilder rpc = new StringBuilder();
+ rpc.append(RPC_TAG_NETCONF_BASE);
+ rpc.append("<edit-config>");
+ rpc.append("<target><" + DatastoreId.CANDIDATE + "/></target>");
+ rpc.append("<config>");
+ rpc.append(filterEditConfig);
+ rpc.append("</config>");
+ rpc.append("</edit-config>");
+ rpc.append(RPC_CLOSE_TAG);
+
+ return rpc.toString();
+ }
+
+ private String filteredGetBuilder(String filter) {
+ StringBuilder rpc = new StringBuilder();
+ rpc.append(RPC_TAG_NETCONF_BASE);
+ rpc.append("<get>");
+ rpc.append("<filter type='subtree'>");
+ rpc.append(filter);
+ rpc.append("</filter>");
+ rpc.append("</get>");
+ rpc.append(RPC_CLOSE_TAG);
+ return rpc.toString();
+ }
+
+ /**
+ * Parse filtering string from port and component.
+ *
+ * @param port Port Number
+ * @param power power value set.
+ * @return filtering string in xml format
+ */
+ private String setTargetPowerFilter(PortNumber port, Double power) {
+
+ //Retrieve optical-channel name
+ String optChannelName = getOpticalChannel(port);
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("<terminal-device xmlns='http://www.ipinfusion.com/yang/ocnos/ipi-platform-terminal-device'>"
+ + "<coherent-module>"
+ + " <slot-index>" + optChannelName + "</slot-index>"
+ + " <config>"
+ + " <slot-index>" + optChannelName + "</slot-index>"
+ + " </config>"
+ + " <network-interfaces>"
+ + " <interface>"
+ + " <net-index>0</net-index>"
+ + " <config>"
+ + " <net-index>0</net-index>"
+ + " <target-output-power>" + power + "</target-output-power>"
+ + " </config>"
+ + " </interface>"
+ + " </network-interfaces>"
+ + "</coherent-module>"
+ + "</terminal-device>");
+
+ return sb.toString();
+ }
+
+ private String getTargetPower(String optChannelName) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("<terminal-device xmlns='http://www.ipinfusion.com/yang/ocnos/ipi-platform-terminal-device'>"
+ + "<coherent-module>"
+ + " <slot-index>" + optChannelName + "</slot-index>"
+ + "<network-interfaces>"
+ + "<interface>"
+ + " <net-index>0</net-index>"
+ + " <state>"
+ + " <target-output-power/>"
+ + " </state>"
+ + "</interface>"
+ + "</network-interfaces>"
+ + "</coherent-module>"
+ + "</terminal-device>");
+
+ return sb.toString();
+ }
+
+ private String getInputPower(String optChannelName) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("<terminal-device xmlns='http://www.ipinfusion.com/yang/ocnos/ipi-platform-terminal-device'>"
+ + "<coherent-module>"
+ + " <slot-index>" + optChannelName + "</slot-index>"
+ + "<network-interfaces>"
+ + "<interface>"
+ + " <net-index>0</net-index>"
+ + " <input-power>"
+ + " <state>"
+ + " <instant/>"
+ + " </state>"
+ + " </input-power>"
+ + "</interface>"
+ + "</network-interfaces>"
+ + "</coherent-module>"
+ + "</terminal-device>");
+
+ return sb.toString();
+ }
+
+ private String getOutputPower(String optChannelName) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("<terminal-device xmlns='http://www.ipinfusion.com/yang/ocnos/ipi-platform-terminal-device'>"
+ + "<coherent-module>"
+ + " <slot-index>" + optChannelName + "</slot-index>"
+ + "<network-interfaces>"
+ + "<interface>"
+ + " <net-index>0</net-index>"
+ + " <output-power>"
+ + " <state>"
+ + " <instant/>"
+ + " </state>"
+ + " </output-power>"
+ + "</interface>"
+ + "</network-interfaces>"
+ + "</coherent-module>"
+ + "</terminal-device>");
+
+ return sb.toString();
+ }
+
+ /**
+ * getOcnosTargetPower.
+ *
+ * @param port port
+ * @param component component
+ * @return target power
+ */
+ Optional<Double> getOcnosTargetPower(PortNumber port, Object component) {
+ NetconfSession session = getNetconfSession(did());
+ checkNotNull(session);
+
+ //Retrieve the optical channel name as port annotation
+ String optChannelName = getOpticalChannel(port);
+
+ //log.info("REQUEST get TargetPower to device/port: {}/{}", did(), port);
+
+ String reply;
+ try {
+ reply = session.get(filteredGetBuilder(getTargetPower(optChannelName)));
+ } catch (Exception e) {
+ throw new IllegalStateException(new NetconfException("Failed to retrieve getTargetPower.", e));
+ }
+
+ //log.info("REPLY from device: {}", reply);
+
+ XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+ if (xconf == null) {
+ log.error("Error in executingRpc");
+ return Optional.empty();
+ }
+
+ String powerString = xconf.getString(("data." +
+ "terminal-device.coherent-module.network-interfaces.interface." +
+ "state.target-output-power"));
+
+ //log.info("TargetPower from device: {}", powerString);
+
+ if (powerString == null) {
+ return Optional.empty();
+ }
+
+ return Optional.of(Double.valueOf(powerString));
+ }
+
+ /**
+ * setOcnosTargetPower.
+ *
+ * @param port port
+ * @param component component
+ * @param power target value
+ */
+ private void setOcnosTargetPower(PortNumber port, Object component, double power) {
+ NetconfSession session = getNetconfSession(did());
+ checkNotNull(session);
+
+ if (!getOcnosTargetPowerRange(port, component).get().contains(power)) {
+ log.error("Specified targetPower out of range {}",
+ getOcnosTargetPowerRange(port, component).get());
+ return;
+ }
+
+ String rpcReq = filteredEditConfigBuilder(setTargetPowerFilter(port, power));
+
+ //log.info("Setting power {}", rpcReq);
+
+ try {
+ session.rpc(rpcReq);
+ } catch (NetconfException e) {
+ log.error("Error wring channel power on CANDIDATE", e);
+ }
+
+ try {
+ session.commit();
+ } catch (NetconfException e) {
+ log.error("Error committing channel power", e);
+ }
+ }
+
+ /**
+ * mirror method in the internal class.
+ *
+ * @param port port
+ * @param component the component.
+ * @return current output power.
+ */
+ private Optional<Double> getOcnosCurrentPower(PortNumber port, Object component) {
+ NetconfSession session = getNetconfSession(did());
+ checkNotNull(session);
+
+ //Compute port name, then retrieve opt channel
+ String optChannelName = getOpticalChannel(port);
+
+ //log.info("REQUEST get CurrentPower to device/port: {}/{}", did(), port);
+
+ String reply;
+ try {
+ reply = session.get(filteredGetBuilder(getOutputPower(optChannelName)));
+ } catch (Exception e) {
+ throw new IllegalStateException(new NetconfException("Failed to retrieve getOcnosCurrentPower.", e));
+ }
+
+ //log.info("REPLY from device: {}", reply);
+
+ XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+ if (xconf == null) {
+ log.error("Error in executingRpc");
+ return Optional.empty();
+ }
+
+ String powerString = xconf.getString(("data." +
+ "terminal-device.coherent-module.network-interfaces.interface." +
+ "output-power.state.instant"));
+
+ //log.info("OutputPower from device: {}", powerString);
+
+ if (powerString == null) {
+ return Optional.empty();
+ }
+
+ return Optional.of(Double.valueOf(powerString));
+ }
+
+ /**
+ * This function get the current input power.
+ *
+ * @param port port
+ * @param component the component
+ * @return current input power
+ */
+ private Optional<Double> getOcnosCurrentInputPower(PortNumber port, Object component) {
+ NetconfSession session = getNetconfSession(did());
+ checkNotNull(session);
+
+ //Compute port name, then retrieve opt channel
+ String optChannelName = getOpticalChannel(port);
+
+ //log.info("REQUEST get InputPower to device/port: {}/{}", did(), port);
+
+ String reply;
+ try {
+ reply = session.get(filteredGetBuilder(getInputPower(optChannelName)));
+ } catch (Exception e) {
+ throw new IllegalStateException(new NetconfException("Failed to retrieve getOcnosCurrentInputPower.", e));
+ }
+
+ //log.info("REPLY from device: {}", reply);
+
+ XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+ if (xconf == null) {
+ log.error("Error in executingRpc");
+ return Optional.empty();
+ }
+
+ String powerString = xconf.getString(("data." +
+ "terminal-device.coherent-module.network-interfaces.interface." +
+ "input-power.state.instant"));
+
+ //log.info("InputPower from device: {}", powerString);
+
+ if (powerString == null) {
+ return Optional.empty();
+ }
+
+ return Optional.of(Double.valueOf(powerString));
+ }
+
+ private Optional<Range<Double>> getOcnosTargetPowerRange(PortNumber port, Object component) {
+ double targetMin = -10;
+ double targetMax = 2;
+ return Optional.of(Range.open(targetMin, targetMax));
+ }
+
+ private Optional<Range<Double>> getOcnosInputPowerRange(PortNumber port, Object component) {
+ double targetMin = -30;
+ double targetMax = 1;
+ return Optional.of(Range.open(targetMin, targetMax));
+ }
+
+ private List<PortNumber> getOcnosPorts(Object component) {
+ // FIXME
+ log.warn("Not Implemented Yet!");
+ return new ArrayList<PortNumber>();
+ }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/TerminalDeviceModulationConfig.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/TerminalDeviceModulationConfig.java
index 3a2a95e..cdc2a9c 100644
--- a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/TerminalDeviceModulationConfig.java
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/TerminalDeviceModulationConfig.java
@@ -18,8 +18,6 @@
package org.onosproject.drivers.odtn;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.onlab.osgi.DefaultServiceDirectory;
@@ -39,7 +37,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.IOException;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -174,6 +171,10 @@
}
}
+ public void setModulationSchemeProcessor(PortNumber port, Object component, ModulationScheme modulationScheme) {
+ //TODO to be implemented
+ }
+
public void setModulationSchemeProcessor(PortNumber port, Object component, long bitRate) {
ModulationScheme modulation = null;
String editConfig = null;
@@ -257,6 +258,19 @@
}
/**
+ * Set the target Modulation Scheme on the component.
+ *
+ * @param port the port
+ * @param component the port component
+ * @param modulationScheme selected modulation scheme
+ **/
+ @Override
+ public void setModulationScheme(PortNumber port, T component, ModulationScheme modulationScheme) {
+ checkType(component);
+ state.setModulationScheme(port, component, modulationScheme);
+ }
+
+ /**
* Get the Modulation Scheme on the component.
*
* @param conf HierarchicalConfiguration for path ../optical-channel/config
@@ -375,6 +389,16 @@
terminalDevice.setModulationSchemeProcessor(port, component, bitRate);
}
+ /*
+ * mirror method in the internal class.
+ * @param port port
+ * @param component component
+ * @param power target value
+ */
+ void setModulationScheme(PortNumber port, Object component, ModulationScheme modulationScheme) {
+ terminalDevice.setModulationSchemeProcessor(port, component, modulationScheme);
+ }
+
/*
* Get filtered content under <optical-channel><state>.
@@ -426,26 +450,6 @@
return deviceService.getPort(deviceId, portNumber).annotations().value("oc-name");
}
- private static String channelSpacing(TerminalDeviceModulationConfig modulationConfig, PortNumber portNumber) {
- DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
- DeviceId deviceId = modulationConfig.handler().data().deviceId();
- String lambda = deviceService.getPort(deviceId, portNumber).annotations().value("lambda");
-
- ObjectMapper mapper = new ObjectMapper();
- String channelSpacing = "";
- try {
- JsonNode actualObj = mapper.readTree(lambda);
- JsonNode csNode = actualObj.get("channelSpacing");
- channelSpacing = csNode.asText();
- log.info("Channel_Spacing : " + channelSpacing);
-
- } catch (IOException e) {
- log.error("Error while parsing Json");
- }
- return channelSpacing;
-
- }
-
private double fetchDeviceSnr(TerminalDeviceModulationConfig modulationConfig, PortNumber portNumber) {
double osnr = 0.0;
XMLConfiguration xconf = getTerminalDeviceSnr(terminalDevice, portNumber);
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/GnmiTerminalDeviceModulationConfig.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/GnmiTerminalDeviceModulationConfig.java
index 5911293..eea6185 100644
--- a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/GnmiTerminalDeviceModulationConfig.java
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/GnmiTerminalDeviceModulationConfig.java
@@ -107,6 +107,11 @@
}
@Override
+ public void setModulationScheme(PortNumber portNumber, T component, ModulationScheme modulationScheme) {
+ //TODO to be implemented
+ }
+
+ @Override
public void setModulationScheme(PortNumber portNumber, T component, long bitRate) {
if (!setupBehaviour("getModulationScheme")) {
return;
diff --git a/drivers/odtn-driver/src/main/resources/odtn-drivers.xml b/drivers/odtn-driver/src/main/resources/odtn-drivers.xml
index a008a60..f3a4ba8 100644
--- a/drivers/odtn-driver/src/main/resources/odtn-drivers.xml
+++ b/drivers/odtn-driver/src/main/resources/odtn-drivers.xml
@@ -45,7 +45,7 @@
impl="org.onosproject.drivers.odtn.openconfig.TerminalDeviceFlowRuleProgrammable"/>
</driver>
- <driver name="client-line-terminal-device" manufacturer="CTTC" hwVersion="" swVersion="">
+ <driver name="client-line-terminal-device" manufacturer="CNIT" hwVersion="" swVersion="">
<behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
impl="org.onosproject.drivers.odtn.openconfig.ClientLineTerminalDeviceDiscovery"/>
<behaviour api ="org.onosproject.net.optical.OpticalDevice"
@@ -97,6 +97,25 @@
<behaviour api="org.onosproject.net.behaviour.BitErrorRateState"
impl="org.onosproject.drivers.odtn.CassiniBitErrorRateState"/>
</driver>
+ <driver name="cassini-ocnos5" manufacturer="Edgecore"
+ hwVersion="2019-05-29T06:35:43Z"
+ swVersion="EC_AS7716-24SC-OcNOS-5.0.187-OTN_IPBASE-S0-P0">
+ <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
+ impl="org.onosproject.drivers.odtn.CassiniOcnos5DeviceDiscovery"/>
+ <behaviour api ="org.onosproject.net.optical.OpticalDevice"
+ impl="org.onosproject.net.optical.DefaultOpticalDevice"/>
+ <behaviour api ="org.onosproject.net.behaviour.LambdaQuery"
+ impl="org.onosproject.drivers.odtn.openconfig.TerminalDeviceLambdaQuery"/>
+ <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
+ impl="org.onosproject.drivers.odtn.CassiniOcnos5FlowRuleProgrammable"/>
+ <behaviour api="org.onosproject.net.behaviour.PowerConfig"
+ impl="org.onosproject.drivers.odtn.CassiniOcnos5PowerConfig"/>
+ <behaviour api="org.onosproject.net.behaviour.ModulationConfig"
+ impl="org.onosproject.drivers.odtn.CassiniOcnos5Modulation"/>
+ <behaviour api="org.onosproject.net.behaviour.BitErrorRateState"
+ impl="org.onosproject.drivers.odtn.CassiniOcnos5BitErrorRate"/>
+ </driver>
+
<driver name="cassini-ocnos-old" manufacturer="Edgecore" hwVersion="cassini" swVersion="OcNOS">
<behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
impl="org.onosproject.drivers.odtn.CassiniTerminalDeviceDiscoveryOld"/>