blob: 2549de80d7288bdad64128b0151e3a662afa8e7d [file] [log] [blame]
/*
* Copyright 2020-2023 Open Networking Foundation (ONF) and the ONF Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ponresourcemanager
import (
"context"
"encoding/json"
"errors"
"github.com/boljen/go-bitmap"
"strings"
"testing"
"time"
"github.com/opencord/voltha-lib-go/v7/pkg/db"
"github.com/opencord/voltha-lib-go/v7/pkg/db/kvstore"
"github.com/opencord/voltha-lib-go/v7/pkg/log"
"github.com/stretchr/testify/assert"
)
const (
GEM_POOL_PATH = "gemport_id_pool"
RESERVED_GEM_PORT_ID = uint32(5)
)
// MockKVClient mocks the AdapterProxy interface.
type MockResKVClient struct {
resourceMap map[string]interface{}
}
func newMockKvClient(ctx context.Context) *MockResKVClient {
var mockResKVClient MockResKVClient
mockResKVClient.resourceMap = make(map[string]interface{})
return &mockResKVClient
}
// List function implemented for KVClient.
func (kvclient *MockResKVClient) List(ctx context.Context, key string) (map[string]*kvstore.KVPair, error) {
return nil, errors.New("key didn't find")
}
// Get mock function implementation for KVClient
func (kvclient *MockResKVClient) Get(ctx context.Context, key string) (*kvstore.KVPair, error) {
logger.Debugw(ctx, "Get of MockKVClient called", log.Fields{"key": key})
if key != "" {
if strings.Contains(key, RESERVED_GEMPORT_IDS_PATH) {
logger.Debug(ctx, "Getting Key:", RESERVED_GEMPORT_IDS_PATH)
reservedGemPorts := []uint32{RESERVED_GEM_PORT_ID}
str, _ := json.Marshal(reservedGemPorts)
return kvstore.NewKVPair(key, str, "mock", 3000, 1), nil
}
if strings.Contains(key, GEM_POOL_PATH) {
logger.Debug(ctx, "Getting Key:", GEM_POOL_PATH)
resource := kvclient.resourceMap[key]
return kvstore.NewKVPair(key, resource, "mock", 3000, 1), nil
}
maps := make(map[string]*kvstore.KVPair)
maps[key] = &kvstore.KVPair{Key: key}
return maps[key], nil
}
return nil, errors.New("key didn't find")
}
// Put mock function implementation for KVClient
func (kvclient *MockResKVClient) Put(ctx context.Context, key string, value interface{}) error {
if key != "" {
if strings.Contains(key, GEMPORT_ID_POOL_PATH) && value != nil {
kvclient.resourceMap[key] = value
}
return nil
}
return errors.New("key didn't find")
}
// Delete mock function implementation for KVClient
func (kvclient *MockResKVClient) Delete(ctx context.Context, key string) error {
return nil
}
func (c *MockResKVClient) DeleteWithPrefix(ctx context.Context, prefixKey string) error {
return nil
}
// Reserve mock function implementation for KVClient
func (kvclient *MockResKVClient) Reserve(ctx context.Context, key string, value interface{}, ttl time.Duration) (interface{}, error) {
return nil, errors.New("key didn't find")
}
// ReleaseReservation mock function implementation for KVClient
func (kvclient *MockResKVClient) ReleaseReservation(ctx context.Context, key string) error {
return nil
}
// ReleaseAllReservations mock function implementation for KVClient
func (kvclient *MockResKVClient) ReleaseAllReservations(ctx context.Context) error {
return nil
}
// RenewReservation mock function implementation for KVClient
func (kvclient *MockResKVClient) RenewReservation(ctx context.Context, key string) error {
return nil
}
// Watch mock function implementation for KVClient
func (kvclient *MockResKVClient) Watch(ctx context.Context, key string, withPrefix bool) chan *kvstore.Event {
return nil
}
// AcquireLock mock function implementation for KVClient
func (kvclient *MockResKVClient) AcquireLock(ctx context.Context, lockName string, timeout time.Duration) error {
return nil
}
// ReleaseLock mock function implementation for KVClient
func (kvclient *MockResKVClient) ReleaseLock(lockName string) error {
return nil
}
// IsConnectionUp mock function implementation for KVClient
func (kvclient *MockResKVClient) IsConnectionUp(ctx context.Context) bool { // timeout in second
return true
}
// CloseWatch mock function implementation for KVClient
func (kvclient *MockResKVClient) CloseWatch(ctx context.Context, key string, ch chan *kvstore.Event) {
}
// Close mock function implementation for KVClient
func (kvclient *MockResKVClient) Close(ctx context.Context) {
}
func TestExcludeReservedGemPortIdFromThePool(t *testing.T) {
ctx := context.Background()
PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
"etcd", "1:1", "service/voltha")
if err != nil {
return
}
PONRMgr.KVStore = &db.Backend{
Client: newMockKvClient(ctx),
}
PONRMgr.KVStoreForConfig = &db.Backend{
Client: newMockKvClient(ctx),
}
// create a pool in the range of [1,16]
// and exclude id 5 from this pool
StartIndex := uint32(1)
EndIndex := uint32(16)
reservedGemPortIds, defined := PONRMgr.getReservedGemPortIdsFromKVStore(ctx)
if !defined {
return
}
FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, reservedGemPortIds)
if err != nil {
t.Error("Failed to format resource", err)
return
}
// Add resource as json in kv store.
err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
if err != nil {
t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
return
}
for i := StartIndex; i <= (EndIndex - uint32(len(reservedGemPortIds))); i++ {
// get gem port id pool from the kv store
resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
if err != nil {
t.Error("Failed to get resource from gem port id pool", err)
return
}
// get a gem port id from the pool
nextID, err := PONRMgr.GenerateNextID(ctx, resource)
if err != nil {
t.Error("Failed to get gem port id from the pool", err)
return
}
//given gem port id should not equal to the reserved gem port id
assert.NotEqual(t, nextID, RESERVED_GEM_PORT_ID)
// put updated gem port id pool into the kv store
err = PONRMgr.UpdateResource(context.Background(), GEMPORT_ID_POOL_PATH, resource)
if err != nil {
t.Error("Failed to put updated gem port id pool into the kv store", err)
return
}
}
}
func TestResourcePoolOverflow(t *testing.T) {
ctx := context.Background()
PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
"etcd", "1:1", "service/voltha")
if err != nil {
return
}
PONRMgr.KVStore = &db.Backend{
Client: newMockKvClient(ctx),
}
PONRMgr.KVStoreForConfig = &db.Backend{
Client: newMockKvClient(ctx),
}
// create a pool in the range of [1,16]
StartIndex := uint32(1)
EndIndex := uint32(16)
FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, []uint32{})
if err != nil {
t.Error("Failed to format resource", err)
return
}
// Add resource as json in kv store.
err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
if err != nil {
t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
return
}
for i := 1; i <= 20; i++ {
// get gem port id pool from the kv store
resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
if err != nil {
t.Error("Failed to get resource from gem port id pool", err)
return
}
// get a gem port id from the pool
nextID, err := PONRMgr.GenerateNextID(ctx, resource)
// all free ids in the pool will be consumed by the first 16 steps of the loop
// resource-exhausted error is expected from the pool at the 17th step of the loop
if i > int(EndIndex) {
assert.NotNil(t, err)
} else if err != nil {
t.Error("Failed to get gem port id from the pool", err)
return
} else {
assert.NotEqual(t, 0, nextID)
// put updated gem port id pool into the kv store
err = PONRMgr.UpdateResource(context.Background(), GEMPORT_ID_POOL_PATH, resource)
if err != nil {
t.Error("Failed to put updated gem port id pool into the kv store", err)
return
}
}
}
}
func TestPONResourceManager_ReleaseInvalidID(t *testing.T) {
ctx := context.Background()
PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
"etcd", "1:1", "service/voltha")
if err != nil {
return
}
PONRMgr.KVStore = &db.Backend{
Client: newMockKvClient(ctx),
}
PONRMgr.KVStoreForConfig = &db.Backend{
Client: newMockKvClient(ctx),
}
// create a pool in the range of [1,16]
StartIndex := uint32(1)
EndIndex := uint32(16)
FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, []uint32{})
if err != nil {
t.Error("Failed to format resource", err)
return
}
// Add resource as json in kv store.
err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
if err != nil {
t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
return
}
// get gem port id pool from the kv store
resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
if err != nil {
t.Error("Failed to get resource from gem port id pool", err)
return
}
//try to release an ID whose value is out of the boundaries of the pool and expect false
released := PONRMgr.ReleaseID(ctx, resource, uint32(EndIndex+1))
assert.Equal(t, false, released)
}
func TestPONResourceManager_ReserveInvalidID(t *testing.T) {
ctx := context.Background()
PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
"etcd", "1:1", "service/voltha")
if err != nil {
return
}
PONRMgr.KVStore = &db.Backend{
Client: newMockKvClient(ctx),
}
PONRMgr.KVStoreForConfig = &db.Backend{
Client: newMockKvClient(ctx),
}
// create a pool in the range of [1,16]
StartIndex := uint32(1)
EndIndex := uint32(16)
FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, []uint32{})
if err != nil {
t.Error("Failed to format resource", err)
return
}
// Add resource as json in kv store.
err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
if err != nil {
t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
return
}
// get gem port id pool from the kv store
resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
if err != nil {
t.Error("Failed to get resource from gem port id pool", err)
return
}
ByteArray, err := ToByte(resource[POOL])
if err != nil {
t.Error(ctx, "Failed to convert resource to byte array")
return
}
Data := bitmap.TSFromData(ByteArray, false)
if Data == nil {
t.Error(ctx, "Failed to get resource pool")
return
}
//try to reserve an ID whose value is out of the boundaries of the pool and expect false
reserved := PONRMgr.reserveID(ctx, Data, StartIndex, EndIndex+1)
assert.Equal(t, false, reserved)
}