Add flow support to the Polatis SNMP driver
Change-Id: I150e174acd54c945c95ca9a1885f1f6313d44ce9
diff --git a/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisDeviceDescription.java b/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisDeviceDescription.java
index ff2862d..8d7ec4e 100644
--- a/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisDeviceDescription.java
+++ b/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisDeviceDescription.java
@@ -17,8 +17,10 @@
package org.onosproject.drivers.polatis.snmp;
import com.google.common.collect.Lists;
+import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceDescription;
@@ -28,6 +30,12 @@
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onlab.packet.ChassisId;
+import org.onlab.util.Frequency;
+import org.onlab.util.Spectrum;
+
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.VariableBinding;
+import org.snmp4j.util.TableEvent;
import org.slf4j.Logger;
@@ -37,7 +45,9 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.net.optical.device.OmsPortHelper.omsPortDescription;
import static org.onosproject.drivers.polatis.snmp.PolatisSnmpUtility.getOid;
+import static org.onosproject.drivers.polatis.snmp.PolatisSnmpUtility.getTable;
/**
* Representation of device information and ports via SNMP for all Polatis
@@ -53,6 +63,12 @@
private static final String PRODUCT_CODE_OID = ".1.3.6.1.4.1.26592.2.1.2.2.1.0";
private static final String SERIAL_NUMBER_OID = ".1.3.6.1.4.1.26592.2.1.2.2.2.0";
+ private static final String PORT_ENTRY_OID = ".1.3.6.1.4.1.26592.2.2.2.1.2";
+ private static final String PORT_PATCH_OID = PORT_ENTRY_OID + ".1.2";
+ private static final String PORT_CURRENT_STATE_OID = PORT_ENTRY_OID + ".1.3";
+
+ public static final int POLATIS_NUM_OF_WAVELENGTHS = 39;
+
private final Logger log = getLogger(getClass());
/**
@@ -76,7 +92,7 @@
try {
hardwareVersion = hardwareVersion();
} catch (IOException e) {
- log.error("Error reading hardware version for device {} exception {}", deviceId, e);
+ log.error("Error reading hardware version for device {} exception ", deviceId, e);
}
String softwareVersion = DEFAULT_DESCRIPTION_DATA;
@@ -106,9 +122,55 @@
*/
@Override
public List<PortDescription> discoverPortDetails() {
- // TODO: Implement me
- return Lists.newLinkedList();
- // return ImmutableList.copyOf(this.getPorts());
+ List<PortDescription> ports = Lists.newArrayList();
+ List<TableEvent> events;
+ DeviceId deviceId = handler().data().deviceId();
+
+ try {
+ OID[] columnOIDs = {new OID(PORT_CURRENT_STATE_OID)};
+ events = getTable(handler(), columnOIDs);
+ } catch (IOException e) {
+ log.error("Error reading ports table for device {} exception {}", deviceId, e);
+ return ports;
+ }
+
+ if (events == null) {
+ log.error("Error reading ports table for device {}", deviceId);
+ return ports;
+ }
+
+ for (TableEvent event : events) {
+ if (event == null) {
+ log.error("Error reading event for device {}", deviceId);
+ continue;
+ }
+ VariableBinding[] columns = event.getColumns();
+ if (columns == null) {
+ log.error("Error reading columns for device {} event {}", deviceId, event);
+ continue;
+ }
+
+ VariableBinding portColumn = columns[0];
+ if (portColumn == null) {
+ continue;
+ }
+
+ int port = event.getIndex().last();
+ boolean enabled = (portColumn.getVariable().toInt() == 1);
+ PortNumber portNumber = PortNumber.portNumber(port);
+ DefaultAnnotations annotations = DefaultAnnotations.builder().build();
+ double opticalBand = Spectrum.O_BAND_MIN.asGHz() - Spectrum.L_BAND_MAX.asGHz();
+ Frequency opticalGrid = Frequency.ofGHz(opticalBand / POLATIS_NUM_OF_WAVELENGTHS);
+ PortDescription p = omsPortDescription(portNumber,
+ enabled,
+ Spectrum.O_BAND_MIN,
+ Spectrum.L_BAND_MAX,
+ opticalGrid,
+ annotations);
+ ports.add(p);
+ }
+
+ return ports;
}
private String hardwareVersion() throws IOException {
diff --git a/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisFlowRuleProgrammable.java b/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisFlowRuleProgrammable.java
new file mode 100644
index 0000000..e195dc3
--- /dev/null
+++ b/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisFlowRuleProgrammable.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2018 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.polatis.snmp;
+
+import com.google.common.collect.ImmutableList;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleProgrammable;
+
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.VariableBinding;
+import org.snmp4j.util.TableEvent;
+
+import org.slf4j.Logger;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.onosproject.drivers.polatis.snmp.PolatisOpticalUtility.fromFlowRule;
+import static org.onosproject.drivers.polatis.snmp.PolatisSnmpUtility.getTable;
+import static org.onosproject.drivers.polatis.snmp.PolatisSnmpUtility.set;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Flow rule programmable behaviour for Polatis optical snmp devices.
+ */
+public class PolatisFlowRuleProgrammable
+ extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
+
+ private static final String PORT_ENTRY_OID = ".1.3.6.1.4.1.26592.2.2.2.1.2";
+ private static final String PORT_PATCH_OID = PORT_ENTRY_OID + ".1.2";
+
+ private final Logger log = getLogger(getClass());
+
+ @Override
+ public Collection<FlowEntry> getFlowEntries() {
+ List<TableEvent> events;
+ DeviceId deviceId = handler().data().deviceId();
+ ImmutableList.Builder<FlowEntry> connectionsBuilder = ImmutableList.builder();
+
+ try {
+ OID[] columnOIDs = {new OID(PORT_PATCH_OID)};
+ events = getTable(handler(), columnOIDs);
+ } catch (IOException e) {
+ log.error("Error reading ports table for device {} exception {}", deviceId, e);
+ return connectionsBuilder.build();
+ }
+
+ if (events == null) {
+ log.error("Error reading ports table for device {}", deviceId);
+ return connectionsBuilder.build();
+ }
+
+ for (TableEvent event : events) {
+ if (event == null) {
+ log.error("Error reading event for device {}", deviceId);
+ continue;
+ }
+ VariableBinding[] columns = event.getColumns();
+ if (columns == null) {
+ log.error("Error reading columns for device {}", deviceId);
+ continue;
+ }
+
+ VariableBinding patchColumn = columns[0];
+ if (patchColumn == null) {
+ continue;
+ }
+
+ int port = event.getIndex().last();
+ int patch = patchColumn.getVariable().toInt();
+ if (patch == 0) {
+ continue;
+ }
+
+ FlowRule flowRule = PolatisOpticalUtility.toFlowRule(this,
+ PortNumber.portNumber(port), PortNumber.portNumber(patch));
+ connectionsBuilder.add(new DefaultFlowEntry(flowRule, FlowEntry.FlowEntryState.ADDED));
+ }
+
+ return connectionsBuilder.build();
+ }
+
+ private boolean editConnection(FlowRule rule, boolean delete) {
+ List<VariableBinding> vbs = new ArrayList<>();
+ vbs.add(fromFlowRule(rule, delete));
+ DeviceId deviceId = handler().data().deviceId();
+ try {
+ set(handler(), vbs);
+ } catch (IOException e) {
+ log.error("Error writing ports table for device {} exception {}", deviceId, e);
+ return false;
+ }
+ // TODO: check for errors
+ return true;
+ }
+
+ @Override
+ public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
+ return rules.stream()
+ .filter(c -> editConnection(c, false))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
+ return rules.stream()
+ .filter(c -> editConnection(c, true))
+ .collect(Collectors.toList());
+ }
+}
diff --git a/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisOpticalUtility.java b/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisOpticalUtility.java
new file mode 100644
index 0000000..bfc0c3e
--- /dev/null
+++ b/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisOpticalUtility.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2018 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.polatis.snmp;
+
+import com.google.common.collect.Range;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.driver.HandlerBehaviour;
+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.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.UnsignedInteger32;
+import org.snmp4j.smi.Variable;
+import org.snmp4j.smi.VariableBinding;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Polatis optical utilities.
+ */
+public final class PolatisOpticalUtility {
+
+ private static final int DEFAULT_PRIORITY = 88;
+ private static final String DEFAULT_APP = "org.onosproject.drivers.polatis.snmp";
+ public static final int POWER_MULTIPLIER = 100;
+ public static final int VOA_MULTIPLIER = 100;
+ public static final Range<Long> POWER_RANGE = Range.closed(-6000L, 2800L);
+
+ private static final String PORT_ENTRY_OID = ".1.3.6.1.4.1.26592.2.2.2.1.2";
+ private static final String PORT_PATCH_OID = PORT_ENTRY_OID + ".1.2";
+
+ private PolatisOpticalUtility() {
+ }
+
+ /**
+ * Transforms a flow FlowRule object to a variable binding.
+ * @param rule FlowRule object
+ * @param delete whether it is a delete or edit request
+ * @return variable binding
+ */
+ public static VariableBinding fromFlowRule(FlowRule rule, boolean delete) {
+ Set<Criterion> criterions = rule.selector().criteria();
+ PortNumber inPort = criterions.stream()
+ .filter(c -> c instanceof PortCriterion)
+ .map(c -> ((PortCriterion) c).port())
+ .findAny()
+ .orElse(null);
+ long input = inPort.toLong();
+ List<Instruction> instructions = rule.treatment().immediate();
+ PortNumber outPort = instructions.stream()
+ .filter(c -> c instanceof Instructions.OutputInstruction)
+ .map(c -> ((Instructions.OutputInstruction) c).port())
+ .findAny()
+ .orElse(null);
+ long output = outPort.toLong();
+ OID oid = new OID(PORT_PATCH_OID + "." + input);
+ Variable var = new UnsignedInteger32(delete ? 0 : output);
+ return new VariableBinding(oid, var);
+ }
+
+ /**
+ * Finds the FlowRule from flow rule store by the given ports and channel.
+ * Returns an extra flow to remove the flow by ONOS if not found.
+ * @param behaviour the parent driver handler
+ * @param inPort the input port
+ * @param outPort the output port
+ * @return the flow rule
+ */
+ public static FlowRule toFlowRule(HandlerBehaviour behaviour, PortNumber inPort,
+ PortNumber outPort) {
+ FlowRuleService service = behaviour.handler().get(FlowRuleService.class);
+ Iterable<FlowEntry> entries = service.getFlowEntries(behaviour.data().deviceId());
+ // Try to Find the flow from flow rule store.
+ for (FlowEntry entry : entries) {
+ Set<Criterion> criterions = entry.selector().criteria();
+ // input port
+ PortNumber ip = criterions.stream()
+ .filter(c -> c instanceof PortCriterion)
+ .map(c -> ((PortCriterion) c).port())
+ .findAny()
+ .orElse(null);
+ // output port
+ PortNumber op = entry.treatment().immediate().stream()
+ .filter(c -> c instanceof Instructions.OutputInstruction)
+ .map(c -> ((Instructions.OutputInstruction) c).port())
+ .findAny()
+ .orElse(null);
+ if (inPort.equals(ip) && outPort.equals(op)) {
+ // Find the flow.
+ return entry;
+ }
+ }
+ // Cannot find the flow from store. So report an extra flow to remove the flow by ONOS.
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(inPort)
+ .build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(outPort)
+ .build();
+ return DefaultFlowRule.builder()
+ .forDevice(behaviour.data().deviceId())
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .makePermanent()
+ .withPriority(DEFAULT_PRIORITY)
+ .fromApp(behaviour.handler().get(CoreService.class).getAppId(DEFAULT_APP))
+ .build();
+
+ }
+}
diff --git a/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisSnmpUtility.java b/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisSnmpUtility.java
index 654294f..50bc254 100644
--- a/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisSnmpUtility.java
+++ b/drivers/polatis/snmp/src/main/java/org/onosproject/drivers/polatis/snmp/PolatisSnmpUtility.java
@@ -29,10 +29,18 @@
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.VariableBinding;
+import org.snmp4j.util.DefaultPDUFactory;
+import org.snmp4j.util.TableEvent;
+import org.snmp4j.util.TableUtils;
+
+import org.slf4j.Logger;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
/**
* SNP utility for Polatis SNMP drivers.
@@ -41,6 +49,8 @@
private static final int MAX_SIZE_RESPONSE_PDU = 65535;
+ private static final Logger log = getLogger(PolatisSnmpUtility.class);
+
private PolatisSnmpUtility() {
}
@@ -93,12 +103,42 @@
* @throws IOException if unable to retrieve the object value
*/
public static String getOid(DriverHandler handler, String oid) throws IOException {
- PDU pdu = new PDU();
- pdu.add(new VariableBinding(new OID(oid)));
- pdu.setType(PDU.GET);
+ List<VariableBinding> vbs = new ArrayList<>();
+ vbs.add(new VariableBinding(new OID(oid)));
+ PDU pdu = new PDU(PDU.GET, vbs);
Snmp session = getSession(handler);
CommunityTarget target = getTarget(handler);
ResponseEvent event = session.send(pdu, target);
return event.getResponse().get(0).getVariable().toString();
}
+
+ /**
+ * Retrieves a table.
+ *
+ * @param handler parent driver handler
+ * @param columnOIDs column oid object identifiers
+ * @return the table
+ * @throws IOException if unable to retrieve the object value
+ */
+ public static List<TableEvent> getTable(DriverHandler handler, OID[] columnOIDs) throws IOException {
+ Snmp session = getSession(handler);
+ CommunityTarget target = getTarget(handler);
+ TableUtils tableUtils = new TableUtils(session, new DefaultPDUFactory());
+ return tableUtils.getTable(target, columnOIDs, null, null);
+ }
+
+ /**
+ * Sends a synchronous SET request to the supplied target.
+ *
+ * @param handler parent driver handler
+ * @param vbs a list of variable bindings
+ * @return the response event
+ * @throws IOException if unable to set the target
+ */
+ public static ResponseEvent set(DriverHandler handler, List<? extends VariableBinding> vbs) throws IOException {
+ Snmp session = getSession(handler);
+ CommunityTarget target = getTarget(handler);
+ PDU pdu = new PDU(PDU.SET, vbs);
+ return session.set(pdu, target);
+ }
}
diff --git a/drivers/polatis/snmp/src/main/resources/polatis-snmp-drivers.xml b/drivers/polatis/snmp/src/main/resources/polatis-snmp-drivers.xml
index 43e4063..cf089f3 100644
--- a/drivers/polatis/snmp/src/main/resources/polatis-snmp-drivers.xml
+++ b/drivers/polatis/snmp/src/main/resources/polatis-snmp-drivers.xml
@@ -21,6 +21,8 @@
impl="org.onosproject.net.optical.DefaultOpticalDevice"/>
<behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
impl="org.onosproject.drivers.polatis.snmp.PolatisDeviceDescription"/>
+ <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
+ impl="org.onosproject.drivers.polatis.snmp.PolatisFlowRuleProgrammable"/>
<property name="uiType">policon</property>
</driver>
</drivers>