blob: dbdc0cf763d8961f2fe62e0c13b0c1a455ef65bb [file] [log] [blame]
// +build integration
/*
* 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 core
import (
"context"
"fmt"
"github.com/google/uuid"
fu "github.com/opencord/voltha-go/rw_core/utils"
tu "github.com/opencord/voltha-go/tests/utils"
"github.com/opencord/voltha-lib-go/v2/pkg/log"
"github.com/opencord/voltha-protos/go/common"
ofp "github.com/opencord/voltha-protos/go/openflow_13"
"github.com/opencord/voltha-protos/go/voltha"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/metadata"
"math"
"os"
"reflect"
"sync"
"testing"
"time"
)
var stub voltha.VolthaServiceClient
var volthaSerialNumberKey string
var mutex sync.Mutex
/*
This local "integration" test uses one RW-Core, one simulated_olt and one simulated_onu adapter to test flows
(add/delete), in a development environment. It uses docker-compose to set up the local environment. However, it can
easily be extended to run in k8s environment.
The compose files used are located under %GOPATH/src/github.com/opencord/voltha-go/compose. If the GOPATH is not set
then you can specify the location of the compose files by using COMPOSE_PATH to set the compose files location.
To run this test: DOCKER_HOST_IP=<local IP> go test -v
NOTE: Since this is an integration test that involves several containers and features (device creation, device
activation, validation of parent and discovered devices, validation of logical device as well as add/delete flows)
then a failure can occur anywhere not just when testing flows.
*/
var allDevices map[string]*voltha.Device
var allLogicalDevices map[string]*voltha.LogicalDevice
var composePath string
const (
GrpcPort = 50057
NumOfOLTs = 1
NumOfONUsPerOLT = 4 // This should coincide with the number of onus per olt in adapters-simulated.yml file
MeterIDStart = 1
MeterIDStop = 1000
)
func setup() {
var err error
if _, err = log.AddPackage(log.JSON, log.WarnLevel, log.Fields{"instanceId": "testing"}); err != nil {
log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
}
log.UpdateAllLoggers(log.Fields{"instanceId": "testing"})
log.SetAllLogLevel(log.ErrorLevel)
volthaSerialNumberKey = "voltha_serial_number"
allDevices = make(map[string]*voltha.Device)
allLogicalDevices = make(map[string]*voltha.LogicalDevice)
grpcHostIP := os.Getenv("DOCKER_HOST_IP")
goPath := os.Getenv("GOPATH")
if goPath != "" {
composePath = fmt.Sprintf("%s/src/github.com/opencord/voltha-go/compose", goPath)
} else {
composePath = os.Getenv("COMPOSE_PATH")
}
fmt.Println("Using compose path:", composePath)
//Start the simulated environment
if err = tu.StartSimulatedEnv(composePath); err != nil {
fmt.Println("Failure starting simulated environment:", err)
os.Exit(10)
}
stub, err = tu.SetupGrpcConnectionToCore(grpcHostIP, GrpcPort)
if err != nil {
fmt.Println("Failure connecting to Voltha Core:", err)
os.Exit(11)
}
// Wait for the simulated devices to be registered in the Voltha Core
adapters := []string{"simulated_olt", "simulated_onu"}
if _, err = tu.WaitForAdapterRegistration(stub, adapters, 20); err != nil {
fmt.Println("Failure retrieving adapters:", err)
os.Exit(12)
}
}
func shutdown() {
err := tu.StopSimulatedEnv(composePath)
if err != nil {
fmt.Println("Failure stop simulated environment:", err)
}
}
func refreshLocalDeviceCache(stub voltha.VolthaServiceClient) error {
retrievedDevices, err := tu.ListDevices(stub)
if err != nil {
return err
}
mutex.Lock()
for _, d := range retrievedDevices.Items {
allDevices[d.Id] = d
}
retrievedLogicalDevices, err := tu.ListLogicalDevices(stub)
if err != nil {
return err
}
for _, ld := range retrievedLogicalDevices.Items {
allLogicalDevices[ld.Id] = ld
}
mutex.Unlock()
return nil
}
func makeSimpleFlowMod(fa *fu.FlowArgs) *ofp.OfpFlowMod {
matchFields := make([]*ofp.OfpOxmField, 0)
for _, val := range fa.MatchFields {
matchFields = append(matchFields, &ofp.OfpOxmField{Field: &ofp.OfpOxmField_OfbField{OfbField: val}})
}
return fu.MkSimpleFlowMod(matchFields, fa.Actions, fa.Command, fa.KV)
}
func addEAPOLFlow(stub voltha.VolthaServiceClient, ld *voltha.LogicalDevice, port *voltha.LogicalPort, meterID uint64,
ch chan interface{}) {
var fa *fu.FlowArgs
fa = &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 2000, "meter_id": meterID},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(port.OfpPort.PortNo),
fu.EthType(0x888e),
},
Actions: []*ofp.OfpAction{
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
// Don't add meterID 0
if meterID == 0 {
delete(fa.KV, "meter_id")
}
matchFields := make([]*ofp.OfpOxmField, 0)
for _, val := range fa.MatchFields {
matchFields = append(matchFields, &ofp.OfpOxmField{Field: &ofp.OfpOxmField_OfbField{OfbField: val}})
}
f := ofp.FlowTableUpdate{FlowMod: makeSimpleFlowMod(fa), Id: ld.Id}
ui := uuid.New()
ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(volthaSerialNumberKey, ui.String()))
if response, err := stub.UpdateLogicalDeviceFlowTable(ctx, &f); err != nil {
ch <- err
} else {
ch <- response
}
}
func getNumUniPort(ld *voltha.LogicalDevice, lPortNos ...uint32) int {
num := 0
if len(lPortNos) > 0 {
for _, pNo := range lPortNos {
for _, lPort := range ld.Ports {
if !lPort.RootPort && lPort.OfpPort.PortNo == pNo {
num += 1
}
}
}
} else {
for _, port := range ld.Ports {
if !port.RootPort {
num += 1
}
}
}
return num
}
func filterOutPort(lPort *voltha.LogicalPort, lPortNos ...uint32) bool {
if len(lPortNos) == 0 {
return false
}
for _, pNo := range lPortNos {
if lPort.OfpPort.PortNo == pNo {
return false
}
}
return true
}
func verifyEAPOLFlows(t *testing.T, ld *voltha.LogicalDevice, meterID uint64, lPortNos ...uint32) {
// First get the flows from the logical device
fmt.Println("Info: verifying EAPOL flows")
lFlows := ld.Flows
assert.Equal(t, getNumUniPort(ld, lPortNos...), len(lFlows.Items))
onuDeviceId := ""
// Verify that the flows in the logical device is what was pushed
for _, lPort := range ld.Ports {
if lPort.RootPort {
continue
}
if filterOutPort(lPort, lPortNos...) {
continue
}
onuDeviceId = lPort.DeviceId
var fa *fu.FlowArgs
fa = &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 2000, "meter_id": meterID},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(lPort.OfpPort.PortNo),
fu.EthType(0x888e),
},
Actions: []*ofp.OfpAction{
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
if meterID == 0 {
delete(fa.KV, "meter_id")
}
expectedLdFlow := fu.MkFlowStat(fa)
assert.Equal(t, true, tu.IsFlowPresent(expectedLdFlow, lFlows.Items))
}
// Verify the OLT flows
retrievedOltFlows := allDevices[ld.RootDeviceId].Flows.Items
assert.Equal(t, NumOfOLTs*getNumUniPort(ld, lPortNos...)*1, len(retrievedOltFlows))
for _, lPort := range ld.Ports {
if lPort.RootPort {
continue
}
if filterOutPort(lPort, lPortNos...) {
continue
}
fa := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 2000, "meter_id": meterID},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(1),
fu.TunnelId(uint64(lPort.OfpPort.PortNo)),
fu.EthType(0x888e),
},
Actions: []*ofp.OfpAction{
fu.PushVlan(0x8100),
fu.SetField(fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 4000)),
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
expectedOltFlow := fu.MkFlowStat(fa)
assert.Equal(t, true, tu.IsFlowPresent(expectedOltFlow, retrievedOltFlows))
}
// Verify the ONU flows
retrievedOnuFlows := allDevices[onuDeviceId].Flows.Items
assert.Equal(t, 0, len(retrievedOnuFlows))
}
func verifyNOFlows(t *testing.T, ld *voltha.LogicalDevice, lPortNos ...uint32) {
if len(lPortNos) == 0 {
assert.Equal(t, 0, len(ld.Flows.Items))
for _, d := range allDevices {
if d.ParentId == ld.Id {
assert.Equal(t, 0, len(d.Flows.Items))
}
}
return
}
for _, p := range lPortNos {
// Check absence of flows in logical device for that port
for _, f := range ld.Flows.Items {
assert.NotEqual(t, p, fu.GetInPort(f))
}
// Check absence of flows in the parent device for that port
for _, d := range allDevices {
if d.ParentId == ld.Id {
for _, f := range d.Flows.Items {
assert.NotEqual(t, p, fu.GetTunnelId(f))
}
}
}
// TODO: check flows in child device. Not required for the use cases being tested
}
}
func installEapolFlows(stub voltha.VolthaServiceClient, lDevice *voltha.LogicalDevice, meterID uint64, lPortNos ...uint32) error {
requestNum := 0
combineCh := make(chan interface{})
if len(lPortNos) > 0 {
fmt.Println("Installing EAPOL flows on ports:", lPortNos)
for _, p := range lPortNos {
for _, lport := range lDevice.Ports {
if !lport.RootPort && lport.OfpPort.PortNo == p {
go addEAPOLFlow(stub, lDevice, lport, meterID, combineCh)
requestNum += 1
}
}
}
} else {
fmt.Println("Installing EAPOL flows on logical device", lDevice.Id)
for _, lport := range lDevice.Ports {
if !lport.RootPort {
go addEAPOLFlow(stub, lDevice, lport, meterID, combineCh)
requestNum += 1
}
}
}
receivedResponse := 0
var err error
for {
select {
case res, ok := <-combineCh:
receivedResponse += 1
if !ok {
} else if er, ok := res.(error); ok {
err = er
}
}
if receivedResponse == requestNum {
break
}
}
return err
}
func deleteAllFlows(stub voltha.VolthaServiceClient, lDevice *voltha.LogicalDevice) error {
fmt.Println("Deleting all flows for logical device:", lDevice.Id)
ui := uuid.New()
ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(volthaSerialNumberKey, ui.String()))
ch := make(chan interface{})
defer close(ch)
fa := &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"table_id": uint64(ofp.OfpTable_OFPTT_ALL),
"cookie_mask": 0,
"out_port": uint64(ofp.OfpPortNo_OFPP_ANY),
"out_group": uint64(ofp.OfpGroup_OFPG_ANY),
},
}
cmd := ofp.OfpFlowModCommand_OFPFC_DELETE
fa.Command = &cmd
flowMod := fu.MkSimpleFlowMod(fu.ToOfpOxmField(fa.MatchFields), fa.Actions, fa.Command, fa.KV)
f := ofp.FlowTableUpdate{FlowMod: flowMod, Id: lDevice.Id}
_, err := stub.UpdateLogicalDeviceFlowTable(ctx, &f)
return err
}
func deleteEapolFlow(stub voltha.VolthaServiceClient, lDevice *voltha.LogicalDevice, meterID uint64, lPortNo uint32) error {
fmt.Println("Deleting flows from port ", lPortNo, " of logical device ", lDevice.Id)
ui := uuid.New()
var fa *fu.FlowArgs
ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(volthaSerialNumberKey, ui.String()))
fa = &fu.FlowArgs{
KV: fu.OfpFlowModArgs{"priority": 2000, "meter_id": meterID},
MatchFields: []*ofp.OfpOxmOfbField{
fu.InPort(lPortNo),
fu.EthType(0x888e),
},
Actions: []*ofp.OfpAction{
fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
},
}
matchFields := make([]*ofp.OfpOxmField, 0)
for _, val := range fa.MatchFields {
matchFields = append(matchFields, &ofp.OfpOxmField{Field: &ofp.OfpOxmField_OfbField{OfbField: val}})
}
cmd := ofp.OfpFlowModCommand_OFPFC_DELETE
fa.Command = &cmd
f := ofp.FlowTableUpdate{FlowMod: makeSimpleFlowMod(fa), Id: lDevice.Id}
_, err := stub.UpdateLogicalDeviceFlowTable(ctx, &f)
return err
}
func runInstallEapolFlows(t *testing.T, stub voltha.VolthaServiceClient, meterID uint64, lPortNos ...uint32) {
err := refreshLocalDeviceCache(stub)
assert.Nil(t, err)
for _, ld := range allLogicalDevices {
err = installEapolFlows(stub, ld, meterID, lPortNos...)
assert.Nil(t, err)
}
err = refreshLocalDeviceCache(stub)
assert.Nil(t, err)
for _, ld := range allLogicalDevices {
verifyEAPOLFlows(t, ld, meterID, lPortNos...)
}
}
func runDeleteAllFlows(t *testing.T, stub voltha.VolthaServiceClient) {
fmt.Println("Removing ALL flows ...")
err := refreshLocalDeviceCache(stub)
assert.Nil(t, err)
for _, ld := range allLogicalDevices {
err = deleteAllFlows(stub, ld)
assert.Nil(t, err)
}
err = refreshLocalDeviceCache(stub)
assert.Nil(t, err)
for _, ld := range allLogicalDevices {
verifyNOFlows(t, ld)
}
}
func runDeleteEapolFlows(t *testing.T, stub voltha.VolthaServiceClient, ld *voltha.LogicalDevice, meterID uint64, lPortNos ...uint32) {
err := refreshLocalDeviceCache(stub)
assert.Nil(t, err)
if len(lPortNos) == 0 {
err = deleteAllFlows(stub, ld)
assert.Nil(t, err)
} else {
for _, lPortNo := range lPortNos {
err = deleteEapolFlow(stub, ld, meterID, lPortNo)
assert.Nil(t, err)
}
}
err = refreshLocalDeviceCache(stub)
assert.Nil(t, err)
for _, lde := range allLogicalDevices {
if lde.Id == ld.Id {
verifyNOFlows(t, lde, lPortNos...)
break
}
}
}
func formulateMeterModUpdateRequest(command ofp.OfpMeterModCommand, meterType ofp.OfpMeterBandType, ldID string, rate,
burstSize uint32, meterID uint32) *ofp.MeterModUpdate {
meterModUpdateRequest := &ofp.MeterModUpdate{
Id: ldID,
MeterMod: &ofp.OfpMeterMod{
Command: command,
Flags: 0,
MeterId: meterID,
Bands: []*ofp.OfpMeterBandHeader{{
Type: meterType,
Rate: rate,
BurstSize: burstSize,
Data: nil,
}},
},
}
return meterModUpdateRequest
}
func formulateMeters(rate, burstsize, meterID uint32, flowCount uint32) *ofp.Meters {
// Formulate and return the applied meter band
ofpMeterBandHeaderSlice := []*ofp.OfpMeterBandHeader{{
Type: ofp.OfpMeterBandType_OFPMBT_EXPERIMENTER,
Rate: rate,
BurstSize: burstsize,
}}
BandStats := []*ofp.OfpMeterBandStats{{}}
appliedMeters_Meters := []*ofp.OfpMeterEntry{{
Config: &ofp.OfpMeterConfig{
Flags: 0,
MeterId: meterID,
Bands: ofpMeterBandHeaderSlice,
},
Stats: &ofp.OfpMeterStats{
MeterId: meterID,
BandStats: BandStats,
FlowCount: flowCount,
},
}}
appliedMeters := &ofp.Meters{
Items: appliedMeters_Meters,
}
return appliedMeters
}
func meterAdd(t *testing.T, meterID, rate, burstSize uint32) {
var err error
for _, logicalDevice := range allLogicalDevices {
meterModupdateRequest := formulateMeterModUpdateRequest(ofp.OfpMeterModCommand_OFPMC_ADD,
ofp.OfpMeterBandType_OFPMBT_EXPERIMENTER, logicalDevice.Id, rate, burstSize, meterID)
_, err = stub.UpdateLogicalDeviceMeterTable(context.Background(), meterModupdateRequest)
assert.Nil(t, err)
}
}
func meterMod(t *testing.T, meterID, rate, burstSize uint32) {
for _, logicalDevice := range allLogicalDevices {
meterModUpdateRequest := formulateMeterModUpdateRequest(ofp.OfpMeterModCommand_OFPMC_MODIFY,
ofp.OfpMeterBandType_OFPMBT_EXPERIMENTER, logicalDevice.Id, rate, burstSize, meterID)
_, err := stub.UpdateLogicalDeviceMeterTable(context.Background(), meterModUpdateRequest)
assert.Nil(t, err)
}
}
//MeterDel deletes a meter with given meter-ID
func meterDel(t *testing.T, meterID uint32) {
for _, logicalDevice := range allLogicalDevices {
meterModUpdateRequest := formulateMeterModUpdateRequest(ofp.OfpMeterModCommand_OFPMC_DELETE,
ofp.OfpMeterBandType_OFPMBT_EXPERIMENTER, logicalDevice.Id, uint32(1600), uint32(1600), meterID)
_, err := stub.UpdateLogicalDeviceMeterTable(context.Background(), meterModUpdateRequest)
assert.Nil(t, err)
}
}
// WaitGroup passed to this function must not be nil
func addMultipleMeterSequentially(t *testing.T, startMeterId, endMeterId, rate, burstSize uint32, wg *sync.WaitGroup) {
defer wg.Done()
for meterID := startMeterId; meterID <= endMeterId; meterID++ {
meterAdd(t, meterID, rate, burstSize)
}
}
func modMultipleMeterSequentially(t *testing.T, startMeterId, endMeterId, rate, burstSize uint32, wg *sync.WaitGroup) {
defer wg.Done()
for meterID := startMeterId; meterID <= endMeterId; meterID++ {
meterMod(t, meterID, rate, burstSize)
}
}
func delMultipleMeterSequential(t *testing.T, startMeterId, endMeterId uint32, wg *sync.WaitGroup) {
defer wg.Done()
for meterID := startMeterId; meterID <= endMeterId; meterID++ {
meterDel(t, meterID)
}
}
func verifyMeter(t *testing.T, meterID, rate, burstSize, flowCount uint32) {
expectedMeter := formulateOfpMeterEntry(meterID, flowCount, rate, burstSize)
isMeterPresent := false
for _, lD := range allLogicalDevices {
for _, meter := range lD.Meters.Items {
isMeterPresent = reflect.DeepEqual(meter, expectedMeter)
if isMeterPresent == true {
break
}
}
if isMeterPresent {
break
}
}
if !isMeterPresent {
fmt.Printf("Error : Expected %+v\n", expectedMeter)
}
assert.Equal(t, true, isMeterPresent)
}
func verifyNoMeters(t *testing.T) {
err := refreshLocalDeviceCache(stub)
assert.Nil(t, err)
expectedMeters := &ofp.Meters{}
for _, logicalDevice := range allLogicalDevices {
result := reflect.DeepEqual(logicalDevice.Meters, expectedMeters)
fmt.Println("Meter Present After Delete :--- ", logicalDevice.Meters)
assert.Equal(t, true, result)
}
}
func formulateOfpMeterEntry(meterID, flowCount, rate, burstSize uint32) *ofp.OfpMeterEntry {
value := &ofp.OfpMeterEntry{
Config: &ofp.OfpMeterConfig{
MeterId: meterID,
Bands: []*ofp.OfpMeterBandHeader{{
Type: ofp.OfpMeterBandType_OFPMBT_EXPERIMENTER,
Rate: rate,
BurstSize: burstSize,
}},
},
Stats: &ofp.OfpMeterStats{
MeterId: meterID,
FlowCount: flowCount,
BandStats: []*ofp.OfpMeterBandStats{{}},
},
}
return value
}
func deleteAllMeters(t *testing.T) {
err := refreshLocalDeviceCache(stub)
assert.Nil(t, err)
for _, lD := range allLogicalDevices {
for _, meter := range lD.Meters.Items {
meterDel(t, meter.Config.MeterId)
}
}
}
func installMultipleFlowsWithMeter(t *testing.T, startMeterId, stopMeterId uint64, wg *sync.WaitGroup) {
defer wg.Done()
err := refreshLocalDeviceCache(stub)
assert.Nil(t, err)
for meterID := startMeterId; meterID <= stopMeterId; meterID++ {
runInstallEapolFlows(t, stub, uint64(meterID), 100, 101)
}
}
func createAndEnableDevices(t *testing.T) {
err := tu.SetAllLogLevel(stub, voltha.Logging{Level: common.LogLevel_WARNING})
assert.Nil(t, err)
err = tu.SetLogLevel(stub, voltha.Logging{Level: common.LogLevel_DEBUG, PackageName: "github.com/opencord/voltha-go/rw_core/core"})
assert.Nil(t, err)
startTime := time.Now()
//Pre-provision the parent device
oltDevice, err := tu.PreProvisionDevice(stub)
assert.Nil(t, err)
fmt.Println("Creation of ", NumOfOLTs, " OLT devices took:", time.Since(startTime))
startTime = time.Now()
//Enable all parent device - this will enable the child devices as well as validate the child devices
err = tu.EnableDevice(stub, oltDevice, NumOfONUsPerOLT)
assert.Nil(t, err)
fmt.Println("Enabling of OLT device took:", time.Since(startTime))
// Wait until the core and adapters sync up after an enabled
time.Sleep(time.Duration(math.Max(10, float64(NumOfOLTs*NumOfONUsPerOLT)/2)) * time.Second)
err = tu.VerifyDevices(stub, NumOfONUsPerOLT)
assert.Nil(t, err)
lds, err := tu.VerifyLogicalDevices(stub, oltDevice, NumOfONUsPerOLT)
assert.Nil(t, err)
assert.Equal(t, 1, len(lds.Items))
}
func TestFlowManagement(t *testing.T) {
//1. Test creation and activation of the devices. This will validate the devices as well as the logical device created
createAndEnableDevices(t)
//2. Test installation of EAPOL flows
runInstallEapolFlows(t, stub, 0)
//3. Test deletion of all EAPOL flows
runDeleteAllFlows(t, stub)
//4. Test installation of EAPOL flows on specific ports
runInstallEapolFlows(t, stub, 0, 101, 102)
lds, err := tu.ListLogicalDevices(stub)
assert.Nil(t, err)
//5. Test deletion of EAPOL on a specific port for a given logical device
runDeleteEapolFlows(t, stub, lds.Items[0], 0, 101)
}
// Meter Add Test
func TestMeters(t *testing.T) {
//1. Start test for meter add
meterAdd(t, 1, 1600, 1600)
err := refreshLocalDeviceCache(stub)
assert.Nil(t, err)
verifyMeter(t, 1, 1600, 1600, 0)
//2. Start test for meter mod
meterMod(t, 1, 6400, 6400)
err = refreshLocalDeviceCache(stub)
assert.Nil(t, err)
verifyMeter(t, 1, 6400, 6400, 0)
//3. Start test for meter del
meterDel(t, 1)
err = refreshLocalDeviceCache(stub)
assert.Nil(t, err)
verifyNoMeters(t)
}
func TestFlowManagementWithMeters(t *testing.T) {
//1. Delete existing flows
for _, logicaldevice := range allLogicalDevices {
err := deleteAllFlows(stub, logicaldevice)
assert.Nil(t, err)
}
deleteAllMeters(t)
//2. Refresh the local cache so that the changes are reflected here
err := refreshLocalDeviceCache(stub)
assert.Nil(t, err)
//3. Add meter with ID
meterAdd(t, 1, 1600, 1600)
err = refreshLocalDeviceCache(stub)
assert.Nil(t, err)
//4. Verify that the meter is installed
verifyMeter(t, 1, 1600, 1600, 0)
//3. Test installation of EAPOL flows with meter (Add and verify the flow)
runInstallEapolFlows(t, stub, MeterIDStart)
//4. Test deletion of all EAPOL flows
runDeleteAllFlows(t, stub)
//5. Test installation of EAPOL flows on specific ports with meter (Add and verify the flows)
runInstallEapolFlows(t, stub, MeterIDStart, 101, 102)
lds, err := tu.ListLogicalDevices(stub)
assert.Nil(t, err)
//6. Test deletion of EAPOL on a specific port for a given logical device
runDeleteEapolFlows(t, stub, lds.Items[0], MeterIDStart, 101)
}
func TestMultipleMeterAddSequential(t *testing.T) {
//1. Delete existing flows
for _, logicaldevice := range allLogicalDevices {
err := deleteAllFlows(stub, logicaldevice)
assert.Nil(t, err)
}
// Delete All The Previously Installed Meters
deleteAllMeters(t)
// Verify that no meters are present
err := refreshLocalDeviceCache(stub)
assert.Nil(t, err)
verifyNoMeters(t)
//2. Add Meter Sequentially
// Here wait-group is not required, creating and passing wait-group only because the function addMultipleMeterSequentially
// expects wait group.
var wg sync.WaitGroup
wg.Add(1)
addMultipleMeterSequentially(t, MeterIDStart, MeterIDStop, 1600, 1600, &wg)
err = refreshLocalDeviceCache(stub)
assert.Nil(t, err)
//3. Verify that the meters are installed
for meterID := MeterIDStart; meterID <= MeterIDStop; meterID++ {
verifyMeter(t, uint32(meterID), 1600, 1600, 0)
}
}
func TestMeterDeleteParallel(t *testing.T) {
var wg sync.WaitGroup
wg.Add(4)
go delMultipleMeterSequential(t, MeterIDStart, MeterIDStop/4, &wg)
go delMultipleMeterSequential(t, MeterIDStop/4+1, (MeterIDStop / 2), &wg)
go delMultipleMeterSequential(t, (MeterIDStop/2)+1, MeterIDStop/4*3, &wg)
go delMultipleMeterSequential(t, (MeterIDStop/4*3)+1, MeterIDStop, &wg)
wg.Wait()
verifyNoMeters(t)
}
func TestMultipleMeterAddParallel(t *testing.T) {
err := refreshLocalDeviceCache(stub)
assert.Nil(t, err)
for _, logicaldevice := range allLogicalDevices {
err := deleteAllFlows(stub, logicaldevice)
assert.Nil(t, err)
}
var wg sync.WaitGroup
wg.Add(4)
go addMultipleMeterSequentially(t, MeterIDStart, (MeterIDStop / 4), 3200, 3200, &wg)
go addMultipleMeterSequentially(t, (MeterIDStop/4 + 1), MeterIDStop/2, 3200, 3200, &wg)
go addMultipleMeterSequentially(t, MeterIDStop/2+1, (MeterIDStop / 4 * 3), 3200, 3200, &wg)
go addMultipleMeterSequentially(t, (MeterIDStop/4*3)+1, MeterIDStop, 3200, 3200, &wg)
wg.Wait()
// Verify the devices
err = tu.VerifyDevices(stub, NumOfONUsPerOLT)
assert.Nil(t, err)
err = refreshLocalDeviceCache(stub)
assert.Nil(t, err)
for meterID := MeterIDStart; meterID <= MeterIDStop; meterID++ {
fmt.Println("Verifying meter ID :", meterID)
verifyMeter(t, uint32(meterID), 3200, 3200, 0)
}
}
func TestMultipleMeterModSequential(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
//1. Modify all the existing meters
modMultipleMeterSequentially(t, MeterIDStart, MeterIDStop, 6400, 6400, &wg)
//2. Verify that the device state is proper after updating so many meters
err := tu.VerifyDevices(stub, NumOfONUsPerOLT)
assert.Nil(t, err)
//3. Refresh logical device cache
err = refreshLocalDeviceCache(stub)
assert.Nil(t, err)
//4. Verify that all the meters got modified
for meterID := MeterIDStart; meterID <= MeterIDStop; meterID++ {
fmt.Println("Verifying meter ID :", meterID)
verifyMeter(t, uint32(meterID), 6400, 6400, 0)
}
}
func TestMultipleMeterModParallel(t *testing.T) {
var wg sync.WaitGroup
wg.Add(4)
//1. Modify all the existing meters
go modMultipleMeterSequentially(t, MeterIDStart, MeterIDStop/4, 1600, 1600, &wg)
go modMultipleMeterSequentially(t, (MeterIDStop/4 + 1), MeterIDStop/2, 1600, 1600, &wg)
go modMultipleMeterSequentially(t, (MeterIDStop/2 + 1), (MeterIDStop / 4 * 3), 1600, 1600, &wg)
go modMultipleMeterSequentially(t, (MeterIDStop/4*3)+1, MeterIDStop, 1600, 1600, &wg)
//2. Verify that the device state is proper after updating so many meters
err := tu.VerifyDevices(stub, NumOfONUsPerOLT)
assert.Nil(t, err)
wg.Wait()
//3. Refresh logical device cache
err = refreshLocalDeviceCache(stub)
assert.Nil(t, err)
//4. Verify that all the meters got modified
for meterID := MeterIDStart; meterID <= MeterIDStop; meterID++ {
fmt.Println("Verifying meter ID :", meterID)
verifyMeter(t, uint32(meterID), 1600, 1600, 0)
}
}
func TestMultipleMeterDelSequential(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
//1. Delete all the given meters sequentially
delMultipleMeterSequential(t, MeterIDStart, MeterIDStop, &wg)
//2. Verify that all the given meters are deleted
verifyNoMeters(t)
}
func TestMultipleFlowsWithMeters(t *testing.T) {
err := refreshLocalDeviceCache(stub)
assert.Nil(t, err)
//1. Delete all the previous flows
for _, lD := range allLogicalDevices {
deleteAllFlows(stub, lD)
}
//2. Add multiple meters for the flows to refer to
var wg sync.WaitGroup
wg.Add(2)
addMultipleMeterSequentially(t, MeterIDStart, 10, 1600, 1600, &wg)
// Adding meter verification here again will increase the time taken by the test. So leaving meter verification
installMultipleFlowsWithMeter(t, 1, 10, &wg)
}
func TestFlowDeletionWithMeterDelete(t *testing.T) {
err := refreshLocalDeviceCache(stub)
assert.Nil(t, err)
//1. Delete all meters
deleteAllMeters(t)
//2. Verify that all the meters are deleted
verifyNoMeters(t)
//3. As no meters are present, so all flows referring to these meters should also be deleted
// Flow referring to meterID 1 - 10 were installed in the above test "TestMultipleFlowsWithMeters"
err = refreshLocalDeviceCache(stub)
assert.Nil(t, err)
for _, lD := range allLogicalDevices {
verifyNOFlows(t, lD, 100, 101)
}
}
func TestAddFlowWithInvalidMeter(t *testing.T) {
var err error
for _, ld := range allLogicalDevices {
// Adding flows with meter-ID which is not present should throw error stating -
// "Meter-referred-by-flow-is-not-found-in-logicaldevice"
err = installEapolFlows(stub, ld, 1, 100, 101)
assert.NotNil(t, err)
}
}
func TestMain(m *testing.M) {
setup()
code := m.Run()
shutdown()
os.Exit(code)
}