SNMP based device and flow rule provider for Lumentum SDN ROADMs.
ONOS-3690 and ONOS-3842
Change-Id: If00ba70fa26e01924c225596c52a5ffb24987cc2
diff --git a/drivers/lumentum/features.xml b/drivers/lumentum/features.xml
new file mode 100644
index 0000000..17413d9
--- /dev/null
+++ b/drivers/lumentum/features.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ ~ 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.
+ -->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
+ <feature name="${project.artifactId}" version="${project.version}"
+ description="${project.description}">
+ <feature>onos-api</feature>
+ <bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
+
+ <bundle>mvn:${project.groupId}/onos-restsb-api/${project.version}</bundle>
+
+ <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.snmp4j/2.3.4_1</bundle>
+
+ </feature>
+</features>
diff --git a/drivers/lumentum/pom.xml b/drivers/lumentum/pom.xml
new file mode 100644
index 0000000..3375e2f
--- /dev/null
+++ b/drivers/lumentum/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>onos-drivers-general</artifactId>
+ <groupId>org.onosproject</groupId>
+ <version>1.5.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>onos-drivers-lumentum</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>Lumentum device drivers</description>
+
+ <properties>
+ <onos.app.name>org.onosproject.drivers.lumentum</onos.app.name>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.snmp4j</artifactId>
+ <version>2.3.4_1</version>
+ </dependency>
+ </dependencies>
+</project>
\ 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 0000000..42937d0
--- /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 0000000..6d013fb
--- /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<FlowId, Integer> 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 0000000..b6a51d7
--- /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<PortNumber> linePorts) {
+ super(rule);
+
+ Set<Criterion> criteria = rule.selector().criteria();
+ List<Instruction> 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 0000000..849add1
--- /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<Integer, Pair<FlowId, Integer>> cache = new HashMap<>();
+
+ @Override
+ public Pair<FlowId, Integer> 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 0000000..36ad521
--- /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<OchSignal> 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 0000000..c398019
--- /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 0000000..c1a5fe8
--- /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<FlowEntry> 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<Port> 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<FlowEntry> 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<FlowRule> applyFlowRules(Collection<FlowRule> 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<Port> ports = deviceService.getPorts(data().deviceId());
+ List<PortNumber> linePorts = ports.subList(ports.size() - 2, ports.size()).stream()
+ .map(p -> p.number())
+ .collect(Collectors.toList());
+
+ // Apply the valid rules on the device
+ Collection<FlowRule> 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<FlowRule> removeFlowRules(Collection<FlowRule> 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<Port> ports = deviceService.getPorts(data().deviceId());
+ List<PortNumber> linePorts = ports.subList(ports.size() - 2, ports.size()).stream()
+ .map(p -> p.number())
+ .collect(Collectors.toList());
+
+ // Apply the valid rules on the device
+ Collection<FlowRule> 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<OID> 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<FlowRule> fetchRules(OID oid, boolean isAdd, PortNumber linePort) {
+ List<FlowRule> 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<FlowId, Integer> 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 0000000..f0138f1
--- /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<TreeEvent> 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 0000000..9d250c0
--- /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<PortDescription> getPorts() {
+ try {
+ snmp = new LumentumSnmpDevice(handler().data().deviceId());
+ } catch (IOException e) {
+ log.error("Failed to connect to device: ", e);
+
+ return Collections.emptyList();
+ }
+
+ List<PortDescription> 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 0000000..0c34578
--- /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 0000000..0465fe0
--- /dev/null
+++ b/drivers/lumentum/src/main/resources/lumentum-drivers.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+<drivers>
+ <driver name="lumentum" manufacturer="Lumentum" hwVersion="SDN ROADM" swVersion="1.0">
+ <behaviour api="org.onosproject.net.behaviour.PortDiscovery"
+ impl="org.onosproject.drivers.lumentum.PortDiscoveryLumentumRoadm"/>
+ <behaviour api="org.onosproject.net.behaviour.LambdaQuery"
+ impl="org.onosproject.drivers.lumentum.LambdaQueryLumentumRoadm"/>
+ <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
+ impl="org.onosproject.drivers.lumentum.LumentumFlowRuleDriver"/>
+ </driver>
+</drivers>
+