diff --git a/core/api/src/main/java/org/onosproject/net/behaviour/ConfigGetter.java b/core/api/src/main/java/org/onosproject/net/behaviour/ConfigGetter.java index e77183be01..89180ff8fa 100644 --- a/core/api/src/main/java/org/onosproject/net/behaviour/ConfigGetter.java +++ b/core/api/src/main/java/org/onosproject/net/behaviour/ConfigGetter.java @@ -29,7 +29,7 @@ public interface ConfigGetter extends HandlerBehaviour { /** * Returns the string representation of a device configuration, returns a - * failure string if the configuration cannot be retreived. + * failure string if the configuration cannot be retrieved. * @param type the type of configuration to get (i.e. running). * @return string representation of the configuration or an error string. */ diff --git a/drivers/lumentum/features.xml b/drivers/lumentum/features.xml new file mode 100644 index 0000000000..17413d98d1 --- /dev/null +++ b/drivers/lumentum/features.xml @@ -0,0 +1,28 @@ + + + + + onos-api + mvn:${project.groupId}/${project.artifactId}/${project.version} + + mvn:${project.groupId}/onos-restsb-api/${project.version} + + mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.snmp4j/2.3.4_1 + + + diff --git a/drivers/lumentum/pom.xml b/drivers/lumentum/pom.xml new file mode 100644 index 0000000000..3375e2f202 --- /dev/null +++ b/drivers/lumentum/pom.xml @@ -0,0 +1,43 @@ + + + + + onos-drivers-general + org.onosproject + 1.5.0-SNAPSHOT + + 4.0.0 + + onos-drivers-lumentum + bundle + + Lumentum device drivers + + + org.onosproject.drivers.lumentum + + + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.snmp4j + 2.3.4_1 + + + \ No newline at end of file diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/CrossConnect.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/CrossConnect.java new file mode 100644 index 0000000000..42937d0dc9 --- /dev/null +++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/CrossConnect.java @@ -0,0 +1,46 @@ +/* + * Copyright 2016 Open Networking Laboratory + * + * 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.onosproject.net.flow.FlowRule; + +/** + * Interface for cross connects as common in optical networking. + */ +public interface CrossConnect extends FlowRule { + /** + * Returns the add/drop port of the cross connect. + * + * @return port number + */ + PortNumber addDrop(); + + /** + * Returns the wavelength of the cross connect. + * + * @return OCh signal + */ + OchSignal ochSignal(); + + /** + * Returns true if cross connect is adding traffic. + * + * @return true if add rule, false if drop rule + */ + boolean isAddRule(); +} diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/CrossConnectCache.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/CrossConnectCache.java new file mode 100644 index 0000000000..6d013fbadc --- /dev/null +++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/CrossConnectCache.java @@ -0,0 +1,48 @@ +/* + * Copyright 2016 Open Networking Laboratory + * + * 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.apache.commons.lang3.tuple.Pair; +import org.onosproject.net.flow.FlowId; + +/** + * Simple interface to cache flow ID and priority of cross connect flows. + */ +interface CrossConnectCache { + /** + * Returns the flow ID and priority corresponding to the flow hash. + * + * @param hash flow hash + * @return flow ID and priority, null if not in cache + */ + Pair get(int hash); + + /** + * Stores the flow ID and priority corresponding to the flow hash. + * + * @param hash flow hash + * @param flowId flow ID + * @param priority flow priority + */ + void set(int hash, FlowId flowId, int priority); + + /** + * Removes the given hash from the cache. + * + * @param hash flow hash + */ + void remove(int hash); +} diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/CrossConnectFlowRule.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/CrossConnectFlowRule.java new file mode 100644 index 0000000000..b6a51d7256 --- /dev/null +++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/CrossConnectFlowRule.java @@ -0,0 +1,98 @@ +/* + * Copyright 2016 Open Networking Laboratory + * + * 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.onosproject.net.flow.DefaultFlowRule; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.criteria.Criterion; +import org.onosproject.net.flow.criteria.OchSignalCriterion; +import org.onosproject.net.flow.criteria.OchSignalTypeCriterion; +import org.onosproject.net.flow.criteria.PortCriterion; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions; + +import java.util.List; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * Cross connect abstraction based on a flow rule. + */ +public class CrossConnectFlowRule extends DefaultFlowRule implements CrossConnect { + private PortNumber addDrop; + private OchSignal ochSignal; + private boolean isAddRule; + + public CrossConnectFlowRule(FlowRule rule, List linePorts) { + super(rule); + + Set criteria = rule.selector().criteria(); + List instructions = rule.treatment().immediate(); + + // Proper cross connect has criteria for input port, OChSignal and OCh signal type. + // Instruction must be output to port. + checkArgument(criteria.size() == 3, "Wrong size of flow rule criteria for cross connect."); + checkArgument(instructions.size() == 1, "Wrong size of flow rule instructions for cross connect."); + // FIXME: Ensure criteria has exactly one of each match type + criteria.forEach( + c -> checkArgument(c instanceof OchSignalCriterion || + c instanceof OchSignalTypeCriterion || + c instanceof PortCriterion, + "Incompatible flow rule criteria for cross connect: " + criteria + ) + ); + checkArgument(instructions.get(0).type() == Instruction.Type.OUTPUT, + "Incompatible flow rule instructions for cross connect: " + instructions); + + ochSignal = criteria.stream() + .filter(c -> c instanceof OchSignalCriterion) + .map(c -> ((OchSignalCriterion) c).lambda()) + .findAny() + .orElse(null); + + // Add or drop rule? + Instructions.OutputInstruction outInstruction = (Instructions.OutputInstruction) instructions.get(0); + if (linePorts.contains(outInstruction.port())) { + addDrop = criteria.stream() + .filter(c -> c instanceof PortCriterion) + .map(c -> ((PortCriterion) c).port()) + .findAny() + .orElse(null); + isAddRule = true; + } else { + addDrop = outInstruction.port(); + isAddRule = false; + } + } + + @Override + public PortNumber addDrop() { + return addDrop; + } + + @Override + public OchSignal ochSignal() { + return ochSignal; + } + + @Override + public boolean isAddRule() { + return isAddRule; + } +} diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/DefaultCrossConnectCache.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/DefaultCrossConnectCache.java new file mode 100644 index 0000000000..849add176f --- /dev/null +++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/DefaultCrossConnectCache.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016 Open Networking Laboratory + * + * 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.apache.commons.lang3.tuple.Pair; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; +import org.onosproject.net.flow.FlowId; + +import java.util.HashMap; +import java.util.Map; + +@Component(immediate = true, enabled = true) +@Service +public class DefaultCrossConnectCache implements CrossConnectCache { + private final Map> cache = new HashMap<>(); + + @Override + public Pair get(int hash) { + return cache.get(hash); + } + + @Override + public void set(int hash, FlowId flowId, int priority) { + cache.put(hash, Pair.of(flowId, priority)); + } + + @Override + public void remove(int hash) { + cache.remove(hash); + } +} diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LambdaQueryLumentumRoadm.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LambdaQueryLumentumRoadm.java new file mode 100644 index 0000000000..36ad52170c --- /dev/null +++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LambdaQueryLumentumRoadm.java @@ -0,0 +1,49 @@ +/* + * Copyright 2016 Open Networking Laboratory + * + * 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.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.Collectors; +import java.util.stream.IntStream; + +/** + * Implementation of lambda query interface for Lumentum SDN ROADMs. + * + * Device supports 96 wavelengths of 50 GHz, between center frequencies 191.350 THz and 196.075 THz. + */ +public class LambdaQueryLumentumRoadm extends AbstractHandlerBehaviour implements LambdaQuery { + private static final int LAMBDA_COUNT = 96; + + @Override + public Set queryLambdas(PortNumber port) { + int startMultiplier = (int) (LumentumSnmpDevice.START_CENTER_FREQ.subtract(Spectrum.CENTER_FREQUENCY).asHz() + / Frequency.ofGHz(50).asHz()); + + return IntStream.range(0, LAMBDA_COUNT) + .mapToObj(x -> new OchSignal(LumentumSnmpDevice.GRID_TYPE, + LumentumSnmpDevice.CHANNEL_SPACING, + startMultiplier + x, + 4)) + .collect(Collectors.toSet()); + } +} diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumDriversLoader.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumDriversLoader.java new file mode 100644 index 0000000000..c3980192e5 --- /dev/null +++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumDriversLoader.java @@ -0,0 +1,31 @@ +/* + * Copyright 2016 Open Networking Laboratory + * + * 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.apache.felix.scr.annotations.Component; +import org.onosproject.net.driver.AbstractDriverLoader; + +/** + * Loader for Lumentum device drivers from specific xml. + */ +@Component(immediate = true) +public class LumentumDriversLoader extends AbstractDriverLoader { + + public LumentumDriversLoader() { + super("/lumentum-drivers.xml"); + } +} diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumFlowRuleDriver.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumFlowRuleDriver.java new file mode 100644 index 0000000000..c1a5fe82bf --- /dev/null +++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumFlowRuleDriver.java @@ -0,0 +1,360 @@ +/* + * Copyright 2016 Open Networking Laboratory + * + * 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.Lists; +import org.apache.commons.lang3.tuple.Pair; +import org.onosproject.net.ChannelSpacing; +import org.onosproject.net.GridType; +import org.onosproject.net.OchSignal; +import org.onosproject.net.OchSignalType; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.driver.AbstractHandlerBehaviour; +import org.onosproject.net.flow.DefaultFlowEntry; +import org.onosproject.net.flow.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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.snmp4j.PDU; +import org.snmp4j.event.ResponseEvent; +import org.snmp4j.smi.Integer32; +import org.snmp4j.smi.OID; +import org.snmp4j.smi.UnsignedInteger32; +import org.snmp4j.smi.VariableBinding; +import org.snmp4j.util.TreeEvent; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkArgument; + +// TODO: need to convert between OChSignal and XC channel number +public class LumentumFlowRuleDriver extends AbstractHandlerBehaviour implements FlowRuleProgrammable { + + private static final Logger log = + LoggerFactory.getLogger(LumentumFlowRuleDriver.class); + private static final int DEFAULT_ATTENUATION = 160; + private static final int OUT_OF_SERVICE = 1; + private static final int IN_SERVICE = 2; + private static final String CTRL_AMP_MODULE_SERVICE_STATE_PREAMP = ".1.3.6.1.4.1.46184.1.4.4.1.2.1"; + private static final String CTRL_AMP_MODULE_SERVICE_STATE_BOOSTER = ".1.3.6.1.4.1.46184.1.4.4.1.2.2"; + private static final String CTRL_CHANNEL_STATE = ".1.3.6.1.4.1.46184.1.4.2.1.3."; + private static final String CTRL_CHANNEL_ADD_DROP_PORT_INDEX = ".1.3.6.1.4.1.46184.1.4.2.1.13."; + private static final String CTRL_CHANNEL_ABSOLUTE_ATTENUATION = ".1.3.6.1.4.1.46184.1.4.2.1.5."; + + private LumentumSnmpDevice snmp; + + @Override + public Collection getFlowEntries() { + try { + snmp = new LumentumSnmpDevice(handler().data().deviceId()); + } catch (IOException e) { + log.error("Failed to connect to device: ", e); + return Collections.emptyList(); + } + + // Line in is last but one port, line out is last + DeviceService deviceService = this.handler().get(DeviceService.class); + List ports = deviceService.getPorts(data().deviceId()); + if (ports.size() < 2) { + return Collections.emptyList(); + } + PortNumber lineIn = ports.get(ports.size() - 2).number(); + PortNumber lineOut = ports.get(ports.size() - 1).number(); + + Collection entries = Lists.newLinkedList(); + + // Add rules + OID addOid = new OID(CTRL_CHANNEL_STATE + "1"); + entries.addAll( + fetchRules(addOid, true, lineOut).stream() + .map(fr -> new DefaultFlowEntry(fr, FlowEntry.FlowEntryState.ADDED, 0, 0, 0)) + .collect(Collectors.toList()) + ); + + // Drop rules + OID dropOid = new OID(CTRL_CHANNEL_STATE + "2"); + entries.addAll( + fetchRules(dropOid, false, lineIn).stream() + .map(fr -> new DefaultFlowEntry(fr, FlowEntry.FlowEntryState.ADDED, 0, 0, 0)) + .collect(Collectors.toList()) + ); + + return entries; + } + + @Override + public Collection applyFlowRules(Collection rules) { + try { + snmp = new LumentumSnmpDevice(data().deviceId()); + } catch (IOException e) { + log.error("Failed to connect to device: ", e); + } + + // Line ports + DeviceService deviceService = this.handler().get(DeviceService.class); + List ports = deviceService.getPorts(data().deviceId()); + List linePorts = ports.subList(ports.size() - 2, ports.size()).stream() + .map(p -> p.number()) + .collect(Collectors.toList()); + + // Apply the valid rules on the device + Collection added = rules.stream() + .map(r -> new CrossConnectFlowRule(r, linePorts)) + .filter(xc -> installCrossConnect(xc)) + .collect(Collectors.toList()); + + // Cache the cookie/priority + CrossConnectCache cache = this.handler().get(CrossConnectCache.class); + added.stream() + .forEach(xc -> cache.set( + Objects.hash(data().deviceId(), xc.selector(), xc.treatment()), + xc.id(), + xc.priority())); + + return added; + } + + @Override + public Collection removeFlowRules(Collection rules) { + try { + snmp = new LumentumSnmpDevice(data().deviceId()); + } catch (IOException e) { + log.error("Failed to connect to device: ", e); + } + + // Line ports + DeviceService deviceService = this.handler().get(DeviceService.class); + List ports = deviceService.getPorts(data().deviceId()); + List linePorts = ports.subList(ports.size() - 2, ports.size()).stream() + .map(p -> p.number()) + .collect(Collectors.toList()); + + // Apply the valid rules on the device + Collection removed = rules.stream() + .map(r -> new CrossConnectFlowRule(r, linePorts)) + .filter(xc -> removeCrossConnect(xc)) + .collect(Collectors.toList()); + + // Remove flow rule from cache + CrossConnectCache cache = this.handler().get(CrossConnectCache.class); + removed.stream() + .forEach(xc -> cache.remove( + Objects.hash(data().deviceId(), xc.selector(), xc.treatment()))); + + return removed; + } + + // Installs cross connect on device + private boolean installCrossConnect(CrossConnectFlowRule xc) { + + int channel = toChannel(xc.ochSignal()); + long addDrop = xc.addDrop().toLong(); + + // Create the PDU object + PDU pdu = new PDU(); + pdu.setType(PDU.SET); + + // Enable preamp & booster for service + List oids = Arrays.asList(new OID(CTRL_AMP_MODULE_SERVICE_STATE_PREAMP), + new OID(CTRL_AMP_MODULE_SERVICE_STATE_BOOSTER)); + oids.forEach( + oid -> pdu.add(new VariableBinding(oid, new Integer32(IN_SERVICE))) + ); + + // Enable the channel + OID ctrlChannelState = new OID(CTRL_CHANNEL_STATE + (xc.isAddRule() ? "1." : "2.") + channel); + pdu.add(new VariableBinding(ctrlChannelState, new Integer32(IN_SERVICE))); + + // Make cross connect + OID ctrlChannelAddDropPortIndex = new OID(CTRL_CHANNEL_ADD_DROP_PORT_INDEX + + (xc.isAddRule() ? "1." : "2.") + channel); + pdu.add(new VariableBinding(ctrlChannelAddDropPortIndex, new UnsignedInteger32(addDrop))); + + // Set attenuation to zero + OID ctrlChannelAbsoluteAttenuation = new OID(CTRL_CHANNEL_ABSOLUTE_ATTENUATION + + (xc.isAddRule() ? "1." : "2.") + channel); + pdu.add(new VariableBinding(ctrlChannelAbsoluteAttenuation, new UnsignedInteger32(0))); + + try { + ResponseEvent response = snmp.set(pdu); + + // TODO: parse response + } catch (IOException e) { + log.error("Failed to create cross connect, unable to connect to device: ", e); + } + + return true; + } + + // Removes cross connect on device + private boolean removeCrossConnect(CrossConnectFlowRule xc) { + + int channel = toChannel(xc.ochSignal()); + + // Create the PDU object + PDU pdu = new PDU(); + pdu.setType(PDU.SET); + + // Disable the channel + OID ctrlChannelState = new OID(CTRL_CHANNEL_STATE + (xc.isAddRule() ? "1." : "2.") + channel); + pdu.add(new VariableBinding(ctrlChannelState, new Integer32(OUT_OF_SERVICE))); + + // Put cross connect back into default port 1 + OID ctrlChannelAddDropPortIndex = new OID(CTRL_CHANNEL_ADD_DROP_PORT_INDEX + + (xc.isAddRule() ? "1." : "2.") + channel); + pdu.add(new VariableBinding(ctrlChannelAddDropPortIndex, new UnsignedInteger32(OUT_OF_SERVICE))); + + // Set attenuation to default value + OID ctrlChannelAbsoluteAttenuation = new OID(CTRL_CHANNEL_ABSOLUTE_ATTENUATION + + (xc.isAddRule() ? "1." : "2.") + channel); + pdu.add(new VariableBinding(ctrlChannelAbsoluteAttenuation, new UnsignedInteger32(DEFAULT_ATTENUATION)) + ); + + try { + ResponseEvent response = snmp.set(pdu); + + // TODO: parse response + } catch (IOException e) { + log.error("Failed to remove cross connect, unable to connect to device: ", e); + return false; + } + + return true; + } + + /** + * Convert OCh signal to Lumentum channel ID. + * + * @param ochSignal OCh signal + * @return Lumentum channel ID + */ + public static int toChannel(OchSignal ochSignal) { + // FIXME: move to cross connect validation + checkArgument(ochSignal.channelSpacing() == ChannelSpacing.CHL_50GHZ); + checkArgument(LumentumSnmpDevice.START_CENTER_FREQ.compareTo(ochSignal.centralFrequency()) <= 0); + checkArgument(LumentumSnmpDevice.END_CENTER_FREQ.compareTo(ochSignal.centralFrequency()) >= 0); + + return ochSignal.spacingMultiplier() + LumentumSnmpDevice.MULTIPLIER_SHIFT; + } + + /** + * Convert Lumentum channel ID to OCh signal. + * + * @param channel Lumentum channel ID + * @return OCh signal + */ + public static OchSignal toOchSignal(int channel) { + checkArgument(1 <= channel); + checkArgument(channel <= 96); + + return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, + channel - LumentumSnmpDevice.MULTIPLIER_SHIFT, 4); + } + + // Returns the currently configured add/drop port for the given channel. + private PortNumber getAddDropPort(int channel, boolean isAddPort) { + OID oid = new OID(CTRL_CHANNEL_ADD_DROP_PORT_INDEX + (isAddPort ? "1" : "2")); + + for (TreeEvent event : snmp.get(oid)) { + if (event == null) { + return null; + } + + VariableBinding[] varBindings = event.getVariableBindings(); + + for (VariableBinding varBinding : varBindings) { + if (varBinding.getOid().last() == channel) { + int port = varBinding.getVariable().toInt(); + return PortNumber.portNumber(port); + + } + } + + } + + return null; + } + + // Returns the currently installed flow entries on the device. + private List fetchRules(OID oid, boolean isAdd, PortNumber linePort) { + List rules = new LinkedList<>(); + + for (TreeEvent event : snmp.get(oid)) { + if (event == null) { + continue; + } + + VariableBinding[] varBindings = event.getVariableBindings(); + for (VariableBinding varBinding : varBindings) { + CrossConnectCache cache = this.handler().get(CrossConnectCache.class); + + if (varBinding.getVariable().toInt() == IN_SERVICE) { + int channel = varBinding.getOid().removeLast(); + + PortNumber addDropPort = getAddDropPort(channel, isAdd); + if (addDropPort == null) { + continue; + } + + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchInPort(isAdd ? addDropPort : linePort) + .add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID)) + .add(Criteria.matchLambda(toOchSignal(channel))) + .build(); + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(isAdd ? linePort : addDropPort) + .build(); + + // Lookup flow ID and priority + int hash = Objects.hash(data().deviceId(), selector, treatment); + Pair lookup = cache.get(hash); + if (lookup == null) { + continue; + } + + FlowRule fr = DefaultFlowRule.builder() + .forDevice(data().deviceId()) + .makePermanent() + .withSelector(selector) + .withTreatment(treatment) + .withPriority(lookup.getRight()) + .withCookie(lookup.getLeft().value()) + .build(); + rules.add(fr); + } + } + } + + return rules; + } +} 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 new file mode 100644 index 0000000000..f0138f1705 --- /dev/null +++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSnmpDevice.java @@ -0,0 +1,94 @@ +/* + * Copyright 2016 Open Networking Laboratory + * + * 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.base.Preconditions; +import org.onlab.util.Frequency; +import org.onosproject.net.ChannelSpacing; +import org.onosproject.net.DeviceId; +import org.onosproject.net.GridType; +import org.snmp4j.CommunityTarget; +import org.snmp4j.PDU; +import org.snmp4j.Snmp; +import org.snmp4j.TransportMapping; +import org.snmp4j.event.ResponseEvent; +import org.snmp4j.mp.SnmpConstants; +import org.snmp4j.smi.Address; +import org.snmp4j.smi.GenericAddress; +import org.snmp4j.smi.OID; +import org.snmp4j.smi.OctetString; +import org.snmp4j.transport.DefaultUdpTransportMapping; +import org.snmp4j.util.DefaultPDUFactory; +import org.snmp4j.util.TreeEvent; +import org.snmp4j.util.TreeUtils; + +import java.io.IOException; +import java.util.List; + +/** + * Quick and dirty device abstraction for SNMP-based Lumentum devices. + * + * TODO: Refactor once SnmpDevice is finished + */ +public class LumentumSnmpDevice { + + private static final int MAX_SIZE_RESPONSE_PDU = 65535; + private static final int MAX_REPETITIONS = 50; // Only 42 directed ports on our devices + + public static final GridType GRID_TYPE = GridType.DWDM; + public static final ChannelSpacing CHANNEL_SPACING = ChannelSpacing.CHL_50GHZ; + public static final Frequency START_CENTER_FREQ = Frequency.ofGHz(191_350); + public static final Frequency END_CENTER_FREQ = Frequency.ofGHz(196_100); + + // Lumentum SDN ROADM has shifted channel plan. + // Channel 36 corresponds to ITU-T center frequency, which has spacing multiplier 0. + public static final int MULTIPLIER_SHIFT = 36; + + private Snmp snmp; + private CommunityTarget target; + + public LumentumSnmpDevice(DeviceId did) throws IOException { + String[] deviceComponents = did.toString().split(":"); + Preconditions.checkArgument(deviceComponents.length > 1); + + String ipAddress = deviceComponents[1]; + String port = deviceComponents[2]; + + Address targetAddress = GenericAddress.parse("udp:" + ipAddress + "/" + port); + TransportMapping transport = new DefaultUdpTransportMapping(); + transport.listen(); + snmp = new Snmp(transport); + + // setting up target + target = new CommunityTarget(); + target.setCommunity(new OctetString("public")); + target.setAddress(targetAddress); + target.setRetries(3); + target.setTimeout(1000 * 3); + target.setVersion(SnmpConstants.version2c); + target.setMaxSizeRequestPDU(MAX_SIZE_RESPONSE_PDU); + } + + public ResponseEvent set(PDU pdu) throws IOException { + return snmp.set(pdu, target); + } + + public List get(OID oid) { + TreeUtils treeUtils = new TreeUtils(snmp, new DefaultPDUFactory()); + treeUtils.setMaxRepetitions(MAX_REPETITIONS); + return treeUtils.getSubtree(target, oid); + } +} diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/PortDiscoveryLumentumRoadm.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/PortDiscoveryLumentumRoadm.java new file mode 100644 index 0000000000..9d250c0369 --- /dev/null +++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/PortDiscoveryLumentumRoadm.java @@ -0,0 +1,123 @@ +/* + * Copyright 2016 Open Networking Laboratory + * + * 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.Lists; +import org.onosproject.net.AnnotationKeys; +import org.onosproject.net.DefaultAnnotations; +import org.onosproject.net.PortNumber; +import org.onosproject.net.SparseAnnotations; +import org.onosproject.net.behaviour.PortDiscovery; +import org.onosproject.net.device.OmsPortDescription; +import org.onosproject.net.device.PortDescription; +import org.onosproject.net.driver.AbstractHandlerBehaviour; +import org.slf4j.Logger; +import org.snmp4j.smi.OID; +import org.snmp4j.smi.VariableBinding; +import org.snmp4j.util.TreeEvent; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Discovers the ports of a Lumentum SDN ROADM device using SNMP. + */ +public class PortDiscoveryLumentumRoadm extends AbstractHandlerBehaviour + implements PortDiscovery { + + private final Logger log = getLogger(PortDiscoveryLumentumRoadm.class); + + private static final String CTRL_PORT_STATE = ".1.3.6.1.4.1.46184.1.4.1.1.3."; + + private LumentumSnmpDevice snmp; + + @Override + public List getPorts() { + try { + snmp = new LumentumSnmpDevice(handler().data().deviceId()); + } catch (IOException e) { + log.error("Failed to connect to device: ", e); + + return Collections.emptyList(); + } + + List ports = Lists.newLinkedList(); + + OID[] oids = { + new OID(CTRL_PORT_STATE + "1"), + new OID(CTRL_PORT_STATE + "2") + }; + + for (OID oid : oids) { + + for (TreeEvent event : snmp.get(oid)) { + if (event != null) { + VariableBinding[] varBindings = event.getVariableBindings(); + for (VariableBinding varBinding : varBindings) { + if (varBinding.getVariable().toInt() == 1) { + int portNumber = varBinding.getOid().removeLast(); + int portDirection = varBinding.getOid().removeLast(); + SparseAnnotations ann = DefaultAnnotations.builder() + .set(AnnotationKeys.PORT_NAME, portDirection + "-" + portNumber) + .build(); + PortDescription p = new OmsPortDescription( + PortNumber.portNumber(ports.size() + 1), + true, + LumentumSnmpDevice.START_CENTER_FREQ, + LumentumSnmpDevice.END_CENTER_FREQ, + LumentumSnmpDevice.CHANNEL_SPACING.frequency(), + ann); + ports.add(p); + } + } + } + } + } + + // Create LINE IN and LINE OUT ports as these are not reported through SNMP + SparseAnnotations annLineIn = DefaultAnnotations.builder() + .set(AnnotationKeys.PORT_NAME, "LINE IN") + .build(); + ports.add(new OmsPortDescription( + PortNumber.portNumber(ports.size() + 1), + true, + LumentumSnmpDevice.START_CENTER_FREQ, + LumentumSnmpDevice.END_CENTER_FREQ, + LumentumSnmpDevice.CHANNEL_SPACING.frequency(), + annLineIn + )); + + SparseAnnotations annLineOut = DefaultAnnotations.builder() + .set(AnnotationKeys.PORT_NAME, "LINE OUT") + .build(); + ports.add(new OmsPortDescription( + PortNumber.portNumber(ports.size() + 1), + true, + LumentumSnmpDevice.START_CENTER_FREQ, + LumentumSnmpDevice.END_CENTER_FREQ, + LumentumSnmpDevice.CHANNEL_SPACING.frequency(), + annLineOut + )); + + return ports; + } +} + + diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/package-info.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/package-info.java new file mode 100644 index 0000000000..0c34578efb --- /dev/null +++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Open Networking Laboratory + * + * 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 for Lumentum device drivers. + */ +package org.onosproject.drivers.lumentum; \ No newline at end of file diff --git a/drivers/lumentum/src/main/resources/lumentum-drivers.xml b/drivers/lumentum/src/main/resources/lumentum-drivers.xml new file mode 100644 index 0000000000..0465fe062c --- /dev/null +++ b/drivers/lumentum/src/main/resources/lumentum-drivers.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/drivers/netconf/src/main/java/org/onosproject/drivers/netconf/NetconfControllerConfig.java b/drivers/netconf/src/main/java/org/onosproject/drivers/netconf/NetconfControllerConfig.java index 1a6f31e13f..7c0f1b0281 100644 --- a/drivers/netconf/src/main/java/org/onosproject/drivers/netconf/NetconfControllerConfig.java +++ b/drivers/netconf/src/main/java/org/onosproject/drivers/netconf/NetconfControllerConfig.java @@ -58,7 +58,7 @@ public class NetconfControllerConfig extends AbstractHandlerBehaviour controllers.addAll(XmlConfigParser.parseStreamControllers(XmlConfigParser. loadXml(new ByteArrayInputStream(reply.getBytes(StandardCharsets.UTF_8))))); } catch (IOException e) { - log.error("Cannot comunicate to device {} ", ofDeviceId); + log.error("Cannot communicate with device {} ", ofDeviceId); } return controllers; } diff --git a/drivers/pom.xml b/drivers/pom.xml index 290d165964..560eb24ec2 100644 --- a/drivers/pom.xml +++ b/drivers/pom.xml @@ -39,6 +39,7 @@ netconf ovsdb utilities + lumentum