From 13d05bc8458758ee39cb829098241e89616717ee Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Wed, 9 Sep 2015 22:15:21 -0700 Subject: ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60 Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd --- .../src/onos/providers/openflow/group/pom.xml | 34 ++ .../of/group/impl/GroupBucketEntryBuilder.java | 343 ++++++++++++++++++ .../provider/of/group/impl/GroupModBuilder.java | 376 +++++++++++++++++++ .../of/group/impl/GroupStatsCollector.java | 111 ++++++ .../of/group/impl/OpenFlowGroupProvider.java | 366 +++++++++++++++++++ .../provider/of/group/impl/package-info.java | 20 ++ .../of/group/impl/OpenFlowGroupProviderTest.java | 397 +++++++++++++++++++++ 7 files changed, 1647 insertions(+) create mode 100644 framework/src/onos/providers/openflow/group/pom.xml create mode 100644 framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java create mode 100644 framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java create mode 100644 framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java create mode 100644 framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java create mode 100644 framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/package-info.java create mode 100644 framework/src/onos/providers/openflow/group/src/test/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProviderTest.java (limited to 'framework/src/onos/providers/openflow/group') diff --git a/framework/src/onos/providers/openflow/group/pom.xml b/framework/src/onos/providers/openflow/group/pom.xml new file mode 100644 index 00000000..97ac5ffe --- /dev/null +++ b/framework/src/onos/providers/openflow/group/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + + org.onosproject + onos-of-providers + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-of-provider-group + bundle + + ONOS OpenFlow protocol group provider + + \ No newline at end of file diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java new file mode 100644 index 00000000..b9de7c0f --- /dev/null +++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java @@ -0,0 +1,343 @@ +/* + * 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.provider.of.group.impl; + +import com.google.common.collect.Lists; + +import org.onlab.packet.Ip4Address; +import org.onlab.packet.MacAddress; +import org.onlab.packet.MplsLabel; +import org.onlab.packet.VlanId; +import org.onosproject.core.DefaultGroupId; +import org.onosproject.core.GroupId; +import org.onosproject.net.Lambda; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.group.DefaultGroupBucket; +import org.onosproject.net.group.GroupBucket; +import org.onosproject.net.group.GroupBuckets; +import org.projectfloodlight.openflow.protocol.OFBucket; +import org.projectfloodlight.openflow.protocol.OFGroupType; +import org.projectfloodlight.openflow.protocol.action.OFAction; +import org.projectfloodlight.openflow.protocol.action.OFActionCircuit; +import org.projectfloodlight.openflow.protocol.action.OFActionCopyTtlIn; +import org.projectfloodlight.openflow.protocol.action.OFActionCopyTtlOut; +import org.projectfloodlight.openflow.protocol.action.OFActionDecMplsTtl; +import org.projectfloodlight.openflow.protocol.action.OFActionDecNwTtl; +import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter; +import org.projectfloodlight.openflow.protocol.action.OFActionGroup; +import org.projectfloodlight.openflow.protocol.action.OFActionOutput; +import org.projectfloodlight.openflow.protocol.action.OFActionPopMpls; +import org.projectfloodlight.openflow.protocol.action.OFActionPushMpls; +import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst; +import org.projectfloodlight.openflow.protocol.action.OFActionSetDlSrc; +import org.projectfloodlight.openflow.protocol.action.OFActionSetField; +import org.projectfloodlight.openflow.protocol.action.OFActionSetNwDst; +import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc; +import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp; +import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid; +import org.projectfloodlight.openflow.protocol.oxm.OFOxm; +import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.OFVlanVidMatch; +import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.U8; +import org.projectfloodlight.openflow.types.VlanPcp; +import org.slf4j.Logger; + +import java.util.List; + +import static org.slf4j.LoggerFactory.getLogger; + +/* + * Builder for GroupBucketEntry. + */ +public class GroupBucketEntryBuilder { + + private List ofBuckets; + private OFGroupType type; + + private final Logger log = getLogger(getClass()); + + /** + * Creates a builder. + * + * @param ofBuckets list of OFBucket + * @param type Group type + */ + public GroupBucketEntryBuilder(List ofBuckets, OFGroupType type) { + this.ofBuckets = ofBuckets; + this.type = type; + } + + /** + * Builds a GroupBuckets. + * + * @return GroupBuckets object, a list of GroupBuckets + */ + public GroupBuckets build() { + List bucketList = Lists.newArrayList(); + + for (OFBucket bucket: ofBuckets) { + TrafficTreatment treatment = buildTreatment(bucket.getActions()); + // TODO: Use GroupBucketEntry + GroupBucket groupBucket = null; + switch (type) { + case INDIRECT: + groupBucket = + DefaultGroupBucket.createIndirectGroupBucket(treatment); + break; + case SELECT: + groupBucket = + DefaultGroupBucket.createSelectGroupBucket(treatment); + break; + case FF: + PortNumber port = + PortNumber.portNumber(bucket.getWatchPort().getPortNumber()); + GroupId groupId = + new DefaultGroupId(bucket.getWatchGroup().getGroupNumber()); + groupBucket = + DefaultGroupBucket.createFailoverGroupBucket(treatment, + port, groupId); + break; + default: + log.error("Unsupported Group type : {}", type); + } + if (groupBucket != null) { + bucketList.add(groupBucket); + } + } + return new GroupBuckets(bucketList); + } + + + private TrafficTreatment buildTreatment(List actions) { + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); + // If this is a drop rule + if (actions.size() == 0) { + builder.drop(); + return builder.build(); + } + for (OFAction act : actions) { + switch (act.getType()) { + case OUTPUT: + OFActionOutput out = (OFActionOutput) act; + builder.setOutput( + PortNumber.portNumber(out.getPort().getPortNumber())); + break; + case SET_VLAN_VID: + OFActionSetVlanVid vlan = (OFActionSetVlanVid) act; + builder.setVlanId(VlanId.vlanId(vlan.getVlanVid().getVlan())); + break; + case SET_VLAN_PCP: + OFActionSetVlanPcp pcp = (OFActionSetVlanPcp) act; + builder.setVlanPcp(pcp.getVlanPcp().getValue()); + break; + case POP_VLAN: + builder.popVlan(); + break; + case PUSH_VLAN: + builder.pushVlan(); + break; + case SET_DL_DST: + OFActionSetDlDst dldst = (OFActionSetDlDst) act; + builder.setEthDst( + MacAddress.valueOf(dldst.getDlAddr().getLong())); + break; + case SET_DL_SRC: + OFActionSetDlSrc dlsrc = (OFActionSetDlSrc) act; + builder.setEthSrc( + MacAddress.valueOf(dlsrc.getDlAddr().getLong())); + + break; + case SET_NW_DST: + OFActionSetNwDst nwdst = (OFActionSetNwDst) act; + IPv4Address di = nwdst.getNwAddr(); + builder.setIpDst(Ip4Address.valueOf(di.getInt())); + break; + case SET_NW_SRC: + OFActionSetNwSrc nwsrc = (OFActionSetNwSrc) act; + IPv4Address si = nwsrc.getNwAddr(); + builder.setIpSrc(Ip4Address.valueOf(si.getInt())); + break; + case EXPERIMENTER: + OFActionExperimenter exp = (OFActionExperimenter) act; + if (exp.getExperimenter() == 0x80005A06 || + exp.getExperimenter() == 0x748771) { + OFActionCircuit ct = (OFActionCircuit) exp; + short lambda = ((OFOxmOchSigidBasic) ct.getField()).getValue().getChannelNumber(); + builder.add(Instructions.modL0Lambda(Lambda.indexedLambda(lambda))); + } else { + log.warn("Unsupported OFActionExperimenter {}", exp.getExperimenter()); + } + break; + case SET_FIELD: + OFActionSetField setField = (OFActionSetField) act; + handleSetField(builder, setField.getField()); + break; + case POP_MPLS: + OFActionPopMpls popMpls = (OFActionPopMpls) act; + builder.popMpls((short) popMpls.getEthertype().getValue()); + break; + case PUSH_MPLS: + OFActionPushMpls pushMpls = (OFActionPushMpls) act; + builder.pushMpls(); + break; + case COPY_TTL_IN: + OFActionCopyTtlIn copyTtlIn = (OFActionCopyTtlIn) act; + builder.copyTtlIn(); + break; + case COPY_TTL_OUT: + OFActionCopyTtlOut copyTtlOut = (OFActionCopyTtlOut) act; + builder.copyTtlOut(); + break; + case DEC_MPLS_TTL: + OFActionDecMplsTtl decMplsTtl = (OFActionDecMplsTtl) act; + builder.decMplsTtl(); + break; + case DEC_NW_TTL: + OFActionDecNwTtl decNwTtl = (OFActionDecNwTtl) act; + builder.decNwTtl(); + break; + case GROUP: + OFActionGroup grp = (OFActionGroup) act; + builder.group(new DefaultGroupId(grp.getGroup().getGroupNumber())); + break; + case SET_TP_DST: + case SET_TP_SRC: + case POP_PBB: + case PUSH_PBB: + case SET_MPLS_LABEL: + case SET_MPLS_TC: + case SET_MPLS_TTL: + case SET_NW_ECN: + case SET_NW_TOS: + case SET_NW_TTL: + case SET_QUEUE: + case STRIP_VLAN: + case ENQUEUE: + default: + log.warn("Action type {} not yet implemented.", act.getType()); + } + } + + return builder.build(); + } + + private void handleSetField(TrafficTreatment.Builder builder, OFOxm oxm) { + switch (oxm.getMatchField().id) { + case VLAN_PCP: + @SuppressWarnings("unchecked") + OFOxm vlanpcp = (OFOxm) oxm; + builder.setVlanPcp(vlanpcp.getValue().getValue()); + break; + case VLAN_VID: + @SuppressWarnings("unchecked") + OFOxm vlanvid = (OFOxm) oxm; + builder.setVlanId(VlanId.vlanId(vlanvid.getValue().getVlan())); + break; + case ETH_DST: + @SuppressWarnings("unchecked") + OFOxm ethdst = + (OFOxm) oxm; + builder.setEthDst(MacAddress.valueOf(ethdst.getValue().getLong())); + break; + case ETH_SRC: + @SuppressWarnings("unchecked") + OFOxm ethsrc = + (OFOxm) oxm; + builder.setEthSrc(MacAddress.valueOf(ethsrc.getValue().getLong())); + break; + case IPV4_DST: + @SuppressWarnings("unchecked") + OFOxm ip4dst = (OFOxm) oxm; + builder.setIpDst(Ip4Address.valueOf(ip4dst.getValue().getInt())); + break; + case IPV4_SRC: + @SuppressWarnings("unchecked") + OFOxm ip4src = (OFOxm) oxm; + builder.setIpSrc(Ip4Address.valueOf(ip4src.getValue().getInt())); + break; + case MPLS_LABEL: + @SuppressWarnings("unchecked") + OFOxm labelId = (OFOxm) oxm; + builder.setMpls(MplsLabel.mplsLabel((int) labelId.getValue().getValue())); + break; + case MPLS_BOS: + @SuppressWarnings("unchecked") + OFOxm mplsBos = (OFOxm) oxm; + builder.setMplsBos(mplsBos.getValue() == U8.ZERO ? false : true); + break; + case ARP_OP: + case ARP_SHA: + case ARP_SPA: + case ARP_THA: + case ARP_TPA: + case BSN_EGR_PORT_GROUP_ID: + case BSN_GLOBAL_VRF_ALLOWED: + case BSN_IN_PORTS_128: + case BSN_L3_DST_CLASS_ID: + case BSN_L3_INTERFACE_CLASS_ID: + case BSN_L3_SRC_CLASS_ID: + case BSN_LAG_ID: + case BSN_TCP_FLAGS: + case BSN_UDF0: + case BSN_UDF1: + case BSN_UDF2: + case BSN_UDF3: + case BSN_UDF4: + case BSN_UDF5: + case BSN_UDF6: + case BSN_UDF7: + case BSN_VLAN_XLATE_PORT_GROUP_ID: + case BSN_VRF: + case ETH_TYPE: + case ICMPV4_CODE: + case ICMPV4_TYPE: + case ICMPV6_CODE: + case ICMPV6_TYPE: + case IN_PHY_PORT: + case IN_PORT: + case IPV6_DST: + case IPV6_FLABEL: + case IPV6_ND_SLL: + case IPV6_ND_TARGET: + case IPV6_ND_TLL: + case IPV6_SRC: + case IP_DSCP: + case IP_ECN: + case IP_PROTO: + case METADATA: + case MPLS_TC: + case OCH_SIGID: + case OCH_SIGID_BASIC: + case OCH_SIGTYPE: + case OCH_SIGTYPE_BASIC: + case SCTP_DST: + case SCTP_SRC: + case TCP_DST: + case TCP_SRC: + case TUNNEL_ID: + case UDP_DST: + case UDP_SRC: + default: + log.warn("Set field type {} not yet implemented.", oxm.getMatchField().id); + break; + } + } +} diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java new file mode 100644 index 00000000..d5804f44 --- /dev/null +++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java @@ -0,0 +1,376 @@ +/* + * 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.provider.of.group.impl; + +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip6Address; +import org.onosproject.core.GroupId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.flow.instructions.L0ModificationInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction; +import org.onosproject.net.flow.instructions.L3ModificationInstruction; +import org.onosproject.net.group.GroupBucket; +import org.onosproject.net.group.GroupBuckets; +import org.onosproject.net.group.GroupDescription; +import org.projectfloodlight.openflow.protocol.OFBucket; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.protocol.OFGroupAdd; +import org.projectfloodlight.openflow.protocol.OFGroupDelete; +import org.projectfloodlight.openflow.protocol.OFGroupMod; +import org.projectfloodlight.openflow.protocol.OFGroupType; +import org.projectfloodlight.openflow.protocol.action.OFAction; +import org.projectfloodlight.openflow.protocol.action.OFActionGroup; +import org.projectfloodlight.openflow.protocol.action.OFActionOutput; +import org.projectfloodlight.openflow.protocol.oxm.OFOxm; +import org.projectfloodlight.openflow.types.CircuitSignalID; +import org.projectfloodlight.openflow.types.EthType; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; +import org.projectfloodlight.openflow.types.IPv6FlowLabel; +import org.projectfloodlight.openflow.types.MacAddress; +import org.projectfloodlight.openflow.types.OFBooleanValue; +import org.projectfloodlight.openflow.types.OFGroup; +import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.OFVlanVidMatch; +import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.VlanPcp; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +import static org.slf4j.LoggerFactory.getLogger; + +/* + * Builder for GroupMod. + */ +public final class GroupModBuilder { + + private GroupBuckets buckets; + private GroupId groupId; + private GroupDescription.Type type; + private OFFactory factory; + private Long xid; + + private final Logger log = getLogger(getClass()); + + private static final int OFPCML_NO_BUFFER = 0xffff; + + private GroupModBuilder(GroupBuckets buckets, GroupId groupId, + GroupDescription.Type type, OFFactory factory, + Optional xid) { + this.buckets = buckets; + this.groupId = groupId; + this.type = type; + this.factory = factory; + this.xid = xid.orElse((long) 0); + } + + /** + * Creates a builder for GroupMod. + * + * @param buckets GroupBuckets object + * @param groupId Group Id to create + * @param type Group type + * @param factory OFFactory object + * @param xid transaction ID + * @return GroupModBuilder object + */ + public static GroupModBuilder builder(GroupBuckets buckets, GroupId groupId, + GroupDescription.Type type, OFFactory factory, + Optional xid) { + + return new GroupModBuilder(buckets, groupId, type, factory, xid); + } + + /** + * Builds the GroupAdd OF message. + * + * @return GroupAdd OF message + */ + public OFGroupAdd buildGroupAdd() { + + List ofBuckets = new ArrayList(); + for (GroupBucket bucket: buckets.buckets()) { + List actions = buildActions(bucket.treatment()); + + OFBucket.Builder bucketBuilder = factory.buildBucket(); + bucketBuilder.setActions(actions); + if (type == GroupDescription.Type.SELECT) { + bucketBuilder.setWeight(1); + } + bucketBuilder.setWatchGroup(OFGroup.ANY); + bucketBuilder.setWatchPort(OFPort.ANY); + OFBucket ofBucket = bucketBuilder.build(); + ofBuckets.add(ofBucket); + } + + OFGroupAdd groupMsg = factory.buildGroupAdd() + .setGroup(OFGroup.of(groupId.id())) + .setBuckets(ofBuckets) + .setGroupType(getOFGroupType(type)) + .setXid(xid) + .build(); + + return groupMsg; + } + + /** + * Builds the GroupMod OF message. + * + * @return GroupMod OF message + */ + public OFGroupMod buildGroupMod() { + List ofBuckets = new ArrayList(); + for (GroupBucket bucket: buckets.buckets()) { + List actions = buildActions(bucket.treatment()); + + OFBucket.Builder bucketBuilder = factory.buildBucket(); + bucketBuilder.setActions(actions); + if (type == GroupDescription.Type.SELECT) { + bucketBuilder.setWeight(1); + } + bucketBuilder.setWatchGroup(OFGroup.ANY); + bucketBuilder.setWatchPort(OFPort.ANY); + OFBucket ofBucket = bucketBuilder.build(); + ofBuckets.add(ofBucket); + } + + OFGroupMod groupMsg = factory.buildGroupModify() + .setGroup(OFGroup.of(groupId.id())) + .setBuckets(ofBuckets) + .setGroupType(getOFGroupType(type)) + .setXid(xid) + .build(); + + return groupMsg; + } + + /** + * Builds the GroupDel OF message. + * + * @return GroupDel OF message + */ + public OFGroupDelete buildGroupDel() { + + OFGroupDelete groupMsg = factory.buildGroupDelete() + .setGroup(OFGroup.of(groupId.id())) + .setGroupType(OFGroupType.SELECT) + .setXid(xid) + .build(); + + return groupMsg; + } + + private List buildActions(TrafficTreatment treatment) { + if (treatment == null) { + return Collections.emptyList(); + } + + List actions = new LinkedList<>(); + for (Instruction i : treatment.allInstructions()) { + switch (i.type()) { + case DROP: + log.warn("Saw drop action; assigning drop action"); + return Collections.emptyList(); + case L0MODIFICATION: + actions.add(buildL0Modification(i)); + break; + case L2MODIFICATION: + actions.add(buildL2Modification(i)); + break; + case L3MODIFICATION: + actions.add(buildL3Modification(i)); + break; + case OUTPUT: + Instructions.OutputInstruction out = + (Instructions.OutputInstruction) i; + OFActionOutput.Builder action = factory.actions().buildOutput() + .setPort(OFPort.of((int) out.port().toLong())); + if (out.port().equals(PortNumber.CONTROLLER)) { + action.setMaxLen(OFPCML_NO_BUFFER); + } + actions.add(action.build()); + break; + case GROUP: + Instructions.GroupInstruction grp = + (Instructions.GroupInstruction) i; + OFActionGroup.Builder actgrp = factory.actions().buildGroup() + .setGroup(OFGroup.of(grp.groupId().id())); + actions.add(actgrp.build()); + break; + default: + log.warn("Instruction type {} not yet implemented.", i.type()); + } + } + + return actions; + } + + private OFAction buildL0Modification(Instruction i) { + L0ModificationInstruction l0m = (L0ModificationInstruction) i; + switch (l0m.subtype()) { + case LAMBDA: + L0ModificationInstruction.ModLambdaInstruction ml = + (L0ModificationInstruction.ModLambdaInstruction) i; + return factory.actions().circuit(factory.oxms().ochSigidBasic( + new CircuitSignalID((byte) 1, (byte) 2, ml.lambda(), (short) 1))); + default: + log.warn("Unimplemented action type {}.", l0m.subtype()); + break; + } + return null; + } + + private OFAction buildL2Modification(Instruction i) { + L2ModificationInstruction l2m = (L2ModificationInstruction) i; + L2ModificationInstruction.ModEtherInstruction eth; + OFOxm oxm = null; + switch (l2m.subtype()) { + case ETH_DST: + eth = (L2ModificationInstruction.ModEtherInstruction) l2m; + oxm = factory.oxms().ethDst(MacAddress.of(eth.mac().toLong())); + break; + case ETH_SRC: + eth = (L2ModificationInstruction.ModEtherInstruction) l2m; + oxm = factory.oxms().ethSrc(MacAddress.of(eth.mac().toLong())); + break; + case VLAN_ID: + L2ModificationInstruction.ModVlanIdInstruction vlanId = + (L2ModificationInstruction.ModVlanIdInstruction) l2m; + oxm = factory.oxms().vlanVid(OFVlanVidMatch.ofVlan(vlanId.vlanId().toShort())); + break; + case VLAN_PCP: + L2ModificationInstruction.ModVlanPcpInstruction vlanPcp = + (L2ModificationInstruction.ModVlanPcpInstruction) l2m; + oxm = factory.oxms().vlanPcp(VlanPcp.of(vlanPcp.vlanPcp())); + break; + case VLAN_POP: + return factory.actions().popVlan(); + case VLAN_PUSH: + L2ModificationInstruction.PushHeaderInstructions pushVlanInstruction + = (L2ModificationInstruction.PushHeaderInstructions) l2m; + return factory.actions().pushVlan( + EthType.of(pushVlanInstruction.ethernetType().toShort())); + case MPLS_PUSH: + L2ModificationInstruction.PushHeaderInstructions pushHeaderInstructions = + (L2ModificationInstruction.PushHeaderInstructions) l2m; + return factory.actions().pushMpls(EthType.of(pushHeaderInstructions + .ethernetType().toShort())); + case MPLS_POP: + L2ModificationInstruction.PushHeaderInstructions popHeaderInstructions = + (L2ModificationInstruction.PushHeaderInstructions) l2m; + return factory.actions().popMpls(EthType.of(popHeaderInstructions + .ethernetType().toShort())); + case MPLS_LABEL: + L2ModificationInstruction.ModMplsLabelInstruction mplsLabel = + (L2ModificationInstruction.ModMplsLabelInstruction) l2m; + oxm = factory.oxms().mplsLabel(U32.of(mplsLabel.mplsLabel().toInt())); + break; + case MPLS_BOS: + L2ModificationInstruction.ModMplsBosInstruction mplsBos = + (L2ModificationInstruction.ModMplsBosInstruction) l2m; + oxm = factory.oxms() + .mplsBos(mplsBos.mplsBos() ? OFBooleanValue.TRUE + : OFBooleanValue.FALSE); + break; + case DEC_MPLS_TTL: + return factory.actions().decMplsTtl(); + default: + log.warn("Unimplemented action type {}.", l2m.subtype()); + break; + } + + if (oxm != null) { + return factory.actions().buildSetField().setField(oxm).build(); + } + return null; + } + + private OFAction buildL3Modification(Instruction i) { + L3ModificationInstruction l3m = (L3ModificationInstruction) i; + L3ModificationInstruction.ModIPInstruction ip; + Ip4Address ip4; + Ip6Address ip6; + OFOxm oxm = null; + switch (l3m.subtype()) { + case IPV4_SRC: + ip = (L3ModificationInstruction.ModIPInstruction) i; + ip4 = ip.ip().getIp4Address(); + oxm = factory.oxms().ipv4Src(IPv4Address.of(ip4.toInt())); + break; + case IPV4_DST: + ip = (L3ModificationInstruction.ModIPInstruction) i; + ip4 = ip.ip().getIp4Address(); + oxm = factory.oxms().ipv4Dst(IPv4Address.of(ip4.toInt())); + break; + case IPV6_SRC: + ip = (L3ModificationInstruction.ModIPInstruction) i; + ip6 = ip.ip().getIp6Address(); + oxm = factory.oxms().ipv6Src(IPv6Address.of(ip6.toOctets())); + break; + case IPV6_DST: + ip = (L3ModificationInstruction.ModIPInstruction) i; + ip6 = ip.ip().getIp6Address(); + oxm = factory.oxms().ipv6Dst(IPv6Address.of(ip6.toOctets())); + break; + case IPV6_FLABEL: + L3ModificationInstruction.ModIPv6FlowLabelInstruction flowLabelInstruction = + (L3ModificationInstruction.ModIPv6FlowLabelInstruction) i; + int flowLabel = flowLabelInstruction.flowLabel(); + oxm = factory.oxms().ipv6Flabel(IPv6FlowLabel.of(flowLabel)); + break; + case DEC_TTL: + return factory.actions().decNwTtl(); + case TTL_IN: + return factory.actions().copyTtlIn(); + case TTL_OUT: + return factory.actions().copyTtlOut(); + default: + log.warn("Unimplemented action type {}.", l3m.subtype()); + break; + } + + if (oxm != null) { + return factory.actions().buildSetField().setField(oxm).build(); + } + return null; + } + + private OFGroupType getOFGroupType(GroupDescription.Type groupType) { + switch (groupType) { + case INDIRECT: + return OFGroupType.INDIRECT; + case SELECT: + return OFGroupType.SELECT; + case FAILOVER: + return OFGroupType.FF; + case ALL: + return OFGroupType.ALL; + default: + log.error("Unsupported group type : {}", groupType); + break; + } + return null; + } +} + diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java new file mode 100644 index 00000000..9816426b --- /dev/null +++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java @@ -0,0 +1,111 @@ +/* + * 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.provider.of.group.impl; + +import org.jboss.netty.util.HashedWheelTimer; +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.TimerTask; +import org.onlab.util.Timer; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFGroupDescStatsRequest; +import org.projectfloodlight.openflow.protocol.OFGroupStatsRequest; +import org.projectfloodlight.openflow.types.OFGroup; +import org.slf4j.Logger; + +import java.util.concurrent.TimeUnit; + +import static org.slf4j.LoggerFactory.getLogger; + +/* + * Sends Group Stats Request and collect the group statistics with a time interval. + */ +public class GroupStatsCollector implements TimerTask { + + private final HashedWheelTimer timer = Timer.getTimer(); + private final OpenFlowSwitch sw; + private final Logger log = getLogger(getClass()); + private final int refreshInterval; + + private Timeout timeout; + + private boolean stopTimer = false; + + /** + * Creates a GroupStatsCollector object. + * + * @param sw Open Flow switch + * @param interval time interval for collecting group statistic + */ + public GroupStatsCollector(OpenFlowSwitch sw, int interval) { + this.sw = sw; + this.refreshInterval = interval; + } + + @Override + public void run(Timeout timeout) throws Exception { + log.trace("Collecting stats for {}", sw.getStringId()); + + sendGroupStatistic(); + + if (!this.stopTimer) { + log.trace("Scheduling stats collection in {} seconds for {}", + this.refreshInterval, this.sw.getStringId()); + timeout.getTimer().newTimeout(this, refreshInterval, + TimeUnit.SECONDS); + } + } + + private void sendGroupStatistic() { + if (log.isTraceEnabled()) { + log.trace("sendGroupStatistics {}:{}", sw.getStringId(), sw.getRole()); + } + if (sw.getRole() != RoleState.MASTER) { + return; + } + Long statsXid = OpenFlowGroupProvider.getXidAndAdd(2); + OFGroupStatsRequest statsRequest = sw.factory().buildGroupStatsRequest() + .setGroup(OFGroup.ALL) + .setXid(statsXid) + .build(); + sw.sendMsg(statsRequest); + + Long descXid = statsXid + 1; + OFGroupDescStatsRequest descStatsRequest = + sw.factory().buildGroupDescStatsRequest() + .setXid(descXid) + .build(); + sw.sendMsg(descStatsRequest); + } + + /** + * Starts the collector. + */ + public void start() { + log.info("Starting Group Stats collection thread for {}", sw.getStringId()); + timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS); + } + + /** + * Stops the collector. + */ + public void stop() { + log.info("Stopping Group Stats collection thread for {}", sw.getStringId()); + this.stopTimer = true; + timeout.cancel(); + } +} diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java new file mode 100644 index 00000000..78650fe6 --- /dev/null +++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java @@ -0,0 +1,366 @@ +/* + * 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.provider.of.group.impl; + +import com.google.common.collect.Maps; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onosproject.core.DefaultGroupId; +import org.onosproject.core.GroupId; +import org.onosproject.net.DeviceId; +import org.onosproject.net.group.DefaultGroup; +import org.onosproject.net.group.Group; +import org.onosproject.net.group.GroupBuckets; +import org.onosproject.net.group.GroupDescription; +import org.onosproject.net.group.GroupOperation; +import org.onosproject.net.group.GroupOperations; +import org.onosproject.net.group.GroupProvider; +import org.onosproject.net.group.GroupProviderRegistry; +import org.onosproject.net.group.GroupProviderService; +import org.onosproject.net.group.StoredGroupBucketEntry; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.openflow.controller.Dpid; +import org.onosproject.openflow.controller.OpenFlowController; +import org.onosproject.openflow.controller.OpenFlowEventListener; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.OpenFlowSwitchListener; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFBucketCounter; +import org.projectfloodlight.openflow.protocol.OFErrorMsg; +import org.projectfloodlight.openflow.protocol.OFErrorType; +import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry; +import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply; +import org.projectfloodlight.openflow.protocol.OFGroupMod; +import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry; +import org.projectfloodlight.openflow.protocol.OFGroupStatsReply; +import org.projectfloodlight.openflow.protocol.OFGroupType; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPortStatus; +import org.projectfloodlight.openflow.protocol.OFStatsReply; +import org.projectfloodlight.openflow.protocol.OFStatsType; +import org.projectfloodlight.openflow.protocol.OFVersion; +import org.slf4j.Logger; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Provider which uses an OpenFlow controller to handle Group. + */ +@Component(immediate = true) +public class OpenFlowGroupProvider extends AbstractProvider implements GroupProvider { + + private final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenFlowController controller; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected GroupProviderRegistry providerRegistry; + + private GroupProviderService providerService; + + static final int POLL_INTERVAL = 10; + + private final InternalGroupProvider listener = new InternalGroupProvider(); + + private static final AtomicLong XID_COUNTER = new AtomicLong(1); + private final Map collectors = Maps.newHashMap(); + private final Map groupStats = Maps.newConcurrentMap(); + private final Map pendingGroupOperations = + Maps.newConcurrentMap(); + + /* Map */ + private final Map pendingXidMaps = Maps.newConcurrentMap(); + + /** + * Creates a OpenFlow group provider. + */ + public OpenFlowGroupProvider() { + super(new ProviderId("of", "org.onosproject.provider.group")); + } + + @Activate + public void activate() { + providerService = providerRegistry.register(this); + controller.addListener(listener); + controller.addEventListener(listener); + + for (OpenFlowSwitch sw : controller.getSwitches()) { + if (isGroupSupported(sw)) { + GroupStatsCollector gsc = new GroupStatsCollector(sw, POLL_INTERVAL); + gsc.start(); + collectors.put(new Dpid(sw.getId()), gsc); + } + } + + log.info("Started"); + } + + @Deactivate + public void deactivate() { + providerRegistry.unregister(this); + providerService = null; + + log.info("Stopped"); + } + + @Override + public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) { + Map mods = Maps.newIdentityHashMap(); + final Dpid dpid = Dpid.dpid(deviceId.uri()); + OpenFlowSwitch sw = controller.getSwitch(dpid); + for (GroupOperation groupOperation: groupOps.operations()) { + if (sw == null) { + log.error("SW {} is not found", dpid); + return; + } + final Long groupModXid = XID_COUNTER.getAndIncrement(); + GroupModBuilder builder = + GroupModBuilder.builder(groupOperation.buckets(), + groupOperation.groupId(), + groupOperation.groupType(), + sw.factory(), + Optional.of(groupModXid)); + OFGroupMod groupMod = null; + switch (groupOperation.opType()) { + case ADD: + groupMod = builder.buildGroupAdd(); + break; + case MODIFY: + groupMod = builder.buildGroupMod(); + break; + case DELETE: + groupMod = builder.buildGroupDel(); + break; + default: + log.error("Unsupported Group operation"); + } + sw.sendMsg(groupMod); + GroupId groudId = new DefaultGroupId(groupMod.getGroup().getGroupNumber()); + pendingGroupOperations.put(groudId, groupOperation); + pendingXidMaps.put(groudId, groupModXid); + } + } + + private void pushGroupMetrics(Dpid dpid, OFStatsReply statsReply) { + DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid)); + + OFGroupStatsReply groupStatsReply = null; + OFGroupDescStatsReply groupDescStatsReply = null; + + synchronized (groupStats) { + if (statsReply.getStatsType() == OFStatsType.GROUP) { + OFStatsReply reply = groupStats.get(statsReply.getXid() + 1); + if (reply != null) { + groupStatsReply = (OFGroupStatsReply) statsReply; + groupDescStatsReply = (OFGroupDescStatsReply) reply; + groupStats.remove(statsReply.getXid() + 1); + } else { + groupStats.put(statsReply.getXid(), statsReply); + } + } else if (statsReply.getStatsType() == OFStatsType.GROUP_DESC) { + OFStatsReply reply = groupStats.get(statsReply.getXid() - 1); + if (reply != null) { + groupStatsReply = (OFGroupStatsReply) reply; + groupDescStatsReply = (OFGroupDescStatsReply) statsReply; + groupStats.remove(statsReply.getXid() - 1); + } else { + groupStats.put(statsReply.getXid(), statsReply); + } + } + } + + if (groupStatsReply != null && groupDescStatsReply != null) { + Collection groups = buildGroupMetrics(deviceId, + groupStatsReply, groupDescStatsReply); + providerService.pushGroupMetrics(deviceId, groups); + for (Group group: groups) { + pendingGroupOperations.remove(group.id()); + pendingXidMaps.remove(group.id()); + } + } + } + + private Collection buildGroupMetrics(DeviceId deviceId, + OFGroupStatsReply groupStatsReply, + OFGroupDescStatsReply groupDescStatsReply) { + + Map groups = Maps.newHashMap(); + + + for (OFGroupDescStatsEntry entry: groupDescStatsReply.getEntries()) { + int id = entry.getGroup().getGroupNumber(); + GroupId groupId = new DefaultGroupId(id); + GroupDescription.Type type = getGroupType(entry.getGroupType()); + GroupBuckets buckets = new GroupBucketEntryBuilder(entry.getBuckets(), + entry.getGroupType()).build(); + DefaultGroup group = new DefaultGroup(groupId, deviceId, type, buckets); + groups.put(id, group); + } + + for (OFGroupStatsEntry entry: groupStatsReply.getEntries()) { + int groupId = entry.getGroup().getGroupNumber(); + DefaultGroup group = (DefaultGroup) groups.get(groupId); + if (group != null) { + group.setBytes(entry.getByteCount().getValue()); + group.setLife(entry.getDurationSec()); + group.setPackets(entry.getPacketCount().getValue()); + group.setReferenceCount(entry.getRefCount()); + int bucketIndex = 0; + for (OFBucketCounter bucketStats:entry.getBucketStats()) { + ((StoredGroupBucketEntry) group.buckets().buckets() + .get(bucketIndex)) + .setPackets(bucketStats + .getPacketCount().getValue()); + ((StoredGroupBucketEntry) group.buckets().buckets() + .get(bucketIndex)) + .setBytes(entry.getBucketStats() + .get(bucketIndex) + .getByteCount().getValue()); + bucketIndex++; + } + } + } + + return groups.values(); + } + + private GroupDescription.Type getGroupType(OFGroupType type) { + switch (type) { + case ALL: + return GroupDescription.Type.ALL; + case INDIRECT: + return GroupDescription.Type.INDIRECT; + case SELECT: + return GroupDescription.Type.SELECT; + case FF: + return GroupDescription.Type.FAILOVER; + default: + log.error("Unsupported OF group type : {}", type); + break; + } + return null; + } + + /** + * Returns a transaction ID for entire group operations and increases + * the counter by the number given. + * + * @param increase the amount to increase the counter by + * @return a transaction ID + */ + public static long getXidAndAdd(int increase) { + return XID_COUNTER.getAndAdd(increase); + } + + private boolean isGroupSupported(OpenFlowSwitch sw) { + if (sw.factory().getVersion() == OFVersion.OF_10 || + sw.factory().getVersion() == OFVersion.OF_11 || + sw.factory().getVersion() == OFVersion.OF_12) { + return false; + } + + return true; + } + + private class InternalGroupProvider + implements OpenFlowSwitchListener, OpenFlowEventListener { + + @Override + public void handleMessage(Dpid dpid, OFMessage msg) { + switch (msg.getType()) { + case STATS_REPLY: + pushGroupMetrics(dpid, (OFStatsReply) msg); + break; + case ERROR: + OFErrorMsg errorMsg = (OFErrorMsg) msg; + if (errorMsg.getErrType() == OFErrorType.GROUP_MOD_FAILED) { + GroupId pendingGroupId = null; + for (Map.Entry entry: pendingXidMaps.entrySet()) { + if (entry.getValue() == errorMsg.getXid()) { + pendingGroupId = entry.getKey(); + break; + } + } + if (pendingGroupId == null) { + log.warn("Error for unknown group operation: {}", + errorMsg.getXid()); + } else { + GroupOperation operation = + pendingGroupOperations.get(pendingGroupId); + DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid)); + if (operation != null) { + providerService.groupOperationFailed(deviceId, + operation); + pendingGroupOperations.remove(pendingGroupId); + pendingXidMaps.remove(pendingGroupId); + log.warn("Received an group mod error {}", msg); + } else { + log.error("Cannot find pending group operation with group ID: {}", + pendingGroupId); + } + } + break; + } + default: + break; + } + } + + @Override + public void switchAdded(Dpid dpid) { + OpenFlowSwitch sw = controller.getSwitch(dpid); + if (isGroupSupported(sw)) { + GroupStatsCollector gsc = new GroupStatsCollector( + controller.getSwitch(dpid), POLL_INTERVAL); + gsc.start(); + collectors.put(dpid, gsc); + } + } + + @Override + public void switchRemoved(Dpid dpid) { + GroupStatsCollector collector = collectors.remove(dpid); + if (collector != null) { + collector.stop(); + } + } + + @Override + public void switchChanged(Dpid dpid) { + } + + @Override + public void portChanged(Dpid dpid, OFPortStatus status) { + } + + @Override + public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) { + } + } + +} diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/package-info.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/package-info.java new file mode 100644 index 00000000..9fda4a31 --- /dev/null +++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/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. + */ + +/** + * Provider that uses OpenFlow controller as a means of device port group management. + */ +package org.onosproject.provider.of.group.impl; \ No newline at end of file diff --git a/framework/src/onos/providers/openflow/group/src/test/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProviderTest.java b/framework/src/onos/providers/openflow/group/src/test/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProviderTest.java new file mode 100644 index 00000000..d66ba090 --- /dev/null +++ b/framework/src/onos/providers/openflow/group/src/test/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProviderTest.java @@ -0,0 +1,397 @@ +package org.onosproject.provider.of.group.impl; + +import com.google.common.collect.Lists; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.core.DefaultGroupId; +import org.onosproject.core.GroupId; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.group.DefaultGroupBucket; +import org.onosproject.net.group.Group; +import org.onosproject.net.group.GroupBucket; +import org.onosproject.net.group.GroupBuckets; +import org.onosproject.net.group.GroupDescription; +import org.onosproject.net.group.GroupOperation; +import org.onosproject.net.group.GroupOperations; +import org.onosproject.net.group.GroupProvider; +import org.onosproject.net.group.GroupProviderRegistry; +import org.onosproject.net.group.GroupProviderService; +import org.onosproject.net.provider.AbstractProviderService; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.openflow.controller.Dpid; +import org.onosproject.openflow.controller.OpenFlowController; +import org.onosproject.openflow.controller.OpenFlowEventListener; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.OpenFlowSwitchListener; +import org.onosproject.openflow.controller.PacketListener; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFFactories; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply; +import org.projectfloodlight.openflow.protocol.OFGroupMod; +import org.projectfloodlight.openflow.protocol.OFGroupModFailedCode; +import org.projectfloodlight.openflow.protocol.OFGroupStatsReply; +import org.projectfloodlight.openflow.protocol.OFGroupType; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPortDesc; +import org.projectfloodlight.openflow.protocol.OFVersion; +import org.projectfloodlight.openflow.protocol.errormsg.OFGroupModFailedErrorMsg; +import org.projectfloodlight.openflow.types.OFGroup; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.*; + +public class OpenFlowGroupProviderTest { + + OpenFlowGroupProvider provider = new OpenFlowGroupProvider(); + private final OpenFlowController controller = new TestController(); + GroupProviderRegistry providerRegistry = new TestGroupProviderRegistry(); + GroupProviderService providerService; + + private DeviceId deviceId = DeviceId.deviceId("of:0000000000000001"); + private Dpid dpid1 = Dpid.dpid(deviceId.uri()); + + @Before + public void setUp() { + provider.controller = controller; + provider.providerRegistry = providerRegistry; + provider.activate(); + } + + @Test + public void basics() { + assertNotNull("registration expected", providerService); + assertEquals("incorrect provider", provider, providerService.provider()); + } + + @Test + public void addGroup() { + + GroupId groupId = new DefaultGroupId(1); + + List bucketList = Lists.newArrayList(); + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); + builder.setOutput(PortNumber.portNumber(1)); + GroupBucket bucket = + DefaultGroupBucket.createSelectGroupBucket(builder.build()); + bucketList.add(bucket); + GroupBuckets buckets = new GroupBuckets(bucketList); + + List operationList = Lists.newArrayList(); + GroupOperation operation = GroupOperation.createAddGroupOperation(groupId, + GroupDescription.Type.SELECT, buckets); + operationList.add(operation); + GroupOperations operations = new GroupOperations(operationList); + + provider.performGroupOperation(deviceId, operations); + + final Dpid dpid = Dpid.dpid(deviceId.uri()); + TestOpenFlowSwitch sw = (TestOpenFlowSwitch) controller.getSwitch(dpid); + assertNotNull("Switch should not be nul", sw); + assertNotNull("OFGroupMsg should not be null", sw.msg); + + } + + + @Test + public void groupModFailure() { + TestOpenFlowGroupProviderService testProviderService = + (TestOpenFlowGroupProviderService) providerService; + + GroupId groupId = new DefaultGroupId(1); + List bucketList = Lists.newArrayList(); + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); + builder.setOutput(PortNumber.portNumber(1)); + GroupBucket bucket = + DefaultGroupBucket.createSelectGroupBucket(builder.build()); + bucketList.add(bucket); + GroupBuckets buckets = new GroupBuckets(bucketList); + List operationList = Lists.newArrayList(); + GroupOperation operation = GroupOperation.createAddGroupOperation(groupId, + GroupDescription.Type.SELECT, buckets); + operationList.add(operation); + GroupOperations operations = new GroupOperations(operationList); + + provider.performGroupOperation(deviceId, operations); + + OFGroupModFailedErrorMsg.Builder errorBuilder = + OFFactories.getFactory(OFVersion.OF_13).errorMsgs().buildGroupModFailedErrorMsg(); + OFGroupMod.Builder groupBuilder = OFFactories.getFactory(OFVersion.OF_13).buildGroupModify(); + groupBuilder.setGroupType(OFGroupType.ALL); + groupBuilder.setGroup(OFGroup.of(1)); + errorBuilder.setCode(OFGroupModFailedCode.GROUP_EXISTS); + errorBuilder.setXid(provider.getXidAndAdd(0) - 1); + + controller.processPacket(dpid1, errorBuilder.build()); + + assertNotNull("Operation failed should not be null", + testProviderService.failedOperation); + } + + + @Test + public void groupStatsEvent() { + TestOpenFlowGroupProviderService testProviderService = + (TestOpenFlowGroupProviderService) providerService; + + OFGroupStatsReply.Builder rep1 = + OFFactories.getFactory(OFVersion.OF_13).buildGroupStatsReply(); + rep1.setXid(1); + controller.processPacket(dpid1, rep1.build()); + OFGroupDescStatsReply.Builder rep2 = + OFFactories.getFactory(OFVersion.OF_13).buildGroupDescStatsReply(); + assertNull("group entries is not set yet", testProviderService.getGroupEntries()); + + rep2.setXid(2); + controller.processPacket(dpid1, rep2.build()); + assertNotNull("group entries should be set", testProviderService.getGroupEntries()); + } + + + + @After + public void tearDown() { + provider.deactivate(); + provider.providerRegistry = null; + provider.controller = null; + } + + private class TestOpenFlowGroupProviderService + extends AbstractProviderService + implements GroupProviderService { + + Collection groups = null; + GroupOperation failedOperation = null; + + protected TestOpenFlowGroupProviderService(GroupProvider provider) { + super(provider); + } + + @Override + public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) { + this.failedOperation = operation; + } + + @Override + public void pushGroupMetrics(DeviceId deviceId, Collection groupEntries) { + this.groups = groupEntries; + } + + public Collection getGroupEntries() { + return groups; + } + } + + private class TestController implements OpenFlowController { + + OpenFlowEventListener eventListener = null; + List switches = Lists.newArrayList(); + + public TestController() { + OpenFlowSwitch testSwitch = new TestOpenFlowSwitch(); + switches.add(testSwitch); + } + + @Override + public void addListener(OpenFlowSwitchListener listener) { + } + + @Override + public void removeListener(OpenFlowSwitchListener listener) { + + } + + @Override + public void addPacketListener(int priority, PacketListener listener) { + + } + + @Override + public void removePacketListener(PacketListener listener) { + + } + + @Override + public void addEventListener(OpenFlowEventListener listener) { + this.eventListener = listener; + } + + @Override + public void removeEventListener(OpenFlowEventListener listener) { + + } + + @Override + public void write(Dpid dpid, OFMessage msg) { + + } + + @Override + public void processPacket(Dpid dpid, OFMessage msg) { + eventListener.handleMessage(dpid, msg); + } + + @Override + public void setRole(Dpid dpid, RoleState role) { + + } + + @Override + public Iterable getSwitches() { + return switches; + } + + @Override + public Iterable getMasterSwitches() { + return null; + } + + @Override + public Iterable getEqualSwitches() { + return null; + } + + @Override + public OpenFlowSwitch getSwitch(Dpid dpid) { + return switches.get(0); + } + + @Override + public OpenFlowSwitch getMasterSwitch(Dpid dpid) { + return null; + } + + @Override + public OpenFlowSwitch getEqualSwitch(Dpid dpid) { + return null; + } + + } + + private class TestGroupProviderRegistry implements GroupProviderRegistry { + + @Override + public GroupProviderService register(GroupProvider provider) { + providerService = new TestOpenFlowGroupProviderService(provider); + return providerService; + } + + @Override + public void unregister(GroupProvider provider) { + } + + @Override + public Set getProviders() { + return null; + } + } + + private class TestOpenFlowSwitch implements OpenFlowSwitch { + + OFMessage msg = null; + + @Override + public void sendMsg(OFMessage msg) { + this.msg = msg; + } + + @Override + public void sendMsg(List msgs) { + + } + + @Override + public void handleMessage(OFMessage fromSwitch) { + + } + + @Override + public void setRole(RoleState role) { + + } + + @Override + public RoleState getRole() { + return null; + } + + @Override + public List getPorts() { + return null; + } + + @Override + public OFFactory factory() { + return OFFactories.getFactory(OFVersion.OF_13); + } + + @Override + public String getStringId() { + return null; + } + + @Override + public long getId() { + return 0; + } + + @Override + public String manufacturerDescription() { + return null; + } + + @Override + public String datapathDescription() { + return null; + } + + @Override + public String hardwareDescription() { + return null; + } + + @Override + public String softwareDescription() { + return null; + } + + @Override + public String serialNumber() { + return null; + } + + @Override + public boolean isConnected() { + return false; + } + + @Override + public void disconnectSwitch() { + + } + + @Override + public void returnRoleReply(RoleState requested, RoleState response) { + + } + + @Override + public Device.Type deviceType() { + return Device.Type.SWITCH; + } + + @Override + public String channelId() { + return null; + } + + } +} \ No newline at end of file -- cgit 1.2.3-korg