| // +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) |
| } |