blob: 626864e3ce174077e40b7ad8cd6df921b7308558 [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
57// Get mock function implementation for KVClient
58func (kvclient *MockResKVClient) Get(ctx context.Context, key string) (*kvstore.KVPair, error) {
Neha Sharma94f16a92020-06-26 04:17:55 +000059 logger.Debugw(ctx, "Get of MockKVClient called", log.Fields{"key": key})
Esin Karaman5351fc52020-02-14 07:45:49 +000060 if key != "" {
61 if strings.Contains(key, RESERVED_GEMPORT_IDS_PATH) {
Neha Sharma94f16a92020-06-26 04:17:55 +000062 logger.Debug(ctx, "Getting Key:", RESERVED_GEMPORT_IDS_PATH)
Esin Karaman5351fc52020-02-14 07:45:49 +000063 reservedGemPorts := []uint32{RESERVED_GEM_PORT_ID}
64 str, _ := json.Marshal(reservedGemPorts)
65 return kvstore.NewKVPair(key, str, "mock", 3000, 1), nil
66 }
67 if strings.Contains(key, GEM_POOL_PATH) {
Neha Sharma94f16a92020-06-26 04:17:55 +000068 logger.Debug(ctx, "Getting Key:", GEM_POOL_PATH)
Esin Karaman5351fc52020-02-14 07:45:49 +000069 resource := kvclient.resourceMap[key]
70 return kvstore.NewKVPair(key, resource, "mock", 3000, 1), nil
71 }
72 maps := make(map[string]*kvstore.KVPair)
73 maps[key] = &kvstore.KVPair{Key: key}
74 return maps[key], nil
75 }
76 return nil, errors.New("key didn't find")
77}
78
pnalmas37560752025-01-11 22:05:35 +053079// GetWithPrefix mock function implementation for KVClient
80func (kvclient *MockResKVClient) GetWithPrefix(ctx context.Context, prefixKey string) (map[string]*kvstore.KVPair, error) {
81 logger.Debugw(ctx, "GetWithPrefix of MockKVClient called", log.Fields{"prefixKey": prefixKey})
82 if prefixKey != "" {
83 if strings.Contains(prefixKey, GEM_POOL_PATH) {
84 logger.Debug(ctx, "Getting keys with prefix:", GEM_POOL_PATH)
85 maps := make(map[string]*kvstore.KVPair)
86 for key, resource := range kvclient.resourceMap {
87 if strings.HasPrefix(key, prefixKey) {
88 maps[key] = kvstore.NewKVPair(key, resource, "mock", 3000, 1)
89 }
90 }
91 return maps, nil
92 }
93 }
94 return nil, errors.New("prefixKey didn't find")
95}
96
97// GetWithPrefixKeysOnly returns only the keys with the specified prefix.
98func (kvclient *MockResKVClient) GetWithPrefixKeysOnly(ctx context.Context, prefixKey string) ([]string, error) {
99 logger.Debugw(ctx, "GetWithPrefixKeysOnly of MockKVClient called", log.Fields{"prefixKey": prefixKey})
100 if prefixKey != "" {
101 if strings.Contains(prefixKey, GEM_POOL_PATH) {
102 logger.Debug(ctx, "Getting keys with prefix:", GEM_POOL_PATH)
103 var keys []string
104 for key := range kvclient.resourceMap {
105 if strings.HasPrefix(key, prefixKey) {
106 keys = append(keys, key)
107 }
108 }
109 return keys, nil
110 }
111 }
112 return nil, errors.New("prefixKey not found")
113}
114
Esin Karaman5351fc52020-02-14 07:45:49 +0000115// Put mock function implementation for KVClient
116func (kvclient *MockResKVClient) Put(ctx context.Context, key string, value interface{}) error {
117 if key != "" {
118 if strings.Contains(key, GEMPORT_ID_POOL_PATH) && value != nil {
119 kvclient.resourceMap[key] = value
120 }
121 return nil
122 }
123 return errors.New("key didn't find")
124}
125
126// Delete mock function implementation for KVClient
127func (kvclient *MockResKVClient) Delete(ctx context.Context, key string) error {
128 return nil
129}
130
Serkant Uluderya198de902020-11-16 20:29:17 +0300131func (c *MockResKVClient) DeleteWithPrefix(ctx context.Context, prefixKey string) error {
132 return nil
133}
134
Esin Karaman5351fc52020-02-14 07:45:49 +0000135// Reserve mock function implementation for KVClient
Neha Sharma130ac6d2020-04-08 08:46:32 +0000136func (kvclient *MockResKVClient) Reserve(ctx context.Context, key string, value interface{}, ttl time.Duration) (interface{}, error) {
Esin Karaman5351fc52020-02-14 07:45:49 +0000137 return nil, errors.New("key didn't find")
138}
139
140// ReleaseReservation mock function implementation for KVClient
141func (kvclient *MockResKVClient) ReleaseReservation(ctx context.Context, key string) error {
142 return nil
143}
144
145// ReleaseAllReservations mock function implementation for KVClient
146func (kvclient *MockResKVClient) ReleaseAllReservations(ctx context.Context) error {
147 return nil
148}
149
150// RenewReservation mock function implementation for KVClient
151func (kvclient *MockResKVClient) RenewReservation(ctx context.Context, key string) error {
152 return nil
153}
154
155// Watch mock function implementation for KVClient
156func (kvclient *MockResKVClient) Watch(ctx context.Context, key string, withPrefix bool) chan *kvstore.Event {
157 return nil
158}
159
160// AcquireLock mock function implementation for KVClient
Neha Sharma130ac6d2020-04-08 08:46:32 +0000161func (kvclient *MockResKVClient) AcquireLock(ctx context.Context, lockName string, timeout time.Duration) error {
Esin Karaman5351fc52020-02-14 07:45:49 +0000162 return nil
163}
164
165// ReleaseLock mock function implementation for KVClient
166func (kvclient *MockResKVClient) ReleaseLock(lockName string) error {
167 return nil
168}
169
170// IsConnectionUp mock function implementation for KVClient
171func (kvclient *MockResKVClient) IsConnectionUp(ctx context.Context) bool { // timeout in second
172 return true
173}
174
175// CloseWatch mock function implementation for KVClient
Neha Sharma94f16a92020-06-26 04:17:55 +0000176func (kvclient *MockResKVClient) CloseWatch(ctx context.Context, key string, ch chan *kvstore.Event) {
Esin Karaman5351fc52020-02-14 07:45:49 +0000177}
178
179// Close mock function implementation for KVClient
Neha Sharma94f16a92020-06-26 04:17:55 +0000180func (kvclient *MockResKVClient) Close(ctx context.Context) {
Esin Karaman5351fc52020-02-14 07:45:49 +0000181}
182
183func TestExcludeReservedGemPortIdFromThePool(t *testing.T) {
Neha Sharma94f16a92020-06-26 04:17:55 +0000184 ctx := context.Background()
185 PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
Matteo Scandolo29ff79c2020-11-06 13:03:17 -0800186 "etcd", "1:1", "service/voltha")
Esin Karaman5351fc52020-02-14 07:45:49 +0000187 if err != nil {
188 return
189 }
190 PONRMgr.KVStore = &db.Backend{
Neha Sharma94f16a92020-06-26 04:17:55 +0000191 Client: newMockKvClient(ctx),
Esin Karaman5351fc52020-02-14 07:45:49 +0000192 }
193
194 PONRMgr.KVStoreForConfig = &db.Backend{
Neha Sharma94f16a92020-06-26 04:17:55 +0000195 Client: newMockKvClient(ctx),
Esin Karaman5351fc52020-02-14 07:45:49 +0000196 }
197 // create a pool in the range of [1,16]
198 // and exclude id 5 from this pool
199 StartIndex := uint32(1)
200 EndIndex := uint32(16)
201
Esin Karaman5351fc52020-02-14 07:45:49 +0000202 reservedGemPortIds, defined := PONRMgr.getReservedGemPortIdsFromKVStore(ctx)
203 if !defined {
204 return
205 }
206
Neha Sharma94f16a92020-06-26 04:17:55 +0000207 FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, reservedGemPortIds)
Esin Karaman5351fc52020-02-14 07:45:49 +0000208 if err != nil {
209 t.Error("Failed to format resource", err)
210 return
211 }
212
213 // Add resource as json in kv store.
214 err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
215 if err != nil {
216 t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
217 return
218 }
219
220 for i := StartIndex; i <= (EndIndex - uint32(len(reservedGemPortIds))); i++ {
221 // get gem port id pool from the kv store
222 resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
223 if err != nil {
224 t.Error("Failed to get resource from gem port id pool", err)
225 return
226 }
227 // get a gem port id from the pool
Neha Sharma94f16a92020-06-26 04:17:55 +0000228 nextID, err := PONRMgr.GenerateNextID(ctx, resource)
Esin Karaman5351fc52020-02-14 07:45:49 +0000229 if err != nil {
230 t.Error("Failed to get gem port id from the pool", err)
231 return
232 }
233
234 //given gem port id should not equal to the reserved gem port id
235 assert.NotEqual(t, nextID, RESERVED_GEM_PORT_ID)
236 // put updated gem port id pool into the kv store
237 err = PONRMgr.UpdateResource(context.Background(), GEMPORT_ID_POOL_PATH, resource)
238 if err != nil {
239 t.Error("Failed to put updated gem port id pool into the kv store", err)
240 return
241 }
242 }
243
244}
Esin Karamanefab54e2021-10-13 11:11:43 +0000245
246func TestResourcePoolOverflow(t *testing.T) {
247 ctx := context.Background()
248 PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
249 "etcd", "1:1", "service/voltha")
250 if err != nil {
251 return
252 }
253 PONRMgr.KVStore = &db.Backend{
254 Client: newMockKvClient(ctx),
255 }
256
257 PONRMgr.KVStoreForConfig = &db.Backend{
258 Client: newMockKvClient(ctx),
259 }
260 // create a pool in the range of [1,16]
261 StartIndex := uint32(1)
262 EndIndex := uint32(16)
263
264 FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, []uint32{})
265 if err != nil {
266 t.Error("Failed to format resource", err)
267 return
268 }
269
270 // Add resource as json in kv store.
271 err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
272 if err != nil {
273 t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
274 return
275 }
276
277 for i := 1; i <= 20; i++ {
278 // get gem port id pool from the kv store
279 resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
280 if err != nil {
281 t.Error("Failed to get resource from gem port id pool", err)
282 return
283 }
284 // get a gem port id from the pool
285 nextID, err := PONRMgr.GenerateNextID(ctx, resource)
286 // all free ids in the pool will be consumed by the first 16 steps of the loop
287 // resource-exhausted error is expected from the pool at the 17th step of the loop
288 if i > int(EndIndex) {
289 assert.NotNil(t, err)
290 } else if err != nil {
291 t.Error("Failed to get gem port id from the pool", err)
292 return
293 } else {
294 assert.NotEqual(t, 0, nextID)
295 // put updated gem port id pool into the kv store
296 err = PONRMgr.UpdateResource(context.Background(), GEMPORT_ID_POOL_PATH, resource)
297 if err != nil {
298 t.Error("Failed to put updated gem port id pool into the kv store", err)
299 return
300 }
301 }
302 }
303}
304
305func TestPONResourceManager_ReleaseInvalidID(t *testing.T) {
306 ctx := context.Background()
307 PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
308 "etcd", "1:1", "service/voltha")
309 if err != nil {
310 return
311 }
312 PONRMgr.KVStore = &db.Backend{
313 Client: newMockKvClient(ctx),
314 }
315
316 PONRMgr.KVStoreForConfig = &db.Backend{
317 Client: newMockKvClient(ctx),
318 }
319 // create a pool in the range of [1,16]
320 StartIndex := uint32(1)
321 EndIndex := uint32(16)
322
323 FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, []uint32{})
324 if err != nil {
325 t.Error("Failed to format resource", err)
326 return
327 }
328
329 // Add resource as json in kv store.
330 err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
331 if err != nil {
332 t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
333 return
334 }
335
336 // get gem port id pool from the kv store
337 resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
338 if err != nil {
339 t.Error("Failed to get resource from gem port id pool", err)
340 return
341 }
342 //try to release an ID whose value is out of the boundaries of the pool and expect false
343 released := PONRMgr.ReleaseID(ctx, resource, uint32(EndIndex+1))
344 assert.Equal(t, false, released)
345}
346
347func TestPONResourceManager_ReserveInvalidID(t *testing.T) {
348 ctx := context.Background()
349 PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
350 "etcd", "1:1", "service/voltha")
351 if err != nil {
352 return
353 }
354 PONRMgr.KVStore = &db.Backend{
355 Client: newMockKvClient(ctx),
356 }
357
358 PONRMgr.KVStoreForConfig = &db.Backend{
359 Client: newMockKvClient(ctx),
360 }
361 // create a pool in the range of [1,16]
362 StartIndex := uint32(1)
363 EndIndex := uint32(16)
364
365 FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, []uint32{})
366 if err != nil {
367 t.Error("Failed to format resource", err)
368 return
369 }
370
371 // Add resource as json in kv store.
372 err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
373 if err != nil {
374 t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
375 return
376 }
377 // get gem port id pool from the kv store
378 resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
379 if err != nil {
380 t.Error("Failed to get resource from gem port id pool", err)
381 return
382 }
383 ByteArray, err := ToByte(resource[POOL])
384 if err != nil {
385 t.Error(ctx, "Failed to convert resource to byte array")
386 return
387 }
388 Data := bitmap.TSFromData(ByteArray, false)
389 if Data == nil {
390 t.Error(ctx, "Failed to get resource pool")
391 return
392 }
393 //try to reserve an ID whose value is out of the boundaries of the pool and expect false
394 reserved := PONRMgr.reserveID(ctx, Data, StartIndex, EndIndex+1)
395 assert.Equal(t, false, reserved)
396}