blob: ff80b1147160d1f019f60b185cb7a705494d4fb6 [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 Karamanefab54e2021-10-13 11:11:43 +000023 "github.com/boljen/go-bitmap"
Esin Karaman5351fc52020-02-14 07:45:49 +000024 "strings"
25 "testing"
Neha Sharma130ac6d2020-04-08 08:46:32 +000026 "time"
khenaidoo26721882021-08-11 17:42:52 -040027
28 "github.com/opencord/voltha-lib-go/v7/pkg/db"
29 "github.com/opencord/voltha-lib-go/v7/pkg/db/kvstore"
30 "github.com/opencord/voltha-lib-go/v7/pkg/log"
31 "github.com/stretchr/testify/assert"
Esin Karaman5351fc52020-02-14 07:45:49 +000032)
33
34const (
35 GEM_POOL_PATH = "gemport_id_pool"
36 RESERVED_GEM_PORT_ID = uint32(5)
37)
38
Esin Karaman5351fc52020-02-14 07:45:49 +000039// MockKVClient mocks the AdapterProxy interface.
40type MockResKVClient struct {
41 resourceMap map[string]interface{}
42}
43
Neha Sharma94f16a92020-06-26 04:17:55 +000044func newMockKvClient(ctx context.Context) *MockResKVClient {
Esin Karaman5351fc52020-02-14 07:45:49 +000045 var mockResKVClient MockResKVClient
46 mockResKVClient.resourceMap = make(map[string]interface{})
47 return &mockResKVClient
48}
49
50// List function implemented for KVClient.
51func (kvclient *MockResKVClient) List(ctx context.Context, key string) (map[string]*kvstore.KVPair, error) {
52 return nil, errors.New("key didn't find")
53}
54
55// Get mock function implementation for KVClient
56func (kvclient *MockResKVClient) Get(ctx context.Context, key string) (*kvstore.KVPair, error) {
Neha Sharma94f16a92020-06-26 04:17:55 +000057 logger.Debugw(ctx, "Get of MockKVClient called", log.Fields{"key": key})
Esin Karaman5351fc52020-02-14 07:45:49 +000058 if key != "" {
59 if strings.Contains(key, RESERVED_GEMPORT_IDS_PATH) {
Neha Sharma94f16a92020-06-26 04:17:55 +000060 logger.Debug(ctx, "Getting Key:", RESERVED_GEMPORT_IDS_PATH)
Esin Karaman5351fc52020-02-14 07:45:49 +000061 reservedGemPorts := []uint32{RESERVED_GEM_PORT_ID}
62 str, _ := json.Marshal(reservedGemPorts)
63 return kvstore.NewKVPair(key, str, "mock", 3000, 1), nil
64 }
65 if strings.Contains(key, GEM_POOL_PATH) {
Neha Sharma94f16a92020-06-26 04:17:55 +000066 logger.Debug(ctx, "Getting Key:", GEM_POOL_PATH)
Esin Karaman5351fc52020-02-14 07:45:49 +000067 resource := kvclient.resourceMap[key]
68 return kvstore.NewKVPair(key, resource, "mock", 3000, 1), nil
69 }
70 maps := make(map[string]*kvstore.KVPair)
71 maps[key] = &kvstore.KVPair{Key: key}
72 return maps[key], nil
73 }
74 return nil, errors.New("key didn't find")
75}
76
77// Put mock function implementation for KVClient
78func (kvclient *MockResKVClient) Put(ctx context.Context, key string, value interface{}) error {
79 if key != "" {
80 if strings.Contains(key, GEMPORT_ID_POOL_PATH) && value != nil {
81 kvclient.resourceMap[key] = value
82 }
83 return nil
84 }
85 return errors.New("key didn't find")
86}
87
88// Delete mock function implementation for KVClient
89func (kvclient *MockResKVClient) Delete(ctx context.Context, key string) error {
90 return nil
91}
92
Serkant Uluderya198de902020-11-16 20:29:17 +030093func (c *MockResKVClient) DeleteWithPrefix(ctx context.Context, prefixKey string) error {
94 return nil
95}
96
Esin Karaman5351fc52020-02-14 07:45:49 +000097// Reserve mock function implementation for KVClient
Neha Sharma130ac6d2020-04-08 08:46:32 +000098func (kvclient *MockResKVClient) Reserve(ctx context.Context, key string, value interface{}, ttl time.Duration) (interface{}, error) {
Esin Karaman5351fc52020-02-14 07:45:49 +000099 return nil, errors.New("key didn't find")
100}
101
102// ReleaseReservation mock function implementation for KVClient
103func (kvclient *MockResKVClient) ReleaseReservation(ctx context.Context, key string) error {
104 return nil
105}
106
107// ReleaseAllReservations mock function implementation for KVClient
108func (kvclient *MockResKVClient) ReleaseAllReservations(ctx context.Context) error {
109 return nil
110}
111
112// RenewReservation mock function implementation for KVClient
113func (kvclient *MockResKVClient) RenewReservation(ctx context.Context, key string) error {
114 return nil
115}
116
117// Watch mock function implementation for KVClient
118func (kvclient *MockResKVClient) Watch(ctx context.Context, key string, withPrefix bool) chan *kvstore.Event {
119 return nil
120}
121
122// AcquireLock mock function implementation for KVClient
Neha Sharma130ac6d2020-04-08 08:46:32 +0000123func (kvclient *MockResKVClient) AcquireLock(ctx context.Context, lockName string, timeout time.Duration) error {
Esin Karaman5351fc52020-02-14 07:45:49 +0000124 return nil
125}
126
127// ReleaseLock mock function implementation for KVClient
128func (kvclient *MockResKVClient) ReleaseLock(lockName string) error {
129 return nil
130}
131
132// IsConnectionUp mock function implementation for KVClient
133func (kvclient *MockResKVClient) IsConnectionUp(ctx context.Context) bool { // timeout in second
134 return true
135}
136
137// CloseWatch mock function implementation for KVClient
Neha Sharma94f16a92020-06-26 04:17:55 +0000138func (kvclient *MockResKVClient) CloseWatch(ctx context.Context, key string, ch chan *kvstore.Event) {
Esin Karaman5351fc52020-02-14 07:45:49 +0000139}
140
141// Close mock function implementation for KVClient
Neha Sharma94f16a92020-06-26 04:17:55 +0000142func (kvclient *MockResKVClient) Close(ctx context.Context) {
Esin Karaman5351fc52020-02-14 07:45:49 +0000143}
144
145func TestExcludeReservedGemPortIdFromThePool(t *testing.T) {
Neha Sharma94f16a92020-06-26 04:17:55 +0000146 ctx := context.Background()
147 PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
Matteo Scandolo29ff79c2020-11-06 13:03:17 -0800148 "etcd", "1:1", "service/voltha")
Esin Karaman5351fc52020-02-14 07:45:49 +0000149 if err != nil {
150 return
151 }
152 PONRMgr.KVStore = &db.Backend{
Neha Sharma94f16a92020-06-26 04:17:55 +0000153 Client: newMockKvClient(ctx),
Esin Karaman5351fc52020-02-14 07:45:49 +0000154 }
155
156 PONRMgr.KVStoreForConfig = &db.Backend{
Neha Sharma94f16a92020-06-26 04:17:55 +0000157 Client: newMockKvClient(ctx),
Esin Karaman5351fc52020-02-14 07:45:49 +0000158 }
159 // create a pool in the range of [1,16]
160 // and exclude id 5 from this pool
161 StartIndex := uint32(1)
162 EndIndex := uint32(16)
163
Esin Karaman5351fc52020-02-14 07:45:49 +0000164 reservedGemPortIds, defined := PONRMgr.getReservedGemPortIdsFromKVStore(ctx)
165 if !defined {
166 return
167 }
168
Neha Sharma94f16a92020-06-26 04:17:55 +0000169 FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, reservedGemPortIds)
Esin Karaman5351fc52020-02-14 07:45:49 +0000170 if err != nil {
171 t.Error("Failed to format resource", err)
172 return
173 }
174
175 // Add resource as json in kv store.
176 err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
177 if err != nil {
178 t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
179 return
180 }
181
182 for i := StartIndex; i <= (EndIndex - uint32(len(reservedGemPortIds))); i++ {
183 // get gem port id pool from the kv store
184 resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
185 if err != nil {
186 t.Error("Failed to get resource from gem port id pool", err)
187 return
188 }
189 // get a gem port id from the pool
Neha Sharma94f16a92020-06-26 04:17:55 +0000190 nextID, err := PONRMgr.GenerateNextID(ctx, resource)
Esin Karaman5351fc52020-02-14 07:45:49 +0000191 if err != nil {
192 t.Error("Failed to get gem port id from the pool", err)
193 return
194 }
195
196 //given gem port id should not equal to the reserved gem port id
197 assert.NotEqual(t, nextID, RESERVED_GEM_PORT_ID)
198 // put updated gem port id pool into the kv store
199 err = PONRMgr.UpdateResource(context.Background(), GEMPORT_ID_POOL_PATH, resource)
200 if err != nil {
201 t.Error("Failed to put updated gem port id pool into the kv store", err)
202 return
203 }
204 }
205
206}
Esin Karamanefab54e2021-10-13 11:11:43 +0000207
208func TestResourcePoolOverflow(t *testing.T) {
209 ctx := context.Background()
210 PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
211 "etcd", "1:1", "service/voltha")
212 if err != nil {
213 return
214 }
215 PONRMgr.KVStore = &db.Backend{
216 Client: newMockKvClient(ctx),
217 }
218
219 PONRMgr.KVStoreForConfig = &db.Backend{
220 Client: newMockKvClient(ctx),
221 }
222 // create a pool in the range of [1,16]
223 StartIndex := uint32(1)
224 EndIndex := uint32(16)
225
226 FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, []uint32{})
227 if err != nil {
228 t.Error("Failed to format resource", err)
229 return
230 }
231
232 // Add resource as json in kv store.
233 err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
234 if err != nil {
235 t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
236 return
237 }
238
239 for i := 1; i <= 20; i++ {
240 // get gem port id pool from the kv store
241 resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
242 if err != nil {
243 t.Error("Failed to get resource from gem port id pool", err)
244 return
245 }
246 // get a gem port id from the pool
247 nextID, err := PONRMgr.GenerateNextID(ctx, resource)
248 // all free ids in the pool will be consumed by the first 16 steps of the loop
249 // resource-exhausted error is expected from the pool at the 17th step of the loop
250 if i > int(EndIndex) {
251 assert.NotNil(t, err)
252 } else if err != nil {
253 t.Error("Failed to get gem port id from the pool", err)
254 return
255 } else {
256 assert.NotEqual(t, 0, nextID)
257 // put updated gem port id pool into the kv store
258 err = PONRMgr.UpdateResource(context.Background(), GEMPORT_ID_POOL_PATH, resource)
259 if err != nil {
260 t.Error("Failed to put updated gem port id pool into the kv store", err)
261 return
262 }
263 }
264 }
265}
266
267func TestPONResourceManager_ReleaseInvalidID(t *testing.T) {
268 ctx := context.Background()
269 PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
270 "etcd", "1:1", "service/voltha")
271 if err != nil {
272 return
273 }
274 PONRMgr.KVStore = &db.Backend{
275 Client: newMockKvClient(ctx),
276 }
277
278 PONRMgr.KVStoreForConfig = &db.Backend{
279 Client: newMockKvClient(ctx),
280 }
281 // create a pool in the range of [1,16]
282 StartIndex := uint32(1)
283 EndIndex := uint32(16)
284
285 FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, []uint32{})
286 if err != nil {
287 t.Error("Failed to format resource", err)
288 return
289 }
290
291 // Add resource as json in kv store.
292 err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
293 if err != nil {
294 t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
295 return
296 }
297
298 // get gem port id pool from the kv store
299 resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
300 if err != nil {
301 t.Error("Failed to get resource from gem port id pool", err)
302 return
303 }
304 //try to release an ID whose value is out of the boundaries of the pool and expect false
305 released := PONRMgr.ReleaseID(ctx, resource, uint32(EndIndex+1))
306 assert.Equal(t, false, released)
307}
308
309func TestPONResourceManager_ReserveInvalidID(t *testing.T) {
310 ctx := context.Background()
311 PONRMgr, err := NewPONResourceManager(ctx, "gpon", "onu", "olt1",
312 "etcd", "1:1", "service/voltha")
313 if err != nil {
314 return
315 }
316 PONRMgr.KVStore = &db.Backend{
317 Client: newMockKvClient(ctx),
318 }
319
320 PONRMgr.KVStoreForConfig = &db.Backend{
321 Client: newMockKvClient(ctx),
322 }
323 // create a pool in the range of [1,16]
324 StartIndex := uint32(1)
325 EndIndex := uint32(16)
326
327 FormatResult, err := PONRMgr.FormatResource(ctx, 1, StartIndex, EndIndex, []uint32{})
328 if err != nil {
329 t.Error("Failed to format resource", err)
330 return
331 }
332
333 // Add resource as json in kv store.
334 err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
335 if err != nil {
336 t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
337 return
338 }
339 // get gem port id pool from the kv store
340 resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
341 if err != nil {
342 t.Error("Failed to get resource from gem port id pool", err)
343 return
344 }
345 ByteArray, err := ToByte(resource[POOL])
346 if err != nil {
347 t.Error(ctx, "Failed to convert resource to byte array")
348 return
349 }
350 Data := bitmap.TSFromData(ByteArray, false)
351 if Data == nil {
352 t.Error(ctx, "Failed to get resource pool")
353 return
354 }
355 //try to reserve an ID whose value is out of the boundaries of the pool and expect false
356 reserved := PONRMgr.reserveID(ctx, Data, StartIndex, EndIndex+1)
357 assert.Equal(t, false, reserved)
358}