blob: 9ad3926165d241fc1265037c523ac715e7f1adc3 [file] [log] [blame]
/*
* Copyright 2019-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 api
import (
"context"
"errors"
"fmt"
"math/rand"
"os"
"runtime"
"runtime/pprof"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/golang/protobuf/ptypes/empty"
"github.com/opencord/voltha-go/db/model"
"github.com/opencord/voltha-go/rw_core/config"
"github.com/opencord/voltha-go/rw_core/core/adapter"
"github.com/opencord/voltha-go/rw_core/core/device"
cm "github.com/opencord/voltha-go/rw_core/mocks"
tst "github.com/opencord/voltha-go/rw_core/test"
"github.com/opencord/voltha-lib-go/v4/pkg/db"
"github.com/opencord/voltha-lib-go/v4/pkg/events"
"github.com/opencord/voltha-lib-go/v4/pkg/flows"
"github.com/opencord/voltha-lib-go/v4/pkg/kafka"
mock_etcd "github.com/opencord/voltha-lib-go/v4/pkg/mocks/etcd"
mock_kafka "github.com/opencord/voltha-lib-go/v4/pkg/mocks/kafka"
ofp "github.com/opencord/voltha-protos/v4/go/openflow_13"
"github.com/opencord/voltha-protos/v4/go/voltha"
"github.com/phayes/freeport"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
const numTrapOnNNIFlows = 4
type NBTest struct {
etcdServer *mock_etcd.EtcdServer
deviceMgr *device.Manager
logicalDeviceMgr *device.LogicalManager
adapterMgr *adapter.Manager
kmp kafka.InterContainerProxy
kClient kafka.Client
kEventClient kafka.Client
kvClientPort int
numONUPerOLT int
startingUNIPortNo int
oltAdapter *cm.OLTAdapter
onuAdapter *cm.ONUAdapter
oltAdapterName string
onuAdapterName string
coreInstanceID string
defaultTimeout time.Duration
maxTimeout time.Duration
}
func newNBTest(ctx context.Context) *NBTest {
test := &NBTest{}
// Start the embedded etcd server
var err error
test.etcdServer, test.kvClientPort, err = tst.StartEmbeddedEtcdServer(ctx, "voltha.rwcore.nb.test", "voltha.rwcore.nb.etcd", "error")
if err != nil {
logger.Fatal(ctx, err)
}
// Create the kafka client
test.kClient = mock_kafka.NewKafkaClient()
test.kEventClient = mock_kafka.NewKafkaClient()
test.oltAdapterName = "olt_adapter_mock"
test.onuAdapterName = "onu_adapter_mock"
test.coreInstanceID = "rw-nbi-test"
test.defaultTimeout = 10 * time.Second
test.maxTimeout = 20 * time.Second
return test
}
func (nb *NBTest) startCore(inCompeteMode bool) {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
cfg := config.NewRWCoreFlags()
cfg.CoreTopic = "rw_core"
cfg.EventTopic = "voltha.events"
cfg.DefaultRequestTimeout = nb.defaultTimeout
cfg.DefaultCoreTimeout = nb.defaultTimeout
cfg.KVStoreAddress = "127.0.0.1" + ":" + strconv.Itoa(nb.kvClientPort)
grpcPort, err := freeport.GetFreePort()
if err != nil {
logger.Fatal(ctx, "Cannot get a freeport for grpc")
}
cfg.GrpcAddress = "127.0.0.1" + ":" + strconv.Itoa(grpcPort)
setCoreCompeteMode(inCompeteMode)
client := tst.SetupKVClient(ctx, cfg, nb.coreInstanceID)
backend := &db.Backend{
Client: client,
StoreType: cfg.KVStoreType,
Address: cfg.KVStoreAddress,
Timeout: cfg.KVStoreTimeout,
LivenessChannelInterval: cfg.LiveProbeInterval / 2}
nb.kmp = kafka.NewInterContainerProxy(
kafka.InterContainerAddress(cfg.KafkaAdapterAddress),
kafka.MsgClient(nb.kClient),
kafka.DefaultTopic(&kafka.Topic{Name: cfg.CoreTopic}))
endpointMgr := kafka.NewEndpointManager(backend)
proxy := model.NewDBPath(backend)
nb.adapterMgr = adapter.NewAdapterManager(ctx, proxy, nb.coreInstanceID, nb.kClient)
eventProxy := events.NewEventProxy(events.MsgClient(nb.kEventClient), events.MsgTopic(kafka.Topic{Name: cfg.EventTopic}))
nb.deviceMgr, nb.logicalDeviceMgr = device.NewManagers(proxy, nb.adapterMgr, nb.kmp, endpointMgr, cfg, nb.coreInstanceID, eventProxy)
nb.adapterMgr.Start(ctx)
if err := nb.kmp.Start(ctx); err != nil {
logger.Fatalf(ctx, "Cannot start InterContainerProxy: %s", err)
}
requestProxy := NewAdapterRequestHandlerProxy(nb.deviceMgr, nb.adapterMgr)
if err := nb.kmp.SubscribeWithRequestHandlerInterface(ctx, kafka.Topic{Name: cfg.CoreTopic}, requestProxy); err != nil {
logger.Fatalf(ctx, "Cannot add request handler: %s", err)
}
}
func (nb *NBTest) stopAll(ctx context.Context) {
if nb.kClient != nil {
nb.kClient.Stop(ctx)
}
if nb.kmp != nil {
nb.kmp.Stop(ctx)
}
if nb.etcdServer != nil {
tst.StopEmbeddedEtcdServer(ctx, nb.etcdServer)
}
if nb.kEventClient != nil {
nb.kEventClient.Stop(ctx)
}
}
func (nb *NBTest) verifyLogicalDevices(t *testing.T, oltDevice *voltha.Device, nbi *NBIHandler) {
// Get the latest set of logical devices
logicalDevices, err := nbi.ListLogicalDevices(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.NotNil(t, logicalDevices)
assert.Equal(t, 1, len(logicalDevices.Items))
ld := logicalDevices.Items[0]
ports, err := nbi.ListLogicalDevicePorts(getContext(), &voltha.ID{Id: ld.Id})
assert.Nil(t, err)
assert.NotEqual(t, "", ld.Id)
assert.NotEqual(t, uint64(0), ld.DatapathId)
assert.Equal(t, "olt_adapter_mock", ld.Desc.HwDesc)
assert.Equal(t, "olt_adapter_mock", ld.Desc.SwDesc)
assert.NotEqual(t, "", ld.RootDeviceId)
assert.NotEqual(t, "", ld.Desc.SerialNum)
assert.Equal(t, uint32(256), ld.SwitchFeatures.NBuffers)
assert.Equal(t, uint32(2), ld.SwitchFeatures.NTables)
assert.Equal(t, uint32(15), ld.SwitchFeatures.Capabilities)
assert.Equal(t, 1+nb.numONUPerOLT, len(ports.Items))
assert.Equal(t, oltDevice.ParentId, ld.Id)
//Expected port no
expectedPortNo := make(map[uint32]bool)
expectedPortNo[uint32(2)] = false
for i := 0; i < nb.numONUPerOLT; i++ {
expectedPortNo[uint32(i+100)] = false
}
for _, p := range ports.Items {
assert.Equal(t, p.OfpPort.PortNo, p.DevicePortNo)
assert.Equal(t, uint32(4), p.OfpPort.State)
expectedPortNo[p.OfpPort.PortNo] = true
if strings.HasPrefix(p.Id, "nni") {
assert.Equal(t, true, p.RootPort)
//assert.Equal(t, uint32(2), p.OfpPort.PortNo)
assert.Equal(t, p.Id, fmt.Sprintf("nni-%d", p.DevicePortNo))
} else {
assert.Equal(t, p.Id, fmt.Sprintf("uni-%d", p.DevicePortNo))
assert.Equal(t, false, p.RootPort)
}
}
}
func (nb *NBTest) verifyDevices(t *testing.T, nbi *NBIHandler) {
// Get the latest set of devices
devices, err := nbi.ListDevices(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.NotNil(t, devices)
// A device is ready to be examined when its ADMIN state is ENABLED and OPERATIONAL state is ACTIVE
var vFunction isDeviceConditionSatisfied = func(device *voltha.Device) bool {
return device.AdminState == voltha.AdminState_ENABLED && device.OperStatus == voltha.OperStatus_ACTIVE
}
var wg sync.WaitGroup
for _, device := range devices.Items {
wg.Add(1)
go func(wg *sync.WaitGroup, device *voltha.Device) {
// Wait until the device is in the right state
err := waitUntilDeviceReadiness(device.Id, nb.maxTimeout, vFunction, nbi)
assert.Nil(t, err)
// Now, verify the details of the device. First get the latest update
d, err := nbi.GetDevice(getContext(), &voltha.ID{Id: device.Id})
assert.Nil(t, err)
dPorts, err := nbi.ListDevicePorts(getContext(), &voltha.ID{Id: device.Id})
assert.Nil(t, err)
assert.Equal(t, voltha.AdminState_ENABLED, d.AdminState)
assert.Equal(t, voltha.ConnectStatus_REACHABLE, d.ConnectStatus)
assert.Equal(t, voltha.OperStatus_ACTIVE, d.OperStatus)
assert.Equal(t, d.Type, d.Adapter)
assert.NotEqual(t, "", d.MacAddress)
assert.NotEqual(t, "", d.SerialNumber)
if d.Type == "olt_adapter_mock" {
assert.Equal(t, true, d.Root)
assert.NotEqual(t, "", d.Id)
assert.NotEqual(t, "", d.ParentId)
assert.Nil(t, d.ProxyAddress)
} else if d.Type == "onu_adapter_mock" {
assert.Equal(t, false, d.Root)
assert.NotEqual(t, uint32(0), d.Vlan)
assert.NotEqual(t, "", d.Id)
assert.NotEqual(t, "", d.ParentId)
assert.NotEqual(t, "", d.ProxyAddress.DeviceId)
assert.Equal(t, "olt_adapter_mock", d.ProxyAddress.DeviceType)
} else {
assert.Error(t, errors.New("invalid-device-type"))
}
assert.Equal(t, 2, len(dPorts.Items))
for _, p := range dPorts.Items {
assert.Equal(t, voltha.AdminState_ENABLED, p.AdminState)
assert.Equal(t, voltha.OperStatus_ACTIVE, p.OperStatus)
if p.Type == voltha.Port_ETHERNET_NNI || p.Type == voltha.Port_ETHERNET_UNI {
assert.Equal(t, 0, len(p.Peers))
} else if p.Type == voltha.Port_PON_OLT {
assert.Equal(t, nb.numONUPerOLT, len(p.Peers))
assert.Equal(t, uint32(1), p.PortNo)
} else if p.Type == voltha.Port_PON_ONU {
assert.Equal(t, 1, len(p.Peers))
assert.Equal(t, uint32(1), p.PortNo)
} else {
assert.Error(t, errors.New("invalid-port"))
}
}
wg.Done()
}(&wg, device)
}
wg.Wait()
}
func (nb *NBTest) getADevice(rootDevice bool, nbi *NBIHandler) (*voltha.Device, error) {
devices, err := nbi.ListDevices(getContext(), &empty.Empty{})
if err != nil {
return nil, err
}
for _, d := range devices.Items {
if d.Root == rootDevice {
return d, nil
}
}
return nil, status.Errorf(codes.NotFound, "%v device not found", rootDevice)
}
func (nb *NBTest) testCoreWithoutData(t *testing.T, nbi *NBIHandler) {
lds, err := nbi.ListLogicalDevices(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.NotNil(t, lds)
assert.Equal(t, 0, len(lds.Items))
devices, err := nbi.ListDevices(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.NotNil(t, devices)
assert.Equal(t, 0, len(devices.Items))
adapters, err := nbi.ListAdapters(getContext(), &empty.Empty{})
assert.Equal(t, 0, len(adapters.Items))
assert.Nil(t, err)
assert.NotNil(t, adapters)
}
func (nb *NBTest) testAdapterRegistration(t *testing.T, nbi *NBIHandler) {
ctx := context.Background()
adapters, err := nbi.ListAdapters(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.NotNil(t, adapters)
assert.Equal(t, 2, len(adapters.Items))
for _, a := range adapters.Items {
switch a.Id {
case nb.oltAdapterName:
assert.Equal(t, "Voltha-olt", a.Vendor)
case nb.onuAdapterName:
assert.Equal(t, "Voltha-onu", a.Vendor)
default:
logger.Fatal(ctx, "unregistered-adapter", a.Id)
}
}
deviceTypes, err := nbi.ListDeviceTypes(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.NotNil(t, deviceTypes)
assert.Equal(t, 2, len(deviceTypes.Items))
for _, dt := range deviceTypes.Items {
switch dt.Id {
case nb.oltAdapterName:
assert.Equal(t, nb.oltAdapterName, dt.Adapter)
assert.Equal(t, false, dt.AcceptsBulkFlowUpdate)
assert.Equal(t, true, dt.AcceptsAddRemoveFlowUpdates)
case nb.onuAdapterName:
assert.Equal(t, nb.onuAdapterName, dt.Adapter)
assert.Equal(t, false, dt.AcceptsBulkFlowUpdate)
assert.Equal(t, true, dt.AcceptsAddRemoveFlowUpdates)
default:
logger.Fatal(ctx, "invalid-device-type", dt.Id)
}
}
}
func (nb *NBTest) testCreateDevice(t *testing.T, nbi *NBIHandler) {
// Create a valid device
oltDevice, err := nbi.CreateDevice(getContext(), &voltha.Device{Type: nb.oltAdapterName, MacAddress: "aa:bb:cc:cc:ee:ee"})
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
device, err := nbi.GetDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
assert.NotNil(t, device)
assert.Equal(t, oltDevice.String(), device.String())
// Try to create the same device
_, err = nbi.CreateDevice(getContext(), &voltha.Device{Type: nb.oltAdapterName, MacAddress: "aa:bb:cc:cc:ee:ee"})
assert.NotNil(t, err)
assert.Equal(t, "device is already pre-provisioned", err.Error())
// Try to create a device with invalid data
_, err = nbi.CreateDevice(getContext(), &voltha.Device{Type: nb.oltAdapterName})
assert.NotNil(t, err)
assert.Equal(t, "no-device-info-present; MAC or HOSTIP&PORT", err.Error())
// Ensure we only have 1 device in the Core
devices, err := nbi.ListDevices(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.NotNil(t, devices)
assert.Equal(t, 1, len(devices.Items))
assert.Equal(t, oltDevice.String(), devices.Items[0].String())
//Remove the device
_, err = nbi.DeleteDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
//Ensure there are no devices in the Core now - wait until condition satisfied or timeout
var vFunction isDevicesConditionSatisfied = func(devices *voltha.Devices) bool {
return devices != nil && len(devices.Items) == 0
}
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vFunction)
assert.Nil(t, err)
}
func (nb *NBTest) enableDevice(t *testing.T, nbi *NBIHandler, oltDevice *voltha.Device) {
// Create a logical device monitor will automatically send trap and eapol flows to the devices being enables
var wg sync.WaitGroup
wg.Add(1)
go nb.monitorLogicalDevice(t, nbi, 1, nb.numONUPerOLT, &wg, false, false)
// Enable the oltDevice
_, err := nbi.EnableDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
// Wait for the logical device to be in the ready state
var vldFunction = func(ports []*voltha.LogicalPort) bool {
return len(ports) == nb.numONUPerOLT+1
}
err = waitUntilLogicalDevicePortsReadiness(oltDevice.Id, nb.maxTimeout, nbi, vldFunction)
assert.Nil(t, err)
// Verify that the devices have been setup correctly
nb.verifyDevices(t, nbi)
// Get latest oltDevice data
oltDevice, err = nbi.GetDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
// Verify that the logical device has been setup correctly
nb.verifyLogicalDevices(t, oltDevice, nbi)
// Wait until all flows has been sent to the devices successfully
wg.Wait()
}
func (nb *NBTest) testForceDeletePreProvDevice(t *testing.T, nbi *NBIHandler) {
// Create a valid device
oltDevice, err := nbi.CreateDevice(getContext(), &voltha.Device{Type: nb.oltAdapterName, MacAddress: "aa:bb:cc:cc:ee:ee"})
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
device, err := nbi.GetDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
assert.NotNil(t, device)
assert.Equal(t, oltDevice.String(), device.String())
// Ensure we only have 1 device in the Core
devices, err := nbi.ListDevices(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.NotNil(t, devices)
assert.Equal(t, 1, len(devices.Items))
assert.Equal(t, oltDevice.String(), devices.Items[0].String())
//Remove the device forcefully
_, err = nbi.ForceDeleteDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
//Ensure there are no devices in the Core now - wait until condition satisfied or timeout
var vFunction isDevicesConditionSatisfied = func(devices *voltha.Devices) bool {
return devices != nil && len(devices.Items) == 0
}
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vFunction)
assert.Nil(t, err)
}
func (nb *NBTest) testForceDeleteEnabledDevice(t *testing.T, nbi *NBIHandler) {
// Create a valid device
oltDevice, err := nbi.CreateDevice(getContext(), &voltha.Device{Type: nb.oltAdapterName, MacAddress: "aa:bb:cc:cc:ee:ee"})
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
device, err := nbi.GetDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
assert.NotNil(t, device)
assert.Equal(t, oltDevice.String(), device.String())
nb.enableDevice(t, nbi, oltDevice)
//Remove the device forcefully
_, err = nbi.ForceDeleteDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
//Ensure there are no devices in the Core now - wait until condition satisfied or timeout
var vFunction isDevicesConditionSatisfied = func(devices *voltha.Devices) bool {
return devices != nil && len(devices.Items) == 0
}
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vFunction)
assert.Nil(t, err)
}
func (nb *NBTest) testDeletePreProvDevice(t *testing.T, nbi *NBIHandler) {
// Create a valid device
oltDevice, err := nbi.CreateDevice(getContext(), &voltha.Device{Type: nb.oltAdapterName, MacAddress: "aa:bb:cc:cc:ee:ee"})
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
device, err := nbi.GetDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
assert.NotNil(t, device)
assert.Equal(t, oltDevice.String(), device.String())
// Ensure we only have 1 device in the Core
devices, err := nbi.ListDevices(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.NotNil(t, devices)
assert.Equal(t, 1, len(devices.Items))
assert.Equal(t, oltDevice.String(), devices.Items[0].String())
//Remove the device forcefully
_, err = nbi.DeleteDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
//Ensure there are no devices in the Core now - wait until condition satisfied or timeout
var vFunction isDevicesConditionSatisfied = func(devices *voltha.Devices) bool {
return devices != nil && len(devices.Items) == 0
}
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vFunction)
assert.Nil(t, err)
}
func (nb *NBTest) testDeleteEnabledDevice(t *testing.T, nbi *NBIHandler) {
// Create a valid device
oltDevice, err := nbi.CreateDevice(getContext(), &voltha.Device{Type: nb.oltAdapterName, MacAddress: "aa:bb:cc:cc:ee:ee"})
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
device, err := nbi.GetDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
assert.NotNil(t, device)
assert.Equal(t, oltDevice.String(), device.String())
nb.enableDevice(t, nbi, oltDevice)
//Remove the device
_, err = nbi.DeleteDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
//Ensure there are no devices in the Core now - wait until condition satisfied or timeout
var vFunction isDevicesConditionSatisfied = func(devices *voltha.Devices) bool {
return devices != nil && len(devices.Items) == 0
}
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vFunction)
assert.Nil(t, err)
}
func (nb *NBTest) testForceDeleteDeviceFailure(t *testing.T, nbi *NBIHandler) {
// Create a valid device
oltDevice, err := nbi.CreateDevice(getContext(), &voltha.Device{Type: nb.oltAdapterName, MacAddress: "aa:bb:cc:cc:ee:ee"})
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
device, err := nbi.GetDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
assert.NotNil(t, device)
assert.Equal(t, oltDevice.String(), device.String())
nb.enableDevice(t, nbi, oltDevice)
nb.oltAdapter.SetDeleteAction(true)
//Remove the device
_, err = nbi.ForceDeleteDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
//Ensure there are no devices in the Core although delete was failed - wait until condition satisfied or timeout
var vFunction isDevicesConditionSatisfied = func(devices *voltha.Devices) bool {
return devices != nil && len(devices.Items) == 0
}
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vFunction)
assert.Nil(t, err)
}
func (nb *NBTest) testDeleteDeviceFailure(t *testing.T, nbi *NBIHandler) {
// Create a valid device
oltDevice, err := nbi.CreateDevice(getContext(), &voltha.Device{Type: nb.oltAdapterName, MacAddress: "aa:bb:cc:cc:ee:ee"})
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
device, err := nbi.GetDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
assert.NotNil(t, device)
assert.Equal(t, oltDevice.String(), device.String())
nb.enableDevice(t, nbi, oltDevice)
nb.oltAdapter.SetDeleteAction(true)
//Remove the device
_, err = nbi.DeleteDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
//Ensure there are devices in the Core as delete was failed - wait until condition satisfied or timeout
var vFunction1 isDevicesConditionSatisfied = func(devices *voltha.Devices) bool {
state, err := nbi.GetTransientState(getContext(), oltDevice.Id)
if err != nil {
return false
}
return devices != nil && len(devices.Items) == (nb.numONUPerOLT+1) &&
state == voltha.DeviceTransientState_DELETE_FAILED
}
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vFunction1)
assert.Nil(t, err)
nb.oltAdapter.SetDeleteAction(false)
// Now Force Delete this device
_, err = nbi.ForceDeleteDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
//Ensure there are devices in the Core as delete was failed - wait until condition satisfied or timeout
var vFunction2 isDevicesConditionSatisfied = func(devices *voltha.Devices) bool {
return devices != nil && len(devices.Items) == 0
}
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vFunction2)
assert.Nil(t, err)
}
func (nb *NBTest) testEnableDevice(t *testing.T, nbi *NBIHandler) {
// Create a device that has no adapter registered
oltDeviceNoAdapter, err := nbi.CreateDevice(getContext(), &voltha.Device{Type: "noAdapterRegistered", MacAddress: "aa:bb:cc:cc:ee:ff"})
assert.Nil(t, err)
assert.NotNil(t, oltDeviceNoAdapter)
// Try to enable the oltDevice and check the error message
_, err = nbi.EnableDevice(getContext(), &voltha.ID{Id: oltDeviceNoAdapter.Id})
assert.NotNil(t, err)
assert.Equal(t, "adapter-not-registered-for-device-type noAdapterRegistered", err.Error())
//Remove the device
_, err = nbi.DeleteDevice(getContext(), &voltha.ID{Id: oltDeviceNoAdapter.Id})
assert.Nil(t, err)
//Ensure there are no devices in the Core now - wait until condition satisfied or timeout
var vdFunction isDevicesConditionSatisfied = func(devices *voltha.Devices) bool {
return devices != nil && len(devices.Items) == 0
}
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vdFunction)
assert.Nil(t, err)
// Create a logical device monitor will automatically send trap and eapol flows to the devices being enables
var wg sync.WaitGroup
wg.Add(1)
go nb.monitorLogicalDevice(t, nbi, 1, nb.numONUPerOLT, &wg, false, false)
// Create the device with valid data
oltDevice, err := nbi.CreateDevice(getContext(), &voltha.Device{Type: nb.oltAdapterName, MacAddress: "aa:bb:cc:cc:ee:ee"})
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
// Verify oltDevice exist in the core
devices, err := nbi.ListDevices(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.Equal(t, 1, len(devices.Items))
assert.Equal(t, oltDevice.Id, devices.Items[0].Id)
// Enable the oltDevice
_, err = nbi.EnableDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
// Wait for the logical device to be in the ready state
var vldFunction = func(ports []*voltha.LogicalPort) bool {
return len(ports) == nb.numONUPerOLT+1
}
err = waitUntilLogicalDevicePortsReadiness(oltDevice.Id, nb.maxTimeout, nbi, vldFunction)
assert.Nil(t, err)
// Verify that the devices have been setup correctly
nb.verifyDevices(t, nbi)
// Get latest oltDevice data
oltDevice, err = nbi.GetDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
// Verify that the logical device has been setup correctly
nb.verifyLogicalDevices(t, oltDevice, nbi)
// Wait until all flows has been sent to the devices successfully
wg.Wait()
}
func (nb *NBTest) testDisableAndReEnableRootDevice(t *testing.T, nbi *NBIHandler) {
//Get an OLT device
oltDevice, err := nb.getADevice(true, nbi)
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
// Disable the oltDevice
_, err = nbi.DisableDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
// Wait for the old device to be disabled
var vdFunction isDeviceConditionSatisfied = func(device *voltha.Device) bool {
return device.AdminState == voltha.AdminState_DISABLED && device.OperStatus == voltha.OperStatus_UNKNOWN
}
err = waitUntilDeviceReadiness(oltDevice.Id, nb.maxTimeout, vdFunction, nbi)
assert.Nil(t, err)
// Verify that all onu devices are disabled as well
onuDevices, err := nb.deviceMgr.GetAllChildDevices(getContext(), oltDevice.Id)
assert.Nil(t, err)
for _, onu := range onuDevices.Items {
err = waitUntilDeviceReadiness(onu.Id, nb.maxTimeout, vdFunction, nbi)
assert.Nil(t, err)
}
// Wait for the logical device to satisfy the expected condition
var vlFunction = func(ports []*voltha.LogicalPort) bool {
for _, lp := range ports {
if (lp.OfpPort.Config&uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN) != lp.OfpPort.Config) ||
lp.OfpPort.State != uint32(ofp.OfpPortState_OFPPS_LINK_DOWN) {
return false
}
}
return true
}
err = waitUntilLogicalDevicePortsReadiness(oltDevice.Id, nb.maxTimeout, nbi, vlFunction)
assert.Nil(t, err)
// Reenable the oltDevice
_, err = nbi.EnableDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
// Wait for the old device to be enabled
vdFunction = func(device *voltha.Device) bool {
return device.AdminState == voltha.AdminState_ENABLED && device.OperStatus == voltha.OperStatus_ACTIVE
}
err = waitUntilDeviceReadiness(oltDevice.Id, nb.maxTimeout, vdFunction, nbi)
assert.Nil(t, err)
// Verify that all onu devices are enabled as well
onuDevices, err = nb.deviceMgr.GetAllChildDevices(getContext(), oltDevice.Id)
assert.Nil(t, err)
for _, onu := range onuDevices.Items {
err = waitUntilDeviceReadiness(onu.Id, nb.maxTimeout, vdFunction, nbi)
assert.Nil(t, err)
}
// Wait for the logical device to satisfy the expected condition
vlFunction = func(ports []*voltha.LogicalPort) bool {
for _, lp := range ports {
if (lp.OfpPort.Config&^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN) != lp.OfpPort.Config) ||
lp.OfpPort.State != uint32(ofp.OfpPortState_OFPPS_LIVE) {
return false
}
}
return true
}
err = waitUntilLogicalDevicePortsReadiness(oltDevice.Id, nb.maxTimeout, nbi, vlFunction)
assert.Nil(t, err)
}
func (nb *NBTest) testDisableAndDeleteAllDevice(t *testing.T, nbi *NBIHandler) {
//Get an OLT device
oltDevice, err := nb.getADevice(true, nbi)
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
// Disable the oltDevice
_, err = nbi.DisableDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
// Wait for the olt device to be disabled
var vdFunction isDeviceConditionSatisfied = func(device *voltha.Device) bool {
return device.AdminState == voltha.AdminState_DISABLED && device.OperStatus == voltha.OperStatus_UNKNOWN
}
err = waitUntilDeviceReadiness(oltDevice.Id, nb.maxTimeout, vdFunction, nbi)
assert.Nil(t, err)
// Verify that all onu devices are disabled as well
onuDevices, err := nb.deviceMgr.GetAllChildDevices(getContext(), oltDevice.Id)
assert.Nil(t, err)
for _, onu := range onuDevices.Items {
err = waitUntilDeviceReadiness(onu.Id, nb.maxTimeout, vdFunction, nbi)
assert.Nil(t, err)
}
// Delete the oltDevice
_, err = nbi.DeleteDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
var vFunction isDevicesConditionSatisfied = func(devices *voltha.Devices) bool {
return devices != nil && len(devices.Items) == 0
}
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vFunction)
assert.Nil(t, err)
// Wait for absence of logical device
var vlFunction isLogicalDevicesConditionSatisfied = func(lds *voltha.LogicalDevices) bool {
return lds != nil && len(lds.Items) == 0
}
err = waitUntilConditionForLogicalDevices(nb.maxTimeout, nbi, vlFunction)
assert.Nil(t, err)
}
func (nb *NBTest) deleteAllDevices(t *testing.T, nbi *NBIHandler) {
devices, _ := nbi.ListDevices(getContext(), &empty.Empty{})
if len(devices.Items) == 0 {
// Nothing to do
return
}
//Get an OLT device
oltDevice, err := nb.getADevice(true, nbi)
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
// Delete the oltDevice
_, err = nbi.DeleteDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
// Wait for all devices to be deleted
vFunction := func(devices *voltha.Devices) bool {
return devices != nil && len(devices.Items) == 0
}
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vFunction)
assert.Nil(t, err)
// Wait for absence of logical device
vlFunction := func(lds *voltha.LogicalDevices) bool {
return lds != nil && len(lds.Items) == 0
}
err = waitUntilConditionForLogicalDevices(nb.maxTimeout, nbi, vlFunction)
assert.Nil(t, err)
}
func (nb *NBTest) testEnableAndDeleteAllDevice(t *testing.T, nbi *NBIHandler) {
//Create the device with valid data
oltDevice, err := nbi.CreateDevice(getContext(), &voltha.Device{Type: nb.oltAdapterName, MacAddress: "aa:bb:cc:cc:ee:ee"})
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
//Get an OLT device
oltDevice, err = nb.getADevice(true, nbi)
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
// Enable the oltDevice
_, err = nbi.EnableDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
// Wait for the logical device to be in the ready state
var vldFunction = func(ports []*voltha.LogicalPort) bool {
return len(ports) == nb.numONUPerOLT+1
}
err = waitUntilLogicalDevicePortsReadiness(oltDevice.Id, nb.maxTimeout, nbi, vldFunction)
assert.Nil(t, err)
//Get all child devices
onuDevices, err := nb.deviceMgr.GetAllChildDevices(getContext(), oltDevice.Id)
assert.Nil(t, err)
// Wait for the all onu devices to be enabled
var vdFunction isDeviceConditionSatisfied = func(device *voltha.Device) bool {
return device.AdminState == voltha.AdminState_ENABLED
}
for _, onu := range onuDevices.Items {
err = waitUntilDeviceReadiness(onu.Id, nb.maxTimeout, vdFunction, nbi)
assert.Nil(t, err)
}
// Wait for each onu device to get deleted
var vdFunc isDeviceConditionSatisfied = func(device *voltha.Device) bool {
return device == nil
}
// Delete the onuDevice
for _, onu := range onuDevices.Items {
_, err = nbi.DeleteDevice(getContext(), &voltha.ID{Id: onu.Id})
assert.Nil(t, err)
err = waitUntilDeviceReadiness(onu.Id, nb.maxTimeout, vdFunc, nbi)
assert.Nil(t, err)
}
// Disable the oltDevice
_, err = nbi.DisableDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
// Wait for the olt device to be disabled
var vFunction isDeviceConditionSatisfied = func(device *voltha.Device) bool {
return device.AdminState == voltha.AdminState_DISABLED && device.OperStatus == voltha.OperStatus_UNKNOWN
}
err = waitUntilDeviceReadiness(oltDevice.Id, nb.maxTimeout, vFunction, nbi)
assert.Nil(t, err)
// Delete the oltDevice
_, err = nbi.DeleteDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
var vFunc isDevicesConditionSatisfied = func(devices *voltha.Devices) bool {
return devices != nil && len(devices.Items) == 0
}
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vFunc)
assert.Nil(t, err)
}
func (nb *NBTest) testDisableAndEnablePort(t *testing.T, nbi *NBIHandler) {
//Get an OLT device
var cp *voltha.Port
oltDevice, err := nb.getADevice(true, nbi)
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
oltPorts, err := nbi.ListDevicePorts(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
for _, cp = range oltPorts.Items {
if cp.Type == voltha.Port_PON_OLT {
break
}
}
assert.NotNil(t, cp)
cp.DeviceId = oltDevice.Id
// Disable the NW Port of oltDevice
_, err = nbi.DisablePort(getContext(), cp)
assert.Nil(t, err)
// Wait for the olt device Port to be disabled
var vdFunction isDevicePortsConditionSatisfied = func(ports *voltha.Ports) bool {
for _, port := range ports.Items {
if port.PortNo == cp.PortNo {
return port.AdminState == voltha.AdminState_DISABLED
}
}
return false
}
err = waitUntilDevicePortsReadiness(oltDevice.Id, nb.maxTimeout, vdFunction, nbi)
assert.Nil(t, err)
// Wait for the logical device to satisfy the expected condition
var vlFunction = func(ports []*voltha.LogicalPort) bool {
for _, lp := range ports {
if (lp.OfpPort.Config&^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN) != lp.OfpPort.Config) ||
lp.OfpPort.State != uint32(ofp.OfpPortState_OFPPS_LIVE) {
return false
}
}
return true
}
err = waitUntilLogicalDevicePortsReadiness(oltDevice.Id, nb.maxTimeout, nbi, vlFunction)
assert.Nil(t, err)
// Enable the NW Port of oltDevice
_, err = nbi.EnablePort(getContext(), cp)
assert.Nil(t, err)
// Wait for the olt device Port to be enabled
vdFunction = func(ports *voltha.Ports) bool {
for _, port := range ports.Items {
if port.PortNo == cp.PortNo {
return port.AdminState == voltha.AdminState_ENABLED
}
}
return false
}
err = waitUntilDevicePortsReadiness(oltDevice.Id, nb.maxTimeout, vdFunction, nbi)
assert.Nil(t, err)
// Wait for the logical device to satisfy the expected condition
vlFunction = func(ports []*voltha.LogicalPort) bool {
for _, lp := range ports {
if (lp.OfpPort.Config&^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN) != lp.OfpPort.Config) ||
lp.OfpPort.State != uint32(ofp.OfpPortState_OFPPS_LIVE) {
return false
}
}
return true
}
err = waitUntilLogicalDevicePortsReadiness(oltDevice.Id, nb.maxTimeout, nbi, vlFunction)
assert.Nil(t, err)
// Disable a non-PON port
for _, cp = range oltPorts.Items {
if cp.Type != voltha.Port_PON_OLT {
break
}
}
assert.NotNil(t, cp)
cp.DeviceId = oltDevice.Id
// Disable the NW Port of oltDevice
_, err = nbi.DisablePort(getContext(), cp)
assert.NotNil(t, err)
}
func (nb *NBTest) testDeviceRebootWhenOltIsEnabled(t *testing.T, nbi *NBIHandler) {
//Get an OLT device
oltDevice, err := nb.getADevice(true, nbi)
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
assert.Equal(t, oltDevice.ConnectStatus, voltha.ConnectStatus_REACHABLE)
assert.Equal(t, oltDevice.AdminState, voltha.AdminState_ENABLED)
// Verify that we have one or more ONUs to start with
onuDevices, err := nb.deviceMgr.GetAllChildDevices(getContext(), oltDevice.Id)
assert.Nil(t, err)
assert.NotNil(t, onuDevices)
assert.Greater(t, len(onuDevices.Items), 0)
// Reboot the OLT and very that Connection Status goes to UNREACHABLE and operation status to UNKNOWN
_, err = nbi.RebootDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
var vlFunction0 = func(d *voltha.Device) bool {
return d.ConnectStatus == voltha.ConnectStatus_UNREACHABLE && d.OperStatus == voltha.OperStatus_UNKNOWN
}
err = waitUntilDeviceReadiness(oltDevice.Id, nb.maxTimeout, vlFunction0, nbi)
assert.Nil(t, err)
// Wait for the logical device to satisfy the expected condition
var vlFunction1 = func(ld *voltha.LogicalDevice) bool {
return ld == nil
}
err = waitUntilLogicalDeviceReadiness(oltDevice.Id, nb.maxTimeout, nbi, vlFunction1)
assert.Nil(t, err)
// Wait for the device to satisfy the expected condition (device does not have flows)
var vlFunction2 = func(d *voltha.Device) bool {
var deviceFlows *ofp.Flows
var err error
if deviceFlows, err = nbi.ListDeviceFlows(getContext(), &voltha.ID{Id: d.Id}); err != nil {
return false
}
return len(deviceFlows.Items) == 0
}
err = waitUntilDeviceReadiness(oltDevice.Id, nb.maxTimeout, vlFunction2, nbi)
assert.Nil(t, err)
// Wait for the device to satisfy the expected condition (there are no child devices)
var vlFunction3 = func(d *voltha.Device) bool {
var devices *voltha.Devices
var err error
if devices, err = nbi.ListDevices(getContext(), nil); err != nil {
return false
}
for _, device := range devices.Items {
if device.ParentId == d.Id {
// We have a child device still left
return false
}
}
return true
}
err = waitUntilDeviceReadiness(oltDevice.Id, nb.maxTimeout, vlFunction3, nbi)
assert.Nil(t, err)
// Update the OLT Connection Status to REACHABLE and operation status to ACTIVE
// Normally, in a real adapter this happens after connection regain via a heartbeat mechanism with real hardware
err = nbi.UpdateDeviceStatus(getContext(), oltDevice.Id, voltha.OperStatus_ACTIVE, voltha.ConnectStatus_REACHABLE)
assert.Nil(t, err)
// Verify the device connection and operation states
oltDevice, err = nb.getADevice(true, nbi)
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
assert.Equal(t, oltDevice.ConnectStatus, voltha.ConnectStatus_REACHABLE)
assert.Equal(t, oltDevice.AdminState, voltha.AdminState_ENABLED)
// Wait for the logical device to satisfy the expected condition
var vlFunction4 = func(ld *voltha.LogicalDevice) bool {
return ld != nil
}
err = waitUntilLogicalDeviceReadiness(oltDevice.Id, nb.maxTimeout, nbi, vlFunction4)
assert.Nil(t, err)
// Verify that logical device is created again
logicalDevices, err := nbi.ListLogicalDevices(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.NotNil(t, logicalDevices)
assert.Equal(t, 1, len(logicalDevices.Items))
// Verify that we have no ONUs left
onuDevices, err = nb.deviceMgr.GetAllChildDevices(getContext(), oltDevice.Id)
assert.Nil(t, err)
assert.NotNil(t, onuDevices)
assert.Equal(t, 0, len(onuDevices.Items))
}
func (nb *NBTest) testStartOmciTestAction(t *testing.T, nbi *NBIHandler) {
// -----------------------------------------------------------------------
// SubTest 1: Omci test action should fail due to nonexistent device id
request := &voltha.OmciTestRequest{Id: "123", Uuid: "456"}
_, err := nbi.StartOmciTestAction(getContext(), request)
assert.NotNil(t, err)
assert.Equal(t, "rpc error: code = NotFound desc = 123", err.Error())
// -----------------------------------------------------------------------
// SubTest 2: Error should be returned for device with no adapter registered
// Create a device that has no adapter registered
deviceNoAdapter, err := nbi.CreateDevice(getContext(), &voltha.Device{Type: "noAdapterRegisteredOmciTest", MacAddress: "aa:bb:cc:cc:ee:01"})
assert.Nil(t, err)
assert.NotNil(t, deviceNoAdapter)
// Omci test action should fail due to nonexistent adapter
request = &voltha.OmciTestRequest{Id: deviceNoAdapter.Id, Uuid: "456"}
_, err = nbi.StartOmciTestAction(getContext(), request)
assert.NotNil(t, err)
assert.Equal(t, "adapter-not-registered-for-device-type noAdapterRegisteredOmciTest", err.Error())
//Remove the device
_, err = nbi.DeleteDevice(getContext(), &voltha.ID{Id: deviceNoAdapter.Id})
assert.Nil(t, err)
//Ensure there are no devices in the Core now - wait until condition satisfied or timeout
var vFunction isDevicesConditionSatisfied = func(devices *voltha.Devices) bool {
return devices != nil && len(devices.Items) == 0
}
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vFunction)
assert.Nil(t, err)
// -----------------------------------------------------------------------
// SubTest 3: Omci test action should succeed on valid ONU
// Create the device with valid data
oltDevice, err := nbi.CreateDevice(getContext(), &voltha.Device{Type: nb.oltAdapterName, MacAddress: "aa:bb:cc:cc:ee:ee"})
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
// Verify oltDevice exist in the core
devices, err := nbi.ListDevices(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.Equal(t, 1, len(devices.Items))
assert.Equal(t, oltDevice.Id, devices.Items[0].Id)
// Enable the oltDevice
_, err = nbi.EnableDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
// Wait for the logical device to be in the ready state
var vldFunction = func(ports []*voltha.LogicalPort) bool {
return len(ports) == nb.numONUPerOLT+1
}
err = waitUntilLogicalDevicePortsReadiness(oltDevice.Id, nb.maxTimeout, nbi, vldFunction)
assert.Nil(t, err)
// Wait for the olt device to be enabled
vdFunction := func(device *voltha.Device) bool {
return device.AdminState == voltha.AdminState_ENABLED && device.OperStatus == voltha.OperStatus_ACTIVE
}
err = waitUntilDeviceReadiness(oltDevice.Id, nb.maxTimeout, vdFunction, nbi)
assert.Nil(t, err)
onuDevices, err := nb.deviceMgr.GetAllChildDevices(getContext(), oltDevice.Id)
assert.Nil(t, err)
assert.Greater(t, len(onuDevices.Items), 0)
onuDevice := onuDevices.Items[0]
// Omci test action should succeed
request = &voltha.OmciTestRequest{Id: onuDevice.Id, Uuid: "456"}
resp, err := nbi.StartOmciTestAction(getContext(), request)
assert.Nil(t, err)
assert.Equal(t, resp.Result, voltha.TestResponse_SUCCESS)
//Remove the device
_, err = nbi.DeleteDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
//Ensure there are no devices in the Core now - wait until condition satisfied or timeout
err = waitUntilConditionForDevices(nb.maxTimeout, nbi, vFunction)
assert.Nil(t, err)
}
func makeSimpleFlowMod(fa *flows.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 flows.MkSimpleFlowMod(matchFields, fa.Actions, fa.Command, fa.KV)
}
func createMetadata(cTag int, techProfile int, port int) uint64 {
md := 0
md = (md | (cTag & 0xFFFF)) << 16
md = (md | (techProfile & 0xFFFF)) << 32
return uint64(md | (port & 0xFFFFFFFF))
}
func (nb *NBTest) verifyLogicalDeviceFlowCount(t *testing.T, nbi *NBIHandler, numNNIPorts int, numUNIPorts int, flowAddFail bool) {
expectedNumFlows := numNNIPorts*numTrapOnNNIFlows + numNNIPorts*numUNIPorts
if flowAddFail {
expectedNumFlows = 0
}
// Wait for logical device to have the flows (or none
var vlFunction isLogicalDevicesConditionSatisfied = func(lds *voltha.LogicalDevices) bool {
id := ""
if lds != nil {
id = lds.Items[0].Id
}
flws, _ := nbi.ListLogicalDeviceFlows(getContext(), &voltha.ID{Id: id})
return lds != nil && len(lds.Items) == 1 && len(flws.Items) == expectedNumFlows
}
// No timeout implies a success
err := waitUntilConditionForLogicalDevices(nb.maxTimeout, nbi, vlFunction)
assert.Nil(t, err)
}
func (nb *NBTest) sendTrapFlows(t *testing.T, nbi *NBIHandler, logicalDeviceID string, ports []*voltha.LogicalPort, meterID uint64, startingVlan int) (numNNIPorts, numUNIPorts int) {
// Send flows for the parent device
var nniPorts []*voltha.LogicalPort
var uniPorts []*voltha.LogicalPort
for _, p := range ports {
if p.RootPort {
nniPorts = append(nniPorts, p)
} else {
uniPorts = append(uniPorts, p)
}
}
assert.Equal(t, 1, len(nniPorts))
//assert.Greater(t, len(uniPorts), 1 )
nniPort := nniPorts[0].OfpPort.PortNo
maxInt32 := uint64(0xFFFFFFFF)
controllerPortMask := uint32(4294967293) // will result in 4294967293&0x7fffffff => 2147483645 which is the actual controller port
var fa *flows.FlowArgs
fa = &flows.FlowArgs{
KV: flows.OfpFlowModArgs{"priority": 10000, "buffer_id": maxInt32, "out_port": maxInt32, "out_group": maxInt32, "flags": 1},
MatchFields: []*ofp.OfpOxmOfbField{
flows.InPort(nniPort),
flows.EthType(35020),
},
Actions: []*ofp.OfpAction{
flows.Output(controllerPortMask),
},
}
flowLLDP := ofp.FlowTableUpdate{FlowMod: makeSimpleFlowMod(fa), Id: logicalDeviceID}
_, err := nbi.UpdateLogicalDeviceFlowTable(getContext(), &flowLLDP)
assert.Nil(t, err)
fa = &flows.FlowArgs{
KV: flows.OfpFlowModArgs{"priority": 10000, "buffer_id": maxInt32, "out_port": maxInt32, "out_group": maxInt32, "flags": 1},
MatchFields: []*ofp.OfpOxmOfbField{
flows.InPort(nniPort),
flows.EthType(2048),
flows.IpProto(17),
flows.UdpSrc(67),
flows.UdpDst(68),
},
Actions: []*ofp.OfpAction{
flows.Output(controllerPortMask),
},
}
flowIPV4 := ofp.FlowTableUpdate{FlowMod: makeSimpleFlowMod(fa), Id: logicalDeviceID}
_, err = nbi.UpdateLogicalDeviceFlowTable(getContext(), &flowIPV4)
assert.Nil(t, err)
fa = &flows.FlowArgs{
KV: flows.OfpFlowModArgs{"priority": 10000, "buffer_id": maxInt32, "out_port": maxInt32, "out_group": maxInt32, "flags": 1},
MatchFields: []*ofp.OfpOxmOfbField{
flows.InPort(nniPort),
flows.EthType(34525),
flows.IpProto(17),
flows.UdpSrc(546),
flows.UdpDst(547),
},
Actions: []*ofp.OfpAction{
flows.Output(controllerPortMask),
},
}
flowIPV6 := ofp.FlowTableUpdate{FlowMod: makeSimpleFlowMod(fa), Id: logicalDeviceID}
_, err = nbi.UpdateLogicalDeviceFlowTable(getContext(), &flowIPV6)
assert.Nil(t, err)
fa = &flows.FlowArgs{
KV: flows.OfpFlowModArgs{"priority": 10000, "buffer_id": maxInt32, "out_port": maxInt32, "out_group": maxInt32, "flags": 1},
MatchFields: []*ofp.OfpOxmOfbField{
flows.InPort(nniPort),
flows.EthType(34915),
},
Actions: []*ofp.OfpAction{
flows.Output(controllerPortMask),
},
}
flowPPPoEP := ofp.FlowTableUpdate{FlowMod: makeSimpleFlowMod(fa), Id: logicalDeviceID}
_, err = nbi.UpdateLogicalDeviceFlowTable(getContext(), &flowPPPoEP)
assert.Nil(t, err)
return len(nniPorts), len(uniPorts)
}
func (nb *NBTest) sendEAPFlows(t *testing.T, nbi *NBIHandler, logicalDeviceID string, port *ofp.OfpPort, vlan int, meterID uint64) {
maxInt32 := uint64(0xFFFFFFFF)
controllerPortMask := uint32(4294967293) // will result in 4294967293&0x7fffffff => 2147483645 which is the actual controller port
fa := &flows.FlowArgs{
KV: flows.OfpFlowModArgs{"priority": 10000, "buffer_id": maxInt32, "out_port": maxInt32, "out_group": maxInt32, "flags": 1, "write_metadata": createMetadata(vlan, 64, 0), "meter_id": meterID},
MatchFields: []*ofp.OfpOxmOfbField{
flows.InPort(port.PortNo),
flows.EthType(34958),
flows.VlanVid(8187),
},
Actions: []*ofp.OfpAction{
flows.Output(controllerPortMask),
},
}
flowEAP := ofp.FlowTableUpdate{FlowMod: makeSimpleFlowMod(fa), Id: logicalDeviceID}
maxTries := 3
var err error
for {
if _, err = nbi.UpdateLogicalDeviceFlowTable(getContext(), &flowEAP); err == nil {
if maxTries < 3 {
t.Log("Re-sending EAPOL flow succeeded for port:", port)
}
break
}
t.Log("Sending EAPOL flows fail:", err)
time.Sleep(50 * time.Millisecond)
maxTries--
if maxTries == 0 {
break
}
}
assert.Nil(t, err)
}
func (nb *NBTest) monitorLogicalDevice(t *testing.T, nbi *NBIHandler, numNNIPorts int, numUNIPorts int, wg *sync.WaitGroup, flowAddFail bool, flowDeleteFail bool) {
defer wg.Done()
// Clear any existing flows on the adapters
nb.oltAdapter.ClearFlows()
nb.onuAdapter.ClearFlows()
// Set the adapter actions on flow addition/deletion
nb.oltAdapter.SetFlowAction(flowAddFail, flowDeleteFail)
nb.onuAdapter.SetFlowAction(flowAddFail, flowDeleteFail)
// Wait until a logical device is ready
var vlFunction isLogicalDevicesConditionSatisfied = func(lds *voltha.LogicalDevices) bool {
if lds == nil || len(lds.Items) != 1 || lds.Items[0] == nil {
return false
}
// Ensure there are both NNI ports and at least one UNI port on the logical device
ld := lds.Items[0]
ports, err := nbi.ListLogicalDevicePorts(getContext(), &voltha.ID{Id: ld.Id})
if err != nil {
return false
}
nniPort := false
uniPort := false
for _, p := range ports.Items {
nniPort = nniPort || p.RootPort == true
uniPort = uniPort || p.RootPort == false
if nniPort && uniPort {
return true
}
}
return false
}
err := waitUntilConditionForLogicalDevices(nb.maxTimeout, nbi, vlFunction)
assert.Nil(t, err)
logicalDevices, err := nbi.ListLogicalDevices(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.NotNil(t, logicalDevices)
assert.Equal(t, 1, len(logicalDevices.Items))
logicalDeviceID := logicalDevices.Items[0].Id
meterID := rand.Uint32()
// Add a meter to the logical device
meterMod := &ofp.OfpMeterMod{
Command: ofp.OfpMeterModCommand_OFPMC_ADD,
Flags: rand.Uint32(),
MeterId: meterID,
Bands: []*ofp.OfpMeterBandHeader{
{Type: ofp.OfpMeterBandType_OFPMBT_EXPERIMENTER,
Rate: rand.Uint32(),
BurstSize: rand.Uint32(),
Data: nil,
},
},
}
_, err = nbi.UpdateLogicalDeviceMeterTable(getContext(), &ofp.MeterModUpdate{Id: logicalDeviceID, MeterMod: meterMod})
assert.Nil(t, err)
ports, err := nbi.ListLogicalDevicePorts(getContext(), &voltha.ID{Id: logicalDeviceID})
assert.Nil(t, err)
// Send initial set of Trap flows
startingVlan := 4091
nb.sendTrapFlows(t, nbi, logicalDeviceID, ports.Items, uint64(meterID), startingVlan)
// Listen for port events
start := time.Now()
processedNniLogicalPorts := 0
processedUniLogicalPorts := 0
for event := range nbi.GetChangeEventsQueueForTest() {
startingVlan++
if portStatus, ok := (event.Event).(*ofp.ChangeEvent_PortStatus); ok {
ps := portStatus.PortStatus
if ps.Reason == ofp.OfpPortReason_OFPPR_ADD {
if ps.Desc.PortNo >= uint32(nb.startingUNIPortNo) {
processedUniLogicalPorts++
nb.sendEAPFlows(t, nbi, logicalDeviceID, ps.Desc, startingVlan, uint64(meterID))
} else {
processedNniLogicalPorts++
}
}
}
if processedNniLogicalPorts >= numNNIPorts && processedUniLogicalPorts >= numUNIPorts {
fmt.Println("Total time to send all flows:", time.Since(start))
break
}
}
//Verify the flow count on the logical device
nb.verifyLogicalDeviceFlowCount(t, nbi, numNNIPorts, numUNIPorts, flowAddFail)
// Wait until all flows have been sent to the OLT adapters (or all failed)
expectedFlowCount := (numNNIPorts * numTrapOnNNIFlows) + numNNIPorts*numUNIPorts
if flowAddFail {
expectedFlowCount = 0
}
var oltVFunc isConditionSatisfied = func() bool {
return nb.oltAdapter.GetFlowCount() >= expectedFlowCount
}
err = waitUntilCondition(nb.maxTimeout, nbi, oltVFunc)
assert.Nil(t, err)
// Wait until all flows have been sent to the ONU adapters (or all failed)
expectedFlowCount = numUNIPorts
if flowAddFail {
expectedFlowCount = 0
}
var onuVFunc isConditionSatisfied = func() bool {
return nb.onuAdapter.GetFlowCount() == expectedFlowCount
}
err = waitUntilCondition(nb.maxTimeout, nbi, onuVFunc)
assert.Nil(t, err)
}
func (nb *NBTest) testFlowAddFailure(t *testing.T, nbi *NBIHandler) {
// Create a logical device monitor will automatically send trap and eapol flows to the devices being enables
var wg sync.WaitGroup
wg.Add(1)
go nb.monitorLogicalDevice(t, nbi, 1, nb.numONUPerOLT, &wg, true, false)
// Create the device with valid data
oltDevice, err := nbi.CreateDevice(getContext(), &voltha.Device{Type: nb.oltAdapterName, MacAddress: "aa:bb:cc:cc:ee:ee"})
assert.Nil(t, err)
assert.NotNil(t, oltDevice)
// Verify oltDevice exist in the core
devices, err := nbi.ListDevices(getContext(), &empty.Empty{})
assert.Nil(t, err)
assert.Equal(t, 1, len(devices.Items))
assert.Equal(t, oltDevice.Id, devices.Items[0].Id)
// Enable the oltDevice
_, err = nbi.EnableDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
// Wait for the logical device to be in the ready state
var vldFunction = func(ports []*voltha.LogicalPort) bool {
return len(ports) == nb.numONUPerOLT+1
}
err = waitUntilLogicalDevicePortsReadiness(oltDevice.Id, nb.maxTimeout, nbi, vldFunction)
assert.Nil(t, err)
// Verify that the devices have been setup correctly
nb.verifyDevices(t, nbi)
// Get latest oltDevice data
oltDevice, err = nbi.GetDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
assert.Nil(t, err)
// Verify that the logical device has been setup correctly
nb.verifyLogicalDevices(t, oltDevice, nbi)
// Wait until all flows has been sent to the devices successfully
wg.Wait()
}
func TestSuiteNbiApiHandler(t *testing.T) {
ctx := context.Background()
f, err := os.Create("../../../tests/results/profile.cpu")
if err != nil {
logger.Fatalf(ctx, "could not create CPU profile: %v\n ", err)
}
defer func() {
err = f.Close()
if err != nil {
logger.Errorf(ctx, "failed to close file: %v\n", err)
}
}()
runtime.SetBlockProfileRate(1)
runtime.SetMutexProfileFraction(-1)
if err := pprof.StartCPUProfile(f); err != nil {
logger.Fatalf(ctx, "could not start CPU profile: %v\n", err)
}
defer pprof.StopCPUProfile()
//log.SetPackageLogLevel("github.com/opencord/voltha-go/rw_core/core", log.DebugLevel)
nb := newNBTest(ctx)
assert.NotNil(t, nb)
defer nb.stopAll(ctx)
// Start the Core
nb.startCore(false)
// Set the grpc API interface - no grpc server is running in unit test
nbi := NewNBIHandler(nb.deviceMgr, nb.logicalDeviceMgr, nb.adapterMgr)
// 1. Basic test with no data in Core
nb.testCoreWithoutData(t, nbi)
// Create/register the adapters
nb.oltAdapter, nb.onuAdapter = tst.CreateAndregisterAdapters(ctx, t, nb.kClient, nb.coreInstanceID, nb.oltAdapterName, nb.onuAdapterName, nb.adapterMgr)
nb.numONUPerOLT = nb.oltAdapter.GetNumONUPerOLT()
nb.startingUNIPortNo = nb.oltAdapter.GetStartingUNIPortNo()
// 2. Test adapter registration
nb.testAdapterRegistration(t, nbi)
numberOfTestRuns := 2
for i := 1; i <= numberOfTestRuns; i++ {
// 3. Test create device
nb.testCreateDevice(t, nbi)
// 4. Test Delete Device Scenarios
nb.testForceDeletePreProvDevice(t, nbi)
nb.testDeletePreProvDevice(t, nbi)
nb.testForceDeleteEnabledDevice(t, nbi)
nb.testDeleteEnabledDevice(t, nbi)
nb.testForceDeleteDeviceFailure(t, nbi)
nb.testDeleteDeviceFailure(t, nbi)
// 5. Test Enable a device
nb.testEnableDevice(t, nbi)
// 6. Test disable and ReEnable a root device
nb.testDisableAndReEnableRootDevice(t, nbi)
// 7. Test disable and Enable pon port of OLT device
nb.testDisableAndEnablePort(t, nbi)
// 8.Test Device unreachable when OLT is enabled
nb.testDeviceRebootWhenOltIsEnabled(t, nbi)
// 9. Test disable and delete all devices
nb.testDisableAndDeleteAllDevice(t, nbi)
// 10. Test enable and delete all devices
nb.testEnableAndDeleteAllDevice(t, nbi)
// 11. Test omci test
nb.testStartOmciTestAction(t, nbi)
// 12. Remove all devices from tests above
nb.deleteAllDevices(t, nbi)
// 13. Test flow add failure
nb.testFlowAddFailure(t, nbi)
// 14. Clean up
nb.deleteAllDevices(t, nbi)
}
}