aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/drivers
diff options
context:
space:
mode:
authorAshlee Young <ashlee@wildernessvoice.com>2015-12-01 05:49:27 -0800
committerAshlee Young <ashlee@wildernessvoice.com>2015-12-01 05:49:27 -0800
commite63291850fd0795c5700e25e67e5dee89ba54c5f (patch)
tree9707289536ad95bb739c9856761ad43275e07d8c /framework/src/onos/drivers
parent671823e12bc13be9a8b87a5d7de33da1bb7a44e8 (diff)
onos commit hash c2999f30c69e50df905a9d175ef80b3f23a98514
Change-Id: I2bb8562c4942b6d6a6d60b663db2e17540477b81 Signed-off-by: Ashlee Young <ashlee@wildernessvoice.com>
Diffstat (limited to 'framework/src/onos/drivers')
-rw-r--r--framework/src/onos/drivers/features.xml1
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraExtensionTreatmentInterpreter.java (renamed from framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraExtensionInterpreter.java)48
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraResubmit.java107
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraSetNshSpi.java104
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraSetTunnelDst.java10
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitch13.java11
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OfOpticalSwitchImplLinc13.java95
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/netconf/NetconfControllerConfig.java89
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/netconf/XmlConfigParser.java126
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/netconf/package-info.java20
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbControllerConfig.java3
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbTunnelConfig.java4
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA2Pipeline.java89
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java818
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java238
-rw-r--r--framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java328
-rw-r--r--framework/src/onos/drivers/src/main/resources/onos-drivers.xml13
-rw-r--r--framework/src/onos/drivers/src/main/resources/org/onosproject/driver/netconf/controllers.xml36
-rw-r--r--framework/src/onos/drivers/src/test/java/org/onosproject/driver/netconf/XmlConfigParserTest.java80
-rw-r--r--framework/src/onos/drivers/src/test/resources/org/onosproject/driver/netconf/testConfig.xml60
20 files changed, 1902 insertions, 378 deletions
diff --git a/framework/src/onos/drivers/features.xml b/framework/src/onos/drivers/features.xml
index e83f93fa..cb2ca0b4 100644
--- a/framework/src/onos/drivers/features.xml
+++ b/framework/src/onos/drivers/features.xml
@@ -15,7 +15,6 @@
~ limitations under the License.
-->
<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
- <repository>mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features</repository>
<feature name="${project.artifactId}" version="${project.version}"
description="${project.description}">
<feature>onos-api</feature>
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraExtensionInterpreter.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraExtensionTreatmentInterpreter.java
index b8f1fd91..a7f70f98 100644
--- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraExtensionInterpreter.java
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraExtensionTreatmentInterpreter.java
@@ -17,11 +17,11 @@
package org.onosproject.driver.extensions;
import org.onlab.packet.Ip4Address;
-import org.onosproject.net.behaviour.ExtensionResolver;
+import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
-import org.onosproject.net.flow.instructions.ExtensionInstruction;
-import org.onosproject.net.flow.instructions.ExtensionType;
-import org.onosproject.openflow.controller.ExtensionInterpreter;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+import org.onosproject.openflow.controller.ExtensionTreatmentInterpreter;
import org.projectfloodlight.openflow.protocol.OFActionType;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.action.OFAction;
@@ -33,36 +33,45 @@ import org.projectfloodlight.openflow.types.IPv4Address;
/**
* Interpreter for Nicira OpenFlow extensions.
*/
-public class NiciraExtensionInterpreter extends AbstractHandlerBehaviour
- implements ExtensionInterpreter, ExtensionResolver {
+public class NiciraExtensionTreatmentInterpreter extends AbstractHandlerBehaviour
+ implements ExtensionTreatmentInterpreter, ExtensionTreatmentResolver {
@Override
- public boolean supported(ExtensionType extensionType) {
- if (extensionType.equals(ExtensionType.ExtensionTypes.NICIRA_SET_TUNNEL_DST.type())) {
+ public boolean supported(ExtensionTreatmentType extensionTreatmentType) {
+ if (extensionTreatmentType.equals(
+ ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST.type())) {
return true;
}
- if (extensionType.equals(ExtensionType.ExtensionTypes.NICIRA_RESUBMIT.type())) {
+ if (extensionTreatmentType.equals(
+ ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT.type())) {
+ return true;
+ }
+ if (extensionTreatmentType.equals(
+ ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SPI.type())) {
return true;
}
return false;
}
@Override
- public OFAction mapInstruction(OFFactory factory, ExtensionInstruction extensionInstruction) {
- ExtensionType type = extensionInstruction.type();
- if (type.equals(ExtensionType.ExtensionTypes.NICIRA_SET_TUNNEL_DST.type())) {
- NiciraSetTunnelDst tunnelDst = (NiciraSetTunnelDst) extensionInstruction;
+ public OFAction mapInstruction(OFFactory factory, ExtensionTreatment extensionTreatment) {
+ ExtensionTreatmentType type = extensionTreatment.type();
+ if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST.type())) {
+ NiciraSetTunnelDst tunnelDst = (NiciraSetTunnelDst) extensionTreatment;
return factory.actions().setField(factory.oxms().tunnelIpv4Dst(
IPv4Address.of(tunnelDst.tunnelDst().toInt())));
}
- if (type.equals(ExtensionType.ExtensionTypes.NICIRA_RESUBMIT.type())) {
+ if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT.type())) {
// TODO this will be implemented later
}
+ if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SPI.type())) {
+ // TODO this will be implemented later
+ }
return null;
}
@Override
- public ExtensionInstruction mapAction(OFAction action) {
+ public ExtensionTreatment mapAction(OFAction action) {
if (action.getType().equals(OFActionType.SET_FIELD)) {
OFActionSetField setFieldAction = (OFActionSetField) action;
OFOxm<?> oxm = setFieldAction.getField();
@@ -79,13 +88,16 @@ public class NiciraExtensionInterpreter extends AbstractHandlerBehaviour
}
@Override
- public ExtensionInstruction getExtensionInstruction(ExtensionType type) {
- if (type.equals(ExtensionType.ExtensionTypes.NICIRA_SET_TUNNEL_DST.type())) {
+ public ExtensionTreatment getExtensionInstruction(ExtensionTreatmentType type) {
+ if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST.type())) {
return new NiciraSetTunnelDst();
}
- if (type.equals(ExtensionType.ExtensionTypes.NICIRA_RESUBMIT.type())) {
+ if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT.type())) {
return new NiciraResubmit();
}
+ if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SPI.type())) {
+ return new NiciraSetNshSpi();
+ }
throw new UnsupportedOperationException(
"Driver does not support extension type " + type.toString());
}
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraResubmit.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraResubmit.java
new file mode 100644
index 00000000..481b6f9c
--- /dev/null
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraResubmit.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2015 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.driver.extensions;
+
+import com.google.common.base.MoreObjects;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.instructions.AbstractExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+import org.onosproject.store.serializers.PortNumberSerializer;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Nicira resubmit extension instruction.
+ */
+public class NiciraResubmit extends AbstractExtensionTreatment {
+
+ private PortNumber inPort;
+
+ private final KryoNamespace appKryo = new KryoNamespace.Builder()
+ .register(new PortNumberSerializer(), PortNumber.class)
+ .register(byte[].class)
+ .build();
+
+ /**
+ * Creates a new resubmit instruction.
+ */
+ NiciraResubmit() {
+ inPort = null;
+ }
+
+ /**
+ * Creates a new resubmit instruction with a particular inPort.
+ *
+ * @param inPort in port number
+ */
+ public NiciraResubmit(PortNumber inPort) {
+ checkNotNull(inPort);
+ this.inPort = inPort;
+ }
+
+ /**
+ * Gets the inPort.
+ *
+ * @return inPort
+ */
+ public PortNumber inPort() {
+ return inPort;
+ }
+
+ @Override
+ public ExtensionTreatmentType type() {
+ return ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT.type();
+ }
+
+ @Override
+ public void deserialize(byte[] data) {
+ inPort = appKryo.deserialize(data);
+ }
+
+ @Override
+ public byte[] serialize() {
+ return appKryo.serialize(inPort);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(inPort);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof NiciraResubmit) {
+ NiciraResubmit that = (NiciraResubmit) obj;
+ return Objects.equals(inPort, that.inPort);
+
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("inPort", inPort)
+ .toString();
+ }
+}
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraSetNshSpi.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraSetNshSpi.java
new file mode 100644
index 00000000..25358702
--- /dev/null
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraSetNshSpi.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015 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.driver.extensions;
+
+import java.util.Objects;
+
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.flow.instructions.AbstractExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+import org.onosproject.store.serializers.Ip4AddressSerializer;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Nicira set NSH SPI extension instruction.
+ */
+public class NiciraSetNshSpi extends AbstractExtensionTreatment {
+
+ private int nshSpi;
+
+ private final KryoNamespace appKryo = new KryoNamespace.Builder()
+ .register(new Ip4AddressSerializer(), Integer.class)
+ .register(byte[].class)
+ .build();
+
+ /**
+ * Creates a new set nsh spi instruction.
+ */
+ NiciraSetNshSpi() {
+ nshSpi = 0;
+ }
+
+ /**
+ * Creates a new set nsh spi instruction with given spi.
+ *
+ * @param nshSpi nsh service path index
+ */
+ NiciraSetNshSpi(int nshSpi) {
+ this.nshSpi = nshSpi;
+ }
+
+ /**
+ * Gets the nsh service path index.
+ *
+ * @return nsh service path index
+ */
+ public int nshSpi() {
+ return nshSpi;
+ }
+
+ @Override
+ public ExtensionTreatmentType type() {
+ return ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SPI.type();
+ }
+
+ @Override
+ public void deserialize(byte[] data) {
+ nshSpi = appKryo.deserialize(data);
+ }
+
+ @Override
+ public byte[] serialize() {
+ return appKryo.serialize(nshSpi);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(nshSpi);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof NiciraSetNshSpi) {
+ NiciraSetNshSpi that = (NiciraSetNshSpi) obj;
+ return Objects.equals(nshSpi, that.nshSpi);
+
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("nshSpi", nshSpi)
+ .toString();
+ }
+}
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraSetTunnelDst.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraSetTunnelDst.java
index a20b2479..ec23a9e0 100644
--- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraSetTunnelDst.java
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/extensions/NiciraSetTunnelDst.java
@@ -19,8 +19,8 @@ package org.onosproject.driver.extensions;
import com.google.common.base.MoreObjects;
import org.onlab.packet.Ip4Address;
import org.onlab.util.KryoNamespace;
-import org.onosproject.net.flow.instructions.AbstractExtensionInstruction;
-import org.onosproject.net.flow.instructions.ExtensionType;
+import org.onosproject.net.flow.instructions.AbstractExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
import org.onosproject.store.serializers.Ip4AddressSerializer;
import java.util.Objects;
@@ -30,7 +30,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
/**
* Nicira set tunnel destination extension instruction.
*/
-public class NiciraSetTunnelDst extends AbstractExtensionInstruction {
+public class NiciraSetTunnelDst extends AbstractExtensionTreatment {
private Ip4Address tunnelDst;
@@ -67,8 +67,8 @@ public class NiciraSetTunnelDst extends AbstractExtensionInstruction {
}
@Override
- public ExtensionType type() {
- return ExtensionType.ExtensionTypes.NICIRA_SET_TUNNEL_DST.type();
+ public ExtensionTreatmentType type() {
+ return ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST.type();
}
@Override
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitch13.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitch13.java
index a62b93c8..5c6ce360 100644
--- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitch13.java
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitch13.java
@@ -144,7 +144,16 @@ public class OFOpticalSwitch13 extends AbstractOpenFlowSwitch implements OpenFlo
@Override
public Device.Type deviceType() {
- return Device.Type.ROADM;
+ String hwDesc = hardwareDescription();
+ switch (hwDesc) {
+ case "Optical-ROADM":
+ return Device.Type.ROADM;
+ case "Optical-OTN":
+ return Device.Type.OTN;
+ default:
+ log.error("Unsupported hardwareDescription {}", hwDesc);
+ return Device.Type.OTHER;
+ }
}
/*
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OfOpticalSwitchImplLinc13.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OfOpticalSwitchImplLinc13.java
index ff65e0c6..f91e2a7e 100644
--- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OfOpticalSwitchImplLinc13.java
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OfOpticalSwitchImplLinc13.java
@@ -15,8 +15,8 @@
*/
package org.onosproject.driver.handshaker;
-import org.onosproject.net.Device;
import com.google.common.collect.ImmutableSet;
+import org.onosproject.net.Device;
import org.onosproject.openflow.controller.OpenFlowOpticalSwitch;
import org.onosproject.openflow.controller.PortDescPropertyType;
import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
@@ -26,6 +26,8 @@ import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotSta
import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply;
import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest;
+import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFObject;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
@@ -34,11 +36,19 @@ import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFPortOptical;
import org.projectfloodlight.openflow.protocol.OFStatsReply;
import org.projectfloodlight.openflow.protocol.OFStatsType;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetField;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmExpOchSigId;
+import org.projectfloodlight.openflow.types.CircuitSignalID;
import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.U8;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -52,6 +62,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
* LINC sends the tap ports (OCh for our purposes) in the regular port desc stats reply,
* while it sends *all* ports (both tap and WDM ports, i.e., OCh and OMS) in the experimenter port desc stats reply.
*
+ * As LINC implements custom OF optical extensions (in contrast to the final standard as specified in
+ * ONF TS-022 (March 15, 2015), we need to rewrite flow stat requests and flow mods in {@link #sendMsg(OFMessage)}.
+ *
*/
public class OfOpticalSwitchImplLinc13
extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch {
@@ -160,6 +173,86 @@ public class OfOpticalSwitchImplLinc13
return Collections.EMPTY_LIST;
}
+ /**
+ * Rewrite match object to use LINC OF optical extensions.
+ *
+ * @param match original match
+ * @return rewritten match
+ */
+ private Match rewriteMatch(Match match) {
+ Match.Builder mBuilder = factory().buildMatch();
+ for (MatchField mf : match.getMatchFields()) {
+ if (mf == MatchField.EXP_OCH_SIG_ID) {
+ mBuilder.setExact(MatchField.OCH_SIGID, (CircuitSignalID) match.get(mf));
+ continue;
+ }
+ if (mf == MatchField.EXP_OCH_SIGTYPE) {
+ mBuilder.setExact(MatchField.OCH_SIGTYPE, (U8) match.get(mf));
+ continue;
+ }
+ mBuilder.setExact(mf, match.get(mf));
+ }
+
+ return mBuilder.build();
+ }
+
+ /**
+ * Rewrite actions to use LINC OF optical extensions.
+ *
+ * @param actions original actions
+ * @return rewritten actions
+ */
+ private List<OFAction> rewriteActions(List<OFAction> actions) {
+ List<OFAction> newActions = new LinkedList<>();
+
+ for (OFAction action : actions) {
+ if (!(action instanceof OFActionSetField)) {
+ newActions.add(action);
+ continue;
+ }
+
+ OFActionSetField sf = (OFActionSetField) action;
+ if (!(sf instanceof OFOxmExpOchSigId)) {
+ newActions.add(action);
+ }
+
+ OFOxmExpOchSigId oxm = (OFOxmExpOchSigId) sf.getField();
+ CircuitSignalID signalId = oxm.getValue();
+
+ newActions.add(
+ factory().actions().circuit(factory().oxms().ochSigid(signalId)));
+ }
+
+ return newActions;
+ }
+
+ @Override
+ public void sendMsg(OFMessage msg) {
+ // Ignore everything but flow mods and stat requests
+ if (!(msg instanceof OFFlowMod || msg instanceof OFFlowStatsRequest)) {
+ super.sendMsg(msg);
+ return;
+ }
+
+ Match newMatch;
+ OFMessage newMsg = null;
+
+ if (msg instanceof OFFlowStatsRequest) {
+ // Rewrite match only
+ OFFlowStatsRequest fsr = (OFFlowStatsRequest) msg;
+ newMatch = rewriteMatch(fsr.getMatch());
+ newMsg = fsr.createBuilder().setMatch(newMatch).build();
+ } else if (msg instanceof OFFlowMod) {
+ // Rewrite match and actions
+ OFFlowMod fm = (OFFlowMod) msg;
+ newMatch = rewriteMatch(fm.getMatch());
+ List<OFAction> actions = rewriteActions(fm.getActions());
+
+ newMsg = fm.createBuilder().setMatch(newMatch).setActions(actions).build();
+ }
+
+ super.sendMsg(newMsg);
+ }
@Override
public Boolean supportNxRole() {
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/netconf/NetconfControllerConfig.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/netconf/NetconfControllerConfig.java
new file mode 100644
index 00000000..84043b5b
--- /dev/null
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/netconf/NetconfControllerConfig.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2015 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.driver.netconf;
+
+import com.google.common.base.Preconditions;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.ControllerConfig;
+import org.onosproject.net.behaviour.ControllerInfo;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfDevice;
+import org.slf4j.Logger;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of controller config which allows to get and set controllers
+ * through the Netconf protocol.
+ */
+public class NetconfControllerConfig extends AbstractHandlerBehaviour
+ implements ControllerConfig {
+
+ private final Logger log = getLogger(NetconfControllerConfig.class);
+
+ @Override
+ public List<ControllerInfo> getControllers() {
+ DriverHandler handler = handler();
+ NetconfController controller = handler.get(NetconfController.class);
+ DeviceId ofDeviceId = handler.data().deviceId();
+ Preconditions.checkNotNull(controller, "Netconf controller is null");
+ List<ControllerInfo> controllers = new ArrayList<>();
+ controllers.addAll(XmlConfigParser.parseStreamControllers(XmlConfigParser.
+ loadXml(new ByteArrayInputStream(controller.
+ getDevicesMap().get(ofDeviceId).getSession().
+ getConfig("running").getBytes(StandardCharsets.UTF_8)))));
+ return controllers;
+ }
+
+ @Override
+ public void setControllers(List<ControllerInfo> controllers) {
+ DriverHandler handler = handler();
+ NetconfController controller = handler.get(NetconfController.class);
+ DeviceId deviceId = handler.data().deviceId();
+ Preconditions.checkNotNull(controller, "Netconf controller is null");
+ try {
+ NetconfDevice device = controller.getNetconfDevice(deviceId);
+ log.warn("provider map {}", controller.getDevicesMap());
+ String config = XmlConfigParser.createControllersConfig(
+ XmlConfigParser.loadXml(getClass().getResourceAsStream("controllers.xml")),
+ XmlConfigParser.loadXml(
+ new ByteArrayInputStream(device.getSession()
+ .getConfig("running")
+ .getBytes(
+ StandardCharsets.UTF_8))),
+ "running", "merge", "create", controllers
+ );
+ device.getSession().editConfig(config.substring(config.indexOf("-->") + 3));
+ } catch (NullPointerException e) {
+ log.warn("No NETCONF device with requested parameters " + e);
+ throw new NullPointerException("No NETCONF device with requested parameters " + e);
+ }
+
+ }
+
+ //TODO maybe put method getNetconfClientService like in ovsdb if we need it
+
+}
+
+
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/netconf/XmlConfigParser.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/netconf/XmlConfigParser.java
new file mode 100644
index 00000000..55826a25
--- /dev/null
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/netconf/XmlConfigParser.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2015 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.driver.netconf;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.commons.configuration.tree.ConfigurationNode;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.behaviour.ControllerInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parser for Netconf XML configurations and replys.
+ */
+final class XmlConfigParser {
+ public static final Logger log = LoggerFactory
+ .getLogger(XmlConfigParser.class);
+
+ private XmlConfigParser() {
+ //not called, preventing any allocation
+ }
+
+
+ protected static HierarchicalConfiguration loadXml(InputStream xmlStream) {
+ XMLConfiguration cfg = new XMLConfiguration();
+ try {
+ cfg.load(xmlStream);
+ return cfg;
+ } catch (ConfigurationException e) {
+ throw new IllegalArgumentException("Cannot load xml from Stream", e);
+ }
+ }
+
+ protected static List<ControllerInfo> parseStreamControllers(HierarchicalConfiguration cfg) {
+ List<ControllerInfo> controllers = new ArrayList<>();
+ List<HierarchicalConfiguration> fields =
+ cfg.configurationsAt("data.capable-switch." +
+ "logical-switches." +
+ "switch.controllers.controller");
+ for (HierarchicalConfiguration sub : fields) {
+ controllers.add(new ControllerInfo(
+ IpAddress.valueOf(sub.getString("ip-address")),
+ Integer.parseInt(sub.getString("port")),
+ sub.getString("protocol")));
+ }
+ return controllers;
+ }
+
+ protected static String parseSwitchId(HierarchicalConfiguration cfg) {
+ HierarchicalConfiguration field =
+ cfg.configurationAt("data.capable-switch." +
+ "logical-switches." +
+ "switch");
+ return field.getProperty("id").toString();
+ }
+
+ protected static String parseCapableSwitchId(HierarchicalConfiguration cfg) {
+ HierarchicalConfiguration field =
+ cfg.configurationAt("data.capable-switch");
+ return field.getProperty("id").toString();
+ }
+
+ protected static String createControllersConfig(HierarchicalConfiguration cfg,
+ HierarchicalConfiguration actualCfg,
+ String target, String netconfOperation,
+ String controllerOperation,
+ List<ControllerInfo> controllers) {
+ //cfg.getKeys().forEachRemaining(key -> System.out.println(key));
+ cfg.setProperty("edit-config.target", target);
+ cfg.setProperty("edit-config.default-operation", netconfOperation);
+ cfg.setProperty("edit-config.config.capable-switch.id",
+ parseCapableSwitchId(actualCfg));
+ cfg.setProperty("edit-config.config.capable-switch." +
+ "logical-switches.switch.id", parseSwitchId(actualCfg));
+ List<ConfigurationNode> newControllers = new ArrayList<>();
+ for (ControllerInfo ci : controllers) {
+ XMLConfiguration controller = new XMLConfiguration();
+ controller.setRoot(new HierarchicalConfiguration.Node("controller"));
+ String id = ci.type() + ":" + ci.ip() + ":" + ci.port();
+ controller.setProperty("id", id);
+ controller.setProperty("ip-address", ci.ip());
+ controller.setProperty("port", ci.port());
+ controller.setProperty("protocol", ci.type());
+ newControllers.add(controller.getRootNode());
+ }
+ cfg.addNodes("edit-config.config.capable-switch.logical-switches." +
+ "switch.controllers", newControllers);
+ XMLConfiguration editcfg = (XMLConfiguration) cfg;
+ StringWriter stringWriter = new StringWriter();
+ try {
+ editcfg.save(stringWriter);
+ } catch (ConfigurationException e) {
+ e.printStackTrace();
+ }
+ String s = stringWriter.toString()
+ .replaceAll("<controller>",
+ "<controller nc:operation=\"" + controllerOperation + "\">");
+ s = s.replace("<target>" + target + "</target>",
+ "<target><" + target + "/></target>");
+ return s;
+
+ }
+
+ //TODO implement mor methods for parsing configuration when you need them
+}
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/netconf/package-info.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/netconf/package-info.java
new file mode 100644
index 00000000..200312b4
--- /dev/null
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/netconf/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/**
+ * Implementations of the Netconf driver behaviours.
+ */
+package org.onosproject.driver.netconf; \ No newline at end of file
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbControllerConfig.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbControllerConfig.java
index a00d3dbc..f116ab84 100644
--- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbControllerConfig.java
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbControllerConfig.java
@@ -16,6 +16,7 @@
package org.onosproject.driver.ovsdb;
+import com.google.common.collect.ImmutableSet;
import org.onlab.packet.IpAddress;
import org.onlab.packet.TpPort;
import org.onosproject.net.AnnotationKeys;
@@ -56,7 +57,7 @@ public class OvsdbControllerConfig extends AbstractHandlerBehaviour implements C
DriverHandler handler = handler();
OvsdbClientService clientService = getOvsdbClientService(handler);
if (!clientService.getControllers(handler().data().deviceId())
- .equals(controllers)) {
+ .equals(ImmutableSet.copyOf(controllers))) {
clientService.setControllersWithDeviceId(handler().
data().deviceId(), controllers);
}
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbTunnelConfig.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbTunnelConfig.java
index a32553ad..ad90ca44 100644
--- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbTunnelConfig.java
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbTunnelConfig.java
@@ -68,10 +68,10 @@ public class OvsdbTunnelConfig extends AbstractHandlerBehaviour
public boolean createTunnelInterface(BridgeName bridgeName, TunnelDescription tunnel) {
Map<String, String> options = ((DefaultAnnotations) tunnel.annotations()).asMap();
if (tunnel.src() != null) {
- options.put(OPTION_LOCAL_IP, tunnel.src().toString());
+ options.put(OPTION_LOCAL_IP, ((IpTunnelEndPoint) tunnel.src()).ip().toString());
}
if (tunnel.dst() != null) {
- options.put(OPTION_REMOTE_IP, tunnel.dst().toString());
+ options.put(OPTION_REMOTE_IP, ((IpTunnelEndPoint) tunnel.dst()).ip().toString());
}
DriverHandler handler = handler();
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA2Pipeline.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA2Pipeline.java
index 0cb30d28..937c9ac8 100644
--- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA2Pipeline.java
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA2Pipeline.java
@@ -18,15 +18,19 @@ package org.onosproject.driver.pipeline;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Deque;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import org.onlab.packet.Ethernet;
import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.NextGroup;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -35,8 +39,18 @@ import org.onosproject.net.flow.FlowRuleOperations;
import org.onosproject.net.flow.FlowRuleOperationsContext;
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.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.MplsBosCriterion;
+import org.onosproject.net.flow.criteria.MplsCriterion;
import org.onosproject.net.flow.criteria.PortCriterion;
import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupKey;
import org.slf4j.Logger;
@@ -108,6 +122,81 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
return rules;
}
+ @Override
+ protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
+ TrafficSelector selector = fwd.selector();
+ EthTypeCriterion ethType =
+ (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
+ if ((ethType == null) ||
+ (ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
+ (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST)) {
+ log.warn("processSpecific: Unsupported "
+ + "forwarding objective criteraia");
+ fail(fwd, ObjectiveError.UNSUPPORTED);
+ return Collections.emptySet();
+ }
+
+ int forTableId = -1;
+ TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
+ if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
+ filteredSelector.matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(((IPCriterion)
+ selector.getCriterion(Criterion.Type.IPV4_DST)).ip());
+ forTableId = UNICAST_ROUTING_TABLE;
+ log.debug("processing IPv4 specific forwarding objective {} hash{} in dev:{}",
+ fwd.id(), fwd.hashCode(), deviceId);
+ } else {
+ filteredSelector
+ .matchEthType(Ethernet.MPLS_UNICAST)
+ .matchMplsLabel(((MplsCriterion)
+ selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
+ MplsBosCriterion bos = (MplsBosCriterion) selector
+ .getCriterion(Criterion.Type.MPLS_BOS);
+ if (bos != null) {
+ filteredSelector.matchMplsBos(bos.mplsBos());
+ }
+ forTableId = MPLS_TABLE_1;
+ log.debug("processing MPLS specific forwarding objective {} hash:{} in dev {}",
+ fwd.id(), fwd.hashCode(), deviceId);
+ }
+
+ TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
+ if (fwd.treatment() != null) {
+ for (Instruction i : fwd.treatment().allInstructions()) {
+ tb.add(i);
+ }
+ }
+
+ if (fwd.nextId() != null) {
+ NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
+ List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
+ // we only need the top level group's key to point the flow to it
+ Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
+ if (group == null) {
+ log.warn("The group left!");
+ fail(fwd, ObjectiveError.GROUPMISSING);
+ return Collections.emptySet();
+ }
+ tb.deferred().group(group.id());
+ }
+ tb.transition(ACL_TABLE);
+ FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+ .fromApp(fwd.appId())
+ .withPriority(fwd.priority())
+ .forDevice(deviceId)
+ .withSelector(filteredSelector.build())
+ .withTreatment(tb.build())
+ .forTable(forTableId);
+
+ if (fwd.permanent()) {
+ ruleBuilder.makePermanent();
+ } else {
+ ruleBuilder.makeTemporary(fwd.timeout());
+ }
+
+ return Collections.singletonList(ruleBuilder.build());
+ }
+
@Override
protected void initializePipeline() {
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java
index cf3c7e89..863caebb 100644
--- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java
@@ -19,9 +19,11 @@ import static org.onlab.util.Tools.groupedThreads;
import static org.slf4j.LoggerFactory.getLogger;
import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -65,15 +67,20 @@ import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criteria;
import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.Criterion.Type;
import org.onosproject.net.flow.criteria.EthCriterion;
import org.onosproject.net.flow.criteria.EthTypeCriterion;
import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.MplsBosCriterion;
+import org.onosproject.net.flow.criteria.MplsCriterion;
import org.onosproject.net.flow.criteria.PortCriterion;
import org.onosproject.net.flow.criteria.VlanIdCriterion;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType;
import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
import org.onosproject.net.flowobjective.FilteringObjective;
import org.onosproject.net.flowobjective.FlowObjectiveStore;
@@ -128,50 +135,43 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
protected static final int LOWEST_PRIORITY = 0x0;
/*
- * Group keys are normally generated by using the next Objective id. In the
- * case of a next objective resulting in a group chain, each group derives a
- * group key from the next objective id in the following way:
- * The upper 4 bits of the group-key are used to denote the position of the
- * group in the group chain. For example, in the chain
- * group0 --> group1 --> group2 --> port
- * group0's group key would have the upper 4 bits as 0, group1's upper four
- * bits would be 1, and so on
- */
- private static final int GROUP0MASK = 0x0;
- private static final int GROUP1MASK = 0x10000000;
-
- /*
* OFDPA requires group-id's to have a certain form.
* L2 Interface Groups have <4bits-0><12bits-vlanid><16bits-portid>
* L3 Unicast Groups have <4bits-2><28bits-index>
+ * MPLS Interface Groups have <4bits-9><4bits:0><24bits-index>
+ * L3 ECMP Groups have <4bits-7><28bits-index>
+ * L2 Flood Groups have <4bits-4><12bits-vlanid><16bits-index>
+ * L3 VPN Groups have <4bits-9><4bits-2><24bits-index>
*/
private static final int L2INTERFACEMASK = 0x0;
private static final int L3UNICASTMASK = 0x20000000;
- //private static final int MPLSINTERFACEMASK = 0x90000000;
+ private static final int MPLSINTERFACEMASK = 0x90000000;
private static final int L3ECMPMASK = 0x70000000;
private static final int L2FLOODMASK = 0x40000000;
+ private static final int L3VPNMASK = 0x92000000;
private final Logger log = getLogger(getClass());
private ServiceDirectory serviceDirectory;
protected FlowRuleService flowRuleService;
private CoreService coreService;
- private GroupService groupService;
- private FlowObjectiveStore flowObjectiveStore;
+ protected GroupService groupService;
+ protected FlowObjectiveStore flowObjectiveStore;
protected DeviceId deviceId;
protected ApplicationId driverId;
protected PacketService packetService;
protected DeviceService deviceService;
private InternalPacketProcessor processor = new InternalPacketProcessor();
- private KryoNamespace appKryo = new KryoNamespace.Builder()
+ protected KryoNamespace appKryo = new KryoNamespace.Builder()
.register(KryoNamespaces.API)
.register(GroupKey.class)
.register(DefaultGroupKey.class)
- .register(OfdpaGroupChain.class)
+ .register(OfdpaNextGroup.class)
.register(byte[].class)
+ .register(ArrayDeque.class)
.build();
- private Cache<GroupKey, OfdpaGroupChain> pendingNextObjectives;
- private ConcurrentHashMap<GroupKey, GroupChainElem> pendingGroups;
+ private Cache<GroupKey, OfdpaNextGroup> pendingNextObjectives;
+ private ConcurrentHashMap<GroupKey, Set<GroupChainElem>> pendingGroups;
private ScheduledExecutorService groupChecker =
Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner",
@@ -184,6 +184,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
Map<VlanId, Set<PortNumber>> vlan2Port = new ConcurrentHashMap<VlanId,
Set<PortNumber>>();
+ // index number for group creation
+ AtomicInteger l3vpnindex = new AtomicInteger(0);
@Override
@@ -193,15 +195,16 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
pendingNextObjectives = CacheBuilder.newBuilder()
.expireAfterWrite(20, TimeUnit.SECONDS)
- .removalListener((RemovalNotification<GroupKey, OfdpaGroupChain> notification) -> {
- if (notification.getCause() == RemovalCause.EXPIRED) {
- fail(notification.getValue().nextObjective(),
- ObjectiveError.GROUPINSTALLATIONFAILED);
- }
+ .removalListener((
+ RemovalNotification<GroupKey, OfdpaNextGroup> notification) -> {
+ if (notification.getCause() == RemovalCause.EXPIRED) {
+ fail(notification.getValue().nextObjective(),
+ ObjectiveError.GROUPINSTALLATIONFAILED);
+ }
}).build();
groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500, TimeUnit.MILLISECONDS);
- pendingGroups = new ConcurrentHashMap<GroupKey, GroupChainElem>();
+ pendingGroups = new ConcurrentHashMap<GroupKey, Set<GroupChainElem>>();
coreService = serviceDirectory.get(CoreService.class);
flowRuleService = serviceDirectory.get(FlowRuleService.class);
@@ -285,22 +288,49 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
@Override
public void next(NextObjective nextObjective) {
- log.debug("Processing NextObjective id{} op{}", nextObjective.id(),
- nextObjective.op());
- if (nextObjective.op() == Objective.Operation.REMOVE) {
- if (nextObjective.next().isEmpty()) {
- removeGroup(nextObjective);
- } else {
- removeBucketFromGroup(nextObjective);
+ NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
+ switch (nextObjective.op()) {
+ case ADD:
+ if (nextGroup != null) {
+ log.warn("Cannot add next {} that already exists in device {}",
+ nextObjective.id(), deviceId);
+ return;
}
- } else if (nextObjective.op() == Objective.Operation.ADD) {
- NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
+ log.debug("Processing NextObjective id{} in dev{} - add group",
+ nextObjective.id(), deviceId);
+ addGroup(nextObjective);
+ break;
+ case ADD_TO_EXISTING:
if (nextGroup != null) {
+ log.debug("Processing NextObjective id{} in dev{} - add bucket",
+ nextObjective.id(), deviceId);
addBucketToGroup(nextObjective);
} else {
- addGroup(nextObjective);
+ // it is possible that group-chain has not been fully created yet
+ waitToAddBucketToGroup(nextObjective);
}
- } else {
+ break;
+ case REMOVE:
+ if (nextGroup == null) {
+ log.warn("Cannot remove next {} that does not exist in device {}",
+ nextObjective.id(), deviceId);
+ return;
+ }
+ log.debug("Processing NextObjective id{} in dev{} - remove group",
+ nextObjective.id(), deviceId);
+ removeGroup(nextObjective);
+ break;
+ case REMOVE_FROM_EXISTING:
+ if (nextGroup == null) {
+ log.warn("Cannot remove from next {} that does not exist in device {}",
+ nextObjective.id(), deviceId);
+ return;
+ }
+ log.debug("Processing NextObjective id{} in dev{} - remove bucket",
+ nextObjective.id(), deviceId);
+ removeBucketFromGroup(nextObjective);
+ break;
+ default:
log.warn("Unsupported operation {}", nextObjective.op());
}
}
@@ -309,7 +339,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
// Flow handling
//////////////////////////////////////
-
/**
* As per OFDPA 2.0 TTP, filtering of VLAN ids, MAC addresses (for routing)
* and IP addresses configured on switch ports happen in different tables.
@@ -520,7 +549,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
/**
* Allows routed packets with correct destination MAC to be directed
* to unicast-IP routing table or MPLS forwarding table.
- * XXX need to add rule for multicast routing.
*
* @param portCriterion port on device for which this filter is programmed
* @param ethCriterion dstMac of device for which is filter is programmed
@@ -661,38 +689,78 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
/**
* In the OF-DPA 2.0 pipeline, specific forwarding refers to the IP table
- * (unicast or multicast) or the L2 table (mac + vlan).
+ * (unicast or multicast) or the L2 table (mac + vlan) or the MPLS table.
*
* @param fwd the forwarding objective of type 'specific'
* @return a collection of flow rules. Typically there will be only one
* for this type of forwarding objective. An empty set may be
* returned if there is an issue in processing the objective.
*/
- private Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
- log.debug("Processing specific forwarding objective");
+ protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
TrafficSelector selector = fwd.selector();
EthTypeCriterion ethType =
(EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
- // XXX currently supporting only the L3 unicast table
- if (ethType == null || ethType.ethType().toShort() != Ethernet.TYPE_IPV4) {
+ if ((ethType == null) ||
+ (ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
+ (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST)) {
+ log.warn("processSpecific: Unsupported "
+ + "forwarding objective criteraia");
fail(fwd, ObjectiveError.UNSUPPORTED);
return Collections.emptySet();
}
- TrafficSelector filteredSelector =
- DefaultTrafficSelector.builder()
- .matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(
- ((IPCriterion)
- selector.getCriterion(Criterion.Type.IPV4_DST)).ip())
- .build();
+ int forTableId = -1;
+ TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
+ if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
+ filteredSelector.matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(((IPCriterion)
+ selector.getCriterion(Criterion.Type.IPV4_DST)).ip());
+ forTableId = UNICAST_ROUTING_TABLE;
+ log.debug("processing IPv4 specific forwarding objective {} in dev:{}",
+ fwd.id(), deviceId);
+ } else {
+ filteredSelector
+ .matchEthType(Ethernet.MPLS_UNICAST)
+ .matchMplsLabel(((MplsCriterion)
+ selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
+ MplsBosCriterion bos = (MplsBosCriterion) selector
+ .getCriterion(Criterion.Type.MPLS_BOS);
+ if (bos != null) {
+ filteredSelector.matchMplsBos(bos.mplsBos());
+ }
+ forTableId = MPLS_TABLE_1;
+ log.debug("processing MPLS specific forwarding objective {} in dev {}",
+ fwd.id(), deviceId);
+ }
TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
+ boolean popMpls = false;
+ if (fwd.treatment() != null) {
+ for (Instruction i : fwd.treatment().allInstructions()) {
+ tb.add(i);
+ if (i instanceof L2ModificationInstruction &&
+ ((L2ModificationInstruction) i).subtype() == L2SubType.MPLS_POP) {
+ popMpls = true;
+ }
+ }
+ }
if (fwd.nextId() != null) {
+ if (forTableId == MPLS_TABLE_1 && !popMpls) {
+ log.warn("SR CONTINUE case cannot be handled as MPLS ECMP "
+ + "is not implemented in OF-DPA yet. Aborting this flow "
+ + "in this device {}", deviceId);
+ // XXX We could convert to forwarding to a single-port, via a
+ // MPLS interface, or a MPLS SWAP (with-same) but that would
+ // have to be handled in the next-objective. Also the pop-mpls
+ // logic used here won't work in non-BoS case.
+ return Collections.emptySet();
+ }
+
NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
- List<GroupKey> gkeys = appKryo.deserialize(next.data());
- Group group = groupService.getGroup(deviceId, gkeys.get(0));
+ List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
+ // we only need the top level group's key to point the flow to it
+ Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
if (group == null) {
log.warn("The group left!");
fail(fwd, ObjectiveError.GROUPMISSING);
@@ -705,8 +773,9 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
.fromApp(fwd.appId())
.withPriority(fwd.priority())
.forDevice(deviceId)
- .withSelector(filteredSelector)
- .withTreatment(tb.build());
+ .withSelector(filteredSelector.build())
+ .withTreatment(tb.build())
+ .forTable(forTableId);
if (fwd.permanent()) {
ruleBuilder.makePermanent();
@@ -714,7 +783,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
ruleBuilder.makeTemporary(fwd.timeout());
}
- ruleBuilder.forTable(UNICAST_ROUTING_TABLE);
return Collections.singletonList(ruleBuilder.build());
}
@@ -724,7 +792,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
}
}
- private void fail(Objective obj, ObjectiveError error) {
+ protected void fail(Objective obj, ObjectiveError error) {
if (obj.context().isPresent()) {
obj.context().get().onError(obj, error);
}
@@ -765,20 +833,66 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
/**
* As per the OFDPA 2.0 TTP, packets are sent out of ports by using
- * a chain of groups, namely an L3 Unicast Group that points to an L2 Interface
- * Group which in-turn points to an output port. The Next Objective passed
+ * a chain of groups. The simple Next Objective passed
* in by the application has to be broken up into a group chain
- * to satisfy this TTP.
+ * comprising of an L3 Unicast Group that points to an L2 Interface
+ * Group which in-turn points to an output port. In some cases, the simple
+ * next Objective can just be an L2 interface without the need for chaining.
*
* @param nextObj the nextObjective of type SIMPLE
*/
private void processSimpleNextObjective(NextObjective nextObj) {
// break up simple next objective to GroupChain objects
TrafficTreatment treatment = nextObj.next().iterator().next();
+
+ GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(),
+ nextObj.appId(), false,
+ nextObj.meta());
+ if (groupInfo == null) {
+ log.error("Could not process nextObj={} in dev:{}", nextObj.id(), deviceId);
+ return;
+ }
+ // create object for local and distributed storage
+ Deque<GroupKey> gkeyChain = new ArrayDeque<>();
+ gkeyChain.addFirst(groupInfo.innerGrpDesc.appCookie());
+ gkeyChain.addFirst(groupInfo.outerGrpDesc.appCookie());
+ OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(
+ Collections.singletonList(gkeyChain),
+ nextObj);
+
+ // store l3groupkey with the ofdpaGroupChain for the nextObjective that depends on it
+ pendingNextObjectives.put(groupInfo.outerGrpDesc.appCookie(), ofdpaGrp);
+
+ // now we are ready to send the l2 groupDescription (inner), as all the stores
+ // that will get async replies have been updated. By waiting to update
+ // the stores, we prevent nasty race conditions.
+ groupService.addGroup(groupInfo.innerGrpDesc);
+ }
+
+ /**
+ * Creates one of two possible group-chains from the treatment
+ * passed in. Depending on the MPLS boolean, this method either creates
+ * an L3Unicast Group --> L2Interface Group, if mpls is false;
+ * or MPLSInterface Group --> L2Interface Group, if mpls is true;
+ * The returned 'inner' group description is always the L2 Interface group.
+ *
+ * @param treatment that needs to be broken up to create the group chain
+ * @param nextId of the next objective that needs this group chain
+ * @param appId of the application that sent this next objective
+ * @param mpls determines if L3Unicast or MPLSInterface group is created
+ * @param meta metadata passed in by the application as part of the nextObjective
+ * @return GroupInfo containing the GroupDescription of the
+ * L2Interface group(inner) and the GroupDescription of the (outer)
+ * L3Unicast/MPLSInterface group. May return null if there is an
+ * error in processing the chain
+ */
+ private GroupInfo createL2L3Chain(TrafficTreatment treatment, int nextId,
+ ApplicationId appId, boolean mpls,
+ TrafficSelector meta) {
// for the l2interface group, get vlan and port info
- // for the l3unicast group, get the src/dst mac and vlan info
- TrafficTreatment.Builder l3utt = DefaultTrafficTreatment.builder();
- TrafficTreatment.Builder l2itt = DefaultTrafficTreatment.builder();
+ // for the outer group, get the src/dst mac, and vlan info
+ TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
+ TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
VlanId vlanid = null;
long portNum = 0;
for (Instruction ins : treatment.allInstructions()) {
@@ -786,76 +900,144 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
switch (l2ins.subtype()) {
case ETH_DST:
- l3utt.setEthDst(((ModEtherInstruction) l2ins).mac());
+ outerTtb.setEthDst(((ModEtherInstruction) l2ins).mac());
break;
case ETH_SRC:
- l3utt.setEthSrc(((ModEtherInstruction) l2ins).mac());
+ outerTtb.setEthSrc(((ModEtherInstruction) l2ins).mac());
break;
case VLAN_ID:
vlanid = ((ModVlanIdInstruction) l2ins).vlanId();
- l3utt.setVlanId(vlanid);
+ outerTtb.setVlanId(vlanid);
+ break;
+ case VLAN_POP:
+ innerTtb.popVlan();
break;
case DEC_MPLS_TTL:
case MPLS_LABEL:
case MPLS_POP:
case MPLS_PUSH:
case VLAN_PCP:
- case VLAN_POP:
case VLAN_PUSH:
default:
break;
}
} else if (ins.type() == Instruction.Type.OUTPUT) {
portNum = ((OutputInstruction) ins).port().toLong();
- l2itt.add(ins);
+ innerTtb.add(ins);
} else {
log.warn("Driver does not handle this type of TrafficTreatment"
+ " instruction in nextObjectives: {}", ins.type());
}
}
+ if (vlanid == null) {
+ //use the vlanid associated with the port
+ vlanid = port2Vlan.get(PortNumber.portNumber(portNum));
+ }
+
+ if (vlanid == null) {
+ // use metadata
+ for (Criterion metaCriterion : meta.criteria()) {
+ if (metaCriterion.type() == Type.VLAN_VID) {
+ vlanid = ((VlanIdCriterion) metaCriterion).vlanId();
+ }
+ }
+ }
+
+ if (vlanid == null) {
+ log.error("Driver cannot process an L2/L3 group chain without "
+ + "egress vlan information for dev: {} port:{}",
+ deviceId, portNum);
+ return null;
+ }
+
// assemble information for ofdpa l2interface group
- int l2gk = nextObj.id() | GROUP1MASK;
- final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum;
+ // a globally unique groupkey that is different for ports in the same devices
+ // but different for the same portnumber on different devices. Also different
+ // for the various group-types created out of the same next objective.
+ int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum);
+ final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
- // assemble information for ofdpa l3unicast group
- int l3gk = nextObj.id() | GROUP0MASK;
- final GroupKey l3groupkey = new DefaultGroupKey(appKryo.serialize(l3gk));
- Integer l3groupId = L3UNICASTMASK | (int) portNum;
- l3utt.group(new DefaultGroupId(l2groupId));
- GroupChainElem gce = new GroupChainElem(l3groupkey, l3groupId,
- GroupDescription.Type.INDIRECT,
- Collections.singletonList(l3utt.build()),
- nextObj.appId(), 1);
-
- // create object for local and distributed storage
- List<GroupKey> gkeys = new ArrayList<GroupKey>();
- gkeys.add(l3groupkey); // group0 in chain
- gkeys.add(l2groupkey); // group1 in chain
- OfdpaGroupChain ofdpaGrp = new OfdpaGroupChain(gkeys, nextObj);
+ // assemble information for outer group
+ GroupDescription outerGrpDesc = null;
+ if (mpls) {
+ // outer group is MPLSInteface
+ Integer mplsgroupId = MPLSINTERFACEMASK | (int) portNum;
+ // using mplsinterfacemask in groupkey to differentiate from l2interface
+ int mplsgk = MPLSINTERFACEMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum));
+ final GroupKey mplsgroupkey = new DefaultGroupKey(appKryo.serialize(mplsgk));
+ outerTtb.group(new DefaultGroupId(l2groupId));
+ // create the mpls-interface group description to wait for the
+ // l2 interface group to be processed
+ GroupBucket mplsinterfaceGroupBucket =
+ DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
+ outerGrpDesc = new DefaultGroupDescription(
+ deviceId,
+ GroupDescription.Type.INDIRECT,
+ new GroupBuckets(Collections.singletonList(
+ mplsinterfaceGroupBucket)),
+ mplsgroupkey,
+ mplsgroupId,
+ appId);
+ log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} nextid:{}",
+ deviceId, Integer.toHexString(mplsgroupId),
+ mplsgroupkey, nextId);
+ } else {
+ // outer group is L3Unicast
+ Integer l3groupId = L3UNICASTMASK | (int) portNum;
+ int l3gk = L3UNICASTMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum));
+ final GroupKey l3groupkey = new DefaultGroupKey(appKryo.serialize(l3gk));
+ outerTtb.group(new DefaultGroupId(l2groupId));
+ // create the l3unicast group description to wait for the
+ // l2 interface group to be processed
+ GroupBucket l3unicastGroupBucket =
+ DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
+ outerGrpDesc = new DefaultGroupDescription(
+ deviceId,
+ GroupDescription.Type.INDIRECT,
+ new GroupBuckets(Collections.singletonList(
+ l3unicastGroupBucket)),
+ l3groupkey,
+ l3groupId,
+ appId);
+ log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}",
+ deviceId, Integer.toHexString(l3groupId),
+ l3groupkey, nextId);
+ }
- // store l2groupkey with the groupChainElem for the l3group that depends on it
- pendingGroups.put(l2groupkey, gce);
+ // store l2groupkey with the groupChainElem for the outer-group that depends on it
+ GroupChainElem gce = new GroupChainElem(outerGrpDesc, 1);
+ Set<GroupChainElem> gceSet = Collections.newSetFromMap(
+ new ConcurrentHashMap<GroupChainElem, Boolean>());
+ gceSet.add(gce);
+ Set<GroupChainElem> retval = pendingGroups.putIfAbsent(l2groupkey, gceSet);
+ if (retval != null) {
+ retval.add(gce);
+ }
- // store l3groupkey with the ofdpaGroupChain for the nextObjective that depends on it
- pendingNextObjectives.put(l3groupkey, ofdpaGrp);
+ // create group description for the inner l2interfacegroup
+ GroupBucket l2interfaceGroupBucket =
+ DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build());
+ GroupDescription l2groupDescription =
+ new DefaultGroupDescription(
+ deviceId,
+ GroupDescription.Type.INDIRECT,
+ new GroupBuckets(Collections.singletonList(
+ l2interfaceGroupBucket)),
+ l2groupkey,
+ l2groupId,
+ appId);
+ log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
+ deviceId, Integer.toHexString(l2groupId),
+ l2groupkey, nextId);
+ return new GroupInfo(l2groupDescription, outerGrpDesc);
- // create group description for the ofdpa l2interfacegroup and send to groupservice
- GroupBucket bucket =
- DefaultGroupBucket.createIndirectGroupBucket(l2itt.build());
- GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
- GroupDescription.Type.INDIRECT,
- new GroupBuckets(Collections.singletonList(bucket)),
- l2groupkey,
- l2groupId,
- nextObj.appId());
- groupService.addGroup(groupDescription);
}
/**
* As per the OFDPA 2.0 TTP, packets are sent out of ports by using
- * a chain of groups. The Next Objective passed in by the application
+ * a chain of groups. The broadcast Next Objective passed in by the application
* has to be broken up into a group chain comprising of an
* L2 Flood group whose buckets point to L2 Interface groups.
*
@@ -866,9 +1048,9 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
Collection<TrafficTreatment> buckets = nextObj.next();
// each treatment is converted to an L2 interface group
- int indicator = 0;
VlanId vlanid = null;
- List<GroupInfo> groupInfoCollection = new ArrayList<>();
+ List<GroupDescription> l2interfaceGroupDescs = new ArrayList<>();
+ List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
for (TrafficTreatment treatment : buckets) {
TrafficTreatment.Builder newTreatment = DefaultTrafficTreatment.builder();
PortNumber portNum = null;
@@ -907,83 +1089,284 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
}
}
- // assemble info for all l2 interface groups
- indicator += GROUP1MASK;
- int l2gk = nextObj.id() | indicator;
+ // assemble info for l2 interface group
+ int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum.toLong());
final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) |
(int) portNum.toLong();
- GroupBucket newbucket =
+ GroupBucket l2interfaceGroupBucket =
DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build());
+ GroupDescription l2interfaceGroupDescription =
+ new DefaultGroupDescription(
+ deviceId,
+ GroupDescription.Type.INDIRECT,
+ new GroupBuckets(Collections.singletonList(
+ l2interfaceGroupBucket)),
+ l2groupkey,
+ l2groupId,
+ nextObj.appId());
+ log.debug("Trying L2-Interface: device:{} gid:{} gkey:{} nextid:{}",
+ deviceId, Integer.toHexString(l2groupId),
+ l2groupkey, nextObj.id());
+
+ Deque<GroupKey> gkeyChain = new ArrayDeque<>();
+ gkeyChain.addFirst(l2groupkey);
// store the info needed to create this group
- groupInfoCollection.add(new GroupInfo(l2groupId, l2groupkey, newbucket));
+ l2interfaceGroupDescs.add(l2interfaceGroupDescription);
+ allGroupKeys.add(gkeyChain);
}
// assemble info for l2 flood group
- int l2floodgk = nextObj.id() | GROUP0MASK;
- final GroupKey l2floodgroupkey = new DefaultGroupKey(appKryo.serialize(l2floodgk));
Integer l2floodgroupId = L2FLOODMASK | (vlanid.toShort() << 16) | nextObj.id();
- // collection of treatment with groupids of l2 interface groups
- List<TrafficTreatment> floodtt = new ArrayList<>();
- for (GroupInfo gi : groupInfoCollection) {
+ int l2floodgk = L2FLOODMASK | nextObj.id() << 12;
+ final GroupKey l2floodgroupkey = new DefaultGroupKey(appKryo.serialize(l2floodgk));
+ // collection of group buckets pointing to all the l2 interface groups
+ List<GroupBucket> l2floodBuckets = new ArrayList<>();
+ for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) {
TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
- ttb.group(new DefaultGroupId(gi.groupId));
- floodtt.add(ttb.build());
+ ttb.group(new DefaultGroupId(l2intGrpDesc.givenGroupId()));
+ GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
+ l2floodBuckets.add(abucket);
}
- GroupChainElem gce = new GroupChainElem(l2floodgroupkey, l2floodgroupId,
- GroupDescription.Type.ALL,
- floodtt,
- nextObj.appId(),
- groupInfoCollection.size());
+ // create the l2flood group-description to wait for all the
+ // l2interface groups to be processed
+ GroupDescription l2floodGroupDescription =
+ new DefaultGroupDescription(
+ deviceId,
+ GroupDescription.Type.ALL,
+ new GroupBuckets(l2floodBuckets),
+ l2floodgroupkey,
+ l2floodgroupId,
+ nextObj.appId());
+ GroupChainElem gce = new GroupChainElem(l2floodGroupDescription,
+ l2interfaceGroupDescs.size());
+ log.debug("Trying L2-Flood: device:{} gid:{} gkey:{} nextid:{}",
+ deviceId, Integer.toHexString(l2floodgroupId),
+ l2floodgroupkey, nextObj.id());
// create objects for local and distributed storage
- List<GroupKey> gkeys = new ArrayList<GroupKey>();
- gkeys.add(l2floodgroupkey); // group0 in chain
- OfdpaGroupChain ofdpaGrp = new OfdpaGroupChain(gkeys, nextObj);
+ allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l2floodgroupkey));
+ OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
// store l2floodgroupkey with the ofdpaGroupChain for the nextObjective
// that depends on it
pendingNextObjectives.put(l2floodgroupkey, ofdpaGrp);
- for (GroupInfo gi : groupInfoCollection) {
+ for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) {
// store all l2groupkeys with the groupChainElem for the l2floodgroup
// that depends on it
- pendingGroups.put(gi.groupKey, gce);
+ Set<GroupChainElem> gceSet = Collections.newSetFromMap(
+ new ConcurrentHashMap<GroupChainElem, Boolean>());
+ gceSet.add(gce);
+ Set<GroupChainElem> retval = pendingGroups.putIfAbsent(
+ l2intGrpDesc.appCookie(), gceSet);
+ if (retval != null) {
+ retval.add(gce);
+ }
// create and send groups for all l2 interface groups
- GroupDescription groupDescription =
- new DefaultGroupDescription(
- deviceId,
- GroupDescription.Type.INDIRECT,
- new GroupBuckets(Collections.singletonList(gi.groupBucket)),
- gi.groupKey,
- gi.groupId,
- nextObj.appId());
- groupService.addGroup(groupDescription);
+ groupService.addGroup(l2intGrpDesc);
}
}
+ /**
+ * Utility class for moving group information around.
+ *
+ */
private class GroupInfo {
- private Integer groupId;
- private GroupKey groupKey;
- private GroupBucket groupBucket;
-
- GroupInfo(Integer groupId, GroupKey groupKey, GroupBucket groupBucket) {
- this.groupBucket = groupBucket;
- this.groupId = groupId;
- this.groupKey = groupKey;
+ private GroupDescription innerGrpDesc;
+ private GroupDescription outerGrpDesc;
+
+ GroupInfo(GroupDescription innerGrpDesc, GroupDescription outerGrpDesc) {
+ this.innerGrpDesc = innerGrpDesc;
+ this.outerGrpDesc = outerGrpDesc;
}
}
+ /**
+ * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
+ * a chain of groups. The hashed Next Objective passed in by the application
+ * has to be broken up into a group chain comprising of an
+ * L3 ECMP group as the top level group. Buckets of this group can point
+ * to a variety of groups in a group chain, depending on the whether
+ * MPLS labels are being pushed or not.
+ * <p>
+ * NOTE: We do not create MPLS ECMP groups as they are unimplemented in
+ * OF-DPA 2.0 (even though it is in the spec). Therefore we do not
+ * check the nextObjective meta.
+ *
+ * @param nextObj the nextObjective of type HASHED
+ */
private void processHashedNextObjective(NextObjective nextObj) {
- // TODO Auto-generated method stub
+ // break up hashed next objective to multiple groups
+ Collection<TrafficTreatment> buckets = nextObj.next();
+
+ // storage for all group keys in the chain of groups created
+ List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
+ List<GroupInfo> unsentGroups = new ArrayList<>();
+ for (TrafficTreatment bucket : buckets) {
+ //figure out how many labels are pushed in each bucket
+ int labelsPushed = 0;
+ MplsLabel innermostLabel = null;
+ for (Instruction ins : bucket.allInstructions()) {
+ if (ins.type() == Instruction.Type.L2MODIFICATION) {
+ L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
+ if (l2ins.subtype() == L2SubType.MPLS_PUSH) {
+ labelsPushed++;
+ }
+ if (l2ins.subtype() == L2SubType.MPLS_LABEL) {
+ if (innermostLabel == null) {
+ innermostLabel = ((ModMplsLabelInstruction) l2ins).mplsLabel();
+ }
+ }
+ }
+ }
+
+ Deque<GroupKey> gkeyChain = new ArrayDeque<>();
+ // XXX we only deal with 0 and 1 label push right now
+ if (labelsPushed == 0) {
+ GroupInfo nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
+ nextObj.appId(), false,
+ nextObj.meta());
+ if (nolabelGroupInfo == null) {
+ log.error("Could not process nextObj={} in dev:{}",
+ nextObj.id(), deviceId);
+ return;
+ }
+ gkeyChain.addFirst(nolabelGroupInfo.innerGrpDesc.appCookie());
+ gkeyChain.addFirst(nolabelGroupInfo.outerGrpDesc.appCookie());
+
+ // we can't send the inner group description yet, as we have to
+ // create the dependent ECMP group first. So we store..
+ unsentGroups.add(nolabelGroupInfo);
+
+ } else if (labelsPushed == 1) {
+ GroupInfo onelabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
+ nextObj.appId(), true,
+ nextObj.meta());
+ if (onelabelGroupInfo == null) {
+ log.error("Could not process nextObj={} in dev:{}",
+ nextObj.id(), deviceId);
+ return;
+ }
+ // we need to add another group to this chain - the L3VPN group
+ TrafficTreatment.Builder l3vpnTtb = DefaultTrafficTreatment.builder();
+ l3vpnTtb.pushMpls()
+ .setMpls(innermostLabel)
+ .setMplsBos(true)
+ .copyTtlOut()
+ .group(new DefaultGroupId(
+ onelabelGroupInfo.outerGrpDesc.givenGroupId()));
+ GroupBucket l3vpnGrpBkt =
+ DefaultGroupBucket.createIndirectGroupBucket(l3vpnTtb.build());
+ int l3vpngroupId = L3VPNMASK | l3vpnindex.incrementAndGet();
+ int l3vpngk = L3VPNMASK | nextObj.id() << 12 | l3vpnindex.get();
+ GroupKey l3vpngroupkey = new DefaultGroupKey(appKryo.serialize(l3vpngk));
+ GroupDescription l3vpnGroupDesc =
+ new DefaultGroupDescription(
+ deviceId,
+ GroupDescription.Type.INDIRECT,
+ new GroupBuckets(Collections.singletonList(
+ l3vpnGrpBkt)),
+ l3vpngroupkey,
+ l3vpngroupId,
+ nextObj.appId());
+ GroupChainElem l3vpnGce = new GroupChainElem(l3vpnGroupDesc, 1);
+ Set<GroupChainElem> gceSet = Collections.newSetFromMap(
+ new ConcurrentHashMap<GroupChainElem, Boolean>());
+ gceSet.add(l3vpnGce);
+ Set<GroupChainElem> retval = pendingGroups
+ .putIfAbsent(onelabelGroupInfo.outerGrpDesc.appCookie(), gceSet);
+ if (retval != null) {
+ retval.add(l3vpnGce);
+ }
+
+ gkeyChain.addFirst(onelabelGroupInfo.innerGrpDesc.appCookie());
+ gkeyChain.addFirst(onelabelGroupInfo.outerGrpDesc.appCookie());
+ gkeyChain.addFirst(l3vpngroupkey);
+
+ //now we can replace the outerGrpDesc with the one we just created
+ onelabelGroupInfo.outerGrpDesc = l3vpnGroupDesc;
+
+ // we can't send the innermost group yet, as we have to create
+ // the dependent ECMP group first. So we store ...
+ unsentGroups.add(onelabelGroupInfo);
+
+ log.debug("Trying L3VPN: device:{} gid:{} gkey:{} nextId:{}",
+ deviceId, Integer.toHexString(l3vpngroupId),
+ l3vpngroupkey, nextObj.id());
+
+ } else {
+ log.warn("Driver currently does not handle more than 1 MPLS "
+ + "labels. Not processing nextObjective {}", nextObj);
+ return;
+ }
+
+ // all groups in this chain
+ allGroupKeys.add(gkeyChain);
+ }
+
+ // now we can create the outermost L3 ECMP group
+ List<GroupBucket> l3ecmpGroupBuckets = new ArrayList<>();
+ for (GroupInfo gi : unsentGroups) {
+ // create ECMP bucket to point to the outer group
+ TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
+ ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId()));
+ GroupBucket sbucket = DefaultGroupBucket
+ .createSelectGroupBucket(ttb.build());
+ l3ecmpGroupBuckets.add(sbucket);
+ }
+ int l3ecmpGroupId = L3ECMPMASK | nextObj.id() << 12;
+ GroupKey l3ecmpGroupKey = new DefaultGroupKey(appKryo.serialize(l3ecmpGroupId));
+ GroupDescription l3ecmpGroupDesc =
+ new DefaultGroupDescription(
+ deviceId,
+ GroupDescription.Type.SELECT,
+ new GroupBuckets(l3ecmpGroupBuckets),
+ l3ecmpGroupKey,
+ l3ecmpGroupId,
+ nextObj.appId());
+ GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc,
+ l3ecmpGroupBuckets.size());
+
+ // create objects for local and distributed storage
+ allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l3ecmpGroupKey));
+ OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
+
+ // store l3ecmpGroupKey with the ofdpaGroupChain for the nextObjective
+ // that depends on it
+ pendingNextObjectives.put(l3ecmpGroupKey, ofdpaGrp);
+
+ log.debug("Trying L3ECMP: device:{} gid:{} gkey:{} nextId:{}",
+ deviceId, Integer.toHexString(l3ecmpGroupId),
+ l3ecmpGroupKey, nextObj.id());
+ // finally we are ready to send the innermost groups
+ for (GroupInfo gi : unsentGroups) {
+ log.debug("Sending innermost group {} in group chain on device {} ",
+ Integer.toHexString(gi.innerGrpDesc.givenGroupId()), deviceId);
+ Set<GroupChainElem> gceSet = Collections.newSetFromMap(
+ new ConcurrentHashMap<GroupChainElem, Boolean>());
+ gceSet.add(l3ecmpGce);
+ Set<GroupChainElem> retval = pendingGroups
+ .putIfAbsent(gi.outerGrpDesc.appCookie(), gceSet);
+ if (retval != null) {
+ retval.add(l3ecmpGce);
+ }
+
+ groupService.addGroup(gi.innerGrpDesc);
+ }
+
}
private void addBucketToGroup(NextObjective nextObjective) {
// TODO Auto-generated method stub
}
+ private void waitToAddBucketToGroup(NextObjective nextObjective) {
+ // TODO Auto-generated method stub
+ }
+
private void removeBucketFromGroup(NextObjective nextObjective) {
// TODO Auto-generated method stub
}
@@ -1009,45 +1392,11 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
private void processGroupChain(GroupChainElem gce) {
int waitOnGroups = gce.decrementAndGetGroupsWaitedOn();
if (waitOnGroups != 0) {
- log.debug("GCE: {} waiting on {} groups. Not processing yet",
- gce, waitOnGroups);
+ log.debug("GCE: {} not ready to be processed", gce);
return;
}
- List<GroupBucket> buckets = new ArrayList<>();
- switch (gce.groupType) {
- case INDIRECT:
- GroupBucket ibucket = DefaultGroupBucket
- .createIndirectGroupBucket(gce.bucketActions.iterator().next());
- buckets.add(ibucket);
- break;
- case ALL:
- for (TrafficTreatment tt : gce.bucketActions) {
- GroupBucket abucket = DefaultGroupBucket
- .createAllGroupBucket(tt);
- buckets.add(abucket);
- }
- break;
- case SELECT:
- for (TrafficTreatment tt : gce.bucketActions) {
- GroupBucket sbucket = DefaultGroupBucket
- .createSelectGroupBucket(tt);
- buckets.add(sbucket);
- }
- break;
- case FAILOVER:
- default:
- log.error("Unknown or unimplemented GroupChainElem {}", gce);
- }
-
- if (buckets.size() > 0) {
- GroupDescription groupDesc = new DefaultGroupDescription(
- deviceId, gce.groupType,
- new GroupBuckets(buckets),
- gce.gkey,
- gce.givenGroupId,
- gce.appId);
- groupService.addGroup(groupDesc);
- }
+ log.debug("GCE: {} ready to be processed", gce);
+ groupService.addGroup(gce.groupDescription);
}
private class GroupChecker implements Runnable {
@@ -1063,19 +1412,23 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
keys.stream().forEach(key -> {
//first check for group chain
- GroupChainElem gce = pendingGroups.remove(key);
- if (gce != null) {
- log.info("Group service processed group key {}. Processing next "
- + "group in group chain with group key {}",
- appKryo.deserialize(key.key()),
- appKryo.deserialize(gce.gkey.key()));
- processGroupChain(gce);
+ Set<GroupChainElem> gceSet = pendingGroups.remove(key);
+ if (gceSet != null) {
+ for (GroupChainElem gce : gceSet) {
+ log.info("Group service processed group key {} in device {}. "
+ + "Processing next group in group chain with group id {}",
+ key, deviceId,
+ Integer.toHexString(gce.groupDescription.givenGroupId()));
+ processGroupChain(gce);
+ }
} else {
- OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key);
- log.info("Group service processed group key {}. Done implementing "
- + "next objective: {}", appKryo.deserialize(key.key()),
- obj.nextObjective().id());
+ OfdpaNextGroup obj = pendingNextObjectives.getIfPresent(key);
if (obj != null) {
+ log.info("Group service processed group key {} in device:{}. "
+ + "Done implementing next objective: {} <<-->> gid:{}",
+ key, deviceId, obj.nextObjective().id(),
+ Integer.toHexString(groupService.getGroup(deviceId, key)
+ .givenGroupId()));
pass(obj.nextObjective());
pendingNextObjectives.invalidate(key);
flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
@@ -1088,23 +1441,27 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
private class InnerGroupListener implements GroupListener {
@Override
public void event(GroupEvent event) {
- log.debug("received group event of type {}", event.type());
+ log.trace("received group event of type {}", event.type());
if (event.type() == GroupEvent.Type.GROUP_ADDED) {
GroupKey key = event.subject().appCookie();
// first check for group chain
- GroupChainElem gce = pendingGroups.remove(key);
- if (gce != null) {
- log.info("group ADDED with group key {} .. "
- + "Processing next group in group chain with group key {}",
- appKryo.deserialize(key.key()),
- appKryo.deserialize(gce.gkey.key()));
- processGroupChain(gce);
+ Set<GroupChainElem> gceSet = pendingGroups.remove(key);
+ if (gceSet != null) {
+ for (GroupChainElem gce : gceSet) {
+ log.info("group ADDED with group key {} .. "
+ + "Processing next group in group chain with group key {}",
+ key,
+ gce.groupDescription.appCookie());
+ processGroupChain(gce);
+ }
} else {
- OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key);
+ OfdpaNextGroup obj = pendingNextObjectives.getIfPresent(key);
if (obj != null) {
- log.info("group ADDED with key {}.. Done implementing next "
- + "objective: {}",
- appKryo.deserialize(key.key()), obj.nextObjective().id());
+ log.info("group ADDED with key {} in dev {}.. Done implementing next "
+ + "objective: {} <<-->> gid:{}",
+ key, deviceId, obj.nextObjective().id(),
+ Integer.toHexString(groupService.getGroup(deviceId, key)
+ .givenGroupId()));
pass(obj.nextObjective());
pendingNextObjectives.invalidate(key);
flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
@@ -1115,30 +1472,35 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
}
/**
- * Represents a group-chain that implements a Next-Objective from
- * the application. Includes information about the next objective Id, and the
- * group keys for the groups in the group chain. The chain is expected to
- * look like group0 --> group 1 --> outPort. Information about the groups
- * themselves can be fetched from the Group Service using the group keys from
- * objects instantiating this class.
+ * Represents an entire group-chain that implements a Next-Objective from
+ * the application. The objective is represented as a list of deques, where
+ * each deque can is a separate chain of groups.
+ * <p>
+ * For example, an ECMP group with 3 buckets, where each bucket points to
+ * a group chain of L3 Unicast and L2 interface groups will look like this:
+ * <ul>
+ * <li>List[0] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
+ * <li>List[1] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
+ * <li>List[2] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
+ * </ul>
+ * where the first element of each deque is the same, representing the
+ * top level ECMP group, while every other element represents a unique groupKey.
+ * <p>
+ * Also includes information about the next objective that
+ * resulted in this group-chain.
*
- * XXX Revisit this - since the forwarding objective only ever needs the
- * groupkey of the top-level group in the group chain, why store a series
- * of groupkeys. Also the group-chain list only works for 1-to-1 chaining,
- * not for 1-to-many chaining.
*/
- private class OfdpaGroupChain implements NextGroup {
+ private class OfdpaNextGroup implements NextGroup {
private final NextObjective nextObj;
- private final List<GroupKey> gkeys;
+ private final List<Deque<GroupKey>> gkeys;
- /** expected group chain: group0 --> group1 --> port. */
- public OfdpaGroupChain(List<GroupKey> gkeys, NextObjective nextObj) {
+ public OfdpaNextGroup(List<Deque<GroupKey>> gkeys, NextObjective nextObj) {
this.gkeys = gkeys;
this.nextObj = nextObj;
}
@SuppressWarnings("unused")
- public List<GroupKey> groupKeys() {
+ public List<Deque<GroupKey>> groupKey() {
return gkeys;
}
@@ -1161,22 +1523,11 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
* preceding groups in the group chain to be created.
*/
private class GroupChainElem {
- private Collection<TrafficTreatment> bucketActions;
- private Integer givenGroupId;
- private GroupDescription.Type groupType;
- private GroupKey gkey;
- private ApplicationId appId;
+ private GroupDescription groupDescription;
private AtomicInteger waitOnGroups;
- GroupChainElem(GroupKey gkey, Integer givenGroupId,
- GroupDescription.Type groupType,
- Collection<TrafficTreatment> tr, ApplicationId appId,
- int waitOnGroups) {
- this.bucketActions = tr;
- this.givenGroupId = givenGroupId;
- this.groupType = groupType;
- this.gkey = gkey;
- this.appId = appId;
+ GroupChainElem(GroupDescription groupDescription, int waitOnGroups) {
+ this.groupDescription = groupDescription;
this.waitOnGroups = new AtomicInteger(waitOnGroups);
}
@@ -1194,7 +1545,10 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
@Override
public String toString() {
- return Integer.toHexString(givenGroupId);
+ return (Integer.toHexString(groupDescription.givenGroupId()) +
+ " groupKey: " + groupDescription.appCookie() +
+ " waiting-on-groups: " + waitOnGroups.get() +
+ " device: " + deviceId);
}
}
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
new file mode 100644
index 00000000..8a7b22b8
--- /dev/null
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2015 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.driver.pipeline;
+
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.packet.EthType;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.behaviour.PipelinerContext;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceProvider;
+import org.onosproject.net.device.DeviceProviderRegistry;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Pipeliner for OLT device.
+ */
+public class OltPipeline extends AbstractHandlerBehaviour implements Pipeliner {
+
+ private final Logger log = getLogger(getClass());
+
+ static final ProviderId PID = new ProviderId("olt", "org.onosproject.olt", true);
+
+ static final String DEVICE = "isAccess";
+ static final String OLT = "true";
+
+ private ServiceDirectory serviceDirectory;
+ private FlowRuleService flowRuleService;
+ private DeviceId deviceId;
+ private CoreService coreService;
+
+ private ApplicationId appId;
+
+ private DeviceProvider provider = new AnnotationProvider();
+
+
+ @Override
+ public void init(DeviceId deviceId, PipelinerContext context) {
+ this.serviceDirectory = context.directory();
+ this.deviceId = deviceId;
+ DeviceProviderRegistry registry =
+ serviceDirectory.get(DeviceProviderRegistry.class);
+ flowRuleService = serviceDirectory.get(FlowRuleService.class);
+ coreService = serviceDirectory.get(CoreService.class);
+
+ /*try {
+ DeviceProviderService providerService = registry.register(provider);
+ providerService.deviceConnected(deviceId,
+ description(deviceId, DEVICE, OLT));
+ } finally {
+ registry.unregister(provider);
+ }*/
+
+ appId = coreService.registerApplication(
+ "org.onosproject.driver.OLTPipeline");
+
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(EthType.EtherType.EAPOL.ethType().toShort())
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .punt()
+ .build();
+
+ FlowRule flowRule = new DefaultFlowRule(deviceId, selector, treatment,
+ PacketPriority.CONTROL.priorityValue(),
+ appId, 0, true, null);
+
+ //flowRuleService.applyFlowRules(flowRule);
+ }
+
+ @Override
+ public void filter(FilteringObjective filter) {
+ throw new UnsupportedOperationException("OLT does not filter.");
+ }
+
+ @Override
+ public void forward(ForwardingObjective fwd) {
+ FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
+
+ if (fwd.flag() != ForwardingObjective.Flag.VERSATILE) {
+ throw new UnsupportedOperationException(
+ "Only VERSATILE is supported.");
+ }
+
+ boolean isPunt = fwd.treatment().immediate().stream().anyMatch(i -> {
+ if (i instanceof Instructions.OutputInstruction) {
+ Instructions.OutputInstruction out = (Instructions.OutputInstruction) i;
+ return out.port().equals(PortNumber.CONTROLLER);
+ }
+ return false;
+ });
+
+ if (isPunt) {
+ return;
+ }
+
+ TrafficSelector selector = fwd.selector();
+ TrafficTreatment treatment = fwd.treatment();
+
+ FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .fromApp(fwd.appId())
+ .withPriority(fwd.priority());
+
+ if (fwd.permanent()) {
+ ruleBuilder.makePermanent();
+ } else {
+ ruleBuilder.makeTemporary(fwd.timeout());
+ }
+
+ switch (fwd.op()) {
+ case ADD:
+ flowBuilder.add(ruleBuilder.build());
+ break;
+ case REMOVE:
+ flowBuilder.remove(ruleBuilder.build());
+ break;
+ default:
+ log.warn("Unknown operation {}", fwd.op());
+ }
+
+ flowRuleService.apply(flowBuilder.build(new FlowRuleOperationsContext() {
+ @Override
+ public void onSuccess(FlowRuleOperations ops) {
+ if (fwd.context().isPresent()) {
+ fwd.context().get().onSuccess(fwd);
+ }
+ }
+
+ @Override
+ public void onError(FlowRuleOperations ops) {
+ if (fwd.context().isPresent()) {
+ fwd.context().get().onError(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
+ }
+ }
+ }));
+ }
+
+ @Override
+ public void next(NextObjective nextObjective) {
+ throw new UnsupportedOperationException("OLT does not next hop.");
+ }
+
+ /**
+ * Build a device description.
+ *
+ * @param deviceId a deviceId
+ * @param key the key of the annotation
+ * @param value the value for the annotation
+ * @return a device description
+ */
+ private DeviceDescription description(DeviceId deviceId, String key, String value) {
+ DeviceService deviceService = serviceDirectory.get(DeviceService.class);
+ Device device = deviceService.getDevice(deviceId);
+
+ checkNotNull(device, "Device not found in device service.");
+
+ DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
+ if (value != null) {
+ builder.set(key, value);
+ } else {
+ builder.remove(key);
+ }
+ return new DefaultDeviceDescription(device.id().uri(), device.type(),
+ device.manufacturer(), device.hwVersion(),
+ device.swVersion(), device.serialNumber(),
+ device.chassisId(), builder.build());
+ }
+
+ /**
+ * Simple ancillary provider used to annotate device.
+ */
+ private static final class AnnotationProvider
+ extends AbstractProvider implements DeviceProvider {
+ private AnnotationProvider() {
+ super(PID);
+ }
+
+ @Override
+ public void triggerProbe(DeviceId deviceId) {
+ }
+
+ @Override
+ public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
+ }
+
+ @Override
+ public boolean isReachable(DeviceId deviceId) {
+ return false;
+ }
+ }
+
+}
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
index b5541065..8ac5eec8 100644
--- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
@@ -47,6 +47,7 @@ import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criteria;
import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.Criterion.Type;
import org.onosproject.net.flow.criteria.EthCriterion;
import org.onosproject.net.flow.criteria.EthTypeCriterion;
import org.onosproject.net.flow.criteria.IPCriterion;
@@ -73,6 +74,7 @@ import org.onosproject.net.group.GroupEvent;
import org.onosproject.net.group.GroupKey;
import org.onosproject.net.group.GroupListener;
import org.onosproject.net.group.GroupService;
+import org.onosproject.store.serializers.KryoNamespaces;
import org.slf4j.Logger;
import java.util.ArrayList;
@@ -129,8 +131,13 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
groupedThreads("onos/pipeliner",
"spring-open-%d"));
protected KryoNamespace appKryo = new KryoNamespace.Builder()
- .register(GroupKey.class).register(DefaultGroupKey.class)
- .register(SegmentRoutingGroup.class).register(byte[].class).build();
+ .register(KryoNamespaces.API)
+ .register(GroupKey.class)
+ .register(DefaultGroupKey.class)
+ .register(TrafficTreatment.class)
+ .register(SpringOpenGroup.class)
+ .register(byte[].class)
+ .build();
@Override
public void init(DeviceId deviceId, PipelinerContext context) {
@@ -202,17 +209,15 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
@Override
public void onSuccess(FlowRuleOperations ops) {
pass(fwd);
- log.debug("Provisioned tables in {} with "
- + "forwarding rules for segment "
- + "router", deviceId);
+ log.debug("Provisioned tables in {} successfully with "
+ + "forwarding rules", deviceId);
}
@Override
public void onError(FlowRuleOperations ops) {
fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
log.warn("Failed to provision tables in {} with "
- + "forwarding rules for segment router",
- deviceId);
+ + "forwarding rules", deviceId);
}
}));
@@ -220,26 +225,50 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
@Override
public void next(NextObjective nextObjective) {
-
- log.debug("Processing NextObjective id{} op{}", nextObjective.id(),
- nextObjective.op());
- if (nextObjective.op() == Objective.Operation.REMOVE) {
- if (nextObjective.next().isEmpty()) {
- removeGroup(nextObjective);
- } else {
- removeBucketFromGroup(nextObjective);
+ NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
+ switch (nextObjective.op()) {
+ case ADD:
+ if (nextGroup != null) {
+ log.warn("Cannot add next {} that already exists in device {}",
+ nextObjective.id(), deviceId);
+ return;
}
- } else if (nextObjective.op() == Objective.Operation.ADD) {
- NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
+ log.debug("Processing NextObjective id{} in dev{} - add group",
+ nextObjective.id(), deviceId);
+ addGroup(nextObjective);
+ break;
+ case ADD_TO_EXISTING:
if (nextGroup != null) {
+ log.debug("Processing NextObjective id{} in dev{} - add bucket",
+ nextObjective.id(), deviceId);
addBucketToGroup(nextObjective);
} else {
- addGroup(nextObjective);
+ log.warn("Cannot add to group that does not exist");
}
- } else {
+ break;
+ case REMOVE:
+ if (nextGroup == null) {
+ log.warn("Cannot remove next {} that does not exist in device {}",
+ nextObjective.id(), deviceId);
+ return;
+ }
+ log.debug("Processing NextObjective id{} in dev{} - remove group",
+ nextObjective.id(), deviceId);
+ removeGroup(nextObjective);
+ break;
+ case REMOVE_FROM_EXISTING:
+ if (nextGroup == null) {
+ log.warn("Cannot remove from next {} that does not exist in device {}",
+ nextObjective.id(), deviceId);
+ return;
+ }
+ log.debug("Processing NextObjective id{} in dev{} - remove bucket",
+ nextObjective.id(), deviceId);
+ removeBucketFromGroup(nextObjective);
+ break;
+ default:
log.warn("Unsupported operation {}", nextObjective.op());
}
-
}
private void removeGroup(NextObjective nextObjective) {
@@ -256,7 +285,6 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
List<GroupBucket> buckets;
switch (nextObjective.type()) {
case SIMPLE:
- log.debug("processing SIMPLE next objective");
Collection<TrafficTreatment> treatments = nextObjective.next();
if (treatments.size() == 1) {
TrafficTreatment treatment = treatments.iterator().next();
@@ -273,39 +301,57 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
key,
null,
nextObjective.appId());
- log.debug("Creating SIMPLE group for next objective id {}",
- nextObjective.id());
- groupService.addGroup(groupDescription);
+ log.debug("Creating SIMPLE group for next objective id {} "
+ + "in dev:{}", nextObjective.id(), deviceId);
pendingGroups.put(key, nextObjective);
+ groupService.addGroup(groupDescription);
}
break;
case HASHED:
- log.debug("processing HASHED next objective");
- buckets = nextObjective
- .next()
- .stream()
- .map((treatment) -> DefaultGroupBucket
- .createSelectGroupBucket(treatment))
- .collect(Collectors.toList());
- if (!buckets.isEmpty()) {
- final GroupKey key = new DefaultGroupKey(
- appKryo.serialize(nextObjective
- .id()));
- GroupDescription groupDescription = new DefaultGroupDescription(
- deviceId,
- GroupDescription.Type.SELECT,
- new GroupBuckets(buckets),
- key,
- null,
- nextObjective.appId());
- log.debug("Creating HASHED group for next objective id {}",
- nextObjective.id());
- groupService.addGroup(groupDescription);
- pendingGroups.put(key, nextObjective);
+ // we convert MPLS ECMP groups to flow-actions for a single
+ // bucket(output port).
+ boolean mplsEcmp = false;
+ if (nextObjective.meta() != null) {
+ for (Criterion c : nextObjective.meta().criteria()) {
+ if (c.type() == Type.MPLS_LABEL) {
+ mplsEcmp = true;
+ }
+ }
+ }
+ if (mplsEcmp) {
+ // covert to flow-actions in a dummy group by choosing the first bucket
+ log.debug("Converting HASHED group for next objective id {} " +
+ "to flow-actions in device:{}", nextObjective.id(),
+ deviceId);
+ TrafficTreatment treatment = nextObjective.next().iterator().next();
+ flowObjectiveStore.putNextGroup(nextObjective.id(),
+ new SpringOpenGroup(null, treatment));
+ } else {
+ // process as ECMP group
+ buckets = nextObjective
+ .next()
+ .stream()
+ .map((treatment) -> DefaultGroupBucket
+ .createSelectGroupBucket(treatment))
+ .collect(Collectors.toList());
+ if (!buckets.isEmpty()) {
+ final GroupKey key = new DefaultGroupKey(
+ appKryo.serialize(nextObjective.id()));
+ GroupDescription groupDescription = new DefaultGroupDescription(
+ deviceId,
+ GroupDescription.Type.SELECT,
+ new GroupBuckets(buckets),
+ key,
+ null,
+ nextObjective.appId());
+ log.debug("Creating HASHED group for next objective id {}"
+ + " in dev:{}", nextObjective.id(), deviceId);
+ pendingGroups.put(key, nextObjective);
+ groupService.addGroup(groupDescription);
+ }
}
break;
case BROADCAST:
- log.debug("processing BROADCAST next objective");
buckets = nextObjective
.next()
.stream()
@@ -323,10 +369,10 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
key,
null,
nextObjective.appId());
- log.debug("Creating BROADCAST group for next objective id {}",
- nextObjective.id());
- groupService.addGroup(groupDescription);
+ log.debug("Creating BROADCAST group for next objective id {} "
+ + "in device {}", nextObjective.id(), deviceId);
pendingGroups.put(key, nextObjective);
+ groupService.addGroup(groupDescription);
}
break;
case FAILOVER:
@@ -417,9 +463,8 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
}
private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
- log.debug("Processing versatile forwarding objective");
+ log.debug("Processing versatile forwarding objective in dev:{}", deviceId);
TrafficSelector selector = fwd.selector();
- TrafficTreatment treatment = null;
EthTypeCriterion ethType =
(EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
if (ethType == null) {
@@ -428,50 +473,60 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
return Collections.emptySet();
}
+ if (fwd.treatment() == null && fwd.nextId() == null) {
+ log.error("VERSATILE forwarding objective needs next objective ID "
+ + "or treatment.");
+ return Collections.emptySet();
+ }
+ // emulation of ACL table (for versatile fwd objective) requires
+ // overriding any previous instructions
TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
.builder();
treatmentBuilder.wipeDeferred();
if (fwd.nextId() != null) {
NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
-
if (next != null) {
- GroupKey key = appKryo.deserialize(next.data());
-
- Group group = groupService.getGroup(deviceId, key);
-
- if (group == null) {
- log.warn("The group left!");
- fail(fwd, ObjectiveError.GROUPMISSING);
- return Collections.emptySet();
+ SpringOpenGroup soGroup = appKryo.deserialize(next.data());
+ if (soGroup.dummy) {
+ // need to convert to flow-actions
+ for (Instruction ins : soGroup.treatment.allInstructions()) {
+ treatmentBuilder.add(ins);
+ }
+ } else {
+ GroupKey key = soGroup.key;
+ Group group = groupService.getGroup(deviceId, key);
+ if (group == null) {
+ log.warn("The group left!");
+ fail(fwd, ObjectiveError.GROUPMISSING);
+ return Collections.emptySet();
+ }
+ treatmentBuilder.deferred().group(group.id());
+ log.debug("Adding OUTGROUP action");
}
- treatmentBuilder.deferred().group(group.id());
- treatment = treatmentBuilder.build();
- log.debug("Adding OUTGROUP action");
}
- } else if (fwd.treatment() != null) {
+ }
+
+ if (fwd.treatment() != null) {
if (fwd.treatment().allInstructions().size() == 1 &&
fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
if (o.port() == PortNumber.CONTROLLER) {
treatmentBuilder.punt();
- treatment = treatmentBuilder.build();
} else {
- treatment = fwd.treatment();
+ treatmentBuilder.add(o);
}
} else {
- treatment = fwd.treatment();
+ for (Instruction ins : fwd.treatment().allInstructions()) {
+ treatmentBuilder.add(ins);
+ }
}
- } else {
- log.warn("VERSATILE forwarding objective needs next objective ID "
- + "or treatment.");
- return Collections.emptySet();
}
FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
.fromApp(fwd.appId()).withPriority(fwd.priority())
.forDevice(deviceId).withSelector(fwd.selector())
- .withTreatment(treatment);
+ .withTreatment(treatmentBuilder.build());
if (fwd.permanent()) {
ruleBuilder.makePermanent();
@@ -508,7 +563,8 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
}
protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
- log.debug("Processing specific");
+ log.debug("Processing specific fwd objective:{} in dev:{} with next:{}",
+ fwd.id(), deviceId, fwd.nextId());
boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
boolean isEthDstObj = isSupportedEthDstObjective(fwd);
@@ -518,7 +574,7 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
return processEthDstSpecificObjective(fwd);
} else {
log.warn("processSpecific: Unsupported "
- + "forwarding objective criteraia");
+ + "forwarding objective criteria");
fail(fwd, ObjectiveError.UNSUPPORTED);
return Collections.emptySet();
}
@@ -540,7 +596,8 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
.getCriterion(Criterion.Type.IPV4_DST))
.ip());
forTableId = ipv4UnicastTableId;
- log.debug("processing IPv4 specific forwarding objective");
+ log.debug("processing IPv4 specific forwarding objective:{} in dev:{}",
+ fwd.id(), deviceId);
} else {
filteredSelectorBuilder = filteredSelectorBuilder
.matchEthType(Ethernet.MPLS_UNICAST)
@@ -550,7 +607,8 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
//if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) {
//}
forTableId = mplsTableId;
- log.debug("processing MPLS specific forwarding objective");
+ log.debug("processing MPLS specific forwarding objective:{} in dev:{}",
+ fwd.id(), deviceId);
}
TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
@@ -561,24 +619,28 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
}
}
- //TODO: Analyze the forwarding objective here to make
- //device specific decision such as no ECMP groups in Dell
- //switches.
if (fwd.nextId() != null) {
NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
-
if (next != null) {
- GroupKey key = appKryo.deserialize(next.data());
-
- Group group = groupService.getGroup(deviceId, key);
-
- if (group == null) {
- log.warn("The group left!");
- fail(fwd, ObjectiveError.GROUPMISSING);
- return Collections.emptySet();
+ SpringOpenGroup soGroup = appKryo.deserialize(next.data());
+ if (soGroup.dummy) {
+ log.debug("Adding flow-actions for fwd. obj. {} "
+ + "in dev: {}", fwd.id(), deviceId);
+ for (Instruction ins : soGroup.treatment.allInstructions()) {
+ treatmentBuilder.add(ins);
+ }
+ } else {
+ GroupKey key = soGroup.key;
+ Group group = groupService.getGroup(deviceId, key);
+ if (group == null) {
+ log.warn("The group left!");
+ fail(fwd, ObjectiveError.GROUPMISSING);
+ return Collections.emptySet();
+ }
+ treatmentBuilder.deferred().group(group.id());
+ log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
+ + "in dev: {}", group.id(), fwd.id(), deviceId);
}
- treatmentBuilder.deferred().group(group.id());
- log.debug("Adding OUTGROUP action");
} else {
log.warn("processSpecific: No associated next objective object");
fail(fwd, ObjectiveError.GROUPMISSING);
@@ -621,6 +683,12 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
// Do not match MacAddress for subnet broadcast entry
if (!ethCriterion.mac().equals(MacAddress.NONE)) {
filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
+ log.debug("processing L2 forwarding objective:{} in dev:{}",
+ fwd.id(), deviceId);
+ } else {
+ log.debug("processing L2 Broadcast forwarding objective:{} "
+ + "in dev:{} for vlan:{}",
+ fwd.id(), deviceId, vlanIdCriterion.vlanId());
}
filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
TrafficSelector filteredSelector = filteredSelectorBuilder.build();
@@ -635,14 +703,24 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
if (fwd.nextId() != null) {
NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
if (next != null) {
- GroupKey key = appKryo.deserialize(next.data());
- Group group = groupService.getGroup(deviceId, key);
- if (group != null) {
- treatmentBuilder.deferred().group(group.id());
+ SpringOpenGroup soGrp = appKryo.deserialize(next.data());
+ if (soGrp.dummy) {
+ log.debug("Adding flow-actions for fwd. obj. {} "
+ + "in dev: {}", fwd.id(), deviceId);
+ for (Instruction ins : soGrp.treatment.allInstructions()) {
+ treatmentBuilder.add(ins);
+ }
} else {
- log.warn("Group Missing");
- fail(fwd, ObjectiveError.GROUPMISSING);
- return Collections.emptySet();
+ GroupKey key = soGrp.key;
+ Group group = groupService.getGroup(deviceId, key);
+ if (group == null) {
+ log.warn("The group left!");
+ fail(fwd, ObjectiveError.GROUPMISSING);
+ return Collections.emptySet();
+ }
+ treatmentBuilder.deferred().group(group.id());
+ log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
+ + "in dev: {}", group.id(), fwd.id(), deviceId);
}
}
}
@@ -869,14 +947,14 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
public void onSuccess(FlowRuleOperations ops) {
pass(filt);
log.debug("Provisioned tables in {} with fitering "
- + "rules for segment router", deviceId);
+ + "rules", deviceId);
}
@Override
public void onError(FlowRuleOperations ops) {
fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
log.warn("Failed to provision tables in {} with "
- + "fitering rules for segment router", deviceId);
+ + "fitering rules", deviceId);
}
}));
}
@@ -934,15 +1012,17 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
@Override
public void event(GroupEvent event) {
if (event.type() == GroupEvent.Type.GROUP_ADDED) {
- log.debug("InnerGroupListener: Group ADDED "
+ log.trace("InnerGroupListener: Group ADDED "
+ "event received in device {}", deviceId);
GroupKey key = event.subject().appCookie();
NextObjective obj = pendingGroups.getIfPresent(key);
if (obj != null) {
+ log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
+ deviceId, event.subject().id(), obj.id());
flowObjectiveStore
.putNextGroup(obj.id(),
- new SegmentRoutingGroup(key));
+ new SpringOpenGroup(key, null));
pass(obj);
pendingGroups.invalidate(key);
}
@@ -971,21 +1051,47 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
if (obj == null) {
return;
}
+ log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
+ deviceId,
+ groupService.getGroup(deviceId, key).id(),
+ obj.id());
pass(obj);
pendingGroups.invalidate(key);
- flowObjectiveStore.putNextGroup(obj.id(),
- new SegmentRoutingGroup(
- key));
- });
+ flowObjectiveStore.putNextGroup(
+ obj.id(),
+ new SpringOpenGroup(key, null));
+ });
}
}
- private class SegmentRoutingGroup implements NextGroup {
-
+ /**
+ * SpringOpenGroup can either serve as storage for a GroupKey which can be
+ * used to fetch the group from the Group Service, or it can be serve as storage
+ * for Traffic Treatments which can be used as flow actions. In the latter
+ * case, we refer to this as a dummy group.
+ *
+ */
+ private class SpringOpenGroup implements NextGroup {
+ private final boolean dummy;
private final GroupKey key;
+ private final TrafficTreatment treatment;
- public SegmentRoutingGroup(GroupKey key) {
- this.key = key;
+ /**
+ * Storage for a GroupKey or a TrafficTreatment. One of the params
+ * to this constructor must be null.
+ * @param key represents a GroupKey
+ * @param treatment represents flow actions in a dummy group
+ */
+ public SpringOpenGroup(GroupKey key, TrafficTreatment treatment) {
+ if (key == null) {
+ this.key = new DefaultGroupKey(new byte[]{0});
+ this.treatment = treatment;
+ this.dummy = true;
+ } else {
+ this.key = key;
+ this.treatment = DefaultTrafficTreatment.builder().build();
+ this.dummy = false;
+ }
}
@SuppressWarnings("unused")
@@ -995,7 +1101,7 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
@Override
public byte[] data() {
- return appKryo.serialize(key);
+ return appKryo.serialize(this);
}
}
diff --git a/framework/src/onos/drivers/src/main/resources/onos-drivers.xml b/framework/src/onos/drivers/src/main/resources/onos-drivers.xml
index 0349e1c2..a172c18b 100644
--- a/framework/src/onos/drivers/src/main/resources/onos-drivers.xml
+++ b/framework/src/onos/drivers/src/main/resources/onos-drivers.xml
@@ -32,13 +32,14 @@
impl="org.onosproject.driver.handshaker.NiciraSwitchHandshaker"/>
<behaviour api="org.onosproject.net.behaviour.ControllerConfig"
impl="org.onosproject.driver.ovsdb.OvsdbControllerConfig"/>
- <behaviour api="org.onosproject.openflow.controller.ExtensionInterpreter"
- impl="org.onosproject.driver.extensions.NiciraExtensionInterpreter" />
- <behaviour api="org.onosproject.net.behaviour.ExtensionResolver"
- impl="org.onosproject.driver.extensions.NiciraExtensionInterpreter" />
+ <behaviour api="org.onosproject.openflow.controller.ExtensionTreatmentInterpreter"
+ impl="org.onosproject.driver.extensions.NiciraExtensionTreatmentInterpreter" />
+ <behaviour api="org.onosproject.net.behaviour.ExtensionTreatmentResolver"
+ impl="org.onosproject.driver.extensions.NiciraExtensionTreatmentInterpreter" />
</driver>
+ <!--This driver is for simulated NETCONF devices through of-config tool on top og OVSDB-->
<driver name="ovs-netconf" extends="default"
- manufacturer="Nicira, Inc\." hwVersion="Open vSwitch" swVersion="2\..*">
+ manufacturer="" hwVersion="" swVersion="">
<behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver"
impl="org.onosproject.driver.handshaker.NiciraSwitchHandshaker"/>
<behaviour api="org.onosproject.net.behaviour.ControllerConfig"
@@ -137,7 +138,7 @@
impl="org.onosproject.driver.pipeline.OpenVSwitchPipeline"/>
</driver>
<driver name="eci" extends="default"
- manufacturer="ECI Telecom" hwVersion="optical" swVersion="V_1_0">
+ manufacturer="ECI Telecom" hwVersion="Optical.*" swVersion="V_1_0">
<behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver"
impl="org.onosproject.driver.handshaker.OFOpticalSwitch13"/>
</driver>
diff --git a/framework/src/onos/drivers/src/main/resources/org/onosproject/driver/netconf/controllers.xml b/framework/src/onos/drivers/src/main/resources/org/onosproject/driver/netconf/controllers.xml
new file mode 100644
index 00000000..21e4dd14
--- /dev/null
+++ b/framework/src/onos/drivers/src/main/resources/org/onosproject/driver/netconf/controllers.xml
@@ -0,0 +1,36 @@
+<!--
+ ~ Copyright 2015 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.
+ -->
+
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ </target>
+ <default-operation>
+ </default-operation>
+ <config>
+ <capable-switch xmlns="urn:onf:config:yang">
+ <id></id>
+ <logical-switches>
+ <switch>
+ <id></id>
+ <controllers>
+ </controllers>
+ </switch>
+ </logical-switches>
+ </capable-switch>
+ </config>
+ </edit-config>
+</rpc> \ No newline at end of file
diff --git a/framework/src/onos/drivers/src/test/java/org/onosproject/driver/netconf/XmlConfigParserTest.java b/framework/src/onos/drivers/src/test/java/org/onosproject/driver/netconf/XmlConfigParserTest.java
new file mode 100644
index 00000000..d8b695c3
--- /dev/null
+++ b/framework/src/onos/drivers/src/test/java/org/onosproject/driver/netconf/XmlConfigParserTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2015 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.driver.netconf;
+
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.behaviour.ControllerInfo;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.driver.netconf.XmlConfigParser.*;
+
+//import static org.junit.Assert.*;
+
+/**
+ * Test the XML document Parsing for netconf configuration.
+ */
+public class XmlConfigParserTest {
+
+
+ @Test
+ public void basics() throws IOException {
+ InputStream stream = getClass().getResourceAsStream("testConfig.xml");
+ List<ControllerInfo> controllers = parseStreamControllers(loadXml(stream));
+ assertTrue(controllers.get(0).equals(new ControllerInfo(
+ IpAddress.valueOf("10.128.12.1"), 6653, "tcp")));
+ assertTrue(controllers.get(1).equals(new ControllerInfo(
+ IpAddress.valueOf("10.128.12.2"), 6654, "tcp")));
+
+ }
+
+ @Test
+ public void switchId() {
+ InputStream stream = getClass().getResourceAsStream("testConfig.xml");
+ String switchId = parseSwitchId(loadXml(stream));
+ assertTrue(switchId.equals("ofc-bridge"));
+ }
+
+ @Test
+ public void capableSwitchId() {
+ InputStream stream = getClass().getResourceAsStream("testConfig.xml");
+ String capableSwitchId = parseCapableSwitchId(loadXml(stream));
+ assertTrue(capableSwitchId.equals("openvswitch"));
+ }
+
+ @Test
+ public void controllersConfig() {
+ InputStream streamOrig = getClass().getResourceAsStream("testConfig.xml");
+ InputStream streamCFG = XmlConfigParser.class
+ .getResourceAsStream("controllers.xml");
+ String config = createControllersConfig(loadXml(streamCFG),
+ loadXml(streamOrig), "running", "merge",
+ "create", new ArrayList<>(Arrays.asList(
+ new ControllerInfo(IpAddress.valueOf("192.168.1.1"),
+ 5000, "tcp"))));
+ assertTrue(config.contains("192.168.1.1"));
+ assertTrue(config.contains("tcp"));
+ assertTrue(config.contains("5000"));
+
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/drivers/src/test/resources/org/onosproject/driver/netconf/testConfig.xml b/framework/src/onos/drivers/src/test/resources/org/onosproject/driver/netconf/testConfig.xml
new file mode 100644
index 00000000..88d1a3a3
--- /dev/null
+++ b/framework/src/onos/drivers/src/test/resources/org/onosproject/driver/netconf/testConfig.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 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.
+ -->
+
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="7">
+ <data>
+ <capable-switch xmlns="urn:onf:config:yang">
+ <id>openvswitch</id>
+ <resources>
+ <port>
+ <name>ofc-bridge</name>
+ <requested-number>666</requested-number>
+ <configuration>
+ <admin-state>down</admin-state>
+ <no-receive>false</no-receive>
+ <no-forward>false</no-forward>
+ <no-packet-in>false</no-packet-in>
+ </configuration>
+ </port>
+ </resources>
+ <logical-switches>
+ <switch>
+ <id>ofc-bridge</id>
+ <datapath-id>00:01:02:03:04:05:06:07</datapath-id>
+ <lost-connection-behavior>failSecureMode</lost-connection-behavior>
+ <controllers>
+ <controller>
+ <id>(null)</id>
+ <ip-address>10.128.12.1</ip-address>
+ <port>6653</port>
+ <protocol>tcp</protocol>
+ </controller>
+ <controller>
+ <id>(null)</id>
+ <ip-address>10.128.12.2</ip-address>
+ <port>6654</port>
+ <protocol>tcp</protocol>
+ </controller>
+ </controllers>
+ <resources>
+ <port>ofc-bridge</port>
+ </resources>
+ </switch>
+ </logical-switches>
+ </capable-switch>
+ </data>
+</rpc-reply> \ No newline at end of file