blob: 728cac8bab5452e1d7616646267f357c1cddc965 [file] [log] [blame]
mpagenkoaf801632020-07-03 10:00:42 +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
17//Package adaptercoreonu provides the utility for onu devices, flows and statistics
18package adaptercoreonu
19
20import (
21 "context"
22 "encoding/json"
mpagenko1cc3cb42020-07-27 15:24:38 +000023 "errors"
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +000024 "fmt"
mpagenko3dbcdd22020-07-22 07:38:45 +000025 "strconv"
26 "strings"
mpagenkoaf801632020-07-03 10:00:42 +000027 "sync"
28
mpagenko3dbcdd22020-07-22 07:38:45 +000029 "github.com/looplab/fsm"
mpagenkoaf801632020-07-03 10:00:42 +000030 "github.com/opencord/voltha-lib-go/v3/pkg/db"
31 "github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
32 "github.com/opencord/voltha-lib-go/v3/pkg/log"
33 tp "github.com/opencord/voltha-lib-go/v3/pkg/techprofile"
34)
35
36const cBasePathTechProfileKVStore = "service/voltha/technology_profiles"
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +000037const cBasePathOnuKVStore = "service/voltha/openonu"
38
39//definitions for TechProfileProcessing - copied from OltAdapter:openolt_flowmgr.go
40// could perhaps be defined more globally
41const (
42 // BinaryStringPrefix is binary string prefix
43 BinaryStringPrefix = "0b"
44 // BinaryBit1 is binary bit 1 expressed as a character
45 BinaryBit1 = '1'
46)
mpagenkoaf801632020-07-03 10:00:42 +000047
48type resourceEntry int
49
50const (
51 cResourceGemPort resourceEntry = 1
52 cResourceTcont resourceEntry = 2
53)
54
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +000055type uniPersData struct {
56 PersUniId uint32 `json:"uni_id"`
57 PersTpPath string `json:"tp_path"`
mpagenkoaf801632020-07-03 10:00:42 +000058}
59
60type onuPersistentData struct {
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +000061 PersOnuID uint32 `json:"onu_id"`
62 PersIntfID uint32 `json:"intf_id"`
63 PersSnr string `json:"serial_number"`
64 PersAdminState string `json:"admin_state"`
65 PersOperState string `json:"oper_state"`
66 PersUniTpPath []uniPersData `json:"uni_config"`
mpagenkoaf801632020-07-03 10:00:42 +000067}
68
mpagenko3dbcdd22020-07-22 07:38:45 +000069type tTechProfileIndication struct {
70 techProfileType string
71 techProfileID uint16
72}
73
74type tcontParamStruct struct {
75 allocID uint16
76 schedPolicy uint8
77}
78type gemPortParamStruct struct {
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +000079 ponOmciCC bool
mpagenko3dbcdd22020-07-22 07:38:45 +000080 gemPortID uint16
81 direction uint8
82 gemPortEncState uint8
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +000083 prioQueueIndex uint8
84 pbitString string
mpagenko3dbcdd22020-07-22 07:38:45 +000085 discardPolicy string
86 //could also be a queue specific paramter, not used that way here
87 maxQueueSize uint16
88 queueSchedPolicy string
89 queueWeight uint8
90}
91
92//refers to one tcont and its properties and all assigned GemPorts and their properties
93type tcontGemList struct {
94 tcontParams tcontParamStruct
95 mapGemPortParams map[uint16]*gemPortParamStruct
96}
97
98//refers to all tcont and their Tcont/GemPort Parameters
99type tMapPonAniConfig map[uint16]*tcontGemList
100
mpagenkoaf801632020-07-03 10:00:42 +0000101//OnuUniTechProf structure holds information about the TechProfiles attached to Uni Ports of the ONU
102type OnuUniTechProf struct {
103 deviceID string
104 baseDeviceHandler *DeviceHandler
105 tpProcMutex sync.RWMutex
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000106 mapUniTpPath map[uint32]string
mpagenkoaf801632020-07-03 10:00:42 +0000107 sOnuPersistentData onuPersistentData
108 techProfileKVStore *db.Backend
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000109 onuKVStore *db.Backend
110 onuKVStorePath string
mpagenko3dbcdd22020-07-22 07:38:45 +0000111 chTpProcessingStep chan uint8
112 mapUniTpIndication map[uint32]*tTechProfileIndication //use pointer values to ease assignments to the map
113 mapPonAniConfig map[uint32]*tMapPonAniConfig //per UNI: use pointer values to ease assignments to the map
114 pAniConfigFsm *UniPonAniConfigFsm
mpagenko1cc3cb42020-07-27 15:24:38 +0000115 procResult error //error indication of processing
mpagenkoaf801632020-07-03 10:00:42 +0000116}
117
118//NewOnuUniTechProf returns the instance of a OnuUniTechProf
119//(one instance per ONU/deviceHandler for all possible UNI's)
120func NewOnuUniTechProf(ctx context.Context, aDeviceID string, aDeviceHandler *DeviceHandler) *OnuUniTechProf {
divyadesai4d299552020-08-18 07:13:49 +0000121 logger.Infow("init-OnuUniTechProf", log.Fields{"device-id": aDeviceID})
mpagenkoaf801632020-07-03 10:00:42 +0000122 var onuTP OnuUniTechProf
123 onuTP.deviceID = aDeviceID
124 onuTP.baseDeviceHandler = aDeviceHandler
125 onuTP.tpProcMutex = sync.RWMutex{}
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000126 onuTP.mapUniTpPath = make(map[uint32]string)
127 onuTP.sOnuPersistentData.PersUniTpPath = make([]uniPersData, 1)
mpagenko3dbcdd22020-07-22 07:38:45 +0000128 onuTP.chTpProcessingStep = make(chan uint8)
129 onuTP.mapUniTpIndication = make(map[uint32]*tTechProfileIndication)
130 onuTP.mapPonAniConfig = make(map[uint32]*tMapPonAniConfig)
mpagenko1cc3cb42020-07-27 15:24:38 +0000131 onuTP.procResult = nil //default assumption processing done with success
mpagenkoaf801632020-07-03 10:00:42 +0000132
133 onuTP.techProfileKVStore = aDeviceHandler.SetBackend(cBasePathTechProfileKVStore)
134 if onuTP.techProfileKVStore == nil {
135 logger.Errorw("Can't access techProfileKVStore - no backend connection to service",
divyadesai4d299552020-08-18 07:13:49 +0000136 log.Fields{"device-id": aDeviceID, "service": cBasePathTechProfileKVStore})
mpagenkoaf801632020-07-03 10:00:42 +0000137 }
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000138
139 onuTP.onuKVStorePath = onuTP.deviceID
140 onuTP.onuKVStore = aDeviceHandler.SetBackend(cBasePathOnuKVStore)
141 if onuTP.onuKVStore == nil {
142 logger.Errorw("Can't access onuKVStore - no backend connection to service",
divyadesai4d299552020-08-18 07:13:49 +0000143 log.Fields{"device-id": aDeviceID, "service": cBasePathOnuKVStore})
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000144 }
mpagenkoaf801632020-07-03 10:00:42 +0000145 return &onuTP
146}
147
148// lockTpProcMutex locks OnuUniTechProf processing mutex
149func (onuTP *OnuUniTechProf) lockTpProcMutex() {
150 onuTP.tpProcMutex.Lock()
151}
152
153// unlockTpProcMutex unlocks OnuUniTechProf processing mutex
154func (onuTP *OnuUniTechProf) unlockTpProcMutex() {
155 onuTP.tpProcMutex.Unlock()
156}
157
mpagenko1cc3cb42020-07-27 15:24:38 +0000158// resetProcessingErrorIndication resets the internal error indication
159// need to be called before evaluation of any subsequent processing (given by waitForTpCompletion())
160func (onuTP *OnuUniTechProf) resetProcessingErrorIndication() {
161 onuTP.procResult = nil
162}
163
mpagenkoaf801632020-07-03 10:00:42 +0000164// updateOnuUniTpPath verifies and updates changes in the kvStore onuUniTpPath
165func (onuTP *OnuUniTechProf) updateOnuUniTpPath(aUniID uint32, aPathString string) bool {
166 /* within some specific InterAdapter processing request write/read access to data is ensured to be sequentially,
167 as also the complete sequence is ensured to 'run to completion' before some new request is accepted
168 no specific concurrency protection to sOnuPersistentData is required here
169 */
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000170 if existingPath, present := onuTP.mapUniTpPath[aUniID]; present {
mpagenkoaf801632020-07-03 10:00:42 +0000171 // uni entry already exists
172 //logger.Debugw(" already exists", log.Fields{"for InstanceId": a_uniInstNo})
173 if existingPath != aPathString {
174 if aPathString == "" {
175 //existing entry to be deleted
176 logger.Debugw("UniTp path delete", log.Fields{
divyadesai4d299552020-08-18 07:13:49 +0000177 "device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000178 delete(onuTP.mapUniTpPath, aUniID)
mpagenkoaf801632020-07-03 10:00:42 +0000179 } else {
180 //existing entry to be modified
181 logger.Debugw("UniTp path modify", log.Fields{
divyadesai4d299552020-08-18 07:13:49 +0000182 "device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000183 onuTP.mapUniTpPath[aUniID] = aPathString
mpagenkoaf801632020-07-03 10:00:42 +0000184 }
185 return true
186 }
187 //entry already exists
188 logger.Debugw("UniTp path already exists", log.Fields{
divyadesai4d299552020-08-18 07:13:49 +0000189 "device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
mpagenkoaf801632020-07-03 10:00:42 +0000190 return false
191 }
192 //uni entry does not exist
193 if aPathString == "" {
194 //delete request in non-existing state , accept as no change
195 logger.Debugw("UniTp path already removed", log.Fields{
divyadesai4d299552020-08-18 07:13:49 +0000196 "device-id": onuTP.deviceID, "uniID": aUniID})
mpagenkoaf801632020-07-03 10:00:42 +0000197 return false
198 }
199 //new entry to be set
200 logger.Debugw("New UniTp path set", log.Fields{
divyadesai4d299552020-08-18 07:13:49 +0000201 "device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000202 onuTP.mapUniTpPath[aUniID] = aPathString
mpagenkoaf801632020-07-03 10:00:42 +0000203 return true
204}
205
mpagenko1cc3cb42020-07-27 15:24:38 +0000206func (onuTP *OnuUniTechProf) waitForTpCompletion(cancel context.CancelFunc, wg *sync.WaitGroup) error {
mpagenko3dbcdd22020-07-22 07:38:45 +0000207 defer cancel() //ensure termination of context (may be pro forma)
208 wg.Wait()
209 logger.Debug("some TechProfile Processing completed")
210 onuTP.tpProcMutex.Unlock() //allow further TP related processing
mpagenko1cc3cb42020-07-27 15:24:38 +0000211 return onuTP.procResult
mpagenko3dbcdd22020-07-22 07:38:45 +0000212}
213
214// configureUniTp checks existing tp resources to delete and starts the corresponding OMCI configuation of the UNI port
215// all possibly blocking processing must be run in background to allow for deadline supervision!
216// but take care on sequential background processing when needed (logical dependencies)
217// use waitForTimeoutOrCompletion(ctx, processingStep) for internal synchronisation
218func (onuTP *OnuUniTechProf) configureUniTp(ctx context.Context,
219 aUniID uint32, aPathString string, wg *sync.WaitGroup) {
220 defer wg.Done() //always decrement the waitGroup on return
mpagenkoaf801632020-07-03 10:00:42 +0000221 logger.Debugw("configure the Uni according to TpPath", log.Fields{
divyadesai4d299552020-08-18 07:13:49 +0000222 "device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
mpagenkoaf801632020-07-03 10:00:42 +0000223
mpagenkoaf801632020-07-03 10:00:42 +0000224 if onuTP.techProfileKVStore == nil {
225 logger.Debug("techProfileKVStore not set - abort")
mpagenko1cc3cb42020-07-27 15:24:38 +0000226 onuTP.procResult = errors.New("TechProfile config aborted: techProfileKVStore not set")
mpagenkoaf801632020-07-03 10:00:42 +0000227 return
228 }
229
mpagenko3dbcdd22020-07-22 07:38:45 +0000230 //ensure that the given uniID is available (configured) in the UniPort class (used for OMCI entities)
231 var pCurrentUniPort *OnuUniPort
232 for _, uniPort := range onuTP.baseDeviceHandler.uniEntityMap {
233 // only if this port is validated for operState transfer
234 if uniPort.uniId == uint8(aUniID) {
235 pCurrentUniPort = uniPort
236 break //found - end search loop
237 }
238 }
239 if pCurrentUniPort == nil {
240 logger.Errorw("TechProfile configuration aborted: requested uniID not found in PortDB",
241 log.Fields{"device-id": onuTP.deviceID, "uniID": aUniID})
mpagenko1cc3cb42020-07-27 15:24:38 +0000242 onuTP.procResult = errors.New("TechProfile config aborted: requested uniID not found")
mpagenko3dbcdd22020-07-22 07:38:45 +0000243 return
244 }
mpagenkoaf801632020-07-03 10:00:42 +0000245
mpagenko3dbcdd22020-07-22 07:38:45 +0000246 var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpProcessingStep
mpagenkoaf801632020-07-03 10:00:42 +0000247
mpagenko3dbcdd22020-07-22 07:38:45 +0000248 //according to updateOnuUniTpPath() logic the assumption here is, that this configuration is only called
249 // in case the KVPath has changed for the given UNI,
250 // as T-Cont and Gem-Id's are dependent on TechProfile-Id this means, that possibly previously existing
251 // (ANI) configuration of this port has to be removed first
252 // (moreover in this case a possibly existing flow configuration is also not valid anymore and needs clean-up as well)
253 // existence of configuration can be detected based on tp stored TCONT's
254 //TODO!!!:
255 /* if tcontMap not empty {
256 go onuTP.deleteAniSideConfig(ctx, aUniID, processingStep)
257 if !onuTP.waitForTimeoutOrCompletion(ctx, processingStep) {
258 //timeout or error detected
259 return
260 }
261 clear tcontMap
262 }
263
264 processingStep++
265 */
266 go onuTP.readAniSideConfigFromTechProfile(ctx, aUniID, aPathString, processingStep)
267 if !onuTP.waitForTimeoutOrCompletion(ctx, processingStep) {
268 //timeout or error detected
269 logger.Debugw("tech-profile related configuration aborted on read",
270 log.Fields{"device-id": onuTP.deviceID, "UniId": aUniID})
mpagenko1cc3cb42020-07-27 15:24:38 +0000271 onuTP.procResult = errors.New("TechProfile config aborted: tech-profile read issue")
mpagenko3dbcdd22020-07-22 07:38:45 +0000272 return
273 }
274
275 processingStep++
276 if valuePA, existPA := onuTP.mapPonAniConfig[aUniID]; existPA {
277 if _, existTG := (*valuePA)[0]; existTG {
278 //Config data for this uni and and at least TCont Index 0 exist
279 go onuTP.setAniSideConfigFromTechProfile(ctx, aUniID, pCurrentUniPort, processingStep)
280 if !onuTP.waitForTimeoutOrCompletion(ctx, processingStep) {
281 //timeout or error detected
282 logger.Debugw("tech-profile related configuration aborted on set",
283 log.Fields{"device-id": onuTP.deviceID, "UniId": aUniID})
mpagenko1cc3cb42020-07-27 15:24:38 +0000284 onuTP.procResult = errors.New("TechProfile config aborted: Omci AniSideConfig failed")
mpagenko3dbcdd22020-07-22 07:38:45 +0000285 //this issue here means that the AniConfigFsm has not finished succesfully
286 //which requires to reset it to allow for new usage, e.g. also on a different UNI
287 //(without that it would be reset on device down indication latest)
mpagenko1cc3cb42020-07-27 15:24:38 +0000288 onuTP.pAniConfigFsm.pAdaptFsm.pFsm.Event(aniEvReset)
mpagenko3dbcdd22020-07-22 07:38:45 +0000289 return
mpagenkoaf801632020-07-03 10:00:42 +0000290 }
291 } else {
mpagenko3dbcdd22020-07-22 07:38:45 +0000292 // strange: UNI entry exists, but no ANI data, maybe such situation should be cleared up (if observed)
293 logger.Debugw("no Tcont/Gem data for this UNI found - abort", log.Fields{
divyadesai4d299552020-08-18 07:13:49 +0000294 "device-id": onuTP.deviceID, "uniID": aUniID})
mpagenko1cc3cb42020-07-27 15:24:38 +0000295 onuTP.procResult = errors.New("TechProfile config aborted: no Tcont/Gem data found for this UNI")
296 return
mpagenkoaf801632020-07-03 10:00:42 +0000297 }
298 } else {
mpagenko3dbcdd22020-07-22 07:38:45 +0000299 logger.Debugw("no PonAni data for this UNI found - abort", log.Fields{
divyadesai4d299552020-08-18 07:13:49 +0000300 "device-id": onuTP.deviceID, "uniID": aUniID})
mpagenko1cc3cb42020-07-27 15:24:38 +0000301 onuTP.procResult = errors.New("TechProfile config aborted: no AniSide data found for this UNI")
302 return
mpagenkoaf801632020-07-03 10:00:42 +0000303 }
304}
305
mpagenko3dbcdd22020-07-22 07:38:45 +0000306func (onuTP *OnuUniTechProf) updateOnuTpPathKvStore(ctx context.Context, wg *sync.WaitGroup) {
mpagenkoaf801632020-07-03 10:00:42 +0000307 defer wg.Done()
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000308
309 if onuTP.onuKVStore == nil {
310 logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
311 onuTP.procResult = errors.New("ONU/TP-data update aborted: onuKVStore not set")
312 return
313 }
314 var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpProcessingStep
315 go onuTP.storePersistentData(ctx, processingStep)
316 if !onuTP.waitForTimeoutOrCompletion(ctx, processingStep) {
317 //timeout or error detected
318 logger.Debugw("ONU/TP-data not written - abort", log.Fields{"device-id": onuTP.deviceID})
319 onuTP.procResult = errors.New("ONU/TP-data update aborted: during writing process")
320 return
321 }
322}
323
324func (onuTP *OnuUniTechProf) restoreFromOnuTpPathKvStore(ctx context.Context) error {
325 if onuTP.onuKVStore == nil {
326 logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
327 return fmt.Errorf(fmt.Sprintf("onuKVStore-not-set-abort-%s", onuTP.deviceID))
328 }
329 if err := onuTP.restorePersistentData(ctx); err != nil {
330 logger.Debugw("ONU/TP-data not read - abort", log.Fields{"device-id": onuTP.deviceID})
331 return err
332 }
333 return nil
334}
335
336func (onuTP *OnuUniTechProf) deleteOnuTpPathKvStore(ctx context.Context) error {
337 if onuTP.onuKVStore == nil {
338 logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
339 return fmt.Errorf(fmt.Sprintf("onuKVStore-not-set-abort-%s", onuTP.deviceID))
340 }
341 if err := onuTP.deletePersistentData(ctx); err != nil {
342 logger.Debugw("ONU/TP-data not read - abort", log.Fields{"device-id": onuTP.deviceID})
343 return err
344 }
345 return nil
mpagenkoaf801632020-07-03 10:00:42 +0000346}
347
mpagenko3dbcdd22020-07-22 07:38:45 +0000348// deleteTpResource removes Resources from the ONU's specified Uni
349func (onuTP *OnuUniTechProf) deleteTpResource(ctx context.Context,
350 aUniID uint32, aPathString string, aResource resourceEntry, aEntryID uint32,
351 wg *sync.WaitGroup) {
mpagenkoaf801632020-07-03 10:00:42 +0000352 defer wg.Done()
353 logger.Debugw("this would remove TP resources from ONU's UNI", log.Fields{
divyadesai4d299552020-08-18 07:13:49 +0000354 "device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString, "Resource": aResource})
mpagenkoaf801632020-07-03 10:00:42 +0000355 //TODO!!!
mpagenko3dbcdd22020-07-22 07:38:45 +0000356 //delete the given resource from ONU OMCI config and data base - as background routine
357 /*
358 var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpProcessingStep
359 go onuTp.deleteAniResource(ctx, processingStep)
360 if !onuTP.waitForTimeoutOrCompletion(ctx, processingStep) {
361 //timeout or error detected
362 return
363 }
364 */
mpagenkoaf801632020-07-03 10:00:42 +0000365}
366
mpagenko3dbcdd22020-07-22 07:38:45 +0000367/* internal methods *********************/
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000368
369func (onuTP *OnuUniTechProf) storePersistentData(ctx context.Context, aProcessingStep uint8) {
370
371 onuTP.sOnuPersistentData.PersOnuID = onuTP.baseDeviceHandler.pOnuIndication.OnuId
372 onuTP.sOnuPersistentData.PersIntfID = onuTP.baseDeviceHandler.pOnuIndication.IntfId
373 onuTP.sOnuPersistentData.PersSnr = onuTP.baseDeviceHandler.pOnuOmciDevice.serialNumber
374 //TODO: verify usage of these values during restart UC
375 onuTP.sOnuPersistentData.PersAdminState = "up"
376 onuTP.sOnuPersistentData.PersOperState = "active"
377
378 onuTP.sOnuPersistentData.PersUniTpPath = onuTP.sOnuPersistentData.PersUniTpPath[:0]
379
380 for k, v := range onuTP.mapUniTpPath {
381 onuTP.sOnuPersistentData.PersUniTpPath =
382 append(onuTP.sOnuPersistentData.PersUniTpPath, uniPersData{PersUniId: k, PersTpPath: v})
383 }
divyadesai4d299552020-08-18 07:13:49 +0000384 logger.Debugw("Update ONU/TP-data in KVStore", log.Fields{"device-id": onuTP.deviceID, "onuTP.sOnuPersistentData": onuTP.sOnuPersistentData})
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000385
386 Value, err := json.Marshal(onuTP.sOnuPersistentData)
387 if err != nil {
388 logger.Errorw("unable to marshal ONU/TP-data", log.Fields{"onuTP.sOnuPersistentData": onuTP.sOnuPersistentData,
389 "device-id": onuTP.deviceID, "err": err})
390 onuTP.chTpProcessingStep <- 0 //error indication
391 return
392 }
393 err = onuTP.onuKVStore.Put(ctx, onuTP.onuKVStorePath, Value)
394 if err != nil {
395 logger.Errorw("unable to write ONU/TP-data into KVstore", log.Fields{"device-id": onuTP.deviceID, "err": err})
396 onuTP.chTpProcessingStep <- 0 //error indication
397 return
398 }
399 onuTP.chTpProcessingStep <- aProcessingStep //done
400}
401
402func (onuTP *OnuUniTechProf) restorePersistentData(ctx context.Context) error {
403
404 onuTP.mapUniTpPath = make(map[uint32]string)
405 onuTP.sOnuPersistentData = onuPersistentData{0, 0, "", "", "", make([]uniPersData, 0)}
406
407 Value, err := onuTP.onuKVStore.Get(ctx, onuTP.onuKVStorePath)
408 if err == nil {
409 if Value != nil {
410 logger.Debugw("ONU/TP-data read",
411 log.Fields{"Key": Value.Key, "device-id": onuTP.deviceID})
412 tpTmpBytes, _ := kvstore.ToByte(Value.Value)
413
414 if err = json.Unmarshal(tpTmpBytes, &onuTP.sOnuPersistentData); err != nil {
415 logger.Errorw("unable to unmarshal ONU/TP-data", log.Fields{"error": err, "device-id": onuTP.deviceID})
416 return fmt.Errorf(fmt.Sprintf("unable-to-unmarshal-ONU/TP-data-%s", onuTP.deviceID))
417 }
418 logger.Debugw("ONU/TP-data", log.Fields{"onuTP.sOnuPersistentData": onuTP.sOnuPersistentData,
419 "device-id": onuTP.deviceID})
420
421 for _, uniData := range onuTP.sOnuPersistentData.PersUniTpPath {
422 onuTP.mapUniTpPath[uniData.PersUniId] = uniData.PersTpPath
423 }
424 logger.Debugw("TpPath map", log.Fields{"onuTP.mapUniTpPath": onuTP.mapUniTpPath,
425 "device-id": onuTP.deviceID})
426 } else {
427 logger.Errorw("no ONU/TP-data found", log.Fields{"path": onuTP.onuKVStorePath, "device-id": onuTP.deviceID})
428 return fmt.Errorf(fmt.Sprintf("no-ONU/TP-data-found-%s", onuTP.deviceID))
429 }
430 } else {
431 logger.Errorw("unable to read from KVstore", log.Fields{"device-id": onuTP.deviceID})
432 return fmt.Errorf(fmt.Sprintf("unable-to-read-from-KVstore-%s", onuTP.deviceID))
433 }
434 return nil
435}
436
437func (onuTP *OnuUniTechProf) deletePersistentData(ctx context.Context) error {
438
divyadesai4d299552020-08-18 07:13:49 +0000439 logger.Debugw("delete ONU/TP-data in KVStore", log.Fields{"device-id": onuTP.deviceID})
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000440 err := onuTP.onuKVStore.Delete(ctx, onuTP.onuKVStorePath)
441 if err != nil {
442 logger.Errorw("unable to delete in KVstore", log.Fields{"device-id": onuTP.deviceID, "err": err})
443 return fmt.Errorf(fmt.Sprintf("unable-delete-in-KVstore-%s", onuTP.deviceID))
444 }
445 return nil
446}
447
mpagenko3dbcdd22020-07-22 07:38:45 +0000448func (onuTP *OnuUniTechProf) readAniSideConfigFromTechProfile(
449 ctx context.Context, aUniID uint32, aPathString string, aProcessingStep uint8) {
450 var tpInst tp.TechProfile
451
452 //store profile type and identifier for later usage within the OMCI identifier and possibly ME setup
453 //pathstring is defined to be in the form of <ProfType>/<profID>/<Interface/../Identifier>
454 subStringSlice := strings.Split(aPathString, "/")
455 if len(subStringSlice) <= 2 {
456 logger.Errorw("invalid path name format",
457 log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
458 onuTP.chTpProcessingStep <- 0 //error indication
459 return
460 }
461
462 //just some logical check to avoid unexpected behavior
463 //at this point it is assumed that a new TechProfile is assigned to the UNI
464 //expectation is that no TPIndication entry exists here, if yes,
465 // then we throw a warning and remove it (and the possible ANIConfig) simply
466 // note that the ONU config state may be ambivalent in such a case
467 // also note, that the PonAniConfig map is not checked additionally
468 // consistency to TPIndication is assumed
469 if _, existTP := onuTP.mapUniTpIndication[aUniID]; existTP {
470 logger.Warnw("Some active profile entry at reading new TechProfile",
471 log.Fields{"path": aPathString, "device-id": onuTP.deviceID,
472 "UniId": aUniID, "wrongProfile": onuTP.mapUniTpIndication[aUniID].techProfileID})
473 //delete on the mapUniTpIndication map not needed, just overwritten later
474 //delete on the PonAniConfig map should be safe, even if not existing
475 delete(onuTP.mapPonAniConfig, aUniID)
476 } else {
477 // this is normal processing
478 onuTP.mapUniTpIndication[aUniID] = &tTechProfileIndication{} //need to assign some (empty) struct memory first!
479 }
480
481 onuTP.mapUniTpIndication[aUniID].techProfileType = subStringSlice[0]
482 profID, err := strconv.ParseUint(subStringSlice[1], 10, 32)
483 if err != nil {
484 logger.Errorw("invalid ProfileId from path",
485 log.Fields{"ParseErr": err})
486 onuTP.chTpProcessingStep <- 0 //error indication
487 return
488 }
489
490 //note the limitation on ID range (probably even more limited) - based on usage within OMCI EntityID
491 onuTP.mapUniTpIndication[aUniID].techProfileID = uint16(profID)
492 logger.Debugw("tech-profile path indications",
493 log.Fields{"device-id": onuTP.deviceID, "UniId": aUniID,
494 "profType": onuTP.mapUniTpIndication[aUniID].techProfileType,
495 "profID": onuTP.mapUniTpIndication[aUniID].techProfileID})
496
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000497 Value, err := onuTP.techProfileKVStore.Get(ctx, aPathString)
mpagenko3dbcdd22020-07-22 07:38:45 +0000498 if err == nil {
499 if Value != nil {
500 logger.Debugw("tech-profile read",
501 log.Fields{"Key": Value.Key, "device-id": onuTP.deviceID})
502 tpTmpBytes, _ := kvstore.ToByte(Value.Value)
503
504 if err = json.Unmarshal(tpTmpBytes, &tpInst); err != nil {
505 logger.Errorw("TechProf - Failed to unmarshal tech-profile into tpInst",
506 log.Fields{"error": err, "device-id": onuTP.deviceID})
507 onuTP.chTpProcessingStep <- 0 //error indication
508 return
509 }
510 logger.Debugw("TechProf - tpInst", log.Fields{"tpInst": tpInst})
511 // access examples
512 logger.Debugw("TechProf content", log.Fields{"Name": tpInst.Name,
513 "MaxGemPayloadSize": tpInst.InstanceCtrl.MaxGemPayloadSize,
514 "DownstreamGemDiscardmaxThreshold": tpInst.DownstreamGemPortAttributeList[0].DiscardConfig.MaxThreshold})
515 } else {
516 logger.Errorw("No tech-profile found",
517 log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
518 onuTP.chTpProcessingStep <- 0 //error indication
519 return
520 }
521 } else {
522 logger.Errorw("kvstore-get failed for path",
523 log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
524 onuTP.chTpProcessingStep <- 0 //error indication
525 return
526 }
527
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000528 //default start with 1Tcont1Gem profile, later extend for multi GemPerTcont and perhaps even MultiTcontMultiGem
mpagenko3dbcdd22020-07-22 07:38:45 +0000529 localMapGemPortParams := make(map[uint16]*gemPortParamStruct)
530 localMapGemPortParams[0] = &gemPortParamStruct{}
531 localMapPonAniConfig := make(map[uint16]*tcontGemList)
532 localMapPonAniConfig[0] = &tcontGemList{tcontParamStruct{}, localMapGemPortParams}
533 onuTP.mapPonAniConfig[aUniID] = (*tMapPonAniConfig)(&localMapPonAniConfig)
534
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000535 //note: the code is currently restricted to one TCcont per Onu (index [0])
mpagenko3dbcdd22020-07-22 07:38:45 +0000536 //get the relevant values from the profile and store to mapPonAniConfig
537 (*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.allocID = uint16(tpInst.UsScheduler.AllocID)
538 //maybe tCont scheduling not (yet) needed - just to basicaly have it for future
539 // (would only be relevant in case of ONU-2G QOS configuration flexibility)
540 if tpInst.UsScheduler.QSchedPolicy == "StrictPrio" {
541 (*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.schedPolicy = 1 //for the moment fixed value acc. G.988 //TODO: defines!
542 } else {
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000543 //default profile defines "Hybrid" - which probably comes down to WRR with some weigthts for SP
mpagenko3dbcdd22020-07-22 07:38:45 +0000544 (*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.schedPolicy = 2 //for G.988 WRR
545 }
mpagenko1cc3cb42020-07-27 15:24:38 +0000546 loNumGemPorts := tpInst.NumGemPorts
547 loGemPortRead := false
mpagenko3dbcdd22020-07-22 07:38:45 +0000548 for pos, content := range tpInst.UpstreamGemPortAttributeList {
mpagenko1cc3cb42020-07-27 15:24:38 +0000549 if uint32(pos) == loNumGemPorts {
550 logger.Debugw("PonAniConfig abort GemPortList - GemList exceeds set NumberOfGemPorts",
551 log.Fields{"device-id": onuTP.deviceID, "index": pos, "NumGem": loNumGemPorts})
mpagenko3dbcdd22020-07-22 07:38:45 +0000552 break
553 }
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000554 if pos == 0 {
555 //at least one upstream GemPort should always exist (else traffic profile makes no sense)
556 loGemPortRead = true
557 } else {
558 //for all further GemPorts we need to extend the mapGemPortParams
559 (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)] = &gemPortParamStruct{}
560 }
561 (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].gemPortID =
562 uint16(content.GemportID)
563 //direction can be correlated later with Downstream list,
564 // for now just assume bidirectional (upstream never exists alone)
mpagenko3dbcdd22020-07-22 07:38:45 +0000565 (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].direction = 3 //as defined in G.988
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000566 // expected Prio-Queue values 0..7 with 7 for highest PrioQueue, QueueIndex=Prio = 0..7
567 if 7 < content.PriorityQueue {
568 logger.Errorw("PonAniConfig reject on GemPortList - PrioQueue value invalid",
569 log.Fields{"device-id": onuTP.deviceID, "index": pos, "PrioQueue": content.PriorityQueue})
570 //remove PonAniConfig as done so far, delete map should be safe, even if not existing
571 delete(onuTP.mapPonAniConfig, aUniID)
572 onuTP.chTpProcessingStep <- 0 //error indication
573 return
574 }
575 (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].prioQueueIndex =
576 uint8(content.PriorityQueue)
577 (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].pbitString =
578 strings.TrimPrefix(content.PbitMap, BinaryStringPrefix)
mpagenko3dbcdd22020-07-22 07:38:45 +0000579 if content.AesEncryption == "True" {
580 (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].gemPortEncState = 1
581 } else {
582 (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].gemPortEncState = 0
583 }
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000584 (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].discardPolicy =
585 content.DiscardPolicy
586 (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].queueSchedPolicy =
587 content.SchedulingPolicy
mpagenko3dbcdd22020-07-22 07:38:45 +0000588 //'GemWeight' looks strange in default profile, for now we just copy the weight to first queue
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000589 (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].queueWeight =
590 uint8(content.Weight)
mpagenko3dbcdd22020-07-22 07:38:45 +0000591 }
mpagenko1cc3cb42020-07-27 15:24:38 +0000592 if loGemPortRead == false {
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000593 logger.Errorw("PonAniConfig reject - no GemPort could be read from TechProfile",
mpagenko1cc3cb42020-07-27 15:24:38 +0000594 log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000595 //remove PonAniConfig as done so far, delete map should be safe, even if not existing
596 delete(onuTP.mapPonAniConfig, aUniID)
mpagenko1cc3cb42020-07-27 15:24:38 +0000597 onuTP.chTpProcessingStep <- 0 //error indication
598 return
599 }
mpagenko3dbcdd22020-07-22 07:38:45 +0000600 //TODO!! MC (downstream) GemPorts can be set using DownstreamGemPortAttributeList seperately
601
602 //logger does not simply output the given structures, just give some example debug values
603 logger.Debugw("PonAniConfig read from TechProfile", log.Fields{
Holger Hildebrandt9ca8b132020-08-07 14:45:03 +0000604 "device-id": onuTP.deviceID,
605 "AllocId": (*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.allocID})
606 for gemIndex, gemEntry := range (*(onuTP.mapPonAniConfig[0]))[0].mapGemPortParams {
607 logger.Debugw("PonAniConfig read from TechProfile", log.Fields{
608 "GemIndex": gemIndex,
609 "GemPort": gemEntry.gemPortID,
610 "QueueScheduling": gemEntry.queueSchedPolicy})
611 }
mpagenko3dbcdd22020-07-22 07:38:45 +0000612
613 onuTP.chTpProcessingStep <- aProcessingStep //done
614}
615
616func (onuTP *OnuUniTechProf) setAniSideConfigFromTechProfile(
617 ctx context.Context, aUniID uint32, apCurrentUniPort *OnuUniPort, aProcessingStep uint8) {
618
619 //OMCI transfer of ANI data acc. to mapPonAniConfig
620 // also the FSM's are running in background,
621 // hence we have to make sure they indicate 'success' success on chTpProcessingStep with aProcessingStep
622 if onuTP.pAniConfigFsm == nil {
623 onuTP.createAniConfigFsm(aUniID, apCurrentUniPort, OmciAniConfigDone, aProcessingStep)
624 } else { //AniConfigFsm already init
625 onuTP.runAniConfigFsm(aProcessingStep)
626 }
627}
628
629func (onuTP *OnuUniTechProf) waitForTimeoutOrCompletion(
630 ctx context.Context, aProcessingStep uint8) bool {
631 select {
632 case <-ctx.Done():
633 logger.Warnw("processing not completed in-time: force release of TpProcMutex!",
divyadesai4d299552020-08-18 07:13:49 +0000634 log.Fields{"device-id": onuTP.deviceID, "error": ctx.Err()})
mpagenko3dbcdd22020-07-22 07:38:45 +0000635 return false
636 case rxStep := <-onuTP.chTpProcessingStep:
637 if rxStep == aProcessingStep {
638 return true
639 }
640 //all other values are not accepted - including 0 for error indication
641 logger.Warnw("Invalid processing step received: abort and force release of TpProcMutex!",
divyadesai4d299552020-08-18 07:13:49 +0000642 log.Fields{"device-id": onuTP.deviceID,
mpagenko3dbcdd22020-07-22 07:38:45 +0000643 "wantedStep": aProcessingStep, "haveStep": rxStep})
644 return false
645 }
646}
647
648// createUniLockFsm initialises and runs the AniConfig FSM to transfer the OMCI related commands for ANI side configuration
649func (onuTP *OnuUniTechProf) createAniConfigFsm(aUniID uint32,
650 apCurrentUniPort *OnuUniPort, devEvent OnuDeviceEvent, aProcessingStep uint8) {
divyadesai4d299552020-08-18 07:13:49 +0000651 logger.Debugw("createAniConfigFsm", log.Fields{"device-id": onuTP.deviceID})
mpagenko3dbcdd22020-07-22 07:38:45 +0000652 chAniConfigFsm := make(chan Message, 2048)
653 pDevEntry := onuTP.baseDeviceHandler.GetOnuDeviceEntry(true)
654 if pDevEntry == nil {
divyadesai4d299552020-08-18 07:13:49 +0000655 logger.Errorw("No valid OnuDevice - aborting", log.Fields{"device-id": onuTP.deviceID})
mpagenko3dbcdd22020-07-22 07:38:45 +0000656 return
657 }
658 pAniCfgFsm := NewUniPonAniConfigFsm(pDevEntry.PDevOmciCC, apCurrentUniPort, onuTP,
659 pDevEntry.pOnuDB, onuTP.mapUniTpIndication[aUniID].techProfileID, devEvent,
660 "AniConfigFsm", onuTP.deviceID, chAniConfigFsm)
661 if pAniCfgFsm != nil {
662 onuTP.pAniConfigFsm = pAniCfgFsm
663 onuTP.runAniConfigFsm(aProcessingStep)
664 } else {
divyadesai4d299552020-08-18 07:13:49 +0000665 logger.Errorw("AniConfigFSM could not be created - abort!!", log.Fields{"device-id": onuTP.deviceID})
mpagenko3dbcdd22020-07-22 07:38:45 +0000666 }
667}
668
669// runAniConfigFsm starts the AniConfig FSM to transfer the OMCI related commands for ANI side configuration
670func (onuTP *OnuUniTechProf) runAniConfigFsm(aProcessingStep uint8) {
671 /* Uni related ANI config procedure -
672 ***** should run via 'aniConfigDone' state and generate the argument requested event *****
673 */
674 var pACStatemachine *fsm.FSM
675 pACStatemachine = onuTP.pAniConfigFsm.pAdaptFsm.pFsm
676 if pACStatemachine != nil {
mpagenko1cc3cb42020-07-27 15:24:38 +0000677 if pACStatemachine.Is(aniStDisabled) {
mpagenko3dbcdd22020-07-22 07:38:45 +0000678 //FSM init requirement to get informed abou FSM completion! (otherwise timeout of the TechProf config)
679 onuTP.pAniConfigFsm.SetFsmCompleteChannel(onuTP.chTpProcessingStep, aProcessingStep)
mpagenko1cc3cb42020-07-27 15:24:38 +0000680 if err := pACStatemachine.Event(aniEvStart); err != nil {
mpagenko3dbcdd22020-07-22 07:38:45 +0000681 logger.Warnw("AniConfigFSM: can't start", log.Fields{"err": err})
682 // maybe try a FSM reset and then again ... - TODO!!!
683 } else {
684 /***** AniConfigFSM started */
685 logger.Debugw("AniConfigFSM started", log.Fields{
divyadesai4d299552020-08-18 07:13:49 +0000686 "state": pACStatemachine.Current(), "device-id": onuTP.deviceID})
mpagenko3dbcdd22020-07-22 07:38:45 +0000687 }
688 } else {
689 logger.Warnw("wrong state of AniConfigFSM - want: disabled", log.Fields{
divyadesai4d299552020-08-18 07:13:49 +0000690 "have": pACStatemachine.Current(), "device-id": onuTP.deviceID})
mpagenko3dbcdd22020-07-22 07:38:45 +0000691 // maybe try a FSM reset and then again ... - TODO!!!
692 }
693 } else {
divyadesai4d299552020-08-18 07:13:49 +0000694 logger.Errorw("AniConfigFSM StateMachine invalid - cannot be executed!!", log.Fields{"device-id": onuTP.deviceID})
mpagenko3dbcdd22020-07-22 07:38:45 +0000695 // maybe try a FSM reset and then again ... - TODO!!!
696 }
mpagenkoaf801632020-07-03 10:00:42 +0000697}