[ONOS-7980] Lumentum ROADM-20 driver: aligned to distributed DeviceConnectionCache.

Supports the configuration of attenuation parameter using the ROADM app.

Patch 2: fixed a conflict on RoadmManager.java

Patch 3: checkstyle

Patch 4: addressed comments by Andrea, checkstyle.

Patch 5: addressed comments by Ramon, revised logging info.

Patch 6: checkstyle.

Change-Id: Id848220ef3a2cb5af6940cbca127a62d9afea2e2
This commit is contained in:
alessio 2019-06-04 15:47:39 +02:00 committed by Andrea Campanella
parent 9c9b06c3a8
commit 1bf2a63f4b
8 changed files with 399 additions and 334 deletions

View File

@ -593,11 +593,12 @@ public class RoadmManager implements RoadmService {
}
// Delay the call to setTargetPower because the flow may not be in the store yet
// Tested with Lumentum ROADM-20 1 seconds was not enough, increased to 5 seconds
private void delayedSetAttenuation(DeviceId deviceId, PortNumber outPort,
OchSignal ochSignal, long attenuation) {
Runnable setAtt = () -> {
try {
TimeUnit.SECONDS.sleep(1);
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
log.warn("Thread interrupted. Setting attenuation early.");
Thread.currentThread().interrupt();

View File

@ -5,6 +5,7 @@ COMPILE_DEPS = CORE_DEPS + [
"//protocols/tl1/api:onos-protocols-tl1-api",
"//protocols/netconf/api:onos-protocols-netconf-api",
"//apps/optical-model:onos-apps-optical-model",
"//drivers/odtn-driver:onos-drivers-odtn-driver",
"//drivers/optical:onos-drivers-optical",
]
@ -31,6 +32,8 @@ onos_app(
"org.onosproject.optical-model",
"org.onosproject.tl1",
"org.onosproject.netconf",
"org.onosproject.drivers.odtn-driver",
"org.onosproject.drivers.netconf",
"org.onosproject.drivers.optical",
],
title = "Lumentum Drivers",

View File

@ -1,101 +0,0 @@
/*
* 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.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;
protected LumentumFlowRule rule;
//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, LumentumFlowRule xc) {
connectionId = id;
hashCode = flowRuleHash;
isAddRule = xc.isAddRule();
ochSignal = xc.ochSignal();
attenuation = 0.0; //dB
inputPower = 0.0; //dBm
outputPower = 0.0; //dBm
rule = xc;
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;
}
}

View File

@ -44,6 +44,9 @@ public class LumentumFlowRule extends DefaultFlowRule {
}
public Type type;
private String connectionName;
private int connectionId;
private int connectionModule;
private PortNumber addDrop;
private PortNumber inputPort;
@ -52,6 +55,12 @@ public class LumentumFlowRule extends DefaultFlowRule {
private OchSignal ochSignal;
private boolean isAddRule;
protected double attenuation;
protected double targetAttenuation;
protected double targetPower;
protected double inputPower;
protected double outputPower;
//As generated by the OpticalConnectivityIntentCompiler
private static final int NUM_CRITERIA_INTENT = 3;
private static final int NUM_INSTRUCTIONS_INTENT = 2;
@ -60,6 +69,7 @@ public class LumentumFlowRule extends DefaultFlowRule {
private static final int NUM_CRITERIA_ROADM = 3;
private static final int NUM_INSTRUCTIONS_ROADM = 1;
public LumentumFlowRule(FlowRule rule, List<PortNumber> linePorts) {
super(rule);
@ -137,6 +147,8 @@ public class LumentumFlowRule extends DefaultFlowRule {
addDrop = outInstruction.port();
isAddRule = false;
}
connectionName = "inPort" + inputPort.toString() + "-ochSig-" + ochSignal.centralFrequency().toString();
}
public PortNumber addDrop() {
@ -158,4 +170,36 @@ public class LumentumFlowRule extends DefaultFlowRule {
public PortNumber getOutputPort() {
return outputPort;
}
public String getConnectionName() {
return connectionName;
}
public int getConnectionId() {
return connectionId;
}
public int getConnectionModule() {
return connectionModule;
}
public void setConnectionId(int id) {
connectionId = id;
}
public void setConnectionModule(int id) {
connectionModule = id;
}
protected void setAttenuation(double att) {
attenuation = att;
}
protected void setInputPower(double power) {
inputPower = power;
}
protected void setOutputPower(double power) {
outputPower = power;
}
}

View File

@ -45,7 +45,6 @@ import org.onosproject.net.intent.OpticalPathIntent;
import org.onosproject.netconf.NetconfController;
import org.onosproject.netconf.NetconfException;
import org.onosproject.netconf.NetconfSession;
import org.onosproject.netconf.NetconfDevice;
import org.slf4j.Logger;
@ -87,13 +86,17 @@ public class LumentumNetconfRoadmDiscovery
private static final int MAX_DEM_PORT = 5220;
private static final int DELTA_MUX_DEM_PORT = MIN_DEM_PORT - MIN_MUX_PORT;
private static final String MUX_PORT_NAME = "Mux Input";
private static final String DEMUX_PORT_NAME = "Demux Output";
private static final String LINE_PORT_NAME = "Optical Line";
private final Logger log = getLogger(getClass());
@Override
public DeviceDescription discoverDeviceDetails() {
SparseAnnotations annotations = DefaultAnnotations.builder().build();
log.info("Lumentum NETCONF - starting discoverDeviceDetails");
log.debug("Lumentum NETCONF - starting discoverDeviceDetails");
// Some defaults values
String vendor = "Lumentum";
@ -106,7 +109,6 @@ public class LumentumNetconfRoadmDiscovery
DeviceId deviceId = handler().data().deviceId();
NetconfSession session = getNetconfSession();
if (session == null) {
log.error("Lumentum NETCONF - session not found for {}", deviceId);
return null;
@ -119,7 +121,7 @@ public class LumentumNetconfRoadmDiscovery
try {
String reply = session.get(systemRequestBuilder.toString(), null);
log.info("Lumentum NETCONF - session.get reply {}", reply);
log.debug("Lumentum NETCONF - session.get reply {}", reply);
XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
@ -136,7 +138,7 @@ public class LumentumNetconfRoadmDiscovery
try {
String reply = session.get(chassisRequestBuilder.toString(), null);
log.info("Lumentum NETCONF - session.get reply {}", reply);
log.debug("Lumentum NETCONF - session.get reply {}", reply);
XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
@ -152,10 +154,11 @@ public class LumentumNetconfRoadmDiscovery
}
//Upon connection of a new devices all pre-configured connections are removed
//TODO: do not cancel and import already configured connections
//TODO consider a way to keep "external" FlowRules
rpcRemoveAllConnections("1");
rpcRemoveAllConnections("2");
log.info("Lumentum ROADM20 - discovered details:");
log.info("TYPE {}", Device.Type.ROADM);
log.info("VENDOR {}", vendor);
log.info("HWVERSION {}", hwVersion);
@ -170,7 +173,6 @@ public class LumentumNetconfRoadmDiscovery
@Override
public List<PortDescription> discoverPortDetails() {
String reply;
DeviceId deviceId = handler().data().deviceId();
DeviceService deviceService = checkNotNull(handler().get(DeviceService.class));
Device device = deviceService.getDevice(deviceId);
@ -182,7 +184,6 @@ public class LumentumNetconfRoadmDiscovery
}
NetconfSession session = getNetconfSession();
if (session == null) {
log.error("Lumentum NETCONF - session not found for {}", deviceId);
return ImmutableList.of();
@ -195,6 +196,7 @@ public class LumentumNetconfRoadmDiscovery
requestBuilder.append("xmlns:loteeth=\"http://www.lumentum.com/lumentum-ote-port-ethernet\">");
requestBuilder.append("</physical-ports>");
String reply;
try {
reply = session.get(requestBuilder.toString(), null);
} catch (NetconfException e) {
@ -259,9 +261,6 @@ public class LumentumNetconfRoadmDiscovery
log.error("Port Type not correctly loaded");
}
//Store reverse port index in the annotations
Long reversePortId;
/**
* Setting the reverse port value for the unidirectional ports.
*
@ -272,11 +271,11 @@ public class LumentumNetconfRoadmDiscovery
* Where port 520x is always the reverse of 410x.
*/
if ((portNum.toLong() >= MIN_MUX_PORT) && (portNum.toLong() <= MAX_MUX_PORT)) {
reversePortId = portNum.toLong() + DELTA_MUX_DEM_PORT;
Long reversePortId = portNum.toLong() + DELTA_MUX_DEM_PORT;
annotations.set(OpticalPathIntent.REVERSE_PORT_ANNOTATION_KEY, reversePortId.toString());
}
if ((portNum.toLong() >= MIN_DEM_PORT) && (portNum.toLong() <= MAX_DEM_PORT)) {
reversePortId = portNum.toLong() - DELTA_MUX_DEM_PORT;
Long reversePortId = portNum.toLong() - DELTA_MUX_DEM_PORT;
annotations.set(OpticalPathIntent.REVERSE_PORT_ANNOTATION_KEY, reversePortId.toString());
}
@ -305,7 +304,13 @@ public class LumentumNetconfRoadmDiscovery
log.debug("Lumentum NETCONF - retrieved port {},{},{},{},{}",
portNum, isEnabled, type, speed, annotations.build());
if (type == Port.Type.FIBER) {
if ((type == Port.Type.FIBER) &&
((annotations.build().value(AnnotationKeys.PORT_NAME)).contains(MUX_PORT_NAME) ||
(annotations.build().value(AnnotationKeys.PORT_NAME)).contains(DEMUX_PORT_NAME) ||
(annotations.build().value(AnnotationKeys.PORT_NAME)).contains(LINE_PORT_NAME))) {
//These are the ports supporting OchSignals
portDescriptions.add(omsPortDescription(portNum,
isEnabled,
START_CENTER_FREQ_50,
@ -313,6 +318,7 @@ public class LumentumNetconfRoadmDiscovery
CHANNEL_SPACING_50.frequency(),
annotations.build()));
} else {
//These are COPPER ports, or FIBER ports not supporting OchSignals
DefaultPortDescription.Builder portDescriptionBuilder = DefaultPortDescription.builder();
portDescriptionBuilder.withPortNumber(portNum)
.isEnabled(isEnabled)
@ -342,7 +348,6 @@ public class LumentumNetconfRoadmDiscovery
private boolean editCrossConnect(String xcString) {
NetconfSession session = getNetconfSession();
if (session == null) {
log.error("Lumentum NETCONF - session not found for {}", handler().data().deviceId());
return false;
@ -358,15 +363,19 @@ public class LumentumNetconfRoadmDiscovery
}
}
/**
* Helper method to get the Netconf session.
*/
private NetconfSession getNetconfSession() {
NetconfController controller = checkNotNull(handler().get(NetconfController.class));
NetconfDevice ncDevice = controller.getNetconfDevice(handler().data().deviceId());
NetconfController controller =
checkNotNull(handler().get(NetconfController.class));
return controller.getNetconfDevice(did()).getSession();
}
if (ncDevice == null) {
log.error("Lumentum NETCONF - device not found for {}", handler().data().deviceId());
return null;
}
return ncDevice.getSession();
/**
* Helper method to get the device id.
*/
private DeviceId did() {
return data().deviceId();
}
}

View File

@ -21,27 +21,23 @@ 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.drivers.odtn.impl.DeviceConnectionCache;
import org.onosproject.drivers.utilities.XmlConfigParser;
import org.onosproject.net.ChannelSpacing;
import org.onosproject.net.GridType;
import org.onosproject.net.PortNumber;
import org.onosproject.net.OchSignal;
import org.onosproject.net.OchSignalType;
import org.onosproject.net.PortNumber;
import org.onosproject.net.DeviceId;
import org.onosproject.net.ChannelSpacing;
import org.onosproject.net.GridType;
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.net.flow.instructions.Instructions;
import org.onosproject.netconf.NetconfController;
import org.onosproject.netconf.NetconfException;
import org.onosproject.netconf.NetconfSession;
@ -49,12 +45,11 @@ 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.ArrayList;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
@ -91,14 +86,7 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
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<>();
protected static final int LUMENTUM_ROADM20_MAX_CONNECTIONS = 100;
/**Get the flow entries that are present on the Lumentum device, called by FlowRuleDriverProvider.
*
@ -109,13 +97,15 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
*/
@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()));
Collection<FlowEntry> fetched = fetchConnectionsFromDevice().stream()
.map(conn -> buildFlowrule(conn))
.map(fr -> new DefaultFlowEntry(fr, FlowEntry.FlowEntryState.ADDED, 0, 0, 0))
.collect(Collectors.toList());
//Print out number of rules actually found on the device that are also included in the cache
log.debug("Device {} getFlowEntries fetched connections {}", did(), fetched.size());
return fetched;
}
/**Apply the flow entries specified in the collection rules.
@ -125,40 +115,54 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
*/
@Override
public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
// Apply the rules on the device
Collection<FlowRule> added = rules.stream()
.map(r -> new LumentumFlowRule(r, getLinePorts()))
.filter(xc -> rpcAddConnection(xc))
.collect(Collectors.toList());
//Check NETCONF session
NetconfSession session = getNetconfSession();
if (session == null) {
log.error("Device {} null session", did());
return ImmutableList.of();
}
// Cache the cookie/priority
CrossConnectCache cache = this.handler().get(CrossConnectCache.class);
added.forEach(xc -> cache.set(
Objects.hash(data().deviceId(), xc.selector()),
xc.id(),
xc.priority()));
// Apply the rules on the device and add to cache if success
Collection<FlowRule> added = new ArrayList<>();
for (FlowRule flowRule : rules) {
LumentumFlowRule lumFlowRule = new LumentumFlowRule(flowRule, getLinePorts());
added.forEach(xc -> log.debug("Lumentum build cached FlowRule selector {} treatment {}",
xc.selector().toString(), xc.treatment().toString()));
if (rpcAddConnection(lumFlowRule)) {
added.add(lumFlowRule);
getConnectionCache().add(did(), lumFlowRule.getConnectionName(), lumFlowRule);
}
}
//Print out number of rules sent to the device (without receiving errors)
log.debug("Device {} applyFlowRules added {}", did(), added.size());
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 LumentumFlowRule(r, getLinePorts()))
.filter(xc -> rpcDeleteConnection(xc))
.collect(Collectors.toList());
NetconfSession session = getNetconfSession();
if (session == null) {
log.error("Device {} null session", did());
return ImmutableList.of();
}
// Remove flow rule from cache
CrossConnectCache cache = this.handler().get(CrossConnectCache.class);
removed.forEach(xc -> cache.remove(
Objects.hash(data().deviceId(), xc.selector())));
// Remove the rules from the device and from the cache
List<FlowRule> removed = new ArrayList<>();
for (FlowRule r : rules) {
try {
LumentumFlowRule flowRule = new LumentumFlowRule(r, getLinePorts());
rpcDeleteConnection(flowRule);
getConnectionCache().remove(did(), r);
removed.add(r);
} catch (Exception e) {
log.error("Device {} Error {}", did(), e);
continue;
}
}
removed.forEach(xc -> log.debug("Lumentum NETCONF - removed cached FlowRule selector {} treatment {}",
xc.selector(), xc.treatment()));
//Print out number of removed rules from the device (without receiving errors)
log.debug("Device {} removeFlowRules removed {}", did(), removed.size());
return removed;
}
@ -184,7 +188,6 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
requestBuilder.append("</connections>");
NetconfSession session = getNetconfSession();
if (session == null) {
log.error("Lumentum NETCONF - session not found for {}", handler().data().deviceId());
return ImmutableList.of();
@ -192,7 +195,7 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
try {
reply = session.get(requestBuilder.toString(), null);
log.info("Lumentum NETCONF - fetchConnectionsFromDevice reply {}", reply);
log.debug("Lumentum NETCONF - fetchConnectionsFromDevice reply {}", reply);
} catch (NetconfException e) {
log.error("Failed to retrieve configuration details for device {}",
handler().data().deviceId(), e);
@ -238,6 +241,15 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
String dn = connection.getString(DN);
Pair<Short, Short> pair = parseDn(dn);
short connId = pair.getRight();
short moduleId = pair.getLeft();
if (pair == null) {
log.error("Lumentum NETCONF - device {} error in retrieving DN field", did());
return null;
}
log.debug("Lumentum NETCONF - retrieved FlowRule module {} connection {}", moduleId, connId);
HierarchicalConfiguration config = connection.configurationAt(CONFIG);
double startFreq = config.getDouble(START_FREQ);
@ -250,65 +262,44 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
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);
PortNumber portNumber = getPortNumber(moduleId, inputPortReference, outputPortReference);
//If rule is on module 1 it means input port in the Flow rule is contained in portNumber.
//Otherwise the input port in the Flow rule must is the line port.
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchInPort(pair.getLeft() == 1 ? portNumber : LINE_PORT_NUMBER)
.matchInPort(moduleId == 1 ? portNumber : LINE_PORT_NUMBER)
.add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID))
.add(Criteria.matchLambda(toOchSignal(startFreq, endFreq)))
.build();
log.debug("Lumentum NETCONF - retrieved FlowRule startFreq {} endFreq {}", startFreq, endFreq);
// Lookup flow ID and priority
int hash = Objects.hash(data().deviceId(), selector);
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);
TrafficTreatment treatment;
if (conn.rule.type == LumentumFlowRule.Type.OPTICAL_CONNECTIVITY_INTENT_RULE) {
treatment = DefaultTrafficTreatment.builder()
.add(Instructions.modL0Lambda(toOchSignal(startFreq, endFreq)))
.setOutput(pair.getLeft() == 1 ? LINE_PORT_NUMBER : portNumber)
.build();
} else { // This is ROADM_APP_RULE
treatment = DefaultTrafficTreatment.builder()
.setOutput(pair.getLeft() == 1 ? LINE_PORT_NUMBER : portNumber)
.build();
//Lookup of connection
//Retrieved rules, cached rules are considered equal if the selector is equal
FlowRule cacheRule = null;
if (getConnectionCache().size(did()) != 0) {
cacheRule = getConnectionCache().get(did()).stream()
.filter(r -> (r.selector().equals(selector)))
.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;
if (cacheRule == null) {
//TODO consider a way to keep "external" FlowRules
log.error("Lumentum NETCONF connection not in the cache {}", pair.getRight());
rpcDeleteExternalConnection(moduleId, connId);
return null;
} else {
log.debug("Lumentum NETCONF attenuation and parameters set {} for connection id {}",
attenuation,
conn.getConnectionId());
//Update monitored values
log.debug("Attenuation retrieved {} dB for connection {}",
attenuation, ((LumentumFlowRule) cacheRule).getConnectionId());
((LumentumFlowRule) cacheRule).setAttenuation(attenuation);
((LumentumFlowRule) cacheRule).setInputPower(inputPower);
((LumentumFlowRule) cacheRule).setOutputPower(outputPower);
conn.setAttenuation(attenuation);
conn.setInputPower(inputPower);
conn.setOutputPower(outputPower);
return cacheRule;
}
return DefaultFlowRule.builder()
.forDevice(data().deviceId())
.makePermanent()
.withSelector(selector)
.withTreatment(treatment)
.withPriority(lookup.getRight())
.withCookie(lookup.getLeft().value())
.build();
}
/**
@ -339,47 +330,16 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
*/
private Pair<Short, Short> setModuleConnection(LumentumFlowRule xc, Integer id) {
if (xc.isAddRule()) {
xc.setConnectionModule((short) 1);
xc.setConnectionId(id.shortValue());
return Pair.of((short) 1, id.shortValue());
} else {
xc.setConnectionModule((short) 2);
xc.setConnectionId(id.shortValue());
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
* @return pair of module (1 for MUX/ADD, 2 for DEMUX/DROP) and connection number
*/
private Pair<Short, Short> retrieveModuleConnection(LumentumFlowRule xc) {
int hash = Objects.hash(data().deviceId(), xc.selector());
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(LumentumFlowRule xc) {
@ -390,11 +350,6 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
return false;
}
LumentumConnection connection = new LumentumConnection(currentConnectionId,
Objects.hash(data().deviceId(), xc.selector()), xc);
CONNECTION_SET.add(connection);
Pair<Short, Short> pair = setModuleConnection(xc, currentConnectionId);
String module = pair.getLeft().toString();
String connectionId = pair.getRight().toString();
@ -431,7 +386,8 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
stringBuilder.append("</add-connection>" + "\n");
stringBuilder.append("</rpc>" + "\n");
log.info("Lumentum NETCONF - RPC add-connection {}", stringBuilder);
log.info("Lumentum ROADM20 - RPC add-connection sent to device {}", did());
log.debug("Lumentum ROADM20 - RPC add-connection sent to device {} {}", did(), stringBuilder);
return editCrossConnect(stringBuilder.toString());
}
@ -467,48 +423,59 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
stringBuilder.append("</edit-config>" + "\n");
stringBuilder.append("</rpc>" + "\n");
log.info("Lumentum {} - edit-connection {}", data().deviceId(), stringBuilder);
log.info("Lumentum ROADM20 - edit-connection sent to device {}", did());
log.debug("Lumentum ROADM20 - edit-connection sent to device {} {}", did(), stringBuilder);
return editCrossConnect(stringBuilder.toString());
}
//Following Lumentum documentation rpc operation to delete a new connection
private boolean rpcDeleteConnection(LumentumFlowRule xc) {
Pair<Short, Short> pair = retrieveModuleConnection(xc);
if (pair == null) {
//Look for corresponding rule into the cache
FlowRule cacheRule = getConnectionCache().get(did()).stream()
.filter(r -> (r.selector().equals(xc.selector()) && r.treatment().equals(xc.treatment())))
.findFirst()
.orElse(null);
if (cacheRule == null) {
log.error("Lumentum RPC delete-connection, connection not found on the local cache");
throw new IllegalStateException("Lumentum RPC delete-connection, connection not found on the local cache");
}
String module = pair.getLeft().toString();
String connection = pair.getRight().toString();
int moduleId = ((LumentumFlowRule) cacheRule).getConnectionModule();
int connId = ((LumentumFlowRule) cacheRule).getConnectionId();
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");
"<dn>ne=1;chassis=1;card=1;module=" + moduleId + ";connection=" + connId + "</dn>" + "\n");
stringBuilder.append("</delete-connection>" + "\n");
stringBuilder.append("</rpc>" + " \n");
log.info("Lumentum RPC delete-connection {}", stringBuilder);
log.info("Lumentum ROADM20 - RPC delete-connection sent to device {}", did());
log.debug("Lumentum ROADM20 - - RPC delete-connection sent to device {} {}", did(), 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) {
private boolean rpcDeleteExternalConnection(short moduleId, short 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("<dn>ne=1;chassis=1;card=1;module="
+ moduleId + ";connection=" + connectionId + "</dn>" + "\n");
stringBuilder.append("</delete-connection>" + "\n");
stringBuilder.append("</rpc>" + "\n");
log.info("Lumentum {} - RPC delete-connection unwanted {}", data().deviceId(), stringBuilder);
log.info("Lumentum ROADM20 - RPC delete-external-connection sent to device {}", did());
log.debug("Lumentum ROADM20 - - RPC delete-external-connection sent to device {} {}", did(), stringBuilder);
return editCrossConnect(stringBuilder.toString());
}
@ -516,7 +483,6 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
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;
@ -532,20 +498,7 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
}
}
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
@ -585,18 +538,43 @@ public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBeh
*
* Device only supports connection id < 100
*/
private static Integer generateConnectionId() {
private int generateConnectionId() {
//LUMENTUM_ROADM20_MAX_CONNECTIONS = 100, device only supports connection id < 100
for (int i = 1; i < LUMENTUM_ROADM20_MAX_CONNECTIONS; i++) {
Set<FlowRule> rulesForDevice = getConnectionCache().get(did());
//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 (rulesForDevice == null) {
return 1;
} else {
Set<Integer> connIds = rulesForDevice.stream()
.map(flow -> ((LumentumFlowRule) flow).getConnectionId())
.collect(Collectors.toSet());
if (!connIds.contains(i)) {
return i;
if (!connIds.contains(i)) {
return i;
}
}
}
return 0;
}
private DeviceConnectionCache getConnectionCache() {
return DeviceConnectionCache.init();
}
/**
* Helper method to get the device id.
*/
private DeviceId did() {
return data().deviceId();
}
/**
* Helper method to get the Netconf session.
*/
private NetconfSession getNetconfSession() {
NetconfController controller =
checkNotNull(handler().get(NetconfController.class));
return controller.getNetconfDevice(did()).getSession();
}
}

View File

@ -17,6 +17,8 @@
package org.onosproject.drivers.lumentum;
import com.google.common.collect.Range;
import org.onosproject.drivers.odtn.impl.DeviceConnectionCache;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.OchSignal;
import org.onosproject.net.behaviour.PowerConfig;
@ -27,8 +29,13 @@ import java.util.Optional;
import java.util.Set;
import java.util.stream.IntStream;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.netconf.NetconfController;
import org.onosproject.netconf.NetconfException;
import org.onosproject.netconf.NetconfSession;
import org.slf4j.Logger;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
public class LumentumPowerConfig<T> extends AbstractHandlerBehaviour
@ -43,6 +50,7 @@ public class LumentumPowerConfig<T> extends AbstractHandlerBehaviour
return Optional.ofNullable(acquireTargetPower(port, component));
}
//Used by the ROADM app to set the "attenuation" parameter
@Override
public void setTargetPower(PortNumber port, T component, long power) {
if (component instanceof OchSignal) {
@ -101,22 +109,33 @@ public class LumentumPowerConfig<T> extends AbstractHandlerBehaviour
//TODO implement actual get configuration from the device
//This is used by ROADM application to retrieve attenuation parameter, with T instanceof OchSignal
//The ROADM app expresses the attenuation in 0.01 dB units
private Long acquireTargetPower(PortNumber port, T component) {
log.debug("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.debug("Lumentum NETCONF fail to retrieve attenuation signal {} port {}", component, port);
Set<FlowRule> rules = getConnectionCache().get(did());
FlowRule rule;
if (rules == null) {
log.error("Lumentum NETCONF fail to retrieve attenuation signal {} port {}", component, port);
return 0L;
} else {
log.debug("Lumentum NETCONF on port {} attenuation {}", port, conn.attenuation);
return ((long) (conn.attenuation * 100));
rule = rules.stream()
.filter(c -> ((LumentumFlowRule) c).getOutputPort() == port)
.filter(c -> ((LumentumFlowRule) c).ochSignal() == component)
.findFirst()
.orElse(null);
}
if (rule == null) {
log.error("Lumentum NETCONF fail to retrieve attenuation signal {} port {}", component, port);
return 0L;
} else {
log.debug("Lumentum NETCONF on port {} attenuation {}", port,
(((LumentumFlowRule) rule).attenuation * 100));
return ((long) (((LumentumFlowRule) rule).attenuation * 100));
}
}
@ -124,23 +143,32 @@ public class LumentumPowerConfig<T> extends AbstractHandlerBehaviour
}
//TODO implement actual get configuration from the device
//This is used by ROADM application to retrieve attenuation parameter, with T instanceof OchSignal
//This is used by ROADM application to retrieve power parameter, with T instanceof OchSignal
private Long acquireCurrentPower(PortNumber port, T component) {
log.debug("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.debug("Lumentum NETCONF fail to retrieve power signal {} port {}", component, port);
Set<FlowRule> rules = getConnectionCache().get(did());
FlowRule rule;
if (rules == null) {
log.error("Lumentum NETCONF fail to retrieve power signal {} port {}", component, port);
return 0L;
} else {
log.debug("Lumentum NETCONF on port {} power {}", port, conn.inputPower);
return ((long) (conn.inputPower * 100));
rule = rules.stream()
.filter(c -> ((LumentumFlowRule) c).getInputPort() == port)
.filter(c -> ((LumentumFlowRule) c).ochSignal() == component)
.findFirst()
.orElse(null);
}
if (rule == null) {
log.error("Lumentum NETCONF fail to retrieve power signal {} port {}", component, port);
return 0L;
} else {
log.debug("Lumentum NETCONF on port {} power {}", port, (((LumentumFlowRule) rule).inputPower));
return ((long) (((LumentumFlowRule) rule).inputPower * 100));
}
}
@ -167,9 +195,103 @@ public class LumentumPowerConfig<T> extends AbstractHandlerBehaviour
log.debug("Set port {} target power {}", port, power);
}
//TODO implement configuration on the device
//Nothing to do
//Used by the ROADM app to set the "attenuation" parameter
private void setConnectionTargetPower(PortNumber port, OchSignal signal, long power) {
log.debug("Set connection target power {} ochsignal {} port {}", power, signal, port);
Set<FlowRule> rules = getConnectionCache().get(did());
FlowRule rule = null;
if (rules == null) {
log.error("Lumentum NETCONF fail to retrieve power signal {} port {}", signal, port);
} else {
rule = rules.stream()
.filter(c -> ((LumentumFlowRule) c).getOutputPort() == port)
.filter(c -> ((LumentumFlowRule) c).ochSignal() == signal)
.findFirst()
.orElse(null);
}
if (rule == null) {
log.error("Lumentum NETCONF fail to retrieve attenuation signal {} port {}", signal, port);
} else {
log.debug("Lumentum NETCONF setting attenuation {} on port {} signal {}", power, port, signal);
int moduleId = ((LumentumFlowRule) rule).getConnectionModule();
int connId = ((LumentumFlowRule) rule).getConnectionId();
editConnection(moduleId, connId, power);
}
}
private DeviceConnectionCache getConnectionCache() {
return DeviceConnectionCache.init();
}
//Following Lumentum documentation <edit-config> operation to edit connection parameter
//Currently only edit the "attenuation" parameter
private boolean editConnection(int moduleId, int connectionId, long attenuation) {
double attenuationDouble = ((double) attenuation);
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");
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 ROADM20 - edit-connection sent to device {}", did());
log.debug("Lumentum ROADM20 - edit-connection sent to device {} {}", did(), 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;
}
}
/**
* Helper method to get the device id.
*/
private DeviceId did() {
return data().deviceId();
}
/**
* Helper method to get the Netconf session.
*/
private NetconfSession getNetconfSession() {
NetconfController controller =
checkNotNull(handler().get(NetconfController.class));
return controller.getNetconfDevice(did()).getSession();
}
}

View File

@ -17,17 +17,19 @@ 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.ChannelSpacing;
import org.onosproject.net.OchSignal;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.behaviour.LambdaQuery;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import java.util.Collections;
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.
@ -53,19 +55,23 @@ public class LumentumRoadmLambdaQuery extends AbstractHandlerBehaviour implement
private static final int LAMBDA_COUNT_100 = 48;
@Override
public Set<OchSignal> queryLambdas(PortNumber port) {
public Set<OchSignal> queryLambdas(PortNumber portNumber) {
DeviceService deviceService = this.handler().get(DeviceService.class);
Port port = deviceService.getPort(data().deviceId(), portNumber);
//Complete set of 50GHz OchSignal
int startMultiplier50 = (int) (START_CENTER_FREQ_50.subtract(Spectrum.CENTER_FREQUENCY).asHz()
/ Frequency.ofGHz(50).asHz());
if (port.type() == Port.Type.FIBER) {
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 50GHz OchSignal
int startMultiplier50 = (int) (START_CENTER_FREQ_50.subtract(Spectrum.CENTER_FREQUENCY).asHz()
/ Frequency.ofGHz(50).asHz());
//Complete set of 100GHz OchSignal
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());
@ -74,9 +80,12 @@ public class LumentumRoadmLambdaQuery extends AbstractHandlerBehaviour implement
CHANNEL_SPACING_100, startMultiplier100 + x, 8))
.collect(Collectors.toSet());
Set<OchSignal> channels = Sets.union(channels50, channels100);
Set<OchSignal> channels = Sets.union(channels50, channels100);*/
return channels;
return channels50;
} else {
return Collections.emptySet();
}
}
}