blob: 7d9c85de27e292712298ea69916ad03e6b7333bb [file] [log] [blame]
Esin Karaman5351fc52020-02-14 07:45:49 +00001/*
Joey Armstrong9cdee9f2024-01-03 04:56:14 -05002 * Copyright 2020-2024 Open Networking Foundation (ONF) and the ONF Contributors
Esin Karaman5351fc52020-02-14 07:45:49 +00003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package ponresourcemanager
18
19import (
20 "context"
21 "encoding/json"
22 "errors"
Esin Karaman5351fc52020-02-14 07:45:49 +000023 "strings"
24 "testing"
Neha Sharma130ac6d2020-04-08 08:46:32 +000025 "time"
khenaidoo26721882021-08-11 17:42:52 -040026
pnalmas37560752025-01-11 22:05:35 +053027 "github.com/boljen/go-bitmap"
28
khenaidoo26721882021-08-11 17:42:52 -040029 "github.com/opencord/voltha-lib-go/v7/pkg/db"
30 "github.com/opencord/voltha-lib-go/v7/pkg/db/kvstore"
31 "github.com/opencord/voltha-lib-go/v7/pkg/log"
32 "github.com/stretchr/testify/assert"
Esin Karaman5351fc52020-02-14 07:45:49 +000033)
34
35const (
36 GEM_POOL_PATH = "gemport_id_pool"
37 RESERVED_GEM_PORT_ID = uint32(5)
38)
39
Esin Karaman5351fc52020-02-14 07:45:49 +000040// MockKVClient mocks the AdapterProxy interface.
41type MockResKVClient struct {
42 resourceMap map[string]interface{}
43}
44
Neha Sharma94f16a92020-06-26 04:17:55 +000045func newMockKvClient(ctx context.Context) *MockResKVClient {
Esin Karaman5351fc52020-02-14 07:45:49 +000046 var mockResKVClient MockResKVClient
47 mockResKVClient.resourceMap = make(map[string]interface{})
pnalmas37560752025-01-11 22:05:35 +053048 logger.Debug(ctx, "Creating new MockKVClient")
Esin Karaman5351fc52020-02-14 07:45:49 +000049 return &mockResKVClient
50}
51
52// List function implemented for KVClient.
53func (kvclient *MockResKVClient) List(ctx context.Context, key string) (map[string]*kvstore.KVPair, error) {
54 return nil, errors.New("key didn't find")
55}
56
Sridhar Ravindra037d7cd2025-10-23 12:52:50 +053057// List function implemented for KVClient.
58func (kvclient *MockResKVClient) KeyExists(ctx context.Context, key string) (bool, error) {
59 return false, errors.New("key didn't find")
60}
61
Esin Karaman5351fc52020-02-14 07:45:49 +000062// Get mock function implementation for KVClient
63func (kvclient *MockResKVClient) Get(ctx context.Context, key string) (*kvstore.KVPair, error) {
Neha Sharma94f16a92020-06-26 04:17:55 +000064 logger.Debugw(ctx, "Get of MockKVClient called", log.Fields{"key": key})
Esin Karaman5351fc52020-02-14 07:45:49 +000065 if key != "" {
66 if strings.Contains(key, RESERVED_GEMPORT_IDS_PATH) {
Neha Sharma94f16a92020-06-26 04:17:55 +000067 logger.Debug(ctx, "Getting Key:", RESERVED_GEMPORT_IDS_PATH)
Esin Karaman5351fc52020-02-14 07:45:49 +000068 reservedGemPorts := []uint32{RESERVED_GEM_PORT_ID}
69 str, _ := json.Marshal(reservedGemPorts)
70 return kvstore.NewKVPair(key, str, "mock", 3000, 1), nil
71 }
72 if strings.Contains(key, GEM_POOL_PATH) {
Neha Sharma94f16a92020-06-26 04:17:55 +000073 logger.Debug(ctx, "Getting Key:", GEM_POOL_PATH)
Esin Karaman5351fc52020-02-14 07:45:49 +000074 resource := kvclient.resourceMap[key]
75 return kvstore.NewKVPair(key, resource, "mock", 3000, 1), nil
76 }
77 maps := make(map[string]*kvstore.KVPair)
78 maps[key] = &kvstore.KVPair{Key: key}
79 return maps[key], nil
80 }
81 return nil, errors.New("key didn't find")
82}
83
pnalmas37560752025-01-11 22:05:35 +053084// GetWithPrefix mock function implementation for KVClient
85func (kvclient *MockResKVClient) GetWithPrefix(ctx context.Context, prefixKey string) (map[string]*kvstore.KVPair, error) {
86 logger.Debugw(ctx, "GetWithPrefix of MockKVClient called", log.Fields{"prefixKey": prefixKey})
87 if prefixKey != "" {
88 if strings.Contains(prefixKey, GEM_POOL_PATH) {
89 logger.Debug(ctx, "Getting keys with prefix:", GEM_POOL_PATH)
90 maps := make(map[string]*kvstore.KVPair)
91 for key, resource := range kvclient.resourceMap {
92 if strings.HasPrefix(key, prefixKey) {
93 maps[key] = kvstore.NewKVPair(key, resource, "mock", 3000, 1)
94 }
95 }
96 return maps, nil
97 }
98 }
99 return nil, errors.New("prefixKey didn't find")
100}
101
102// GetWithPrefixKeysOnly returns only the keys with the specified prefix.
103func (kvclient *MockResKVClient) GetWithPrefixKeysOnly(ctx context.Context, prefixKey string) ([]string, error) {
104 logger.Debugw(ctx, "GetWithPrefixKeysOnly of MockKVClient called", log.Fields{"prefixKey": prefixKey})
105 if prefixKey != "" {
106 if strings.Contains(prefixKey, GEM_POOL_PATH) {
107 logger.Debug(ctx, "Getting keys with prefix:", GEM_POOL_PATH)
108 var keys []string
109 for key := range kvclient.resourceMap {
110 if strings.HasPrefix(key, prefixKey) {
111 keys = append(keys, key)
112 }
113 }
114 return keys, nil
115 }
116 }
117 return nil, errors.New("prefixKey not found")
118}
119
Esin Karaman5351fc52020-02-14 07:45:49 +0000120// Put mock function implementation for KVClient
121func (kvclient *MockResKVClient) Put(ctx context.Context, key string, value interface{}) error {
122 if key != "" {
123 if strings.Contains(key, GEMPORT_ID_POOL_PATH) && value != nil {
124 kvclient.resourceMap[key] = value
125 }
126 return nil
127 }
128 return errors.New("key didn't find")
129}
130
131// Delete mock function implementation for KVClient
132func (kvclient *MockResKVClient) Delete(ctx context.Context, key string) error {
133 return nil
134}
135
Serkant Uluderya198de902020-11-16 20:29:17 +0300136func (c *MockResKVClient) DeleteWithPrefix(ctx context.Context, prefixKey string) error {
137 return nil
138}
139
Esin Karaman5351fc52020-02-14 07:45:49 +0000140// Reserve mock function implementation for KVClient
Neha Sharma130ac6d2020-04-08 08:46:32 +0000141func (kvclient *MockResKVClient) Reserve(ctx context.Context, key string, value interface{}, ttl time.Duration) (interface{}, error) {
Esin Karaman5351fc52020-02-14 07:45:49 +0000142 return nil, errors.New("key didn't find")
143}
144
145// ReleaseReservation mock function implementation for KVClient
146func (kvclient *MockResKVClient) ReleaseReservation(ctx context.Context, key string) error {
147 return nil
148}
149
150// ReleaseAllReservations mock function implementation for KVClient
151func (kvclient *MockResKVClient) ReleaseAllReservations(ctx context.Context) error {
152 return nil
153}
154
155// RenewReservation mock function implementation for KVClient
156func (kvclient *MockResKVClient) RenewReservation(ctx context.Context, key string) error {
157 return nil
158}
159
160// Watch mock function implementation for KVClient
161func (kvclient *MockResKVClient) Watch(ctx context.Context, key string, withPrefix bool) chan *kvstore.Event {
162 return nil
163}
164
165// AcquireLock mock function implementation for KVClient
Neha Sharma130ac6d2020-04-08 08:46:32 +0000166func (kvclient *MockResKVClient) AcquireLock(ctx context.Context, lockName string, timeout time.Duration) error {
Esin Karaman5351fc52020-02-14 07:45:49 +0000167 return nil
168}
169
170// ReleaseLock mock function implementation for KVClient
171func (kvclient *MockResKVClient) ReleaseLock(lockName string) error {
172 return nil
173}
174
175// IsConnectionUp mock function implementation for KVClient
176func (kvclient *MockResKVClient) IsConnectionUp(ctx context.Context) bool { // timeout in second
177 return true
178}
179
180// CloseWatch mock function implementation for KVClient
Neha Sharma94f16a92020-06-26 04:17:55 +0000181func (kvclient *MockResKVClient) CloseWatch(ctx context.Context, key string, ch chan *kvstore.Event) {
Esin Karaman5351fc52020-02-14 07:45:49 +0000182}
183
184// Close mock function implementation for KVClient
Neha Sharma94f16a92020-06-26 04:17:55 +0000185func (kvclient *MockResKVClient) Close(ctx context.Context) {
Esin Karaman5351fc52020-02-14 07:45:49 +0000186}
187
188func TestExcludeReservedGemPortIdFromThePool(t *testing.T) {
Neha Sharma94f16a92020-06-26 04:17:55 +0000189 ctx := context.Background()
190 PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
Matteo Scandolo29ff79c2020-11-06 13:03:17 -0800191 "etcd", "1:1", "service/voltha")
Esin Karaman5351fc52020-02-14 07:45:49 +0000192 if err != nil {
193 return
194 }
195 PONRMgr.KVStore = &db.Backend{
Neha Sharma94f16a92020-06-26 04:17:55 +0000196 Client: newMockKvClient(ctx),
Esin Karaman5351fc52020-02-14 07:45:49 +0000197 }
198
199 PONRMgr.KVStoreForConfig = &db.Backend{
Neha Sharma94f16a92020-06-26 04:17:55 +0000200 Client: newMockKvClient(ctx),
Esin Karaman5351fc52020-02-14 07:45:49 +0000201 }
202 // create a pool in the range of [1,16]
203 // and exclude id 5 from this pool
204 StartIndex := uint32(1)
205 EndIndex := uint32(16)
206
Esin Karaman5351fc52020-02-14 07:45:49 +0000207 reservedGemPortIds, defined := PONRMgr.getReservedGemPortIdsFromKVStore(ctx)
208 if !defined {
209 return
210 }
211
Neha Sharma94f16a92020-06-26 04:17:55 +0000212 FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, reservedGemPortIds)
Esin Karaman5351fc52020-02-14 07:45:49 +0000213 if err != nil {
214 t.Error("Failed to format resource", err)
215 return
216 }
217
218 // Add resource as json in kv store.
219 err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
220 if err != nil {
221 t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
222 return
223 }
224
225 for i := StartIndex; i <= (EndIndex - uint32(len(reservedGemPortIds))); i++ {
226 // get gem port id pool from the kv store
227 resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
228 if err != nil {
229 t.Error("Failed to get resource from gem port id pool", err)
230 return
231 }
232 // get a gem port id from the pool
Neha Sharma94f16a92020-06-26 04:17:55 +0000233 nextID, err := PONRMgr.GenerateNextID(ctx, resource)
Esin Karaman5351fc52020-02-14 07:45:49 +0000234 if err != nil {
235 t.Error("Failed to get gem port id from the pool", err)
236 return
237 }
238
239 //given gem port id should not equal to the reserved gem port id
240 assert.NotEqual(t, nextID, RESERVED_GEM_PORT_ID)
241 // put updated gem port id pool into the kv store
242 err = PONRMgr.UpdateResource(context.Background(), GEMPORT_ID_POOL_PATH, resource)
243 if err != nil {
244 t.Error("Failed to put updated gem port id pool into the kv store", err)
245 return
246 }
247 }
248
249}
Esin Karamanefab54e2021-10-13 11:11:43 +0000250
251func TestResourcePoolOverflow(t *testing.T) {
252 ctx := context.Background()
253 PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
254 "etcd", "1:1", "service/voltha")
255 if err != nil {
256 return
257 }
258 PONRMgr.KVStore = &db.Backend{
259 Client: newMockKvClient(ctx),
260 }
261
262 PONRMgr.KVStoreForConfig = &db.Backend{
263 Client: newMockKvClient(ctx),
264 }
265 // create a pool in the range of [1,16]
266 StartIndex := uint32(1)
267 EndIndex := uint32(16)
268
269 FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, []uint32{})
270 if err != nil {
271 t.Error("Failed to format resource", err)
272 return
273 }
274
275 // Add resource as json in kv store.
276 err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
277 if err != nil {
278 t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
279 return
280 }
281
282 for i := 1; i <= 20; i++ {
283 // get gem port id pool from the kv store
284 resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
285 if err != nil {
286 t.Error("Failed to get resource from gem port id pool", err)
287 return
288 }
289 // get a gem port id from the pool
290 nextID, err := PONRMgr.GenerateNextID(ctx, resource)
291 // all free ids in the pool will be consumed by the first 16 steps of the loop
292 // resource-exhausted error is expected from the pool at the 17th step of the loop
293 if i > int(EndIndex) {
294 assert.NotNil(t, err)
295 } else if err != nil {
296 t.Error("Failed to get gem port id from the pool", err)
297 return
298 } else {
299 assert.NotEqual(t, 0, nextID)
300 // put updated gem port id pool into the kv store
301 err = PONRMgr.UpdateResource(context.Background(), GEMPORT_ID_POOL_PATH, resource)
302 if err != nil {
303 t.Error("Failed to put updated gem port id pool into the kv store", err)
304 return
305 }
306 }
307 }
308}
309
310func TestPONResourceManager_ReleaseInvalidID(t *testing.T) {
311 ctx := context.Background()
312 PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
313 "etcd", "1:1", "service/voltha")
314 if err != nil {
315 return
316 }
317 PONRMgr.KVStore = &db.Backend{
318 Client: newMockKvClient(ctx),
319 }
320
321 PONRMgr.KVStoreForConfig = &db.Backend{
322 Client: newMockKvClient(ctx),
323 }
324 // create a pool in the range of [1,16]
325 StartIndex := uint32(1)
326 EndIndex := uint32(16)
327
328 FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, []uint32{})
329 if err != nil {
330 t.Error("Failed to format resource", err)
331 return
332 }
333
334 // Add resource as json in kv store.
335 err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
336 if err != nil {
337 t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
338 return
339 }
340
341 // get gem port id pool from the kv store
342 resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
343 if err != nil {
344 t.Error("Failed to get resource from gem port id pool", err)
345 return
346 }
347 //try to release an ID whose value is out of the boundaries of the pool and expect false
348 released := PONRMgr.ReleaseID(ctx, resource, uint32(EndIndex+1))
349 assert.Equal(t, false, released)
350}
351
352func TestPONResourceManager_ReserveInvalidID(t *testing.T) {
353 ctx := context.Background()
354 PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
355 "etcd", "1:1", "service/voltha")
356 if err != nil {
357 return
358 }
359 PONRMgr.KVStore = &db.Backend{
360 Client: newMockKvClient(ctx),
361 }
362
363 PONRMgr.KVStoreForConfig = &db.Backend{
364 Client: newMockKvClient(ctx),
365 }
366 // create a pool in the range of [1,16]
367 StartIndex := uint32(1)
368 EndIndex := uint32(16)
369
370 FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, []uint32{})
371 if err != nil {
372 t.Error("Failed to format resource", err)
373 return
374 }
375
376 // Add resource as json in kv store.
377 err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
378 if err != nil {
379 t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
380 return
381 }
382 // get gem port id pool from the kv store
383 resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
384 if err != nil {
385 t.Error("Failed to get resource from gem port id pool", err)
386 return
387 }
388 ByteArray, err := ToByte(resource[POOL])
389 if err != nil {
390 t.Error(ctx, "Failed to convert resource to byte array")
391 return
392 }
393 Data := bitmap.TSFromData(ByteArray, false)
394 if Data == nil {
395 t.Error(ctx, "Failed to get resource pool")
396 return
397 }
398 //try to reserve an ID whose value is out of the boundaries of the pool and expect false
399 reserved := PONRMgr.reserveID(ctx, Data, StartIndex, EndIndex+1)
400 assert.Equal(t, false, reserved)
401}