blob: 8274c83cdb016a26a430897f603e1a19293e55e4 [file] [log] [blame]
/*
* Copyright 2018-2023 Open Networking Foundation (ONF) and the ONF Contributors
*
* 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 flowdecomposition
import (
"context"
"errors"
"fmt"
"testing"
"github.com/opencord/voltha-go/rw_core/core/device/state"
"github.com/opencord/voltha-go/rw_core/route"
fu "github.com/opencord/voltha-lib-go/v7/pkg/flows"
ofp "github.com/opencord/voltha-protos/v5/go/openflow_13"
"github.com/opencord/voltha-protos/v5/go/voltha"
"github.com/stretchr/testify/assert"
)
type testDeviceManager struct {
state.DeviceManager
devices map[string]*voltha.Device
devicePorts map[string]map[uint32]*voltha.Port
}
func newTestDeviceManager() *testDeviceManager {
var tdm testDeviceManager
tdm.devices = make(map[string]*voltha.Device)
tdm.devicePorts = make(map[string]map[uint32]*voltha.Port)
tdm.devices["olt"] = &voltha.Device{
Id: "olt",
Root: true,
ParentId: "logical_device",
}
tdm.devicePorts["olt"] = map[uint32]*voltha.Port{
1: {PortNo: 1, Label: "pon"},
2: {PortNo: 2, Label: "nni"},
}
tdm.devices["onu1"] = &voltha.Device{
Id: "onu1",
Root: false,
ParentId: "olt",
}
tdm.devicePorts["onu1"] = map[uint32]*voltha.Port{
1: {PortNo: 1, Label: "pon"},
2: {PortNo: 2, Label: "uni"},
}
tdm.devices["onu2"] = &voltha.Device{
Id: "onu2",
Root: false,
ParentId: "olt",
}
tdm.devicePorts["onu2"] = map[uint32]*voltha.Port{
1: {PortNo: 1, Label: "pon"},
2: {PortNo: 2, Label: "uni"},
}
tdm.devices["onu3"] = &voltha.Device{
Id: "onu3",
Root: false,
ParentId: "olt",
}
tdm.devicePorts["onu3"] = map[uint32]*voltha.Port{
1: {PortNo: 1, Label: "pon"},
2: {PortNo: 2, Label: "uni"},
}
tdm.devices["onu4"] = &voltha.Device{
Id: "onu4",
Root: false,
ParentId: "olt",
}
tdm.devicePorts["onu4"] = map[uint32]*voltha.Port{
1: {PortNo: 1, Label: "pon"},
2: {PortNo: 2, Label: "uni"},
}
return &tdm
}
func (tdm *testDeviceManager) GetDevice(ctx context.Context, deviceID *voltha.ID) (*voltha.Device, error) {
if d, ok := tdm.devices[deviceID.Id]; ok {
return d, nil
}
return nil, errors.New("ABSENT")
}
func (tdm *testDeviceManager) listDevicePorts(ctx context.Context, deviceID string) (map[uint32]*voltha.Port, error) {
ports, have := tdm.devicePorts[deviceID]
if !have {
return nil, errors.New("ABSENT")
}
return ports, nil
}
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[route.OFPortLink][]route.Hop
defaultRules *fu.DeviceRules
deviceRoutes *route.DeviceRoutes
fd *FlowDecomposer
logicalPortsNo map[uint32]bool
}
func newTestFlowDecomposer(t *testing.T, deviceMgr *testDeviceManager) *testFlowDecomposer {
var tfd testFlowDecomposer
tfd.dMgr = deviceMgr
tfd.logicalPorts = make(map[uint32]*voltha.LogicalPort)
tfd.logicalPortsNo = make(map[uint32]bool)
// 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[65536] = &voltha.LogicalPort{Id: "65536", DeviceId: "olt", DevicePortNo: 65536}
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.logicalPortsNo[10] = false
tfd.logicalPortsNo[65536] = true // nni
tfd.routes = make(map[route.OFPortLink][]route.Hop)
//DOWNSTREAM ROUTES
tfd.routes[route.OFPortLink{Ingress: 10, Egress: 1}] = []route.Hop{
{
DeviceID: "olt",
Ingress: 2,
Egress: 1,
},
{
DeviceID: "onu1",
Ingress: 1,
Egress: 2,
},
}
tfd.routes[route.OFPortLink{Ingress: 10, Egress: 2}] = []route.Hop{
{
DeviceID: "olt",
Ingress: 2,
Egress: 1,
},
{
DeviceID: "onu2",
Ingress: 1,
Egress: 2,
},
}
tfd.routes[route.OFPortLink{Ingress: 10, Egress: 3}] = []route.Hop{
{
DeviceID: "olt",
Ingress: 2,
Egress: 1,
},
{
DeviceID: "onu3",
Ingress: 1,
Egress: 2,
},
}
tfd.routes[route.OFPortLink{Ingress: 10, Egress: 4}] = []route.Hop{
{
DeviceID: "olt",
Ingress: 2,
Egress: 1,
},
{
DeviceID: "onu4",
Ingress: 1,
Egress: 2,
},
}
tfd.routes[route.OFPortLink{Ingress: 10, Egress: 10}] = []route.Hop{
{
DeviceID: "olt",
Ingress: 2,
Egress: 2,
},
{
DeviceID: "olt",
Ingress: 2,
Egress: 2,
},
}
//UPSTREAM DATA PLANE
tfd.routes[route.OFPortLink{Ingress: 1, Egress: 10}] = []route.Hop{
{
DeviceID: "onu1",
Ingress: 2,
Egress: 1,
},
{
DeviceID: "olt",
Ingress: 1,
Egress: 2,
},
}
tfd.routes[route.OFPortLink{Ingress: 2, Egress: 10}] = []route.Hop{
{
DeviceID: "onu2",
Ingress: 2,
Egress: 1,
},
{
DeviceID: "olt",
Ingress: 1,
Egress: 2,
},
}
tfd.routes[route.OFPortLink{Ingress: 3, Egress: 10}] = []route.Hop{
{
DeviceID: "onu3",
Ingress: 2,
Egress: 1,
},
{
DeviceID: "olt",
Ingress: 1,
Egress: 2,
},
}
tfd.routes[route.OFPortLink{Ingress: 4, Egress: 10}] = []route.Hop{
{
DeviceID: "onu4",
Ingress: 2,
Egress: 1,
},
{
DeviceID: "olt",
Ingress: 1,
Egress: 2,
},
}
//UPSTREAM NEXT TABLE BASED
// openflow port 0 means absence of a port - go/protobuf interpretation
tfd.routes[route.OFPortLink{Ingress: 1, Egress: 0}] = []route.Hop{
{
DeviceID: "onu1",
Ingress: 2,
Egress: 1,
},
{
DeviceID: "olt",
Ingress: 1,
Egress: 2,
},
}
tfd.routes[route.OFPortLink{Ingress: 2, Egress: 0}] = []route.Hop{
{
DeviceID: "onu2",
Ingress: 2,
Egress: 1,
},
{
DeviceID: "olt",
Ingress: 1,
Egress: 2,
},
}
tfd.routes[route.OFPortLink{Ingress: 3, Egress: 0}] = []route.Hop{
{
DeviceID: "onu3",
Ingress: 2,
Egress: 1,
},
{
DeviceID: "olt",
Ingress: 1,
Egress: 2,
},
}
tfd.routes[route.OFPortLink{Ingress: 4, Egress: 0}] = []route.Hop{
{
DeviceID: "onu4",
Ingress: 2,
Egress: 1,
},
{
DeviceID: "olt",
Ingress: 1,
Egress: 2,
},
}
// DOWNSTREAM NEXT TABLE BASED
tfd.routes[route.OFPortLink{Ingress: 10, Egress: 0}] = []route.Hop{
{
DeviceID: "olt",
Ingress: 2,
Egress: 1,
},
{}, // 2nd hop is not known yet
}
tfd.routes[route.OFPortLink{Ingress: 0, Egress: 10}] = []route.Hop{
{}, // 1st hop is wildcard
{
DeviceID: "olt",
Ingress: 1,
Egress: 2,
},
}
// DEFAULT RULES
tfd.defaultRules = fu.NewDeviceRules()
fg := fu.NewFlowsAndGroups()
fa := &fu.FlowArgs{
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
},
Actions: []*ofp.OfpAction{
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
fu.Output(1),
},
}
fs, err := fu.MkFlowStat(fa)
assert.Nil(t, err)
fg.AddFlow(fs)
tfd.defaultRules.AddFlowsAndGroup("onu1", fg)
fg = fu.NewFlowsAndGroups()
fa = &fu.FlowArgs{
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
},
Actions: []*ofp.OfpAction{
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 102)),
fu.Output(1),
},
}
fs, err = fu.MkFlowStat(fa)
assert.Nil(t, err)
fg.AddFlow(fs)
tfd.defaultRules.AddFlowsAndGroup("onu2", fg)
fg = fu.NewFlowsAndGroups()
fa = &fu.FlowArgs{
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
},
Actions: []*ofp.OfpAction{
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 103)),
fu.Output(1),
},
}
fs, err = fu.MkFlowStat(fa)
assert.Nil(t, err)
fg.AddFlow(fs)
tfd.defaultRules.AddFlowsAndGroup("onu3", fg)
fg = fu.NewFlowsAndGroups()
fa = &fu.FlowArgs{
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
},
Actions: []*ofp.OfpAction{
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 104)),
fu.Output(1),
},
}
fs, err = fu.MkFlowStat(fa)
assert.Nil(t, err)
fg.AddFlow(fs)
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.deviceRoutes = route.NewDeviceRoutes("ldid", "olt", tfd.dMgr.listDevicePorts)
tfd.deviceRoutes.RootPorts = make(map[uint32]uint32)
tfd.deviceRoutes.RootPorts[10] = 10
tfd.fd = NewFlowDecomposer(func(ctx context.Context, deviceID string) (*voltha.Device, error) {
return tfd.dMgr.GetDevice(ctx, &voltha.ID{Id: deviceID})
})
return &tfd
}
func (tfd *testFlowDecomposer) GetDeviceLogicalID() string {
return ""
}
func (tfd *testFlowDecomposer) GetLogicalDevice(ctx context.Context) (*voltha.LogicalDevice, error) {
return nil, nil
}
func (tfd *testFlowDecomposer) GetDeviceRoutes() *route.DeviceRoutes {
return tfd.deviceRoutes
}
func (tfd *testFlowDecomposer) GetAllDefaultRules() *fu.DeviceRules {
return tfd.defaultRules
}
func (tfd *testFlowDecomposer) GetWildcardInputPorts(ctx context.Context, excludePort uint32) map[uint32]struct{} {
lPorts := make(map[uint32]struct{})
for portNo := range tfd.logicalPorts {
if portNo != excludePort {
lPorts[portNo] = struct{}{}
}
}
return lPorts
}
func (tfd *testFlowDecomposer) GetRoute(ctx context.Context, ingressPortNo uint32, egressPortNo uint32) ([]route.Hop, error) {
var portLink route.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, nil
}
}
return nil, fmt.Errorf("no route from:%d to:%d %w", ingressPortNo, egressPortNo, route.ErrNoRoute)
}
func (tfd *testFlowDecomposer) GetNNIPorts() map[uint32]struct{} {
nniPorts := make(map[uint32]struct{})
for portNo, nni := range tfd.logicalPortsNo {
if nni {
nniPorts[portNo] = struct{}{}
}
}
return nniPorts
}
func TestEapolReRouteRuleVlanDecomposition(t *testing.T) {
fa := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 50),
fu.EthType(0x888e),
},
Actions: []*ofp.OfpAction{
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
fs, err := fu.MkFlowStat(fa)
assert.Nil(t, err)
flows := map[uint64]*ofp.OfpFlowStats{fs.Id: fs}
tfd := newTestFlowDecomposer(t, newTestDeviceManager())
deviceRules, err := tfd.fd.DecomposeRules(context.Background(), tfd, flows, nil)
assert.Nil(t, err)
onu1FlowAndGroup := deviceRules.Rules["onu1"]
oltFlowAndGroup := deviceRules.Rules["olt"]
assert.Equal(t, 1, onu1FlowAndGroup.Flows.Len())
assert.Equal(t, 1, oltFlowAndGroup.Flows.Len())
assert.Equal(t, 0, oltFlowAndGroup.Groups.Len())
faParent := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.TunnelId(uint64(1)),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101),
fu.EthType(0x888e),
},
Actions: []*ofp.OfpAction{
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
expectedOltFlow, err := fu.MkFlowStat(faParent)
assert.Nil(t, err)
derivedFlow := oltFlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOltFlow.String(), derivedFlow.String())
faChild := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.TunnelId(uint64(1)),
fu.EthType(0x888e),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 50),
},
Actions: []*ofp.OfpAction{
fu.PushVlan(0x8100),
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
fu.Output(1),
},
}
expectedOnuFlow, err := fu.MkFlowStat(faChild)
assert.Nil(t, err)
derivedFlow = onu1FlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOnuFlow.String(), derivedFlow.String())
}
func TestEapolReRouteRuleZeroVlanDecomposition(t *testing.T) {
fa := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
fu.EthType(0x888e),
},
Actions: []*ofp.OfpAction{
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
fs, err := fu.MkFlowStat(fa)
assert.Nil(t, err)
flows := map[uint64]*ofp.OfpFlowStats{fs.Id: fs}
tfd := newTestFlowDecomposer(t, newTestDeviceManager())
deviceRules, err := tfd.fd.DecomposeRules(context.Background(), tfd, flows, nil)
assert.Nil(t, err)
onu1FlowAndGroup := deviceRules.Rules["onu1"]
oltFlowAndGroup := deviceRules.Rules["olt"]
assert.Equal(t, 1, onu1FlowAndGroup.Flows.Len())
assert.Equal(t, 1, oltFlowAndGroup.Flows.Len())
assert.Equal(t, 0, oltFlowAndGroup.Groups.Len())
faParent := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.TunnelId(uint64(1)),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101),
fu.EthType(0x888e),
},
Actions: []*ofp.OfpAction{
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
expectedOltFlow, err := fu.MkFlowStat(faParent)
assert.Nil(t, err)
derivedFlow := oltFlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOltFlow.String(), derivedFlow.String())
faChild := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.TunnelId(uint64(1)),
fu.EthType(0x888e),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
},
Actions: []*ofp.OfpAction{
fu.PushVlan(0x8100),
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
fu.Output(1),
},
}
expectedOnuFlow, err := fu.MkFlowStat(faChild)
assert.Nil(t, err)
derivedFlow = onu1FlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOnuFlow.String(), derivedFlow.String())
}
func TestEapolReRouteRuleNoVlanDecomposition(t *testing.T) {
fa := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.EthType(0x888e),
},
Actions: []*ofp.OfpAction{
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
fs, err := fu.MkFlowStat(fa)
assert.Nil(t, err)
flows := map[uint64]*ofp.OfpFlowStats{fs.Id: fs}
tfd := newTestFlowDecomposer(t, newTestDeviceManager())
deviceRules, err := tfd.fd.DecomposeRules(context.Background(), tfd, flows, nil)
assert.Nil(t, err)
onu1FlowAndGroup := deviceRules.Rules["onu1"]
oltFlowAndGroup := deviceRules.Rules["olt"]
assert.Equal(t, 1, onu1FlowAndGroup.Flows.Len())
assert.Equal(t, 1, oltFlowAndGroup.Flows.Len())
assert.Equal(t, 0, oltFlowAndGroup.Groups.Len())
faParent := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.TunnelId(uint64(1)),
fu.EthType(0x888e),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101),
},
Actions: []*ofp.OfpAction{
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
expectedOltFlow, err := fu.MkFlowStat(faParent)
assert.Nil(t, err)
derivedFlow := oltFlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOltFlow.String(), derivedFlow.String())
faChild := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.TunnelId(uint64(1)),
fu.EthType(0x888e),
},
Actions: []*ofp.OfpAction{
fu.PushVlan(0x8100),
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
fu.Output(1),
},
}
expectedOnuFlow, err := fu.MkFlowStat(faChild)
assert.Nil(t, err)
derivedFlow = onu1FlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOnuFlow.String(), derivedFlow.String())
}
func TestPppoedReRouteRuleVlanDecomposition(t *testing.T) {
fa := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 50),
fu.EthType(0x8863),
},
Actions: []*ofp.OfpAction{
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
fs, err := fu.MkFlowStat(fa)
assert.Nil(t, err)
flows := map[uint64]*ofp.OfpFlowStats{fs.Id: fs}
tfd := newTestFlowDecomposer(t, newTestDeviceManager())
deviceRules, err := tfd.fd.DecomposeRules(context.Background(), tfd, flows, nil)
assert.Nil(t, err)
onu1FlowAndGroup := deviceRules.Rules["onu1"]
oltFlowAndGroup := deviceRules.Rules["olt"]
assert.Equal(t, 1, onu1FlowAndGroup.Flows.Len())
assert.Equal(t, 1, oltFlowAndGroup.Flows.Len())
assert.Equal(t, 0, oltFlowAndGroup.Groups.Len())
faParent := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.TunnelId(uint64(1)),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101),
fu.EthType(0x8863),
},
Actions: []*ofp.OfpAction{
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
expectedOltFlow, err := fu.MkFlowStat(faParent)
assert.Nil(t, err)
derivedFlow := oltFlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOltFlow.String(), derivedFlow.String())
faChild := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.TunnelId(uint64(1)),
fu.EthType(0x8863),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 50),
},
Actions: []*ofp.OfpAction{
fu.PushVlan(0x8100),
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
fu.Output(1),
},
}
expectedOnuFlow, err := fu.MkFlowStat(faChild)
assert.Nil(t, err)
derivedFlow = onu1FlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOnuFlow.String(), derivedFlow.String())
}
func TestDhcpReRouteRuleDecomposition(t *testing.T) {
fa := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.EthType(0x0800),
fu.Ipv4Dst(0xffffffff),
fu.IpProto(17),
fu.UdpSrc(68),
fu.UdpDst(67),
},
Actions: []*ofp.OfpAction{
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
fs, err := fu.MkFlowStat(fa)
assert.Nil(t, err)
flows := map[uint64]*ofp.OfpFlowStats{fs.Id: fs}
tfd := newTestFlowDecomposer(t, newTestDeviceManager())
deviceRules, err := tfd.fd.DecomposeRules(context.Background(), tfd, flows, nil)
assert.Nil(t, err)
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, 1, oltFlowAndGroup.Flows.Len())
assert.Equal(t, 0, oltFlowAndGroup.Groups.Len())
faParent := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.TunnelId(uint64(1)),
fu.EthType(0x0800),
fu.Ipv4Dst(0xffffffff),
fu.IpProto(17),
fu.UdpSrc(68),
fu.UdpDst(67),
},
Actions: []*ofp.OfpAction{
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
expectedOltFlow, err := fu.MkFlowStat(faParent)
assert.Nil(t, err)
derivedFlow := oltFlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOltFlow.String(), derivedFlow.String())
faChild := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.TunnelId(uint64(1)),
fu.EthType(0x0800),
fu.Ipv4Dst(0xffffffff),
fu.IpProto(17),
fu.UdpSrc(68),
fu.UdpDst(67),
},
Actions: []*ofp.OfpAction{
fu.Output(1),
},
}
expectedOnuFlow, err := fu.MkFlowStat(faChild)
assert.Nil(t, err)
derivedFlow = onu1FlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOnuFlow.String(), derivedFlow.String())
}
func TestLldpReRouteRuleDecomposition(t *testing.T) {
fa := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(10),
fu.EthType(0x88CC),
},
Actions: []*ofp.OfpAction{
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
fs, err := fu.MkFlowStat(fa)
assert.Nil(t, err)
flows := map[uint64]*ofp.OfpFlowStats{fs.Id: fs}
tfd := newTestFlowDecomposer(t, newTestDeviceManager())
deviceRules, err := tfd.fd.DecomposeRules(context.Background(), tfd, flows, nil)
assert.Nil(t, err)
onu1FlowAndGroup := deviceRules.Rules["onu1"]
oltFlowAndGroup := deviceRules.Rules["olt"]
assert.Nil(t, onu1FlowAndGroup)
assert.Equal(t, 1, oltFlowAndGroup.Flows.Len())
assert.Equal(t, 0, oltFlowAndGroup.Groups.Len())
fa = &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.EthType(0x88CC),
},
Actions: []*ofp.OfpAction{
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
expectedOltFlow, err := fu.MkFlowStat(fa)
assert.Nil(t, err)
derivedFlow := oltFlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOltFlow.String(), derivedFlow.String())
}
func TestUnicastUpstreamRuleDecomposition(t *testing.T) {
fa := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 5000, "table_id": 0},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
fu.VlanPcp(0),
},
Actions: []*ofp.OfpAction{
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
},
}
fa2 := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 500, "table_id": 1},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101),
fu.VlanPcp(0),
},
Actions: []*ofp.OfpAction{
fu.PushVlan(0x8100),
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 1000)),
fu.SetField(fu.VlanPcp(0)),
fu.Output(10),
},
}
fs, err := fu.MkFlowStat(fa)
assert.Nil(t, err)
fs2, err := fu.MkFlowStat(fa2)
assert.Nil(t, err)
fs.Instructions = []*ofp.OfpInstruction{{
Type: uint32(ofp.OfpInstructionType_OFPIT_GOTO_TABLE),
Data: &ofp.OfpInstruction_GotoTable{
GotoTable: &ofp.OfpInstructionGotoTable{
TableId: 1,
},
}}}
flows := map[uint64]*ofp.OfpFlowStats{fs.Id: fs, fs2.Id: fs2}
tfd := newTestFlowDecomposer(t, newTestDeviceManager())
deviceRules, err := tfd.fd.DecomposeRules(context.Background(), tfd, flows, nil)
assert.Nil(t, err)
onu1FlowAndGroup := deviceRules.Rules["onu1"]
oltFlowAndGroup := deviceRules.Rules["olt"]
assert.NotNil(t, onu1FlowAndGroup)
assert.NotNil(t, onu1FlowAndGroup.Flows)
assert.Equal(t, 1, 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": 5000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.TunnelId(uint64(1)),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
fu.VlanPcp(0),
},
Actions: []*ofp.OfpAction{
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101)),
fu.Output(1),
},
}
derivedFlow := onu1FlowAndGroup.GetFlow(0)
// Form the expected flow
expectedOnu1Flow, err := fu.MkFlowStat(fa)
assert.Nil(t, err)
expectedOnu1Flow.Instructions = []*ofp.OfpInstruction{{
Type: uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS),
Data: &ofp.OfpInstruction_Actions{
Actions: &ofp.OfpInstructionActions{
Actions: []*ofp.OfpAction{{
Type: 0,
Action: &ofp.OfpAction_Output{
Output: &ofp.OfpActionOutput{
Port: 1,
MaxLen: 65509,
},
}}}}}}}
expectedOnu1Flow.Id = derivedFlow.Id // Assign same flow ID as derived flowID to match completely
assert.Equal(t, expectedOnu1Flow.String(), derivedFlow.String())
fa = &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 500},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.TunnelId(uint64(1)),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101),
fu.VlanPcp(0),
},
Actions: []*ofp.OfpAction{
fu.PushVlan(0x8100),
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 1000)),
fu.SetField(fu.VlanPcp(0)),
fu.Output(2),
},
}
expectedOltFlow, err := fu.MkFlowStat(fa)
assert.Nil(t, err)
derivedFlow = oltFlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOltFlow.String(), derivedFlow.String())
}
func TestUnicastDownstreamRuleDecomposition(t *testing.T) {
ctx := context.Background()
logger.Debugf(ctx, "Starting Test Unicast Downstream")
fa1 := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 500, "table_id": 0},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(10),
fu.Metadata_ofp((1000 << 32) | 1),
fu.VlanPcp(0),
},
Actions: []*ofp.OfpAction{
fu.PopVlan(),
},
}
// If table-id is provided in the flow-args, the same is also used as go-to-next table
fa2 := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 500 /*"table_id": 1*/},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(10),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101),
fu.VlanPcp(0),
},
Actions: []*ofp.OfpAction{
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT))),
fu.Output(1),
},
}
fs1, err := fu.MkFlowStat(fa1)
assert.Nil(t, err)
fs2, err := fu.MkFlowStat(fa2)
assert.Nil(t, err)
// Table-1, without next table
fs2.TableId = 1
fs1.Instructions = []*ofp.OfpInstruction{{
Type: uint32(ofp.OfpInstructionType_OFPIT_GOTO_TABLE),
Data: &ofp.OfpInstruction_GotoTable{
GotoTable: &ofp.OfpInstructionGotoTable{
TableId: 1,
},
}}}
flows := map[uint64]*ofp.OfpFlowStats{fs1.Id: fs1, fs2.Id: fs2}
tfd := newTestFlowDecomposer(t, newTestDeviceManager())
deviceRules, err := tfd.fd.DecomposeRules(context.Background(), tfd, flows, nil)
assert.Nil(t, err)
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, 1, oltFlowAndGroup.Flows.Len())
assert.Equal(t, 0, oltFlowAndGroup.Groups.Len())
fa1 = &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 500},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.TunnelId(uint64(10)),
fu.Metadata_ofp(4294967296001),
fu.VlanPcp(0),
},
Actions: []*ofp.OfpAction{
fu.PopVlan(),
fu.Output(1),
},
}
derivedFlow := oltFlowAndGroup.GetFlow(0)
expectedOltFlow, err := fu.MkFlowStat(fa1)
assert.Nil(t, err)
expectedOltFlow.Instructions = []*ofp.OfpInstruction{{
Type: uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS),
Data: &ofp.OfpInstruction_Actions{
Actions: &ofp.OfpInstructionActions{
Actions: []*ofp.OfpAction{{
Type: 0,
Action: &ofp.OfpAction_Output{
Output: &ofp.OfpActionOutput{
Port: 1,
MaxLen: 65509,
},
}}}}}}}
expectedOltFlow.Id = derivedFlow.Id
assert.Equal(t, expectedOltFlow.String(), derivedFlow.String())
fa1 = &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 500},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 101),
fu.VlanPcp(0),
},
Actions: []*ofp.OfpAction{
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT))),
fu.Output(2),
},
}
expectedOnu1Flow, err := fu.MkFlowStat(fa1)
assert.Nil(t, err)
derivedFlow = onu1FlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOnu1Flow.String(), derivedFlow.String())
}
func TestMulticastDownstreamRuleDecomposition(t *testing.T) {
fa := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 500},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(10),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 170),
fu.VlanPcp(0),
fu.EthType(0x800),
fu.Ipv4Dst(0xe00a0a0a),
},
Actions: []*ofp.OfpAction{
fu.Group(10),
},
}
ga := &fu.GroupArgs{
GroupId: 10,
Buckets: []*ofp.OfpBucket{
{Actions: []*ofp.OfpAction{
fu.PopVlan(),
fu.Output(1),
},
},
},
}
fs, err := fu.MkFlowStat(fa)
assert.Nil(t, err)
flows := map[uint64]*ofp.OfpFlowStats{fs.Id: fs}
groups := map[uint32]*ofp.OfpGroupEntry{ga.GroupId: fu.MkGroupStat(ga)}
tfd := newTestFlowDecomposer(t, newTestDeviceManager())
deviceRules, err := tfd.fd.DecomposeRules(context.Background(), tfd, flows, groups)
assert.Nil(t, err)
oltFlowAndGroup := deviceRules.Rules["olt"]
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{
fu.InPort(10),
fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 170),
fu.VlanPcp(0),
fu.EthType(0x800),
fu.Ipv4Dst(0xe00a0a0a),
},
Actions: []*ofp.OfpAction{
fu.Group(10),
},
}
expectedOltFlow, err := fu.MkFlowStat(fa)
assert.Nil(t, err)
derivedFlow := oltFlowAndGroup.GetFlow(0)
assert.Equal(t, expectedOltFlow.String(), derivedFlow.String())
}
func TestMplsUpstreamFlowDecomposition(t *testing.T) {
// Note: Olt-Nni=10
// Onu1-Uni=1
/*
ADDED, bytes=0, packets=0, table=0, priority=1000, selector=[IN_PORT:UNI, VLAN_VID:ANY], treatment=[immediate=[],
transition=TABLE:1, meter=METER:1, metadata=METADATA:4100010000/0]
*/
// Here, 'table_id=1' is present to add go-to-table action
faOnu := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000, "table_id": 1, "meter_id": 1, "write_metadata": 4100100000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1), // Onu Uni
fu.VlanVid(4096),
},
Actions: []*ofp.OfpAction{},
}
fsOnu, err := fu.MkFlowStat(faOnu)
assert.NoError(t, err)
assert.NotNil(t, fsOnu)
// Update table-id
fsOnu.TableId = 0
/*
ADDED, bytes=0, packets=0, table=1, priority=1000, selector=[IN_PORT:32, VLAN_VID:ANY], treatment=[immediate=[VLAN_PUSH:vlan,
VLAN_ID:2, MPLS_PUSH:mpls_unicast, MPLS_LABEL:YYY,MPLS_BOS:true, MPLS_PUSH:mpls_unicast ,MPLS_LABEL:XXX, MPLS_BOS:false,
EXTENSION:of:0000000000000227/VolthaPushL2Header{​​​​​​​}​​​​​​​, ETH_SRC:OLT_MAC, ETH_DST:LEAF_MAC, TTL:64, OUTPUT:65536],
meter=METER:1, metadata=METADATA:4100000000/0]
*/
faOlt := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000, "meter_id": 1, "write_metadata": 4100000000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1), // Onu-Uni
fu.VlanVid(4096),
},
Actions: []*ofp.OfpAction{
fu.PushVlan(0x8100),
fu.SetField(fu.VlanVid(2)),
fu.SetField(fu.EthSrc(1111)),
fu.SetField(fu.EthDst(2222)),
fu.PushVlan(0x8847),
fu.SetField(fu.MplsLabel(100)),
fu.SetField(fu.MplsBos(1)),
fu.PushVlan(0x8847),
fu.SetField(fu.MplsLabel(200)),
fu.MplsTtl(64),
fu.Output(10), // Olt-Nni
},
}
fsOlt, err := fu.MkFlowStat(faOlt)
assert.NoError(t, err)
assert.NotNil(t, fsOlt)
// Update table-id
// table-id is skipped in flow-args above as that would also add the go-to-table action
fsOlt.TableId = 1
flows := map[uint64]*ofp.OfpFlowStats{fsOnu.Id: fsOnu, fsOlt.Id: fsOlt}
tfd := newTestFlowDecomposer(t, newTestDeviceManager())
deviceRules, err := tfd.fd.DecomposeRules(context.Background(), tfd, flows, nil)
assert.Nil(t, err)
onuFlowAndGroup := deviceRules.Rules["onu1"]
oltFlowAndGroup := deviceRules.Rules["olt"]
assert.NotNil(t, onuFlowAndGroup)
assert.NotNil(t, onuFlowAndGroup.Flows)
assert.Equal(t, 1, onuFlowAndGroup.Flows.Len())
assert.Equal(t, 0, onuFlowAndGroup.Groups.Len())
assert.Equal(t, 1, oltFlowAndGroup.Flows.Len())
assert.Equal(t, 0, oltFlowAndGroup.Groups.Len())
// Form expected ONU flow args
expectedOnufa := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000, "meter_id": 1, "write_metadata": 4100100000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2), // Onu Uni
fu.TunnelId(uint64(1)),
fu.VlanVid(4096),
},
Actions: []*ofp.OfpAction{
fu.Output(1),
},
}
// Form the expected ONU flow
expectedOnuFlow, err := fu.MkFlowStat(expectedOnufa)
assert.Nil(t, err)
assert.NotNil(t, expectedOnuFlow)
expectedOnuFlow.TableId = 0
derivedOnuFlow := onuFlowAndGroup.GetFlow(0)
expectedOnuFlow.Id = derivedOnuFlow.Id // Assign same flow ID as derived flowID to match completely
assert.Equal(t, expectedOnuFlow.String(), derivedOnuFlow.String())
expectedOltfa := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000, "meter_id": 1, "write_metadata": 4100000000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1), // Onu-Uni
fu.TunnelId(uint64(1)),
fu.VlanVid(4096),
},
Actions: []*ofp.OfpAction{
fu.PushVlan(0x8100),
fu.SetField(fu.VlanVid(2)),
fu.SetField(fu.EthSrc(1111)),
fu.SetField(fu.EthDst(2222)),
fu.PushVlan(0x8847),
fu.SetField(fu.MplsLabel(100)),
fu.SetField(fu.MplsBos(1)),
fu.PushVlan(0x8847),
fu.SetField(fu.MplsLabel(200)),
fu.MplsTtl(64),
fu.Output(2), // Olt-Nni
},
}
expectedOltFlow, err := fu.MkFlowStat(expectedOltfa)
assert.NoError(t, err)
assert.NotNil(t, expectedOltFlow)
derivedOltFlow := oltFlowAndGroup.GetFlow(0)
expectedOltFlow.Id = derivedOltFlow.Id
assert.Equal(t, expectedOltFlow.String(), derivedOltFlow.String())
}
func TestMplsDownstreamFlowDecomposition(t *testing.T) {
faOltSingleMplsLable := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000, "table_id": 1},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(10),
fu.Metadata_ofp((1000 << 32) | 1),
fu.EthType(0x8847),
fu.MplsBos(1),
fu.EthSrc(2222),
},
Actions: []*ofp.OfpAction{
{Type: ofp.OfpActionType_OFPAT_DEC_MPLS_TTL, Action: &ofp.OfpAction_MplsTtl{MplsTtl: &ofp.OfpActionMplsTtl{MplsTtl: 62}}},
fu.PopMpls(0x8847),
},
}
fsOltSingleMplsLabel, err := fu.MkFlowStat(faOltSingleMplsLable)
assert.NoError(t, err)
assert.NotNil(t, fsOltSingleMplsLabel)
fsOltSingleMplsLabel.TableId = 0
flows := map[uint64]*ofp.OfpFlowStats{fsOltSingleMplsLabel.Id: fsOltSingleMplsLabel}
tfd := newTestFlowDecomposer(t, newTestDeviceManager())
deviceRules, err := tfd.fd.DecomposeRules(context.Background(), tfd, flows, nil)
assert.Nil(t, err)
assert.NotNil(t, deviceRules)
oltFlowAndGroup := deviceRules.Rules["olt"]
assert.NotNil(t, oltFlowAndGroup)
derivedFlow := oltFlowAndGroup.GetFlow(0)
assert.NotNil(t, derivedFlow)
// Formulate expected
expectedFa := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.TunnelId(10),
fu.Metadata_ofp((1000 << 32) | 1),
fu.EthType(0x8847),
fu.MplsBos(1),
fu.EthSrc(2222),
},
Actions: []*ofp.OfpAction{
{Type: ofp.OfpActionType_OFPAT_DEC_MPLS_TTL, Action: &ofp.OfpAction_MplsTtl{MplsTtl: &ofp.OfpActionMplsTtl{MplsTtl: 62}}},
fu.PopMpls(0x8847),
fu.Output(1),
},
}
expectedFs, err := fu.MkFlowStat(expectedFa)
assert.NoError(t, err)
expectedFs.Id = derivedFlow.Id
assert.Equal(t, expectedFs.String(), derivedFlow.String())
// Formulate Mpls double label
faOltDoubleMplsLabel := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000, "table_id": 1},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(10),
fu.EthType(0x8847),
fu.EthSrc(2222),
},
Actions: []*ofp.OfpAction{
{Type: ofp.OfpActionType_OFPAT_DEC_MPLS_TTL, Action: &ofp.OfpAction_MplsTtl{MplsTtl: &ofp.OfpActionMplsTtl{MplsTtl: 62}}},
fu.PopMpls(0x8847),
fu.PopMpls(0x8847),
},
}
fsOltDoubleMplsLabel, err := fu.MkFlowStat(faOltDoubleMplsLabel)
assert.NoError(t, err)
assert.NotNil(t, fsOltDoubleMplsLabel)
flows2 := map[uint64]*ofp.OfpFlowStats{fsOltDoubleMplsLabel.Id: fsOltDoubleMplsLabel}
assert.NotNil(t, flows2)
deviceRules, err = tfd.fd.DecomposeRules(context.Background(), tfd, flows2, nil)
assert.NoError(t, err)
assert.NotNil(t, deviceRules)
oltFlowAndGroup = deviceRules.Rules["olt"]
assert.NotNil(t, oltFlowAndGroup)
derivedFlow = oltFlowAndGroup.GetFlow(0)
assert.NotNil(t, derivedFlow)
expectedFa = &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.TunnelId(10),
fu.EthType(0x8847),
fu.EthSrc(2222),
},
Actions: []*ofp.OfpAction{
{Type: ofp.OfpActionType_OFPAT_DEC_MPLS_TTL, Action: &ofp.OfpAction_MplsTtl{MplsTtl: &ofp.OfpActionMplsTtl{MplsTtl: 62}}},
fu.PopMpls(0x8847),
fu.PopMpls(0x8847),
fu.Output(1),
},
}
expectedFs, err = fu.MkFlowStat(expectedFa)
assert.NoError(t, err)
assert.NotNil(t, expectedFs)
expectedFs.Id = derivedFlow.Id
assert.Equal(t, expectedFs.String(), derivedFlow.String())
//olt downstream flows (table-id=1)
faOlt := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000, "table_id": 2, "meter_id": 1},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(10),
fu.VlanVid(2),
},
Actions: []*ofp.OfpAction{
fu.PopVlan(),
},
}
fsOlt, err := fu.MkFlowStat(faOlt)
assert.NoError(t, err)
assert.NotNil(t, fsOlt)
fsOlt.TableId = 1
flows3 := map[uint64]*ofp.OfpFlowStats{fsOlt.Id: fsOlt}
assert.NotNil(t, flows3)
deviceRules, err = tfd.fd.DecomposeRules(context.Background(), tfd, flows3, nil)
assert.NoError(t, err)
assert.NotNil(t, deviceRules)
oltFlowAndGroup = deviceRules.Rules["olt"]
assert.NotNil(t, oltFlowAndGroup)
derivedFlow = oltFlowAndGroup.GetFlow(0)
assert.NotNil(t, derivedFlow)
faOltExpected := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000, "meter_id": 1},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(2),
fu.TunnelId(10),
fu.VlanVid(2),
},
Actions: []*ofp.OfpAction{
fu.PopVlan(),
fu.Output(1),
},
}
fsOltExpected, err := fu.MkFlowStat(faOltExpected)
assert.NoError(t, err)
assert.NotNil(t, fsOltExpected)
fsOltExpected.Id = derivedFlow.Id
assert.Equal(t, fsOltExpected.String(), derivedFlow.String())
// Onu Downstream
faOnu := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000, "meter_id": 1},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(10),
fu.Metadata_ofp((1000 << 32) | 1),
fu.VlanVid(4096),
},
Actions: []*ofp.OfpAction{
fu.Output(1),
},
}
fsOnu, err := fu.MkFlowStat(faOnu)
assert.NoError(t, err)
fsOnu.TableId = 2
flows4 := map[uint64]*ofp.OfpFlowStats{fsOnu.Id: fsOnu}
assert.NotNil(t, flows4)
deviceRules, err = tfd.fd.DecomposeRules(context.Background(), tfd, flows4, nil)
assert.NoError(t, err)
assert.NotNil(t, deviceRules)
onuFlowAndGroup := deviceRules.Rules["onu1"]
assert.NotNil(t, onuFlowAndGroup)
derivedFlow = onuFlowAndGroup.GetFlow(0)
assert.NotNil(t, derivedFlow)
faExpected := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 1000, "meter_id": 1},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.Metadata_ofp((1000 << 32) | 1),
fu.VlanVid(4096),
},
Actions: []*ofp.OfpAction{
fu.Output(2),
},
}
fsExpected, err := fu.MkFlowStat(faExpected)
assert.NoError(t, err)
assert.NotNil(t, fsExpected)
fsExpected.Id = derivedFlow.Id
assert.Equal(t, fsExpected.String(), derivedFlow.String())
}