| /* |
| * Copyright 2018-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package flow_decomposition |
| |
| import ( |
| "errors" |
| "github.com/opencord/voltha-go/common/log" |
| ofp "github.com/opencord/voltha-protos/go/openflow_13" |
| "github.com/opencord/voltha-protos/go/voltha" |
| "github.com/opencord/voltha-go/rw_core/graph" |
| fu "github.com/opencord/voltha-go/rw_core/utils" |
| "github.com/stretchr/testify/assert" |
| |
| "testing" |
| ) |
| |
| func init() { |
| log.AddPackage(log.JSON, log.WarnLevel, nil) |
| log.UpdateAllLoggers(log.Fields{"instanceId": "flow-decomposition"}) |
| log.SetAllLogLevel(log.WarnLevel) |
| } |
| |
| type testDeviceManager struct { |
| devices map[string]*voltha.Device |
| } |
| |
| func newTestDeviceManager() *testDeviceManager { |
| var tdm testDeviceManager |
| tdm.devices = make(map[string]*voltha.Device) |
| tdm.devices["olt"] = &voltha.Device{ |
| Id: "olt", |
| Root: true, |
| ParentId: "logical_device", |
| Ports: []*voltha.Port{ |
| {PortNo: 1, Label: "pon"}, |
| {PortNo: 2, Label: "nni"}, |
| }, |
| } |
| tdm.devices["onu1"] = &voltha.Device{ |
| Id: "onu1", |
| Root: false, |
| ParentId: "olt", |
| Ports: []*voltha.Port{ |
| {PortNo: 1, Label: "pon"}, |
| {PortNo: 2, Label: "uni"}, |
| }, |
| } |
| tdm.devices["onu2"] = &voltha.Device{ |
| Id: "onu2", |
| Root: false, |
| ParentId: "olt", |
| Ports: []*voltha.Port{ |
| {PortNo: 1, Label: "pon"}, |
| {PortNo: 2, Label: "uni"}, |
| }, |
| } |
| tdm.devices["onu3"] = &voltha.Device{ |
| Id: "onu3", |
| Root: false, |
| ParentId: "olt", |
| Ports: []*voltha.Port{ |
| {PortNo: 1, Label: "pon"}, |
| {PortNo: 2, Label: "uni"}, |
| }, |
| } |
| tdm.devices["onu4"] = &voltha.Device{ |
| Id: "onu4", |
| Root: false, |
| ParentId: "olt", |
| Ports: []*voltha.Port{ |
| {PortNo: 1, Label: "pon"}, |
| {PortNo: 2, Label: "uni"}, |
| }, |
| } |
| return &tdm |
| } |
| |
| func (tdm *testDeviceManager) GetDevice(deviceId string) (*voltha.Device, error) { |
| if d, ok := tdm.devices[deviceId]; ok { |
| return d, nil |
| } |
| return nil, errors.New("ABSENT.") |
| } |
| func (tdm *testDeviceManager) IsRootDevice(deviceId string) (bool, error) { |
| if d, ok := tdm.devices[deviceId]; ok { |
| return d.Root, nil |
| } |
| return false, errors.New("ABSENT.") |
| } |
| |
| type testFlowDecomposer struct { |
| dMgr *testDeviceManager |
| logicalPorts map[uint32]*voltha.LogicalPort |
| routes map[graph.OFPortLink][]graph.RouteHop |
| defaultRules *fu.DeviceRules |
| deviceGraph *graph.DeviceGraph |
| fd *FlowDecomposer |
| } |
| |
| func newTestFlowDecomposer(deviceMgr *testDeviceManager) *testFlowDecomposer { |
| var tfd testFlowDecomposer |
| tfd.dMgr = deviceMgr |
| |
| tfd.logicalPorts = make(map[uint32]*voltha.LogicalPort) |
| // Go protobuf interpreted absence of a port as 0, so we can't use port #0 as an openflow |
| // port |
| tfd.logicalPorts[10] = &voltha.LogicalPort{Id: "10", DeviceId: "olt", DevicePortNo: 2} |
| tfd.logicalPorts[1] = &voltha.LogicalPort{Id: "1", DeviceId: "onu1", DevicePortNo: 2} |
| tfd.logicalPorts[2] = &voltha.LogicalPort{Id: "2", DeviceId: "onu2", DevicePortNo: 2} |
| tfd.logicalPorts[3] = &voltha.LogicalPort{Id: "3", DeviceId: "onu3", DevicePortNo: 2} |
| tfd.logicalPorts[4] = &voltha.LogicalPort{Id: "4", DeviceId: "onu4", DevicePortNo: 2} |
| |
| tfd.routes = make(map[graph.OFPortLink][]graph.RouteHop) |
| |
| //DOWNSTREAM ROUTES |
| |
| tfd.routes[graph.OFPortLink{Ingress: 10, Egress: 1}] = []graph.RouteHop{ |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| }, |
| { |
| DeviceID: "onu1", |
| Ingress: tfd.dMgr.devices["onu1"].Ports[0].PortNo, |
| Egress: tfd.dMgr.devices["onu1"].Ports[1].PortNo, |
| }, |
| } |
| |
| tfd.routes[graph.OFPortLink{Ingress: 10, Egress: 2}] = []graph.RouteHop{ |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| }, |
| { |
| DeviceID: "onu2", |
| Ingress: tfd.dMgr.devices["onu2"].Ports[0].PortNo, |
| Egress: tfd.dMgr.devices["onu2"].Ports[1].PortNo, |
| }, |
| } |
| tfd.routes[graph.OFPortLink{Ingress: 10, Egress: 3}] = []graph.RouteHop{ |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| }, |
| { |
| DeviceID: "onu3", |
| Ingress: tfd.dMgr.devices["onu3"].Ports[0].PortNo, |
| Egress: tfd.dMgr.devices["onu3"].Ports[1].PortNo, |
| }, |
| } |
| tfd.routes[graph.OFPortLink{Ingress: 10, Egress: 4}] = []graph.RouteHop{ |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| }, |
| { |
| DeviceID: "onu4", |
| Ingress: tfd.dMgr.devices["onu4"].Ports[0].PortNo, |
| Egress: tfd.dMgr.devices["onu4"].Ports[1].PortNo, |
| }, |
| } |
| |
| //UPSTREAM DATA PLANE |
| |
| tfd.routes[graph.OFPortLink{Ingress: 1, Egress: 10}] = []graph.RouteHop{ |
| { |
| DeviceID: "onu1", |
| Ingress: tfd.dMgr.devices["onu1"].Ports[1].PortNo, |
| Egress: tfd.dMgr.devices["onu1"].Ports[0].PortNo, |
| }, |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| }, |
| } |
| tfd.routes[graph.OFPortLink{Ingress: 2, Egress: 10}] = []graph.RouteHop{ |
| { |
| DeviceID: "onu2", |
| Ingress: tfd.dMgr.devices["onu2"].Ports[1].PortNo, |
| Egress: tfd.dMgr.devices["onu2"].Ports[0].PortNo, |
| }, |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| }, |
| } |
| tfd.routes[graph.OFPortLink{Ingress: 3, Egress: 10}] = []graph.RouteHop{ |
| { |
| DeviceID: "onu3", |
| Ingress: tfd.dMgr.devices["onu3"].Ports[1].PortNo, |
| Egress: tfd.dMgr.devices["onu3"].Ports[0].PortNo, |
| }, |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| }, |
| } |
| tfd.routes[graph.OFPortLink{Ingress: 4, Egress: 10}] = []graph.RouteHop{ |
| { |
| DeviceID: "onu4", |
| Ingress: tfd.dMgr.devices["onu4"].Ports[1].PortNo, |
| Egress: tfd.dMgr.devices["onu4"].Ports[0].PortNo, |
| }, |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| }, |
| } |
| |
| //UPSTREAM NEXT TABLE BASED |
| |
| // openflow port 0 means absence of a port - go/protobuf interpretation |
| tfd.routes[graph.OFPortLink{Ingress: 1, Egress: 0}] = []graph.RouteHop{ |
| { |
| DeviceID: "onu1", |
| Ingress: tfd.dMgr.devices["onu1"].Ports[1].PortNo, |
| Egress: tfd.dMgr.devices["onu1"].Ports[0].PortNo, |
| }, |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| }, |
| } |
| tfd.routes[graph.OFPortLink{Ingress: 2, Egress: 0}] = []graph.RouteHop{ |
| { |
| DeviceID: "onu2", |
| Ingress: tfd.dMgr.devices["onu2"].Ports[1].PortNo, |
| Egress: tfd.dMgr.devices["onu2"].Ports[0].PortNo, |
| }, |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| }, |
| } |
| tfd.routes[graph.OFPortLink{Ingress: 3, Egress: 0}] = []graph.RouteHop{ |
| { |
| DeviceID: "onu3", |
| Ingress: tfd.dMgr.devices["onu3"].Ports[1].PortNo, |
| Egress: tfd.dMgr.devices["onu3"].Ports[0].PortNo, |
| }, |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| }, |
| } |
| tfd.routes[graph.OFPortLink{Ingress: 4, Egress: 0}] = []graph.RouteHop{ |
| { |
| DeviceID: "onu4", |
| Ingress: tfd.dMgr.devices["onu4"].Ports[1].PortNo, |
| Egress: tfd.dMgr.devices["onu4"].Ports[0].PortNo, |
| }, |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| }, |
| } |
| |
| // DOWNSTREAM NEXT TABLE BASED |
| |
| tfd.routes[graph.OFPortLink{Ingress: 10, Egress: 0}] = []graph.RouteHop{ |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| }, |
| {}, // 2nd hop is not known yet |
| } |
| |
| tfd.routes[graph.OFPortLink{Ingress: 0, Egress: 10}] = []graph.RouteHop{ |
| {}, // 1st hop is wildcard |
| { |
| DeviceID: "olt", |
| Ingress: tfd.dMgr.devices["olt"].Ports[0].PortNo, |
| Egress: tfd.dMgr.devices["olt"].Ports[1].PortNo, |
| }, |
| } |
| |
| // DEFAULT RULES |
| |
| tfd.defaultRules = fu.NewDeviceRules() |
| fg := fu.NewFlowsAndGroups() |
| var fa *fu.FlowArgs |
| fa = &fu.FlowArgs{ |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(2), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)), |
| Output(1), |
| }, |
| } |
| fg.AddFlow(MkFlowStat(fa)) |
| tfd.defaultRules.AddFlowsAndGroup("onu1", fg) |
| |
| fg = fu.NewFlowsAndGroups() |
| fa = &fu.FlowArgs{ |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(2), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 102)), |
| Output(1), |
| }, |
| } |
| fg.AddFlow(MkFlowStat(fa)) |
| tfd.defaultRules.AddFlowsAndGroup("onu2", fg) |
| |
| fg = fu.NewFlowsAndGroups() |
| fa = &fu.FlowArgs{ |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(2), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 103)), |
| Output(1), |
| }, |
| } |
| fg.AddFlow(MkFlowStat(fa)) |
| tfd.defaultRules.AddFlowsAndGroup("onu3", fg) |
| |
| fg = fu.NewFlowsAndGroups() |
| fa = &fu.FlowArgs{ |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(2), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 104)), |
| Output(1), |
| }, |
| } |
| fg.AddFlow(MkFlowStat(fa)) |
| tfd.defaultRules.AddFlowsAndGroup("onu4", fg) |
| |
| //Set up the device graph - flow decomposer uses it only to verify whether a port is a root port. |
| tfd.deviceGraph = graph.NewDeviceGraph("ldid", tfd.getDeviceHelper) |
| tfd.deviceGraph.RootPorts = make(map[uint32]uint32) |
| tfd.deviceGraph.RootPorts[10] = 10 |
| |
| tfd.fd = NewFlowDecomposer(tfd.dMgr) |
| |
| return &tfd |
| } |
| |
| func (tfd *testFlowDecomposer) getDeviceHelper(deviceId string) (*voltha.Device, error) { |
| return tfd.dMgr.GetDevice(deviceId) |
| } |
| |
| func (tfd *testFlowDecomposer) GetDeviceLogicalId() string { |
| return "" |
| } |
| |
| func (tfd *testFlowDecomposer) GetLogicalDevice() (*voltha.LogicalDevice, error) { |
| return nil, nil |
| } |
| |
| func (tfd *testFlowDecomposer) GetDeviceGraph() *graph.DeviceGraph { |
| return tfd.deviceGraph |
| } |
| |
| func (tfd *testFlowDecomposer) GetAllDefaultRules() *fu.DeviceRules { |
| return tfd.defaultRules |
| } |
| |
| func (tfd *testFlowDecomposer) GetWildcardInputPorts(excludePort ...uint32) []uint32 { |
| lPorts := make([]uint32, 0) |
| var exclPort uint32 |
| if len(excludePort) == 1 { |
| exclPort = excludePort[0] |
| } |
| for portno := range tfd.logicalPorts { |
| if portno != exclPort { |
| lPorts = append(lPorts, portno) |
| } |
| } |
| return lPorts |
| } |
| |
| func (tfd *testFlowDecomposer) GetRoute(ingressPortNo uint32, egressPortNo uint32) []graph.RouteHop { |
| var portLink graph.OFPortLink |
| if egressPortNo == 0 { |
| portLink.Egress = 0 |
| } else if egressPortNo&0x7fffffff == uint32(ofp.OfpPortNo_OFPP_CONTROLLER) { |
| portLink.Egress = 10 |
| } else { |
| portLink.Egress = egressPortNo |
| } |
| if ingressPortNo == 0 { |
| portLink.Ingress = 0 |
| } else { |
| portLink.Ingress = ingressPortNo |
| } |
| for key, val := range tfd.routes { |
| if key.Ingress == portLink.Ingress && key.Egress == portLink.Egress { |
| return val |
| } |
| } |
| return nil |
| } |
| |
| func TestEapolReRouteRuleDecomposition(t *testing.T) { |
| |
| var fa *fu.FlowArgs |
| fa = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 1000}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(1), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0), |
| EthType(0x888e), |
| }, |
| Actions: []*ofp.OfpAction{ |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)), |
| Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)), |
| }, |
| } |
| |
| flows := ofp.Flows{Items: []*ofp.OfpFlowStats{MkFlowStat(fa)}} |
| groups := ofp.FlowGroups{} |
| tfd := newTestFlowDecomposer(newTestDeviceManager()) |
| |
| deviceRules := tfd.fd.DecomposeRules(tfd, flows, groups) |
| onu1FlowAndGroup := deviceRules.Rules["onu1"] |
| oltFlowAndGroup := deviceRules.Rules["olt"] |
| assert.Equal(t, 1, onu1FlowAndGroup.Flows.Len()) |
| assert.Equal(t, 0, onu1FlowAndGroup.Groups.Len()) |
| assert.Equal(t, 2, oltFlowAndGroup.Flows.Len()) |
| assert.Equal(t, 0, oltFlowAndGroup.Groups.Len()) |
| |
| fa = &fu.FlowArgs{ |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(2), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)), |
| Output(1), |
| }, |
| } |
| expectedOnu1Flow := MkFlowStat(fa) |
| derivedFlow := onu1FlowAndGroup.GetFlow(0) |
| assert.Equal(t, expectedOnu1Flow.String(), derivedFlow.String()) |
| |
| fa = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 1000}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(1), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 1), |
| EthType(0x888e), |
| }, |
| Actions: []*ofp.OfpAction{ |
| PushVlan(0x8100), |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 4000)), |
| Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)), |
| }, |
| } |
| expectedOltFlow := MkFlowStat(fa) |
| derivedFlow = oltFlowAndGroup.GetFlow(0) |
| assert.Equal(t, expectedOltFlow.String(), derivedFlow.String()) |
| |
| fa = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 1000}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(2), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 4000), |
| VlanPcp(0), |
| Metadata_ofp(1), |
| }, |
| Actions: []*ofp.OfpAction{ |
| PopVlan(), |
| Output(1), |
| }, |
| } |
| expectedOltFlow = MkFlowStat(fa) |
| derivedFlow = oltFlowAndGroup.GetFlow(1) |
| assert.Equal(t, expectedOltFlow.String(), derivedFlow.String()) |
| } |
| |
| func TestDhcpReRouteRuleDecomposition(t *testing.T) { |
| |
| var fa *fu.FlowArgs |
| fa = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 1000}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(1), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0), |
| EthType(0x0800), |
| Ipv4Dst(0xffffffff), |
| IpProto(17), |
| UdpSrc(68), |
| UdpDst(67), |
| }, |
| Actions: []*ofp.OfpAction{ |
| Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)), |
| }, |
| } |
| |
| flows := ofp.Flows{Items: []*ofp.OfpFlowStats{MkFlowStat(fa)}} |
| groups := ofp.FlowGroups{} |
| tfd := newTestFlowDecomposer(newTestDeviceManager()) |
| |
| deviceRules := tfd.fd.DecomposeRules(tfd, flows, groups) |
| onu1FlowAndGroup := deviceRules.Rules["onu1"] |
| oltFlowAndGroup := deviceRules.Rules["olt"] |
| assert.Equal(t, 1, onu1FlowAndGroup.Flows.Len()) |
| assert.Equal(t, 0, onu1FlowAndGroup.Groups.Len()) |
| assert.Equal(t, 2, oltFlowAndGroup.Flows.Len()) |
| assert.Equal(t, 0, oltFlowAndGroup.Groups.Len()) |
| |
| fa = &fu.FlowArgs{ |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(2), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)), |
| Output(1), |
| }, |
| } |
| expectedOnu1Flow := MkFlowStat(fa) |
| derivedFlow := onu1FlowAndGroup.GetFlow(0) |
| assert.Equal(t, expectedOnu1Flow.String(), derivedFlow.String()) |
| |
| fa = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 1000}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(1), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 1), |
| EthType(0x0800), |
| Ipv4Dst(0xffffffff), |
| IpProto(17), |
| UdpSrc(68), |
| UdpDst(67), |
| }, |
| Actions: []*ofp.OfpAction{ |
| PushVlan(0x8100), |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 4000)), |
| Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)), |
| }, |
| } |
| expectedOltFlow := MkFlowStat(fa) |
| derivedFlow = oltFlowAndGroup.GetFlow(0) |
| assert.Equal(t, expectedOltFlow.String(), derivedFlow.String()) |
| |
| fa = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 1000}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(2), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 4000), |
| VlanPcp(0), |
| Metadata_ofp(1), |
| }, |
| Actions: []*ofp.OfpAction{ |
| PopVlan(), |
| Output(1), |
| }, |
| } |
| expectedOltFlow = MkFlowStat(fa) |
| derivedFlow = oltFlowAndGroup.GetFlow(1) |
| assert.Equal(t, expectedOltFlow.String(), derivedFlow.String()) |
| } |
| |
| func TestUnicastUpstreamRuleDecomposition(t *testing.T) { |
| |
| var fa *fu.FlowArgs |
| fa = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 500, "table_id": 1}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(1), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0), |
| VlanPcp(0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)), |
| }, |
| } |
| |
| var fa2 *fu.FlowArgs |
| fa2 = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 500}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(1), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101), |
| VlanPcp(0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| PushVlan(0x8100), |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 1000)), |
| SetField(VlanPcp(0)), |
| Output(10), |
| }, |
| } |
| |
| flows := ofp.Flows{Items: []*ofp.OfpFlowStats{MkFlowStat(fa), MkFlowStat(fa2)}} |
| groups := ofp.FlowGroups{} |
| tfd := newTestFlowDecomposer(newTestDeviceManager()) |
| |
| deviceRules := tfd.fd.DecomposeRules(tfd, flows, groups) |
| onu1FlowAndGroup := deviceRules.Rules["onu1"] |
| oltFlowAndGroup := deviceRules.Rules["olt"] |
| assert.Equal(t, 2, onu1FlowAndGroup.Flows.Len()) |
| assert.Equal(t, 0, onu1FlowAndGroup.Groups.Len()) |
| assert.Equal(t, 1, oltFlowAndGroup.Flows.Len()) |
| assert.Equal(t, 0, oltFlowAndGroup.Groups.Len()) |
| |
| fa = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 500}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(2), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0), |
| VlanPcp(0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)), |
| Output(1), |
| }, |
| } |
| expectedOnu1Flow := MkFlowStat(fa) |
| derivedFlow := onu1FlowAndGroup.GetFlow(1) |
| assert.Equal(t, expectedOnu1Flow.String(), derivedFlow.String()) |
| |
| fa = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 500}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(1), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101), |
| VlanPcp(0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| PushVlan(0x8100), |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 1000)), |
| SetField(VlanPcp(0)), |
| Output(2), |
| }, |
| } |
| expectedOltFlow := MkFlowStat(fa) |
| derivedFlow = oltFlowAndGroup.GetFlow(0) |
| assert.Equal(t, expectedOltFlow.String(), derivedFlow.String()) |
| } |
| |
| func TestUnicastDownstreamRuleDecomposition(t *testing.T) { |
| var fa1 *fu.FlowArgs |
| fa1 = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 500, "table_id": 1}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(10), |
| Metadata_ofp((1000 << 32) | 1), |
| VlanPcp(0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| PopVlan(), |
| }, |
| } |
| |
| var fa2 *fu.FlowArgs |
| fa2 = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 500}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(10), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101), |
| VlanPcp(0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0)), |
| Output(1), |
| }, |
| } |
| |
| flows := ofp.Flows{Items: []*ofp.OfpFlowStats{MkFlowStat(fa1), MkFlowStat(fa2)}} |
| groups := ofp.FlowGroups{} |
| tfd := newTestFlowDecomposer(newTestDeviceManager()) |
| |
| deviceRules := tfd.fd.DecomposeRules(tfd, flows, groups) |
| onu1FlowAndGroup := deviceRules.Rules["onu1"] |
| oltFlowAndGroup := deviceRules.Rules["olt"] |
| assert.Equal(t, 2, onu1FlowAndGroup.Flows.Len()) |
| assert.Equal(t, 0, onu1FlowAndGroup.Groups.Len()) |
| assert.Equal(t, 1, oltFlowAndGroup.Flows.Len()) |
| assert.Equal(t, 0, oltFlowAndGroup.Groups.Len()) |
| |
| fa1 = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 500}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(2), |
| Metadata_ofp(1000), |
| VlanPcp(0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| PopVlan(), |
| Output(1), |
| }, |
| } |
| expectedOltFlow := MkFlowStat(fa1) |
| derivedFlow := oltFlowAndGroup.GetFlow(0) |
| assert.Equal(t, expectedOltFlow.String(), derivedFlow.String()) |
| |
| fa1 = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 500}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(1), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101), |
| VlanPcp(0), |
| }, |
| Actions: []*ofp.OfpAction{ |
| SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0)), |
| Output(2), |
| }, |
| } |
| expectedOnu1Flow := MkFlowStat(fa1) |
| derivedFlow = onu1FlowAndGroup.GetFlow(1) |
| assert.Equal(t, expectedOnu1Flow.String(), derivedFlow.String()) |
| } |
| |
| func TestMulticastDownstreamRuleDecomposition(t *testing.T) { |
| var fa *fu.FlowArgs |
| fa = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 500}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(10), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 170), |
| VlanPcp(0), |
| EthType(0x800), |
| Ipv4Dst(0xe00a0a0a), |
| }, |
| Actions: []*ofp.OfpAction{ |
| Group(10), |
| }, |
| } |
| |
| var ga *fu.GroupArgs |
| ga = &fu.GroupArgs{ |
| GroupId: 10, |
| Buckets: []*ofp.OfpBucket{ |
| {Actions: []*ofp.OfpAction{ |
| PopVlan(), |
| Output(1), |
| }, |
| }, |
| }, |
| } |
| |
| flows := ofp.Flows{Items: []*ofp.OfpFlowStats{MkFlowStat(fa)}} |
| groups := ofp.FlowGroups{Items: []*ofp.OfpGroupEntry{MkGroupStat(ga)}} |
| tfd := newTestFlowDecomposer(newTestDeviceManager()) |
| |
| deviceRules := tfd.fd.DecomposeRules(tfd, flows, groups) |
| onu1FlowAndGroup := deviceRules.Rules["onu1"] |
| oltFlowAndGroup := deviceRules.Rules["olt"] |
| assert.Equal(t, 2, onu1FlowAndGroup.Flows.Len()) |
| assert.Equal(t, 0, onu1FlowAndGroup.Groups.Len()) |
| assert.Equal(t, 1, oltFlowAndGroup.Flows.Len()) |
| assert.Equal(t, 0, oltFlowAndGroup.Groups.Len()) |
| |
| fa = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 500}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(2), |
| VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 170), |
| VlanPcp(0), |
| EthType(0x800), |
| Ipv4Dst(0xe00a0a0a), |
| }, |
| Actions: []*ofp.OfpAction{ |
| PopVlan(), |
| Output(1), |
| }, |
| } |
| expectedOltFlow := MkFlowStat(fa) |
| derivedFlow := oltFlowAndGroup.GetFlow(0) |
| assert.Equal(t, expectedOltFlow.String(), derivedFlow.String()) |
| |
| fa = &fu.FlowArgs{ |
| KV: fu.OfpFlowModArgs{"priority": 500}, |
| MatchFields: []*ofp.OfpOxmOfbField{ |
| InPort(1), |
| EthType(0x800), |
| Ipv4Dst(0xe00a0a0a), |
| }, |
| Actions: []*ofp.OfpAction{ |
| Output(2), |
| }, |
| } |
| expectedOnu1Flow := MkFlowStat(fa) |
| derivedFlow = onu1FlowAndGroup.GetFlow(1) |
| assert.Equal(t, expectedOnu1Flow.String(), derivedFlow.String()) |
| } |