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