Lumentum ROADM-20 Whitebox, NETCONF driver supporting ROADM app main functionalities
Change-Id: Iee24a3db7ebd948a251f74c91e4b5de57b8f1b19
diff --git a/drivers/lumentum/BUCK b/drivers/lumentum/BUCK
index 468aad6..1699870 100644
--- a/drivers/lumentum/BUCK
+++ b/drivers/lumentum/BUCK
@@ -4,6 +4,7 @@
'//drivers/utilities:onos-drivers-utilities',
'//protocols/snmp/api:onos-protocols-snmp-api',
'//protocols/tl1/api:onos-protocols-tl1-api',
+ '//protocols/netconf/api:onos-protocols-netconf-api',
'//incubator/api:onos-incubator-api',
'//apps/optical-model:onos-apps-optical-model',
'//drivers/optical:onos-drivers-optical',
@@ -14,6 +15,11 @@
'//core/api:onos-api-tests',
]
+BUNDLES = [
+ ':onos-drivers-lumentum',
+ '//drivers/utilities:onos-drivers-utilities',
+]
+
osgi_jar_with_tests (
deps = COMPILE_DEPS,
test_deps = TEST_DEPS,
@@ -26,7 +32,10 @@
title = 'Lumentum Drivers',
category = 'Drivers',
url = 'http://onosproject.org',
- description = 'Adds support for Lumentum devices.',
- required_apps = [ 'org.onosproject.snmp', 'org.onosproject.optical-model',
- 'org.onosproject.tl1', 'org.onosproject.drivers.optical'],
+
+ description = 'ONOS Lumentum Device Drivers application.',
+ included_bundles = BUNDLES,
+ required_apps = [ 'org.onosproject.snmp', 'org.onosproject.faultmanagement', 'org.onosproject.optical-model',
+ 'org.onosproject.tl1', 'org.onosproject.netconf', 'org.onosproject.drivers.optical'],
+
)
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumConnection.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumConnection.java
new file mode 100644
index 0000000..c63ac1e
--- /dev/null
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumConnection.java
@@ -0,0 +1,99 @@
+/*
+ * 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.lumentum;
+
+import org.onosproject.driver.optical.flowrule.CrossConnectFlowRule;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Lumentum cross connection abstraction.
+ *
+ * Required to store all information needed by Lumentum device not included in CrossConnectFlowRule.
+ */
+public class LumentumConnection {
+
+ private static final Logger log = LoggerFactory.getLogger(LumentumConnection.class);
+
+ protected boolean isAddRule;
+ protected int connectionId;
+ protected int connectionModule;
+ protected int hashCode;
+ protected PortNumber inPortNumber;
+ protected PortNumber outPortNumber;
+ protected OchSignal ochSignal;
+
+ protected double attenuation;
+ protected double targetAttenuation;
+ protected double targetPower;
+ protected double inputPower;
+ protected double outputPower;
+
+ //TODO: compute target attenuation to obtain the desired targetPower
+
+ /**
+ * Builds a LumentumConnection.
+ *
+ * @param id the id retrieved from the device
+ * @param flowRuleHash the hash code associated to the Flow Rule
+ * @param xc the cross connect flow rule
+ */
+ public LumentumConnection(Integer id, Integer flowRuleHash, CrossConnectFlowRule xc) {
+
+ connectionId = id;
+ hashCode = flowRuleHash;
+
+ isAddRule = xc.isAddRule();
+ ochSignal = xc.ochSignal();
+ attenuation = 0.0; //dB
+ inputPower = 0.0; //dBm
+ outputPower = 0.0; //dBm
+
+ if (isAddRule) {
+ outPortNumber = LumentumNetconfRoadmFlowRuleProgrammable.LINE_PORT_NUMBER;
+ inPortNumber = xc.addDrop();
+ } else {
+ outPortNumber = xc.addDrop();
+ inPortNumber = LumentumNetconfRoadmFlowRuleProgrammable.LINE_PORT_NUMBER;
+ }
+
+ log.debug("Lumentum NETCONF inPort {} outPort {} ochSignal {}", inPortNumber, outPortNumber, xc.ochSignal());
+ }
+
+ protected Integer getConnectionId() {
+ return connectionId;
+ }
+
+ protected Integer getHash() {
+ return hashCode;
+ }
+
+ protected void setAttenuation(double att) {
+ attenuation = att;
+ }
+
+ protected void setInputPower(double power) {
+ inputPower = power;
+ }
+
+ protected void setOutputPower(double power) {
+ outputPower = power;
+ }
+
+}
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmDiscovery.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmDiscovery.java
new file mode 100644
index 0000000..791e0cd
--- /dev/null
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmDiscovery.java
@@ -0,0 +1,324 @@
+/*
+ * 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.lumentum;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.commons.lang3.StringUtils;
+
+import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DefaultPortDescription;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceDescriptionDiscovery;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+import org.onosproject.netconf.NetconfDevice;
+
+import org.slf4j.Logger;
+
+import java.io.ByteArrayInputStream;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Device description behaviour for Lumentum ROADM-A Whitebox devices using NETCONF.
+ */
+public class LumentumNetconfRoadmDiscovery
+ extends AbstractHandlerBehaviour implements DeviceDescriptionDiscovery {
+
+ private static final String PHYSICAL_PORT = "data.physical-ports.physical-port";
+
+ private static final String DN = "dn";
+ private static final String DN_PORT = "port=";
+ private static final String PORT_EXTENSION = "port-extension";
+ protected static final String OPTICAL_INPUT = "port-optical-input";
+ protected static final String OPTICAL_OUTPUT = "port-optical-output";
+ private static final String PORT_PLUGGABLE = "port-pluggable";
+ private static final String PORT_ETHERNET = "port-ethernet";
+
+ private static final String MAINTENANCE_STATE = "config.maintenance-state";
+ private static final String PORT_SPEED = "config.loteeth:port-speed";
+ private static final String IN_SERVICE = "in-service";
+ private static final String PORT_NAME = "entity-description";
+
+ private final Logger log = getLogger(getClass());
+
+ @Override
+ public DeviceDescription discoverDeviceDetails() {
+
+ // Some defaults values
+ String vendor = "Lumentum";
+ String hwVersion = "not loaded";
+ String swVersion = "not loaded";
+ String serialNumber = "not loaded";
+ String chassisId = "not loaded";
+
+ DeviceId deviceId = handler().data().deviceId();
+ DeviceService deviceService = checkNotNull(handler().get(DeviceService.class));
+ Device device = deviceService.getDevice(deviceId);
+
+ //Get the configuration from the device
+ if (device == null) {
+ log.error("Lumentum NETCONF - device object not found for {}", deviceId);
+ return null;
+ }
+
+ NetconfSession session = getNetconfSession();
+
+ if (session == null) {
+ log.error("Lumentum NETCONF - session not found for {}", deviceId);
+ return null;
+ }
+
+ //Retrieve system information from ietf-system
+ StringBuilder systemRequestBuilder = new StringBuilder();
+ systemRequestBuilder.append("<system-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">");
+ systemRequestBuilder.append("</system-state>");
+
+ try {
+ String reply = session.get(systemRequestBuilder.toString(), null);
+ log.info("Lumentum NETCONF - session.get reply {}", reply);
+
+ XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+
+ vendor = xconf.getString("data.system-state.platform.machine", vendor);
+ swVersion = xconf.getString("data.system-state.platform.os-version", swVersion);
+ } catch (NetconfException e) {
+ log.error("Lumentum NETCONF error in session.get with filter <system-state>", e);
+ }
+
+ //Retrieve system information
+ StringBuilder chassisRequestBuilder = new StringBuilder();
+ chassisRequestBuilder.append("<chassis-list xmlns=\"http://www.lumentum.com/lumentum-ote-equipment\">");
+ chassisRequestBuilder.append("</chassis-list>");
+
+ try {
+ String reply = session.get(chassisRequestBuilder.toString(), null);
+ log.info("Lumentum NETCONF - session.get reply {}", reply);
+
+ XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+
+ hwVersion = xconf.getString("data.chassis-list.chassis.state.loteq:hardware-rev", hwVersion);
+ serialNumber = xconf.getString("data.chassis-list.chassis.state.loteq:serial-no", serialNumber);
+ chassisId = xconf.getString("data.chassis-list.chassis.dn", chassisId);
+
+ } catch (NetconfException e) {
+ log.error("Lumentum NETCONF error in session.get", e);
+ }
+
+ //Upon connection of a new devices all pre-configured connections are removed
+ //TODO: do not cancel and import already configured connections
+ rpcRemoveAllConnections("1");
+ rpcRemoveAllConnections("2");
+
+ log.info("TYPE {}", Device.Type.ROADM);
+ log.info("VENDOR {}", vendor);
+ log.info("HWVERSION {}", hwVersion);
+ log.info("SWVERSION {}", swVersion);
+ log.info("SERIAL {}", serialNumber);
+ log.info("CHASSISID {}", chassisId);
+
+ //Return the Device Description
+ return new DefaultDeviceDescription(deviceId.uri(), Device.Type.ROADM,
+ vendor, hwVersion, swVersion, serialNumber,
+ device.chassisId(), (SparseAnnotations) device.annotations());
+ }
+
+ @Override
+ public List<PortDescription> discoverPortDetails() {
+ String reply;
+ DeviceId deviceId = handler().data().deviceId();
+ DeviceService deviceService = checkNotNull(handler().get(DeviceService.class));
+ Device device = deviceService.getDevice(deviceId);
+
+ //Get the configuration from the device
+ if (device == null) {
+ log.error("Lumentum NETCONF - device object not found for {}", deviceId);
+ return ImmutableList.of();
+ }
+
+ NetconfSession session = getNetconfSession();
+
+ if (session == null) {
+ log.error("Lumentum NETCONF - session not found for {}", deviceId);
+ return ImmutableList.of();
+ }
+
+ StringBuilder requestBuilder = new StringBuilder();
+ requestBuilder.append("<physical-ports xmlns=\"http://www.lumentum.com/lumentum-ote-port\" ");
+ requestBuilder.append("xmlns:lotep=\"http://www.lumentum.com/lumentum-ote-port\" ");
+ requestBuilder.append("xmlns:lotepopt=\"http://www.lumentum.com/lumentum-ote-port-optical\" ");
+ requestBuilder.append("xmlns:loteeth=\"http://www.lumentum.com/lumentum-ote-port-ethernet\">");
+ requestBuilder.append("</physical-ports>");
+
+ try {
+ reply = session.get(requestBuilder.toString(), null);
+ } catch (NetconfException e) {
+ log.error("Lumentum NETCONF - " +
+ "discoverPortDetails failed to retrieve port details {}", handler().data().deviceId(), e);
+ return ImmutableList.of();
+ }
+
+ List<PortDescription> descriptions = parseLumentumRoadmPorts(XmlConfigParser.
+ loadXml(new ByteArrayInputStream(reply.getBytes())));
+
+ return ImmutableList.copyOf(descriptions);
+ }
+
+ /**
+ * Parses a configuration and returns a set of ports.
+ *
+ * @param cfg a hierarchical configuration
+ * @return a list of port descriptions
+ */
+ protected List<PortDescription> parseLumentumRoadmPorts(HierarchicalConfiguration cfg) {
+ List<PortDescription> portDescriptions = Lists.newArrayList();
+ List<HierarchicalConfiguration> ports = cfg.configurationsAt(PHYSICAL_PORT);
+
+ ports.stream().forEach(pcfg -> {
+
+ DefaultAnnotations.Builder annotations = DefaultAnnotations.builder();
+
+ //Load port number
+ PortNumber portNum = PortNumber.portNumber(
+ pcfg.getString(DN).substring(pcfg.getString(DN).lastIndexOf(DN_PORT) + 5));
+
+ //Load port state
+ String maintenanceState = pcfg.getString(MAINTENANCE_STATE);
+ boolean isEnabled = ((maintenanceState != null) && (maintenanceState).equals(IN_SERVICE));
+
+ //Load port type (FIBER/COPPER)
+ Port.Type type = null;
+ for (Object o : pcfg.getList(PORT_EXTENSION)) {
+ String s = (String) o;
+ if (s.equals(OPTICAL_INPUT) || s.equals(OPTICAL_OUTPUT)) {
+ type = Port.Type.FIBER;
+
+ } else if (s.equals(PORT_ETHERNET) || s.equals(PORT_PLUGGABLE)) {
+ type = Port.Type.COPPER;
+ }
+ }
+
+ //Load port speed of Ethernet interface, expressed in Mb/s
+ Long speed = 0L; //should be the speed of optical port
+ if (type != null) {
+ if (type.equals(Port.Type.COPPER)) {
+ String speedString = pcfg.getString(PORT_SPEED);
+ if (speedString != null) {
+ speed = Long.parseLong(speedString.substring(speedString.lastIndexOf("speed_") + 6,
+ speedString.lastIndexOf("Mb")));
+ } else {
+ log.error("Lumentum NETCONF - Port speed of Ethernet port not correctly loaded");
+ }
+ }
+ } else {
+ log.error("Port Type not correctly loaded");
+ }
+
+ //Load other information
+ pcfg.getKeys().forEachRemaining(k -> {
+ if (!k.contains(DN) && !k.contains(PORT_SPEED) && !k.contains(PORT_EXTENSION)
+ && !k.contains(MAINTENANCE_STATE)) {
+ String value = pcfg.getString(k);
+ if (!value.isEmpty()) {
+ k = StringUtils.replaceEach(k, new String[]{"loteeth:", "lotep:",
+ "lotepopt:", "config.", "=", ":",
+ "state."},
+ new String[]{"", "", "", "", "", "", ""});
+
+ annotations.set(k, value);
+
+ //To visualize port name in the ROADM app GUI
+ if (k.equals(PORT_NAME)) {
+ annotations.set(AnnotationKeys.PORT_NAME, value);
+ }
+
+ }
+ }
+ });
+
+ log.debug("Lumentum NETCONF - retrieved port {},{},{},{},{}",
+ portNum, isEnabled, type, speed, annotations.build());
+
+ portDescriptions.add(new DefaultPortDescription(portNum, isEnabled,
+ type, speed,
+ annotations.build()));
+ });
+
+ return portDescriptions;
+ }
+
+ //Following Lumentum documentation rpc operation to delete all connections
+ private boolean rpcRemoveAllConnections(String module) {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
+ stringBuilder.append(
+ "<remove-all-connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
+ stringBuilder.append("<dn>ne=1;chassis=1;card=1;module=" + module + "</dn>" + "\n");
+ stringBuilder.append("</remove-all-connections>" + "\n");
+ stringBuilder.append("</rpc>" + "\n");
+
+ return editCrossConnect(stringBuilder.toString());
+ }
+
+ private boolean editCrossConnect(String xcString) {
+ NetconfSession session = getNetconfSession();
+
+ if (session == null) {
+ log.error("Lumentum NETCONF - session not found for {}", handler().data().deviceId());
+ return false;
+ }
+
+ try {
+ return session.editConfig(xcString);
+ } catch (NetconfException e) {
+ log.error("Failed to edit the CrossConnect edid-cfg for device {}",
+ handler().data().deviceId(), e);
+ log.debug("Failed configuration {}", xcString);
+ return false;
+ }
+ }
+
+ private NetconfSession getNetconfSession() {
+ NetconfController controller = checkNotNull(handler().get(NetconfController.class));
+ NetconfDevice ncDevice = controller.getNetconfDevice(handler().data().deviceId());
+
+ if (ncDevice == null) {
+ log.error("Lumentum NETCONF - device not found for {}", handler().data().deviceId());
+ return null;
+ }
+
+ return ncDevice.getSession();
+ }
+}
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmFlowRuleProgrammable.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmFlowRuleProgrammable.java
new file mode 100644
index 0000000..88dd9ee
--- /dev/null
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmFlowRuleProgrammable.java
@@ -0,0 +1,592 @@
+/*
+ * 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.lumentum;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.lang3.tuple.Pair;
+import org.onlab.util.Frequency;
+import org.onlab.util.Spectrum;
+import org.onosproject.driver.optical.flowrule.CrossConnectCache;
+import org.onosproject.driver.optical.flowrule.CrossConnectFlowRule;
+import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.GridType;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.OchSignalType;
+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.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowId;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleProgrammable;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+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.io.ByteArrayInputStream;
+import java.util.stream.Collectors;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+import java.util.List;
+import java.util.HashSet;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Implementation of FlowRuleProgrammable interface for Lumentum ROADM-A Whitebox devices using NETCONF.
+ */
+public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
+
+ private static final Logger log =
+ LoggerFactory.getLogger(LumentumNetconfRoadmFlowRuleProgrammable.class);
+
+ private static final String DN = "dn";
+ private static final String DN_PORT = "port=";
+ private static final String DN_CARD1 = "ne=1;chassis=1;card=1;port=";
+ private static final String CONNECTION = "connection";
+ private static final String CONNECTIONS = "data.connections.connection";
+ private static final String CONFIG = "config";
+ private static final String STATE = "state";
+ private static final String START_FREQ = "start-freq";
+ private static final String END_FREQ = "end-freq";
+ private static final String MODULE = "module";
+ private static final String SEMI_COLON = ";";
+ private static final String EQUAL = "=";
+ private static final String INPUT_PORT_REFERENCE = "input-port-reference";
+ private static final String OUTPUT_PORT_REFERENCE = "output-port-reference";
+
+ private static final String CHANNEL_ATTENUATION = "attenuation";
+ private static final String CHANNEL_INPUT_POWER = "input-channel-attributes.power";
+ private static final String CHANNEL_OUTPUT_POWER = "output-channel-attributes.power";
+
+ protected static final long LINE_PORT = 3001;
+ protected static final PortNumber LINE_PORT_NUMBER = PortNumber.portNumber(LINE_PORT);
+ protected static final long MUX_OUT = 4201;
+ protected static final long DEMUX_IN = 5101;
+ protected static final long GHZ = 1_000_000_000L;
+ protected static final int MAX_CONNECTIONS = 100;
+
+ //List of LumentumConnections to associate ConnectionId and other info to the relative hash
+
+ //This is required because CrossConnect, CrossConnect Cache do not include all parameters required by Lumentum
+ //TODO: Use an external cache as CrossConnectCache to avoid problems in case of multiple devices using this driver
+
+ protected static final Set<LumentumConnection> CONNECTION_SET = new HashSet<>();
+
+ /**Get the flow entries that are present on the Lumentum device, called by FlowRuleDriverProvider.
+ *
+ * The flow entries must match exactly the FlowRule entries in the ONOS store. If they are not an
+ * exact match the device will be requested to remove those flows.
+ *
+ * @return A collection of Flow Entries
+ */
+ @Override
+ public Collection<FlowEntry> getFlowEntries() {
+ return ImmutableList.copyOf(
+ fetchConnectionsFromDevice().stream()
+ .map(conn -> buildFlowrule(conn))
+ .filter(Objects::nonNull)
+ .map(fr -> new DefaultFlowEntry(
+ fr, FlowEntry.FlowEntryState.ADDED, 0, 0, 0))
+ .collect(Collectors.toList()));
+ }
+
+ /**Apply the flow entries specified in the collection rules.
+ *
+ * @param rules A collection of Flow Rules to be applied to the Lumentum device
+ * @return The collection of added Flow Entries
+ */
+ @Override
+ public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
+ // Apply the rules on the device
+ Collection<FlowRule> added = rules.stream()
+ .map(r -> new CrossConnectFlowRule(r, getLinePorts()))
+ .filter(xc -> rpcAddConnection(xc))
+ .collect(Collectors.toList());
+
+ // Cache the cookie/priority
+ CrossConnectCache cache = this.handler().get(CrossConnectCache.class);
+ added.forEach(xc -> cache.set(
+ Objects.hash(data().deviceId(), xc.selector(), xc.treatment()),
+ xc.id(),
+ xc.priority()));
+
+ added.forEach(xc -> log.debug("Lumentum build cached FlowRule selector {} treatment {}",
+ xc.selector().toString(), xc.treatment().toString()));
+
+ return added;
+ }
+
+ @Override
+ public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
+ // Remove the valid rules from the device
+ Collection<FlowRule> removed = rules.stream()
+ .map(r -> new CrossConnectFlowRule(r, getLinePorts()))
+ .filter(xc -> rpcDeleteConnection(xc))
+ .collect(Collectors.toList());
+
+ // Remove flow rule from cache
+ CrossConnectCache cache = this.handler().get(CrossConnectCache.class);
+ removed.forEach(xc -> cache.remove(
+ Objects.hash(data().deviceId(), xc.selector(), xc.treatment())));
+
+ removed.forEach(xc -> log.debug("Lumentum NETCONF - removed cached FlowRule selector {} treatment {}",
+ xc.selector(), xc.treatment()));
+
+ return removed;
+ }
+
+ private List<PortNumber> getLinePorts() {
+ DeviceService deviceService = this.handler().get(DeviceService.class);
+ return deviceService.getPorts(data().deviceId()).stream()
+ .filter(p -> p.number().toLong() == LINE_PORT)
+ .map(p -> p.number())
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Fetches list of connections from device.
+ *
+ * @return list of connections as XML hierarchy
+ */
+ private List<HierarchicalConfiguration> fetchConnectionsFromDevice() {
+ String reply;
+
+ StringBuilder requestBuilder = new StringBuilder();
+ requestBuilder.append("<connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">");
+ requestBuilder.append("</connections>");
+
+ NetconfSession session = getNetconfSession();
+
+ if (session == null) {
+ log.error("Lumentum NETCONF - session not found for {}", handler().data().deviceId());
+ return ImmutableList.of();
+ }
+
+ try {
+ reply = session.get(requestBuilder.toString(), null);
+ log.info("Lumentum NETCONF - fetchConnectionsFromDevice reply {}", reply);
+ } catch (NetconfException e) {
+ log.error("Failed to retrieve configuration details for device {}",
+ handler().data().deviceId(), e);
+ return ImmutableList.of();
+ }
+
+ HierarchicalConfiguration cfg =
+ XmlConfigParser.loadXml(new ByteArrayInputStream(reply.getBytes()));
+
+ return cfg.configurationsAt(CONNECTIONS);
+ }
+
+ // Example input dn: ne=1;chassis=1;card=1;module=2;connection=89
+ private Pair<Short, Short> parseDn(String dn) {
+ Short module = null;
+ Short connection = null;
+ for (String entry : dn.split(SEMI_COLON)) {
+ String[] keyVal = entry.split(EQUAL);
+ if (keyVal.length != 2) {
+ continue;
+ }
+ if (keyVal[0].equals(MODULE)) {
+ module = Short.valueOf(keyVal[1]);
+ }
+ if (keyVal[0].equals(CONNECTION)) {
+ connection = Short.valueOf(keyVal[1]);
+ }
+ if (module != null && connection != null) {
+ return Pair.of(module, connection);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Builds a flow rule from a connection hierarchy.
+ *
+ * @param connection the connection hierarchy
+ * @return the flow rule
+ */
+ private FlowRule buildFlowrule(HierarchicalConfiguration connection) {
+
+ String dn = connection.getString(DN);
+ Pair<Short, Short> pair = parseDn(dn);
+
+ HierarchicalConfiguration config = connection.configurationAt(CONFIG);
+ double startFreq = config.getDouble(START_FREQ);
+ double endFreq = config.getDouble(END_FREQ);
+ String inputPortReference = config.getString(INPUT_PORT_REFERENCE);
+ String outputPortReference = config.getString(OUTPUT_PORT_REFERENCE);
+
+ HierarchicalConfiguration state = connection.configurationAt(STATE);
+ double attenuation = state.getDouble(CHANNEL_ATTENUATION);
+ double inputPower = state.getDouble(CHANNEL_INPUT_POWER);
+ double outputPower = state.getDouble(CHANNEL_OUTPUT_POWER);
+
+ if (pair == null) {
+ return null;
+ }
+
+ PortNumber portNumber = getPortNumber(pair.getLeft(), inputPortReference, outputPortReference);
+
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(pair.getLeft() == 1 ? portNumber : LINE_PORT_NUMBER)
+ .add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID))
+ .add(Criteria.matchLambda(toOchSignal(startFreq, endFreq)))
+ .build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(pair.getLeft() == 1 ? LINE_PORT_NUMBER : portNumber)
+ .build();
+
+ log.debug("Lumentum NETCONF - retrieved FlowRule startFreq {} endFreq {}", startFreq, endFreq);
+
+ // Lookup flow ID and priority
+ int hash = Objects.hash(data().deviceId(), selector, treatment);
+ CrossConnectCache cache = this.handler().get(CrossConnectCache.class);
+ Pair<FlowId, Integer> lookup = cache.get(hash);
+
+ LumentumConnection conn = CONNECTION_SET.stream()
+ .filter(c -> hash == c.getHash())
+ .findFirst()
+ .orElse(null);
+
+ //If the flow entry is not in the cache: return null/publish the flow rule
+ if ((lookup == null) || (conn == null)) {
+ log.error("Lumentum NETCONF connection not in connectionSet {}", pair.getRight());
+ rpcDeleteUnwantedConnection(pair.getRight().toString());
+ return null;
+ } else {
+ log.debug("Lumentum NETCONF attenuation and parameters set {} for connection id {}",
+ attenuation,
+ conn.getConnectionId());
+
+ conn.setAttenuation(attenuation);
+ conn.setInputPower(inputPower);
+ conn.setOutputPower(outputPower);
+ }
+
+ return DefaultFlowRule.builder()
+ .forDevice(data().deviceId())
+ .makePermanent()
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(lookup.getRight())
+ .withCookie(lookup.getLeft().value())
+ .build();
+ }
+
+ /**
+ * Get the port number.
+ * If this is a MUX connection return input-port. Outport is always MUX_OUT = 4201.
+ * If this is a DEMUX connection return output-port. Inport is always DEMUX_IN= 5101.
+ *
+ * @param module the module (1 for MUX/ADD, 2 for DEMUX/DROP)
+ * @return the add/drop port number
+ */
+ private PortNumber getPortNumber(short module, String inputPort, String outputPort) {
+ checkArgument(module == 1 || module == 2, "Module must be 1 (MUX/ADD) or 2 (DEMUX/DROP)");
+
+ if (module == 1) {
+ return PortNumber.portNumber(inputPort.split(DN_PORT)[1]);
+ } else {
+ return PortNumber.portNumber(outputPort.split(DN_PORT)[1]);
+ }
+ }
+
+ /**
+ * Converts cross connect flow rule to module and connection.
+ *
+ * Connection number is incremental within the class and associated to the rule hash.
+ *
+ * @param xc the cross connect flow rule
+ * @return pair of module (1 for MUX/ADD, 2 for DEMUX/DROP) and connection number
+ */
+ private Pair<Short, Short> setModuleConnection(CrossConnectFlowRule xc, Integer id) {
+ if (xc.isAddRule()) {
+ return Pair.of((short) 1, id.shortValue());
+ } else {
+ return Pair.of((short) 2, id.shortValue());
+ }
+ }
+
+ /**
+ * Retrieve module and connection from the cache.
+ *
+ * Connection number is incremental within the class and associated to the rule hash.
+ *
+ * @param xc the cross connect flow rule
+ * @return pair of module (1 for MUX/ADD, 2 for DEMUX/DROP) and connection number
+ */
+ private Pair<Short, Short> retrieveModuleConnection(CrossConnectFlowRule xc) {
+
+ int hash = Objects.hash(data().deviceId(), xc.selector(), xc.treatment());
+
+ LumentumConnection retrievedConnection = CONNECTION_SET.stream()
+ .filter(conn -> conn.getHash() == hash)
+ .findFirst()
+ .orElse(null);
+
+ if (retrievedConnection == null) {
+ log.error("Lumentum connection not found");
+ return null;
+ }
+
+ //Remove connection id from the local cache
+ CONNECTION_SET.remove(retrievedConnection);
+
+ log.debug("Lumentum NETCONF - retrieveModuleConnection {} retrievedConnectionId {} port {}",
+ xc.isAddRule(), retrievedConnection.getConnectionId(), xc.addDrop());
+
+ if (xc.isAddRule()) {
+ return Pair.of((short) 1, retrievedConnection.getConnectionId().shortValue());
+ } else {
+ return Pair.of((short) 2, retrievedConnection.getConnectionId().shortValue());
+ }
+ }
+
+ //Following Lumentum documentation rpc operation to configure a new connection
+ private boolean rpcAddConnection(CrossConnectFlowRule xc) {
+
+ int currentConnectionId = generateConnectionId();
+
+ if (currentConnectionId == 0) {
+ log.error("Lumentum driver - 100 connections are already configured on the device");
+ return false;
+ }
+
+ LumentumConnection connection = new LumentumConnection(currentConnectionId,
+ Objects.hash(data().deviceId(), xc.selector(), xc.treatment()), xc);
+
+ CONNECTION_SET.add(connection);
+
+ Pair<Short, Short> pair = setModuleConnection(xc, currentConnectionId);
+ String module = pair.getLeft().toString();
+ String connectionId = pair.getRight().toString();
+
+ //Conversion of ochSignal format (center frequency + diameter) to Lumentum frequency slot format (start - end)
+ Frequency freqRadius = Frequency.ofHz(xc.ochSignal().channelSpacing().frequency().asHz() / 2);
+ Frequency center = xc.ochSignal().centralFrequency();
+ String startFreq = String.valueOf(center.subtract(freqRadius).asHz() / GHZ);
+ String endFreq = String.valueOf(center.add(freqRadius).asHz() / GHZ);
+
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
+ stringBuilder.append("<add-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
+ stringBuilder.append(
+ "<dn>ne=1;chassis=1;card=1;module=" + module + ";connection=" + connectionId + "</dn>" + "\n");
+ stringBuilder.append("<start-freq>" + startFreq + "</start-freq>" + "\n");
+ stringBuilder.append("<end-freq>" + endFreq + "</end-freq>" + "\n");
+ stringBuilder.append("<attenuation>" + "0.0" + "</attenuation>" + "\n");
+ stringBuilder.append("<blocked>" + "false" + "</blocked>" + "\n");
+ stringBuilder.append("<maintenance-state>" + "in-service" + "</maintenance-state>" + "\n");
+
+ if (xc.isAddRule()) {
+ stringBuilder.append(
+ "<input-port-reference>" + DN_CARD1 + xc.addDrop().toString() + "</input-port-reference>" + "\n");
+ stringBuilder.append(
+ "<output-port-reference>" + DN_CARD1 + MUX_OUT + "</output-port-reference>" + "\n");
+ } else {
+ stringBuilder.append(
+ "<input-port-reference>" + DN_CARD1 + DEMUX_IN + "</input-port-reference>" + "\n");
+ stringBuilder.append(
+ "<output-port-reference>" + DN_CARD1 + xc.addDrop().toString() + "</output-port-reference>" + "\n");
+ }
+ stringBuilder.append("<custom-name>" + "onos-connection" + "</custom-name>" + "\n");
+ stringBuilder.append("</add-connection>" + "\n");
+ stringBuilder.append("</rpc>" + "\n");
+
+ log.info("Lumentum NETCONF - RPC add-connection {}", stringBuilder);
+
+ return editCrossConnect(stringBuilder.toString());
+ }
+
+ //Following Lumentum documentation <edit-config> operation to edit connection parameter
+ //Currently only edit the "attenuation" parameter
+ private boolean editConnection(String moduleId, String connectionId, int attenuation) {
+
+ double attenuationDouble = ((double) attenuation) / 100;
+
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
+ stringBuilder.append("<edit-config>" + "\n");
+ stringBuilder.append("<target>" + "\n");
+ stringBuilder.append("<running/>" + "\n");
+ stringBuilder.append("</target>" + "\n");
+ stringBuilder.append("<config>" + "\n");
+ stringBuilder.append("<connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
+ stringBuilder.append("<connection>" + "\n");
+ stringBuilder.append("" +
+ "<dn>ne=1;chassis=1;card=1;module=" + moduleId + ";connection=" + connectionId + "</dn>" + "\n");
+ //Other configurable parameters
+ //stringBuilder.append("<custom-name/>" + "\n");
+ //stringBuilder.append("<maintenance-state>" + "in-service" + "</maintenance-state>" + "\n");
+ //stringBuilder.append("<start-freq>" + startFreq + "</start-freq>" + "\n");
+ //stringBuilder.append("<end-freq>" + endFreq + "</end-freq>" + "\n");
+ stringBuilder.append("<config>" + "\n");
+ stringBuilder.append("<attenuation>" + attenuationDouble + "</attenuation>" + "\n");
+ stringBuilder.append("</config>" + "\n");
+ stringBuilder.append("</connection>" + "\n");
+ stringBuilder.append("</connections>" + "\n");
+ stringBuilder.append("</config>" + "\n");
+ stringBuilder.append("</edit-config>" + "\n");
+ stringBuilder.append("</rpc>" + "\n");
+
+ log.info("Lumentum {} - edit-connection {}", data().deviceId(), stringBuilder);
+
+ return editCrossConnect(stringBuilder.toString());
+ }
+
+ //Following Lumentum documentation rpc operation to delete a new connection
+ private boolean rpcDeleteConnection(CrossConnectFlowRule xc) {
+ Pair<Short, Short> pair = retrieveModuleConnection(xc);
+
+ if (pair == null) {
+ log.error("Lumentum RPC delete-connection, connection not found on the local cache");
+ }
+
+ String module = pair.getLeft().toString();
+ String connection = pair.getRight().toString();
+
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
+ stringBuilder.append("<delete-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
+ stringBuilder.append(
+ "<dn>ne=1;chassis=1;card=1;module=" + module + ";connection=" + connection + "</dn>" + "\n");
+ stringBuilder.append("</delete-connection>" + "\n");
+ stringBuilder.append("</rpc>" + " \n");
+
+ log.info("Lumentum RPC delete-connection {}", stringBuilder);
+
+ return editCrossConnect(stringBuilder.toString());
+ }
+
+ //Following Lumentum documentation rpc operation to delete a new connection
+ //Executed if for some reason a connection not in the cache is detected
+ private boolean rpcDeleteUnwantedConnection(String connectionId) {
+
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
+ stringBuilder.append("<delete-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
+ stringBuilder.append("<dn>ne=1;chassis=1;card=1;module=1;connection=" + connectionId + "</dn>" + "\n");
+ stringBuilder.append("</delete-connection>" + "\n");
+ stringBuilder.append("</rpc>" + "\n");
+
+ log.info("Lumentum {} - RPC delete-connection unwanted {}", data().deviceId(), stringBuilder);
+
+ return editCrossConnect(stringBuilder.toString());
+ }
+
+
+ private boolean editCrossConnect(String xcString) {
+ NetconfSession session = getNetconfSession();
+
+ if (session == null) {
+ log.error("Lumentum NETCONF - session not found for device {}", handler().data().deviceId());
+ return false;
+ }
+
+ try {
+ return session.editConfig(xcString);
+ } catch (NetconfException e) {
+ log.error("Failed to edit the CrossConnect edid-cfg for device {}",
+ handler().data().deviceId(), e);
+ log.debug("Failed configuration {}", xcString);
+ return false;
+ }
+ }
+
+ private NetconfSession getNetconfSession() {
+ NetconfController controller = checkNotNull(handler().get(NetconfController.class));
+
+ try {
+ NetconfSession session = checkNotNull(
+ controller.getNetconfDevice(handler().data().deviceId()).getSession());
+ return session;
+ } catch (NullPointerException e) {
+ log.error("Lumentum NETCONF - session not found for {}", handler().data().deviceId());
+ return null;
+ }
+ }
+
+ /**
+ * Convert start and end frequencies to OCh signal.
+ *
+ * FIXME: assumes slots of 12.5 GHz while devices allows granularity 6.25 GHz
+ * FIXME: supports channel spacing 50 and 100
+ *
+ * @param start starting frequency as double in GHz
+ * @param end end frequency as double in GHz
+ * @return OCh signal
+ */
+ public static OchSignal toOchSignal(double start, double end) {
+ int slots = (int) ((end - start) / ChannelSpacing.CHL_12P5GHZ.frequency().asGHz());
+ int multiplier = 0;
+
+ //Conversion for 50 GHz slots
+ if (end - start == 50) {
+ multiplier = (int) (((end - start) / 2 + start - Spectrum.CENTER_FREQUENCY.asGHz())
+ / ChannelSpacing.CHL_50GHZ.frequency().asGHz());
+
+ return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, multiplier, slots);
+ }
+
+ //Conversion for 100 GHz slots
+ if (end - start == 100) {
+ multiplier = (int) (((end - start) / 2 + start - Spectrum.CENTER_FREQUENCY.asGHz())
+ / ChannelSpacing.CHL_100GHZ.frequency().asGHz());
+
+ return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_100GHZ, multiplier, slots);
+ }
+
+ return null;
+ }
+
+ /**
+ * Generate a valid connectionId, the connectionId is a field required by the device every time
+ * a connection is created/edited/removed.
+ *
+ *
+ * Device only supports connection id < 100
+ */
+ private static Integer generateConnectionId() {
+
+ //Device only supports connection id < 100
+ for (int i = 1; i < MAX_CONNECTIONS; i++) {
+ Set<Integer> connIds = CONNECTION_SET.stream()
+ .map(conn -> conn.getConnectionId())
+ .collect(Collectors.toSet());
+
+ if (!connIds.contains(i)) {
+ return i;
+ }
+ }
+ return 0;
+ }
+}
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumPowerConfig.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumPowerConfig.java
new file mode 100644
index 0000000..e292947
--- /dev/null
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumPowerConfig.java
@@ -0,0 +1,175 @@
+/*
+ * 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.lumentum;
+
+import com.google.common.collect.Range;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.behaviour.PowerConfig;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.IntStream;
+
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+public class LumentumPowerConfig<T> extends AbstractHandlerBehaviour
+ implements PowerConfig<T> {
+
+ // log
+ private final Logger log = getLogger(getClass());
+
+
+ @Override
+ public Optional<Long> getTargetPower(PortNumber port, T component) {
+ return Optional.ofNullable(acquireTargetPower(port, component));
+ }
+
+ @Override
+ public void setTargetPower(PortNumber port, T component, long power) {
+ if (component instanceof OchSignal) {
+ setConnectionTargetPower(port, (OchSignal) component, power);
+ } else {
+ setPortTargetPower(port, power);
+ }
+ }
+
+ @Override
+ public Optional<Long> currentPower(PortNumber port, T component) {
+ return Optional.ofNullable(acquireCurrentPower(port, component));
+ }
+
+ @Override
+ public Optional<Range<Long>> getTargetPowerRange(PortNumber portNumber, T component) {
+
+ log.debug("Lumentum getTargetPowerRange {}", portNumber);
+ //TODO automatically read if a port is input or output
+
+ Set<PortNumber> outputPorts = new HashSet<>();
+
+ //Output port on the optical-line
+ outputPorts.add(PortNumber.portNumber(3001));
+
+ //Output ports of the demux module (module=2)
+ IntStream.rangeClosed(5201, 5220)
+ .forEach(i -> outputPorts.add(PortNumber.portNumber(i)));
+
+ if (outputPorts.contains(portNumber)) {
+ return Optional.ofNullable(getTxPowerRange(portNumber, component));
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<Range<Long>> getInputPowerRange(PortNumber portNumber, T component) {
+
+ log.debug("Lumentum getInputPowerRange {}", portNumber);
+ //TODO automatically read if a port is input or output
+
+ Set<PortNumber> inputPorts = new HashSet<>();
+
+ //Input port on the optical-line
+ inputPorts.add(PortNumber.portNumber(3001));
+
+ //Input ports of the mux module (module=1)
+ IntStream.rangeClosed(4101, 4120)
+ .forEach(i -> inputPorts.add(PortNumber.portNumber(i)));
+
+ if (inputPorts.contains(portNumber)) {
+ return Optional.ofNullable(getRxPowerRange(portNumber, component));
+ }
+ return Optional.empty();
+ }
+
+ //TODO implement actual get configuration from the device
+ //This is used by ROADM application to retrieve attenuation parameter, with T instanceof OchSignal
+ private Long acquireTargetPower(PortNumber port, T component) {
+ log.info("Lumentum get port {} target power...", port);
+
+ if (component instanceof OchSignal) {
+ //FIXME include port in the filter
+ LumentumConnection conn = LumentumNetconfRoadmFlowRuleProgrammable.CONNECTION_SET.stream()
+ .filter(c -> c.ochSignal == component)
+ .findFirst()
+ .orElse(null);
+
+ if (conn == null) {
+ log.info("Lumentum NETCONF fail to retrieve attenuation signal {} port {}", component, port);
+ return 0L;
+ } else {
+ log.info("Lumentum NETCONF on port {} attenuation {}", port, conn.attenuation);
+ return ((long) (conn.attenuation * 100));
+ }
+ }
+
+ return 0L;
+ }
+
+ //TODO implement actual get configuration from the device
+ //This is used by ROADM application to retrieve attenuation parameter, with T instanceof OchSignal
+ private Long acquireCurrentPower(PortNumber port, T component) {
+ log.info("Lumentum get port {} current power...", port);
+
+ if (component instanceof OchSignal) {
+ //FIXME include port in the filter
+ LumentumConnection conn = LumentumNetconfRoadmFlowRuleProgrammable.CONNECTION_SET.stream()
+ .filter(c -> c.ochSignal == component)
+ .findFirst()
+ .orElse(null);
+
+ if (conn == null) {
+ log.info("Lumentum NETCONF fail to retrieve power signal {} port {}", component, port);
+ return 0L;
+ } else {
+ log.info("Lumentum NETCONF on port {} power {}", port, conn.inputPower);
+ return ((long) (conn.inputPower * 100));
+ }
+ }
+
+ return 0L;
+ }
+
+ //TODO implement actual get configuration from the device
+ //Return PowerRange -60 dBm to 60 dBm
+ private Range<Long> getTxPowerRange(PortNumber port, T component) {
+ log.debug("Get port {} tx power range...", port);
+ return Range.closed(-60L, 60L);
+ }
+
+ //TODO implement actual get configuration from the device
+ //Return PowerRange -60dBm to 60 dBm
+ private Range<Long> getRxPowerRange(PortNumber port, T component) {
+ log.debug("Get port {} rx power range...", port);
+ return Range.closed(-60L, 60L);
+ }
+
+ //TODO implement configuration on the device
+ //Nothing to do
+ private void setPortTargetPower(PortNumber port, long power) {
+ log.debug("Set port {} target power {}", port, power);
+ }
+
+ //TODO implement configuration on the device
+ //Nothing to do
+ private void setConnectionTargetPower(PortNumber port, OchSignal signal, long power) {
+ log.debug("Set connection target power {} ochsignal {} port {}", power, signal, port);
+ }
+}
\ No newline at end of file
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumRoadmLambdaQuery.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumRoadmLambdaQuery.java
new file mode 100644
index 0000000..cfd54c8
--- /dev/null
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumRoadmLambdaQuery.java
@@ -0,0 +1,83 @@
+/*
+ * 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.lumentum;
+
+import org.onlab.util.Frequency;
+import org.onlab.util.Spectrum;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.GridType;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.LambdaQuery;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+
+import java.util.Set;
+import java.util.stream.IntStream;
+import java.util.stream.Collectors;
+import com.google.common.collect.Sets;
+
+/**
+ * Implementation of lambda query interface for Lumentum ROADMs.
+ *
+ * Tested on both Lumentum SDN ROADM and Lumentum ROADM-A Whitebox.
+ *
+ * 96 OchSignals of 50 GHz, center frequencies range 191.350 - 196.100 THz.
+ * 48 OchSignals of 100 GHz, center frequencies 191.375 - 196.075 THz
+ */
+public class LumentumRoadmLambdaQuery extends AbstractHandlerBehaviour implements LambdaQuery {
+
+
+ public static final GridType GRID_TYPE = GridType.DWDM;
+
+ public static final ChannelSpacing CHANNEL_SPACING_50 = ChannelSpacing.CHL_50GHZ;
+ public static final Frequency START_CENTER_FREQ_50 = Frequency.ofGHz(191_350);
+ public static final Frequency END_CENTER_FREQ_50 = Frequency.ofGHz(196_100);
+ private static final int LAMBDA_COUNT_50 = 96;
+
+ public static final ChannelSpacing CHANNEL_SPACING_100 = ChannelSpacing.CHL_100GHZ;
+ public static final Frequency START_CENTER_FREQ_100 = Frequency.ofGHz(191_375);
+ public static final Frequency END_CENTER_FREQ_100 = Frequency.ofGHz(196_075);
+ private static final int LAMBDA_COUNT_100 = 48;
+
+ @Override
+ public Set<OchSignal> queryLambdas(PortNumber port) {
+
+ //Complete set of 50GHz OchSignal
+ int startMultiplier50 = (int) (START_CENTER_FREQ_50.subtract(Spectrum.CENTER_FREQUENCY).asHz()
+ / Frequency.ofGHz(50).asHz());
+
+ Set<OchSignal> channels50 = IntStream.range(0, LAMBDA_COUNT_50)
+ .mapToObj(x -> new OchSignal(GRID_TYPE, CHANNEL_SPACING_50,
+ startMultiplier50 + x,
+ 4))
+ .collect(Collectors.toSet());
+
+ //Complete set of 100GHz OchSignal
+ int startMultiplier100 = (int) (START_CENTER_FREQ_100.subtract(Spectrum.CENTER_FREQUENCY).asHz()
+ / Frequency.ofGHz(100).asHz());
+
+ Set<OchSignal> channels100 = IntStream.range(0, LAMBDA_COUNT_100)
+ .mapToObj(x -> new OchSignal(GRID_TYPE,
+ CHANNEL_SPACING_100, startMultiplier100 + x, 8))
+ .collect(Collectors.toSet());
+
+ Set<OchSignal> channels = Sets.union(channels50, channels100);
+
+ return channels;
+ }
+}
+
+
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSnmpDevice.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSnmpDevice.java
index cd6ba1a..47b98e2 100644
--- a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSnmpDevice.java
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSnmpDevice.java
@@ -85,7 +85,7 @@
target.setCommunity(new OctetString("public"));
target.setAddress(targetAddress);
target.setRetries(3);
- target.setTimeout(1000L * 3L);
+ target.setTimeout(1000 * 3);
target.setVersion(SnmpConstants.version2c);
target.setMaxSizeRequestPDU(MAX_SIZE_RESPONSE_PDU);
}
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumWaveReadyDiscovery.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumWaveReadyDiscovery.java
index 3ced845..5bd629f 100644
--- a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumWaveReadyDiscovery.java
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumWaveReadyDiscovery.java
@@ -182,14 +182,14 @@
Arrays.stream(s.split("\"\"")).forEach(p -> {
if (p.contains(EIGHTFIFTY)) {
PortDescription cltPort = oduCltPortDescription(
- PortNumber.portNumber(ports.size() + 1L),
+ PortNumber.portNumber(ports.size() + 1),
true,
CltSignalType.CLT_10GBE,
extractAnnotations(p));
ports.add(cltPort);
} else {
PortDescription netPort = ochPortDescription(
- PortNumber.portNumber(ports.size() + 1L),
+ PortNumber.portNumber(ports.size() + 1),
true,
OduSignalType.ODU2e,
true,
diff --git a/drivers/lumentum/src/main/resources/lumentum-drivers.xml b/drivers/lumentum/src/main/resources/lumentum-drivers.xml
index 01ccb3e..8bf9d17 100644
--- a/drivers/lumentum/src/main/resources/lumentum-drivers.xml
+++ b/drivers/lumentum/src/main/resources/lumentum-drivers.xml
@@ -19,7 +19,7 @@
<behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
impl="org.onosproject.drivers.lumentum.LumentumRoadmDiscovery"/>
<behaviour api="org.onosproject.net.behaviour.LambdaQuery"
- impl="org.onosproject.drivers.lumentum.LumentumSdnRoadmLambdaQuery"/>
+ impl="org.onosproject.drivers.lumentum.LumentumRoadmLambdaQuery"/>
<behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
impl="org.onosproject.drivers.lumentum.LumentumSdnRoadmFlowRuleProgrammable"/>
<behaviour api="org.onosproject.incubator.net.faultmanagement.alarm.AlarmConsumer"
@@ -37,5 +37,18 @@
<behaviour api="org.onosproject.net.behaviour.LambdaQuery"
impl="org.onosproject.driver.optical.query.CBand50LambdaQuery"/>
</driver>
+
+ <driver name="lumentum-roadm20whitebox-netconf" manufacturer="Lumentum" hwVersion="SDN-ROADM-NETCONF" swVersion="R2.1.4">
+ <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
+ impl="org.onosproject.drivers.lumentum.LumentumNetconfRoadmDiscovery"/>
+ <behaviour api="org.onosproject.net.behaviour.LambdaQuery"
+ impl="org.onosproject.drivers.lumentum.LumentumRoadmLambdaQuery"/>
+ <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
+ impl="org.onosproject.drivers.lumentum.LumentumNetconfRoadmFlowRuleProgrammable"/>
+ <behaviour api="org.onosproject.net.optical.OpticalDevice"
+ impl="org.onosproject.net.optical.DefaultOpticalDevice"/>
+ <behaviour api="org.onosproject.net.behaviour.PowerConfig"
+ impl="org.onosproject.drivers.lumentum.LumentumPowerConfig"/>
+ </driver>
</drivers>