blob: 09ae7bf9bfa195489ba25fa6a084535fd509ff54 [file] [log] [blame]
/*
* Copyright 2020-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Package adaptercoreonu provides the utility for onu devices, flows and statistics
package adaptercoreonu
import (
"context"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"sync"
"github.com/opencord/voltha-lib-go/v3/pkg/db"
"github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
"github.com/opencord/voltha-lib-go/v3/pkg/log"
tp "github.com/opencord/voltha-lib-go/v3/pkg/techprofile"
)
const cBasePathTechProfileKVStore = "service/voltha/technology_profiles"
const cBasePathOnuKVStore = "service/voltha/openonu"
//definitions for TechProfileProcessing - copied from OltAdapter:openolt_flowmgr.go
// could perhaps be defined more globally
const (
binaryStringPrefix = "0b"
)
type resourceEntry int
const (
cResourceGemPort resourceEntry = 1
cResourceTcont resourceEntry = 2
)
type uniPersData struct {
PersUniID uint32 `json:"uni_id"`
PersTpPath string `json:"tp_path"`
}
type onuPersistentData struct {
PersOnuID uint32 `json:"onu_id"`
PersIntfID uint32 `json:"intf_id"`
PersSnr string `json:"serial_number"`
PersAdminState string `json:"admin_state"`
PersOperState string `json:"oper_state"`
PersUniTpPath []uniPersData `json:"uni_config"`
}
type tTechProfileIndication struct {
techProfileType string
techProfileID uint16
techProfileConfigDone bool
}
type tcontParamStruct struct {
allocID uint16
schedPolicy uint8
}
type gemPortParamStruct struct {
gemPortID uint16
direction uint8
gemPortEncState uint8
prioQueueIndex uint8
pbitString string
discardPolicy string
queueSchedPolicy string
queueWeight uint8
}
//refers to one tcont and its properties and all assigned GemPorts and their properties
type tcontGemList struct {
tcontParams tcontParamStruct
mapGemPortParams map[uint16]*gemPortParamStruct
}
//refers to all tcont and their Tcont/GemPort Parameters
type tMapPonAniConfig map[uint16]*tcontGemList
//onuUniTechProf structure holds information about the TechProfiles attached to Uni Ports of the ONU
type onuUniTechProf struct {
deviceID string
baseDeviceHandler *deviceHandler
tpProcMutex sync.RWMutex
mapUniTpPath map[uint32]string
sOnuPersistentData onuPersistentData
techProfileKVStore *db.Backend
onuKVStore *db.Backend
onuKVStorePath string
chTpConfigProcessingStep chan uint8
chTpKvProcessingStep chan uint8
mapUniTpIndication map[uint8]*tTechProfileIndication //use pointer values to ease assignments to the map
mapPonAniConfig map[uint8]*tMapPonAniConfig //per UNI: use pointer values to ease assignments to the map
pAniConfigFsm *uniPonAniConfigFsm
procResult error //error indication of processing
mutexTPState sync.Mutex
}
//newOnuUniTechProf returns the instance of a OnuUniTechProf
//(one instance per ONU/deviceHandler for all possible UNI's)
func newOnuUniTechProf(ctx context.Context, aDeviceID string, aDeviceHandler *deviceHandler) *onuUniTechProf {
logger.Infow("init-OnuUniTechProf", log.Fields{"device-id": aDeviceID})
var onuTP onuUniTechProf
onuTP.deviceID = aDeviceID
onuTP.baseDeviceHandler = aDeviceHandler
onuTP.tpProcMutex = sync.RWMutex{}
onuTP.mapUniTpPath = make(map[uint32]string)
onuTP.sOnuPersistentData.PersUniTpPath = make([]uniPersData, 1)
onuTP.chTpConfigProcessingStep = make(chan uint8)
onuTP.chTpKvProcessingStep = make(chan uint8)
onuTP.mapUniTpIndication = make(map[uint8]*tTechProfileIndication)
onuTP.mapPonAniConfig = make(map[uint8]*tMapPonAniConfig)
onuTP.procResult = nil //default assumption processing done with success
onuTP.techProfileKVStore = aDeviceHandler.setBackend(cBasePathTechProfileKVStore)
if onuTP.techProfileKVStore == nil {
logger.Errorw("Can't access techProfileKVStore - no backend connection to service",
log.Fields{"device-id": aDeviceID, "service": cBasePathTechProfileKVStore})
}
onuTP.onuKVStorePath = onuTP.deviceID
onuTP.onuKVStore = aDeviceHandler.setBackend(cBasePathOnuKVStore)
if onuTP.onuKVStore == nil {
logger.Errorw("Can't access onuKVStore - no backend connection to service",
log.Fields{"device-id": aDeviceID, "service": cBasePathOnuKVStore})
}
return &onuTP
}
// lockTpProcMutex locks OnuUniTechProf processing mutex
func (onuTP *onuUniTechProf) lockTpProcMutex() {
onuTP.tpProcMutex.Lock()
}
// unlockTpProcMutex unlocks OnuUniTechProf processing mutex
func (onuTP *onuUniTechProf) unlockTpProcMutex() {
onuTP.tpProcMutex.Unlock()
}
// resetProcessingErrorIndication resets the internal error indication
// need to be called before evaluation of any subsequent processing (given by waitForTpCompletion())
func (onuTP *onuUniTechProf) resetProcessingErrorIndication() {
onuTP.procResult = nil
}
// updateOnuUniTpPath verifies and updates changes in the kvStore onuUniTpPath
func (onuTP *onuUniTechProf) updateOnuUniTpPath(aUniID uint32, aPathString string) bool {
/* within some specific InterAdapter processing request write/read access to data is ensured to be sequentially,
as also the complete sequence is ensured to 'run to completion' before some new request is accepted
no specific concurrency protection to sOnuPersistentData is required here
*/
if existingPath, present := onuTP.mapUniTpPath[aUniID]; present {
// uni entry already exists
//logger.Debugw(" already exists", log.Fields{"for InstanceId": a_uniInstNo})
if existingPath != aPathString {
if aPathString == "" {
//existing entry to be deleted
logger.Debugw("UniTp path delete", log.Fields{
"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
delete(onuTP.mapUniTpPath, aUniID)
} else {
//existing entry to be modified
logger.Debugw("UniTp path modify", log.Fields{
"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
onuTP.mapUniTpPath[aUniID] = aPathString
}
return true
}
//entry already exists
logger.Debugw("UniTp path already exists", log.Fields{
"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
return false
}
if aPathString == "" {
//delete request in non-existing state , accept as no change
logger.Debugw("UniTp path already removed", log.Fields{
"device-id": onuTP.deviceID, "uniID": aUniID})
return false
}
logger.Debugw("New UniTp path set", log.Fields{
"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
onuTP.mapUniTpPath[aUniID] = aPathString
return true
}
func (onuTP *onuUniTechProf) waitForTpCompletion(cancel context.CancelFunc, wg *sync.WaitGroup) error {
defer cancel() //ensure termination of context (may be pro forma)
wg.Wait()
logger.Debug("some TechProfile Processing completed")
onuTP.tpProcMutex.Unlock() //allow further TP related processing
return onuTP.procResult
}
// configureUniTp checks existing tp resources to delete and starts the corresponding OMCI configuation of the UNI port
// all possibly blocking processing must be run in background to allow for deadline supervision!
// but take care on sequential background processing when needed (logical dependencies)
// use waitForTimeoutOrCompletion(ctx, chTpConfigProcessingStep, processingStep) for internal synchronization
func (onuTP *onuUniTechProf) configureUniTp(ctx context.Context,
aUniID uint8, aPathString string, wg *sync.WaitGroup) {
defer wg.Done() //always decrement the waitGroup on return
logger.Debugw("configure the Uni according to TpPath", log.Fields{
"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
if onuTP.techProfileKVStore == nil {
logger.Debug("techProfileKVStore not set - abort")
onuTP.procResult = errors.New("techProfile config aborted: techProfileKVStore not set")
return
}
var pCurrentUniPort *onuUniPort
for _, uniPort := range onuTP.baseDeviceHandler.uniEntityMap {
// only if this port is validated for operState transfer
if uniPort.uniID == uint8(aUniID) {
pCurrentUniPort = uniPort
break //found - end search loop
}
}
if pCurrentUniPort == nil {
logger.Errorw("TechProfile configuration aborted: requested uniID not found in PortDB",
log.Fields{"device-id": onuTP.deviceID, "uniID": aUniID})
onuTP.procResult = errors.New("techProfile config aborted: requested uniID not found")
return
}
var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpConfigProcessingStep
/* if tcontMap not empty {
go onuTP.deleteAniSideConfig(ctx, aUniID, processingStep)
if !onuTP.waitForTimeoutOrCompletion(ctx, chTpConfigProcessingStep, processingStep) {
//timeout or error detected
return
}
clear tcontMap
}
processingStep++
*/
go onuTP.readAniSideConfigFromTechProfile(ctx, aUniID, aPathString, processingStep)
if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpConfigProcessingStep, processingStep) {
//timeout or error detected
logger.Debugw("tech-profile related configuration aborted on read",
log.Fields{"device-id": onuTP.deviceID, "UniId": aUniID})
onuTP.procResult = errors.New("techProfile config aborted: tech-profile read issue")
return
}
processingStep++
if valuePA, existPA := onuTP.mapPonAniConfig[aUniID]; existPA {
if _, existTG := (*valuePA)[0]; existTG {
//Config data for this uni and and at least TCont Index 0 exist
go onuTP.setAniSideConfigFromTechProfile(ctx, aUniID, pCurrentUniPort, processingStep)
if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpConfigProcessingStep, processingStep) {
//timeout or error detected
logger.Debugw("tech-profile related configuration aborted on set",
log.Fields{"device-id": onuTP.deviceID, "UniId": aUniID})
onuTP.procResult = errors.New("techProfile config aborted: Omci AniSideConfig failed")
//this issue here means that the AniConfigFsm has not finished successfully
//which requires to reset it to allow for new usage, e.g. also on a different UNI
//(without that it would be reset on device down indication latest)
_ = onuTP.pAniConfigFsm.pAdaptFsm.pFsm.Event(aniEvReset)
return
}
} else {
// strange: UNI entry exists, but no ANI data, maybe such situation should be cleared up (if observed)
logger.Debugw("no Tcont/Gem data for this UNI found - abort", log.Fields{
"device-id": onuTP.deviceID, "uniID": aUniID})
onuTP.procResult = errors.New("techProfile config aborted: no Tcont/Gem data found for this UNI")
return
}
} else {
logger.Debugw("no PonAni data for this UNI found - abort", log.Fields{
"device-id": onuTP.deviceID, "uniID": aUniID})
onuTP.procResult = errors.New("techProfile config aborted: no AniSide data found for this UNI")
return
}
}
func (onuTP *onuUniTechProf) updateOnuTpPathKvStore(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done()
if onuTP.onuKVStore == nil {
logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
onuTP.procResult = errors.New("onu/tp-data update aborted: onuKVStore not set")
return
}
var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpKvProcessingStep
go onuTP.storePersistentData(ctx, processingStep)
if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpKvProcessingStep, processingStep) {
//timeout or error detected
logger.Debugw("ONU/TP-data not written - abort", log.Fields{"device-id": onuTP.deviceID})
onuTP.procResult = errors.New("onu/tp-data update aborted: during writing process")
return
}
}
func (onuTP *onuUniTechProf) restoreFromOnuTpPathKvStore(ctx context.Context) error {
if onuTP.onuKVStore == nil {
logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
return fmt.Errorf(fmt.Sprintf("onuKVStore-not-set-abort-%s", onuTP.deviceID))
}
if err := onuTP.restorePersistentData(ctx); err != nil {
logger.Debugw("ONU/TP-data not read - abort", log.Fields{"device-id": onuTP.deviceID})
return err
}
return nil
}
func (onuTP *onuUniTechProf) deleteOnuTpPathKvStore(ctx context.Context) error {
if onuTP.onuKVStore == nil {
logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
return fmt.Errorf(fmt.Sprintf("onuKVStore-not-set-abort-%s", onuTP.deviceID))
}
if err := onuTP.deletePersistentData(ctx); err != nil {
logger.Debugw("ONU/TP-data not read - abort", log.Fields{"device-id": onuTP.deviceID})
return err
}
return nil
}
// deleteTpResource removes Resources from the ONU's specified Uni
func (onuTP *onuUniTechProf) deleteTpResource(ctx context.Context,
aUniID uint32, aPathString string, aResource resourceEntry, aEntryID uint32,
wg *sync.WaitGroup) {
defer wg.Done()
logger.Debugw("this would remove TP resources from ONU's UNI", log.Fields{
"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString, "Resource": aResource})
/*
var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpConfigProcessingStep
go onuTp.deleteAniResource(ctx, processingStep)
if !onuTP.waitForTimeoutOrCompletion(ctx, chTpConfigProcessingStep, processingStep) {
//timeout or error detected
return
}
*/
}
/* internal methods *********************/
func (onuTP *onuUniTechProf) storePersistentData(ctx context.Context, aProcessingStep uint8) {
onuTP.sOnuPersistentData.PersOnuID = onuTP.baseDeviceHandler.pOnuIndication.OnuId
onuTP.sOnuPersistentData.PersIntfID = onuTP.baseDeviceHandler.pOnuIndication.IntfId
onuTP.sOnuPersistentData.PersSnr = onuTP.baseDeviceHandler.pOnuOmciDevice.serialNumber
onuTP.sOnuPersistentData.PersAdminState = "up"
onuTP.sOnuPersistentData.PersOperState = "active"
onuTP.sOnuPersistentData.PersUniTpPath = onuTP.sOnuPersistentData.PersUniTpPath[:0]
for k, v := range onuTP.mapUniTpPath {
onuTP.sOnuPersistentData.PersUniTpPath =
append(onuTP.sOnuPersistentData.PersUniTpPath, uniPersData{PersUniID: k, PersTpPath: v})
}
logger.Debugw("Update ONU/TP-data in KVStore", log.Fields{"device-id": onuTP.deviceID, "onuTP.sOnuPersistentData": onuTP.sOnuPersistentData})
Value, err := json.Marshal(onuTP.sOnuPersistentData)
if err != nil {
logger.Errorw("unable to marshal ONU/TP-data", log.Fields{"onuTP.sOnuPersistentData": onuTP.sOnuPersistentData,
"device-id": onuTP.deviceID, "err": err})
onuTP.chTpKvProcessingStep <- 0 //error indication
return
}
err = onuTP.onuKVStore.Put(ctx, onuTP.onuKVStorePath, Value)
if err != nil {
logger.Errorw("unable to write ONU/TP-data into KVstore", log.Fields{"device-id": onuTP.deviceID, "err": err})
onuTP.chTpKvProcessingStep <- 0 //error indication
return
}
onuTP.chTpKvProcessingStep <- aProcessingStep //done
}
func (onuTP *onuUniTechProf) restorePersistentData(ctx context.Context) error {
onuTP.mapUniTpPath = make(map[uint32]string)
onuTP.sOnuPersistentData = onuPersistentData{0, 0, "", "", "", make([]uniPersData, 0)}
Value, err := onuTP.onuKVStore.Get(ctx, onuTP.onuKVStorePath)
if err == nil {
if Value != nil {
logger.Debugw("ONU/TP-data read",
log.Fields{"Key": Value.Key, "device-id": onuTP.deviceID})
tpTmpBytes, _ := kvstore.ToByte(Value.Value)
if err = json.Unmarshal(tpTmpBytes, &onuTP.sOnuPersistentData); err != nil {
logger.Errorw("unable to unmarshal ONU/TP-data", log.Fields{"error": err, "device-id": onuTP.deviceID})
return fmt.Errorf(fmt.Sprintf("unable-to-unmarshal-ONU/TP-data-%s", onuTP.deviceID))
}
logger.Debugw("ONU/TP-data", log.Fields{"onuTP.sOnuPersistentData": onuTP.sOnuPersistentData,
"device-id": onuTP.deviceID})
for _, uniData := range onuTP.sOnuPersistentData.PersUniTpPath {
onuTP.mapUniTpPath[uniData.PersUniID] = uniData.PersTpPath
}
logger.Debugw("TpPath map", log.Fields{"onuTP.mapUniTpPath": onuTP.mapUniTpPath,
"device-id": onuTP.deviceID})
} else {
logger.Errorw("no ONU/TP-data found", log.Fields{"path": onuTP.onuKVStorePath, "device-id": onuTP.deviceID})
return fmt.Errorf(fmt.Sprintf("no-ONU/TP-data-found-%s", onuTP.deviceID))
}
} else {
logger.Errorw("unable to read from KVstore", log.Fields{"device-id": onuTP.deviceID})
return fmt.Errorf(fmt.Sprintf("unable-to-read-from-KVstore-%s", onuTP.deviceID))
}
return nil
}
func (onuTP *onuUniTechProf) deletePersistentData(ctx context.Context) error {
logger.Debugw("delete ONU/TP-data in KVStore", log.Fields{"device-id": onuTP.deviceID})
err := onuTP.onuKVStore.Delete(ctx, onuTP.onuKVStorePath)
if err != nil {
logger.Errorw("unable to delete in KVstore", log.Fields{"device-id": onuTP.deviceID, "err": err})
return fmt.Errorf(fmt.Sprintf("unable-delete-in-KVstore-%s", onuTP.deviceID))
}
return nil
}
func (onuTP *onuUniTechProf) readAniSideConfigFromTechProfile(
ctx context.Context, aUniID uint8, aPathString string, aProcessingStep uint8) {
var tpInst tp.TechProfile
subStringSlice := strings.Split(aPathString, "/")
if len(subStringSlice) <= 2 {
logger.Errorw("invalid path name format",
log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
onuTP.chTpConfigProcessingStep <- 0 //error indication
return
}
if _, existTP := onuTP.mapUniTpIndication[aUniID]; existTP {
logger.Warnw("Some active profile entry at reading new TechProfile",
log.Fields{"path": aPathString, "device-id": onuTP.deviceID,
"UniId": aUniID, "wrongProfile": onuTP.mapUniTpIndication[aUniID].techProfileID})
//delete on the mapUniTpIndication map not needed, just overwritten later
//delete on the PonAniConfig map should be safe, even if not existing
delete(onuTP.mapPonAniConfig, aUniID)
} else {
// this is normal processing
onuTP.mapUniTpIndication[aUniID] = &tTechProfileIndication{} //need to assign some (empty) struct memory first!
}
onuTP.mapUniTpIndication[aUniID].techProfileType = subStringSlice[0]
profID, err := strconv.ParseUint(subStringSlice[1], 10, 32)
if err != nil {
logger.Errorw("invalid ProfileId from path",
log.Fields{"ParseErr": err})
onuTP.chTpConfigProcessingStep <- 0 //error indication
return
}
onuTP.mapUniTpIndication[aUniID].techProfileID = uint16(profID)
logger.Debugw("tech-profile path indications",
log.Fields{"device-id": onuTP.deviceID, "UniId": aUniID,
"profType": onuTP.mapUniTpIndication[aUniID].techProfileType,
"profID": onuTP.mapUniTpIndication[aUniID].techProfileID})
Value, err := onuTP.techProfileKVStore.Get(ctx, aPathString)
if err == nil {
if Value != nil {
logger.Debugw("tech-profile read",
log.Fields{"Key": Value.Key, "device-id": onuTP.deviceID})
tpTmpBytes, _ := kvstore.ToByte(Value.Value)
if err = json.Unmarshal(tpTmpBytes, &tpInst); err != nil {
logger.Errorw("TechProf - Failed to unmarshal tech-profile into tpInst",
log.Fields{"error": err, "device-id": onuTP.deviceID})
onuTP.chTpConfigProcessingStep <- 0 //error indication
return
}
logger.Debugw("TechProf - tpInst", log.Fields{"tpInst": tpInst})
// access examples
logger.Debugw("TechProf content", log.Fields{"Name": tpInst.Name,
"MaxGemPayloadSize": tpInst.InstanceCtrl.MaxGemPayloadSize,
"DownstreamGemDiscardmaxThreshold": tpInst.DownstreamGemPortAttributeList[0].DiscardConfig.MaxThreshold})
} else {
logger.Errorw("No tech-profile found",
log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
onuTP.chTpConfigProcessingStep <- 0 //error indication
return
}
} else {
logger.Errorw("kvstore-get failed for path",
log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
onuTP.chTpConfigProcessingStep <- 0 //error indication
return
}
localMapGemPortParams := make(map[uint16]*gemPortParamStruct)
localMapGemPortParams[0] = &gemPortParamStruct{}
localMapPonAniConfig := make(map[uint16]*tcontGemList)
localMapPonAniConfig[0] = &tcontGemList{tcontParamStruct{}, localMapGemPortParams}
onuTP.mapPonAniConfig[aUniID] = (*tMapPonAniConfig)(&localMapPonAniConfig)
(*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.allocID = uint16(tpInst.UsScheduler.AllocID)
if tpInst.UsScheduler.QSchedPolicy == "StrictPrio" {
(*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.schedPolicy = 1 //for the moment fixed value acc. G.988 //TODO: defines!
} else {
//default profile defines "Hybrid" - which probably comes down to WRR with some weigthts for SP
(*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.schedPolicy = 2 //for G.988 WRR
}
loNumGemPorts := tpInst.NumGemPorts
loGemPortRead := false
for pos, content := range tpInst.UpstreamGemPortAttributeList {
if uint32(pos) == loNumGemPorts {
logger.Debugw("PonAniConfig abort GemPortList - GemList exceeds set NumberOfGemPorts",
log.Fields{"device-id": onuTP.deviceID, "index": pos, "NumGem": loNumGemPorts})
break
}
if pos == 0 {
//at least one upstream GemPort should always exist (else traffic profile makes no sense)
loGemPortRead = true
} else {
//for all further GemPorts we need to extend the mapGemPortParams
(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)] = &gemPortParamStruct{}
}
(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].gemPortID =
uint16(content.GemportID)
//direction can be correlated later with Downstream list,
// for now just assume bidirectional (upstream never exists alone)
(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].direction = 3 //as defined in G.988
// expected Prio-Queue values 0..7 with 7 for highest PrioQueue, QueueIndex=Prio = 0..7
if 7 < content.PriorityQueue {
logger.Errorw("PonAniConfig reject on GemPortList - PrioQueue value invalid",
log.Fields{"device-id": onuTP.deviceID, "index": pos, "PrioQueue": content.PriorityQueue})
//remove PonAniConfig as done so far, delete map should be safe, even if not existing
delete(onuTP.mapPonAniConfig, aUniID)
onuTP.chTpConfigProcessingStep <- 0 //error indication
return
}
(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].prioQueueIndex =
uint8(content.PriorityQueue)
(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].pbitString =
strings.TrimPrefix(content.PbitMap, binaryStringPrefix)
if content.AesEncryption == "True" {
(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].gemPortEncState = 1
} else {
(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].gemPortEncState = 0
}
(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].discardPolicy =
content.DiscardPolicy
(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].queueSchedPolicy =
content.SchedulingPolicy
//'GemWeight' looks strange in default profile, for now we just copy the weight to first queue
(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].queueWeight =
uint8(content.Weight)
}
if !loGemPortRead {
logger.Errorw("PonAniConfig reject - no GemPort could be read from TechProfile",
log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
//remove PonAniConfig as done so far, delete map should be safe, even if not existing
delete(onuTP.mapPonAniConfig, aUniID)
onuTP.chTpConfigProcessingStep <- 0 //error indication
return
}
logger.Debugw("PonAniConfig read from TechProfile", log.Fields{
"device-id": onuTP.deviceID,
"AllocId": (*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.allocID})
for gemIndex, gemEntry := range (*(onuTP.mapPonAniConfig[0]))[0].mapGemPortParams {
logger.Debugw("PonAniConfig read from TechProfile", log.Fields{
"GemIndex": gemIndex,
"GemPort": gemEntry.gemPortID,
"QueueScheduling": gemEntry.queueSchedPolicy})
}
onuTP.chTpConfigProcessingStep <- aProcessingStep //done
}
func (onuTP *onuUniTechProf) setAniSideConfigFromTechProfile(
ctx context.Context, aUniID uint8, apCurrentUniPort *onuUniPort, aProcessingStep uint8) {
if onuTP.pAniConfigFsm == nil {
onuTP.createAniConfigFsm(aUniID, apCurrentUniPort, OmciAniConfigDone, aProcessingStep)
} else { //AniConfigFsm already init
onuTP.runAniConfigFsm(aProcessingStep)
}
}
func (onuTP *onuUniTechProf) waitForTimeoutOrCompletion(
ctx context.Context, aChTpProcessingStep <-chan uint8, aProcessingStep uint8) bool {
select {
case <-ctx.Done():
logger.Warnw("processing not completed in-time: force release of TpProcMutex!",
log.Fields{"device-id": onuTP.deviceID, "error": ctx.Err()})
return false
case rxStep := <-aChTpProcessingStep:
if rxStep == aProcessingStep {
return true
}
//all other values are not accepted - including 0 for error indication
logger.Warnw("Invalid processing step received: abort and force release of TpProcMutex!",
log.Fields{"device-id": onuTP.deviceID,
"wantedStep": aProcessingStep, "haveStep": rxStep})
return false
}
}
// createUniLockFsm initializes and runs the AniConfig FSM to transfer the OMCI related commands for ANI side configuration
func (onuTP *onuUniTechProf) createAniConfigFsm(aUniID uint8,
apCurrentUniPort *onuUniPort, devEvent OnuDeviceEvent, aProcessingStep uint8) {
logger.Debugw("createAniConfigFsm", log.Fields{"device-id": onuTP.deviceID})
chAniConfigFsm := make(chan Message, 2048)
pDevEntry := onuTP.baseDeviceHandler.getOnuDeviceEntry(true)
if pDevEntry == nil {
logger.Errorw("No valid OnuDevice - aborting", log.Fields{"device-id": onuTP.deviceID})
return
}
pAniCfgFsm := newUniPonAniConfigFsm(pDevEntry.PDevOmciCC, apCurrentUniPort, onuTP,
pDevEntry.pOnuDB, onuTP.mapUniTpIndication[aUniID].techProfileID, devEvent,
"AniConfigFsm", onuTP.deviceID, chAniConfigFsm)
if pAniCfgFsm != nil {
onuTP.pAniConfigFsm = pAniCfgFsm
onuTP.runAniConfigFsm(aProcessingStep)
} else {
logger.Errorw("AniConfigFSM could not be created - abort!!", log.Fields{"device-id": onuTP.deviceID})
}
}
// runAniConfigFsm starts the AniConfig FSM to transfer the OMCI related commands for ANI side configuration
func (onuTP *onuUniTechProf) runAniConfigFsm(aProcessingStep uint8) {
/* Uni related ANI config procedure -
***** should run via 'aniConfigDone' state and generate the argument requested event *****
*/
pACStatemachine := onuTP.pAniConfigFsm.pAdaptFsm.pFsm
if pACStatemachine != nil {
if pACStatemachine.Is(aniStDisabled) {
//FSM init requirement to get informed abou FSM completion! (otherwise timeout of the TechProf config)
onuTP.pAniConfigFsm.setFsmCompleteChannel(onuTP.chTpConfigProcessingStep, aProcessingStep)
if err := pACStatemachine.Event(aniEvStart); err != nil {
logger.Warnw("AniConfigFSM: can't start", log.Fields{"err": err})
// maybe try a FSM reset and then again ... - TODO!!!
} else {
/***** AniConfigFSM started */
logger.Debugw("AniConfigFSM started", log.Fields{
"state": pACStatemachine.Current(), "device-id": onuTP.deviceID})
}
} else {
logger.Warnw("wrong state of AniConfigFSM - want: disabled", log.Fields{
"have": pACStatemachine.Current(), "device-id": onuTP.deviceID})
// maybe try a FSM reset and then again ... - TODO!!!
}
} else {
logger.Errorw("AniConfigFSM StateMachine invalid - cannot be executed!!", log.Fields{"device-id": onuTP.deviceID})
// maybe try a FSM reset and then again ... - TODO!!!
}
}
// setConfigDone sets the requested techProfile config state (if possible)
func (onuTP *onuUniTechProf) setConfigDone(aUniID uint8, aState bool) {
if _, existTP := onuTP.mapUniTpIndication[aUniID]; existTP {
onuTP.mutexTPState.Lock()
onuTP.mapUniTpIndication[aUniID].techProfileConfigDone = aState
onuTP.mutexTPState.Unlock()
} //else: the state is just ignored (does not exist)
}
// getTechProfileDone checks if the Techprofile processing with the requested TechProfile ID was done
func (onuTP *onuUniTechProf) getTechProfileDone(aUniID uint8, aTpID uint16) bool {
if _, existTP := onuTP.mapUniTpIndication[aUniID]; existTP {
if onuTP.mapUniTpIndication[aUniID].techProfileID == aTpID {
onuTP.mutexTPState.Lock()
defer onuTP.mutexTPState.Unlock()
return onuTP.mapUniTpIndication[aUniID].techProfileConfigDone
}
}
return false
}