[VOL-3380] Functional area specific logging
Change-Id: I67414da013d8fc82827fcdb69d4f8a34040625d3
diff --git a/internal/pkg/avcfg/omci_vlan_config.go b/internal/pkg/avcfg/omci_vlan_config.go
new file mode 100755
index 0000000..4a690f7
--- /dev/null
+++ b/internal/pkg/avcfg/omci_vlan_config.go
@@ -0,0 +1,3123 @@
+/*
+ * 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 avcfg provides anig and vlan configuration functionality
+package avcfg
+
+import (
+ "context"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "net"
+ "strconv"
+ "sync"
+ "time"
+
+ meters "github.com/opencord/voltha-lib-go/v7/pkg/meters"
+ "github.com/opencord/voltha-protos/v5/go/voltha"
+
+ gp "github.com/google/gopacket"
+ "github.com/looplab/fsm"
+ "github.com/opencord/omci-lib-go"
+ me "github.com/opencord/omci-lib-go/generated"
+ "github.com/opencord/voltha-lib-go/v7/pkg/log"
+ cmn "github.com/opencord/voltha-openonu-adapter-go/internal/pkg/common"
+ "github.com/opencord/voltha-openonu-adapter-go/internal/pkg/devdb"
+ of "github.com/opencord/voltha-protos/v5/go/openflow_13"
+)
+
+const (
+ // internal predefined values
+ cDefaultDownstreamMode = 0
+ cDefaultTpid = 0x8100
+ cVtfdTableSize = 12 //as per G.988
+ cMaxAllowedFlows = cVtfdTableSize //which might be under discussion, for the moment connected to limit of VLAN's within VTFD
+)
+
+const (
+ // internal offsets for requestEvent according to definition in onu_device_entry::cmn.OnuDeviceEvent
+ cDeviceEventOffsetAddWithKvStore = 0 //OmciVlanFilterAddDone - OmciVlanFilterAddDone cannot use because of lint
+ cDeviceEventOffsetAddNoKvStore = cmn.OmciVlanFilterAddDoneNoKvStore - cmn.OmciVlanFilterAddDone
+ cDeviceEventOffsetRemoveWithKvStore = cmn.OmciVlanFilterRemDone - cmn.OmciVlanFilterAddDone
+ cDeviceEventOffsetRemoveNoKvStore = cmn.OmciVlanFilterRemDoneNoKvStore - cmn.OmciVlanFilterAddDone
+)
+
+const (
+ // bit mask offsets for EVTOCD VlanTaggingOperationTable related to 32 bits (4 bytes)
+ cFilterPrioOffset = 28
+ cFilterVidOffset = 15
+ cFilterTpidOffset = 12
+ cFilterEtherTypeOffset = 0
+ cTreatTTROffset = 30
+ cTreatPrioOffset = 16
+ cTreatVidOffset = 3
+ cTreatTpidOffset = 0
+)
+const (
+ // byte offsets for EVTOCD VlanTaggingOperationTable related to overall 16 byte size with slice byte 0 as first Byte (MSB)
+ cFilterOuterOffset = 0
+ cFilterInnerOffset = 4
+ cTreatOuterOffset = 8
+ cTreatInnerOffset = 12
+)
+const (
+ // basic values used within EVTOCD VlanTaggingOperationTable in respect to their bitfields
+ cPrioIgnoreTag uint32 = 15
+ cPrioDefaultFilter uint32 = 14
+ cPrioDoNotFilter uint32 = 8
+ cDoNotFilterVid uint32 = 4096
+ cDoNotFilterTPID uint32 = 0
+ cDoNotFilterEtherType uint32 = 0
+ cDoNotAddPrio uint32 = 15
+ cCopyPrioFromInner uint32 = 8
+ //cDontCarePrio uint32 = 0
+ cDontCareVid uint32 = 0
+ cDontCareTpid uint32 = 0
+ cSetOutputTpidCopyDei uint32 = 4
+)
+
+// events of config UNI port VLAN FSM
+const (
+ VlanEvStart = "VlanEvStart"
+ VlanEvPrepareDone = "VlanEvPrepareDone"
+ VlanEvWaitTechProf = "VlanEvWaitTechProf"
+ VlanEvCancelOutstandingConfig = "VlanEvCancelOutstandingConfig"
+ VlanEvContinueConfig = "VlanEvContinueConfig"
+ VlanEvStartConfig = "VlanEvStartConfig"
+ VlanEvRxConfigVtfd = "VlanEvRxConfigVtfd"
+ VlanEvRxConfigEvtocd = "VlanEvRxConfigEvtocd"
+ VlanEvWaitTPIncr = "VlanEvWaitTPIncr"
+ VlanEvIncrFlowConfig = "VlanEvIncrFlowConfig"
+ VlanEvRenew = "VlanEvRenew"
+ VlanEvRemFlowConfig = "VlanEvRemFlowConfig"
+ VlanEvRemFlowDone = "VlanEvRemFlowDone"
+ VlanEvFlowDataRemoved = "VlanEvFlowDataRemoved"
+ //VlanEvTimeoutSimple = "VlanEvTimeoutSimple"
+ //VlanEvTimeoutMids = "VlanEvTimeoutMids"
+ VlanEvReset = "VlanEvReset"
+ VlanEvRestart = "VlanEvRestart"
+ VlanEvSkipOmciConfig = "VlanEvSkipOmciConfig"
+ VlanEvSkipIncFlowConfig = "VlanEvSkipIncFlowConfig"
+)
+
+// states of config UNI port VLAN FSM
+const (
+ VlanStDisabled = "VlanStDisabled"
+ VlanStPreparing = "VlanStPreparing"
+ VlanStStarting = "VlanStStarting"
+ VlanStWaitingTechProf = "VlanStWaitingTechProf"
+ VlanStConfigVtfd = "VlanStConfigVtfd"
+ VlanStConfigEvtocd = "VlanStConfigEvtocd"
+ VlanStConfigDone = "VlanStConfigDone"
+ VlanStIncrFlowWaitTP = "VlanStIncrFlowWaitTP"
+ VlanStConfigIncrFlow = "VlanStConfigIncrFlow"
+ VlanStRemoveFlow = "VlanStRemoveFlow"
+ VlanStCleanupDone = "VlanStCleanupDone"
+ VlanStResetting = "VlanStResetting"
+)
+
+// CVlanFsmIdleState - TODO: add comment
+const CVlanFsmIdleState = VlanStConfigDone // state where no OMCI activity is done (for a longer time)
+// CVlanFsmConfiguredState - TODO: add comment
+const CVlanFsmConfiguredState = VlanStConfigDone // state that indicates that at least some valid user related VLAN configuration should exist
+
+type uniRemoveVlanFlowParams struct {
+ isSuspendedOnAdd bool
+ removeChannel chan bool
+ cookie uint64 //just the last cookie valid for removal
+ vlanRuleParams cmn.UniVlanRuleParams
+}
+
+//UniVlanConfigFsm defines the structure for the state machine for configuration of the VLAN related setting via OMCI
+// builds upon 'VLAN rules' that are derived from multiple flows
+type UniVlanConfigFsm struct {
+ pDeviceHandler cmn.IdeviceHandler
+ pOnuDeviceEntry cmn.IonuDeviceEntry
+ deviceID string
+ pOmciCC *cmn.OmciCC
+ pOnuUniPort *cmn.OnuUniPort
+ pUniTechProf *OnuUniTechProf
+ pOnuDB *devdb.OnuDeviceDB
+ requestEvent cmn.OnuDeviceEvent
+ omciMIdsResponseReceived chan bool //seperate channel needed for checking multiInstance OMCI message responses
+ PAdaptFsm *cmn.AdapterFsm
+ acceptIncrementalEvtoOption bool
+ clearPersistency bool
+ isCanceled bool
+ isAwaitingResponse bool
+ mutexIsAwaitingResponse sync.RWMutex
+ mutexFlowParams sync.RWMutex
+ chCookieDeleted chan bool //channel to indicate that a specific cookie (related to the active rule) was deleted
+ actualUniVlanConfigRule cmn.UniVlanRuleParams
+ actualUniVlanConfigMeter *voltha.OfpMeterConfig
+ uniVlanFlowParamsSlice []cmn.UniVlanFlowParams
+ uniRemoveFlowsSlice []uniRemoveVlanFlowParams
+ NumUniFlows uint8 // expected number of flows should be less than 12
+ ConfiguredUniFlow uint8
+ numRemoveFlows uint8
+ numVlanFilterEntries uint8
+ vlanFilterList [cVtfdTableSize]uint16
+ evtocdID uint16
+ mutexPLastTxMeInstance sync.RWMutex
+ pLastTxMeInstance *me.ManagedEntity
+ requestEventOffset uint8
+ TpIDWaitingFor uint8
+ signalOnFlowDelete bool
+ flowDeleteChannel chan<- bool
+ //cookie value that indicates that a rule to add is delayed by waiting for deletion of some other existing rule with the same cookie
+ delayNewRuleCookie uint64
+ // Used to indicate if the FSM is for a reconciling flow and if it's the last flow to be reconciled
+ // thus notification needs to be sent on chan.
+ lastFlowToReconcile bool
+}
+
+//NewUniVlanConfigFsm is the 'constructor' for the state machine to config the PON ANI ports
+// of ONU UNI ports via OMCI
+func NewUniVlanConfigFsm(ctx context.Context, apDeviceHandler cmn.IdeviceHandler, apOnuDeviceEntry cmn.IonuDeviceEntry, apDevOmciCC *cmn.OmciCC, apUniPort *cmn.OnuUniPort,
+ apUniTechProf *OnuUniTechProf, apOnuDB *devdb.OnuDeviceDB, aTechProfileID uint8,
+ aRequestEvent cmn.OnuDeviceEvent, aName string, aCommChannel chan cmn.Message, aAcceptIncrementalEvto bool,
+ aCookieSlice []uint64, aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8, lastFlowToRec bool, aMeter *voltha.OfpMeterConfig) *UniVlanConfigFsm {
+ instFsm := &UniVlanConfigFsm{
+ pDeviceHandler: apDeviceHandler,
+ pOnuDeviceEntry: apOnuDeviceEntry,
+ deviceID: apDeviceHandler.GetDeviceID(),
+ pOmciCC: apDevOmciCC,
+ pOnuUniPort: apUniPort,
+ pUniTechProf: apUniTechProf,
+ pOnuDB: apOnuDB,
+ requestEvent: aRequestEvent,
+ acceptIncrementalEvtoOption: aAcceptIncrementalEvto,
+ NumUniFlows: 0,
+ ConfiguredUniFlow: 0,
+ numRemoveFlows: 0,
+ clearPersistency: true,
+ lastFlowToReconcile: lastFlowToRec,
+ }
+
+ instFsm.PAdaptFsm = cmn.NewAdapterFsm(aName, instFsm.deviceID, aCommChannel)
+ if instFsm.PAdaptFsm == nil {
+ logger.Errorw(ctx, "UniVlanConfigFsm's cmn.AdapterFsm could not be instantiated!!", log.Fields{
+ "device-id": instFsm.deviceID})
+ return nil
+ }
+ instFsm.PAdaptFsm.PFsm = fsm.NewFSM(
+ VlanStDisabled,
+ fsm.Events{
+ {Name: VlanEvStart, Src: []string{VlanStDisabled}, Dst: VlanStPreparing},
+ {Name: VlanEvPrepareDone, Src: []string{VlanStPreparing}, Dst: VlanStStarting},
+ {Name: VlanEvWaitTechProf, Src: []string{VlanStStarting}, Dst: VlanStWaitingTechProf},
+ {Name: VlanEvCancelOutstandingConfig, Src: []string{VlanStWaitingTechProf}, Dst: VlanStConfigDone},
+ {Name: VlanEvContinueConfig, Src: []string{VlanStWaitingTechProf}, Dst: VlanStConfigVtfd},
+ {Name: VlanEvStartConfig, Src: []string{VlanStStarting}, Dst: VlanStConfigVtfd},
+ {Name: VlanEvRxConfigVtfd, Src: []string{VlanStConfigVtfd}, Dst: VlanStConfigEvtocd},
+ {Name: VlanEvRxConfigEvtocd, Src: []string{VlanStConfigEvtocd, VlanStConfigIncrFlow},
+ Dst: VlanStConfigDone},
+ {Name: VlanEvRenew, Src: []string{VlanStConfigDone}, Dst: VlanStStarting},
+ {Name: VlanEvWaitTPIncr, Src: []string{VlanStConfigDone}, Dst: VlanStIncrFlowWaitTP},
+ {Name: VlanEvIncrFlowConfig, Src: []string{VlanStConfigDone, VlanStIncrFlowWaitTP},
+ Dst: VlanStConfigIncrFlow},
+ {Name: VlanEvRemFlowConfig, Src: []string{VlanStConfigDone}, Dst: VlanStRemoveFlow},
+ {Name: VlanEvRemFlowDone, Src: []string{VlanStRemoveFlow}, Dst: VlanStCleanupDone},
+ {Name: VlanEvFlowDataRemoved, Src: []string{VlanStCleanupDone}, Dst: VlanStConfigDone},
+ /*
+ {Name: VlanEvTimeoutSimple, Src: []string{
+ VlanStCreatingDot1PMapper, VlanStCreatingMBPCD, VlanStSettingTconts, VlanStSettingDot1PMapper}, Dst: VlanStStarting},
+ {Name: VlanEvTimeoutMids, Src: []string{
+ VlanStCreatingGemNCTPs, VlanStCreatingGemIWs, VlanStSettingPQs}, Dst: VlanStStarting},
+ */
+ // exceptional treatment for all states except VlanStResetting
+ {Name: VlanEvReset, Src: []string{VlanStStarting, VlanStWaitingTechProf,
+ VlanStConfigVtfd, VlanStConfigEvtocd, VlanStConfigDone, VlanStConfigIncrFlow,
+ VlanStRemoveFlow, VlanStCleanupDone},
+ Dst: VlanStResetting},
+ // the only way to get to resource-cleared disabled state again is via "resseting"
+ {Name: VlanEvRestart, Src: []string{VlanStResetting}, Dst: VlanStDisabled},
+ // transitions for reconcile handling according to VOL-3834
+ {Name: VlanEvSkipOmciConfig, Src: []string{VlanStPreparing}, Dst: VlanStConfigDone},
+ {Name: VlanEvSkipOmciConfig, Src: []string{VlanStConfigDone}, Dst: VlanStConfigIncrFlow},
+ {Name: VlanEvSkipIncFlowConfig, Src: []string{VlanStConfigIncrFlow}, Dst: VlanStConfigDone},
+ },
+ fsm.Callbacks{
+ "enter_state": func(e *fsm.Event) { instFsm.PAdaptFsm.LogFsmStateChange(ctx, e) },
+ "enter_" + VlanStPreparing: func(e *fsm.Event) { instFsm.enterPreparing(ctx, e) },
+ "enter_" + VlanStStarting: func(e *fsm.Event) { instFsm.enterConfigStarting(ctx, e) },
+ "enter_" + VlanStConfigVtfd: func(e *fsm.Event) { instFsm.enterConfigVtfd(ctx, e) },
+ "enter_" + VlanStConfigEvtocd: func(e *fsm.Event) { instFsm.enterConfigEvtocd(ctx, e) },
+ "enter_" + VlanStConfigDone: func(e *fsm.Event) { instFsm.enterVlanConfigDone(ctx, e) },
+ "enter_" + VlanStConfigIncrFlow: func(e *fsm.Event) { instFsm.enterConfigIncrFlow(ctx, e) },
+ "enter_" + VlanStRemoveFlow: func(e *fsm.Event) { instFsm.enterRemoveFlow(ctx, e) },
+ "enter_" + VlanStCleanupDone: func(e *fsm.Event) { instFsm.enterVlanCleanupDone(ctx, e) },
+ "enter_" + VlanStResetting: func(e *fsm.Event) { instFsm.enterResetting(ctx, e) },
+ "enter_" + VlanStDisabled: func(e *fsm.Event) { instFsm.enterDisabled(ctx, e) },
+ },
+ )
+ if instFsm.PAdaptFsm.PFsm == nil {
+ logger.Errorw(ctx, "UniVlanConfigFsm's Base FSM could not be instantiated!!", log.Fields{
+ "device-id": instFsm.deviceID})
+ return nil
+ }
+
+ _ = instFsm.initUniFlowParams(ctx, aTechProfileID, aCookieSlice, aMatchVlan, aSetVlan, aSetPcp, aMeter)
+
+ logger.Debugw(ctx, "UniVlanConfigFsm created", log.Fields{"device-id": instFsm.deviceID,
+ "accIncrEvto": instFsm.acceptIncrementalEvtoOption})
+ return instFsm
+}
+
+//initUniFlowParams is a simplified form of SetUniFlowParams() used for first flow parameters configuration
+func (oFsm *UniVlanConfigFsm) initUniFlowParams(ctx context.Context, aTpID uint8, aCookieSlice []uint64,
+ aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8, aMeter *voltha.OfpMeterConfig) error {
+ loRuleParams := cmn.UniVlanRuleParams{
+ TpID: aTpID,
+ MatchVid: uint32(aMatchVlan),
+ SetVid: uint32(aSetVlan),
+ SetPcp: uint32(aSetPcp),
+ }
+ // some automatic adjustments on the filter/treat parameters as not specifically configured/ensured by flow configuration parameters
+ loRuleParams.TagsToRemove = 1 //one tag to remove as default setting
+ loRuleParams.MatchPcp = cPrioDoNotFilter // do not Filter on prio as default
+
+ if loRuleParams.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+ //then matchVlan is don't care and should be overwritten to 'transparent' here to avoid unneeded multiple flow entries
+ loRuleParams.MatchVid = uint32(of.OfpVlanId_OFPVID_PRESENT)
+ //TODO!!: maybe be needed to be re-checked at flow deletion (but assume all flows are always deleted togehther)
+ } else {
+ if !oFsm.acceptIncrementalEvtoOption {
+ //then matchVlan is don't care and should be overwritten to 'transparent' here to avoid unneeded multiple flow entries
+ loRuleParams.MatchVid = uint32(of.OfpVlanId_OFPVID_PRESENT)
+ }
+ }
+
+ if loRuleParams.MatchVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+ // no prio/vid filtering requested
+ loRuleParams.TagsToRemove = 0 //no tag pop action
+ loRuleParams.MatchPcp = cPrioIgnoreTag // no vlan tag filtering
+ if loRuleParams.SetPcp == cCopyPrioFromInner {
+ //in case of no filtering and configured PrioCopy ensure default prio setting to 0
+ // which is required for stacking of untagged, but obviously also ensures prio setting for prio/singletagged
+ // might collide with NoMatchVid/CopyPrio(/setVid) setting
+ // this was some precondition setting taken over from py adapter ..
+ loRuleParams.SetPcp = 0
+ }
+ }
+
+ loFlowParams := cmn.UniVlanFlowParams{VlanRuleParams: loRuleParams}
+ loFlowParams.CookieSlice = make([]uint64, 0)
+ loFlowParams.CookieSlice = append(loFlowParams.CookieSlice, aCookieSlice...)
+ if aMeter != nil {
+ loFlowParams.Meter = aMeter
+ }
+
+ //no mutex protection is required for initial access and adding the first flow is always possible
+ oFsm.uniVlanFlowParamsSlice = make([]cmn.UniVlanFlowParams, 0)
+ oFsm.uniVlanFlowParamsSlice = append(oFsm.uniVlanFlowParamsSlice, loFlowParams)
+ logger.Debugw(ctx, "first UniVlanConfigFsm flow added", log.Fields{
+ "Cookies": oFsm.uniVlanFlowParamsSlice[0].CookieSlice,
+ "MatchVid": strconv.FormatInt(int64(loRuleParams.MatchVid), 16),
+ "SetVid": strconv.FormatInt(int64(loRuleParams.SetVid), 16),
+ "SetPcp": loRuleParams.SetPcp,
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+
+ if oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+ oFsm.reconcileVlanFilterList(ctx, uint16(loRuleParams.SetVid))
+ }
+ oFsm.NumUniFlows = 1
+ oFsm.uniRemoveFlowsSlice = make([]uniRemoveVlanFlowParams, 0) //initially nothing to remove
+
+ //permanently store flow config for reconcile case
+ if err := oFsm.pDeviceHandler.StorePersUniFlowConfig(ctx, oFsm.pOnuUniPort.UniID,
+ &oFsm.uniVlanFlowParamsSlice, true); err != nil {
+ logger.Errorw(ctx, err.Error(), log.Fields{"device-id": oFsm.deviceID})
+ return err
+ }
+
+ return nil
+}
+
+//CancelProcessing ensures that suspended processing at waiting on some response is aborted and reset of FSM
+func (oFsm *UniVlanConfigFsm) CancelProcessing(ctx context.Context) {
+ //mutex protection is required for possible concurrent access to FSM members
+ oFsm.mutexIsAwaitingResponse.Lock()
+ oFsm.isCanceled = true
+ if oFsm.isAwaitingResponse {
+ //attention: for an unbuffered channel the sender is blocked until the value is received (processed)!
+ // accordingly the mutex must be released before sending to channel here (mutex acquired in receiver)
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ //use channel to indicate that the response waiting shall be aborted
+ oFsm.omciMIdsResponseReceived <- false
+ } else {
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ }
+
+ // in any case (even if it might be automatically requested by above cancellation of waiting) ensure resetting the FSM
+ PAdaptFsm := oFsm.PAdaptFsm
+ if PAdaptFsm != nil {
+ if fsmErr := PAdaptFsm.PFsm.Event(VlanEvReset); fsmErr != nil {
+ logger.Errorw(ctx, "reset-event failed in UniVlanConfigFsm!",
+ log.Fields{"fsmState": oFsm.PAdaptFsm.PFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+ }
+ }
+}
+
+//GetWaitingTpID returns the TpId that the FSM might be waiting for continuation (0 if none)
+func (oFsm *UniVlanConfigFsm) GetWaitingTpID() uint8 {
+ //mutex protection is required for possible concurrent access to FSM members
+ oFsm.mutexFlowParams.RLock()
+ defer oFsm.mutexFlowParams.RUnlock()
+ return oFsm.TpIDWaitingFor
+}
+
+//RequestClearPersistency sets the internal flag to not clear persistency data (false=NoClear)
+func (oFsm *UniVlanConfigFsm) RequestClearPersistency(aClear bool) {
+ //mutex protection is required for possible concurrent access to FSM members
+ oFsm.mutexFlowParams.Lock()
+ defer oFsm.mutexFlowParams.Unlock()
+ oFsm.clearPersistency = aClear
+}
+
+//SetUniFlowParams verifies on existence of flow parameters to be configured,
+// optionally udates the cookie list or appends a new flow if there is space
+// if possible the FSM is trigggerd to start with the processing
+// ignore complexity by now
+// nolint: gocyclo
+func (oFsm *UniVlanConfigFsm) SetUniFlowParams(ctx context.Context, aTpID uint8, aCookieSlice []uint64,
+ aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8, lastFlowToReconcile bool, aMeter *voltha.OfpMeterConfig) error {
+ loRuleParams := cmn.UniVlanRuleParams{
+ TpID: aTpID,
+ MatchVid: uint32(aMatchVlan),
+ SetVid: uint32(aSetVlan),
+ SetPcp: uint32(aSetPcp),
+ }
+ // some automatic adjustments on the filter/treat parameters as not specifically configured/ensured by flow configuration parameters
+ loRuleParams.TagsToRemove = 1 //one tag to remove as default setting
+ loRuleParams.MatchPcp = cPrioDoNotFilter // do not Filter on prio as default
+ if loRuleParams.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+ //then matchVlan is don't care and should be overwritten to 'transparent' here to avoid unneeded multiple flow entries
+ loRuleParams.MatchVid = uint32(of.OfpVlanId_OFPVID_PRESENT)
+ //TODO!!: maybe be needed to be re-checked at flow deletion (but assume all flows are always deleted togehther)
+ } else {
+ if !oFsm.acceptIncrementalEvtoOption {
+ //then matchVlan is don't care and should be overwritten to 'transparent' here to avoid unneeded multiple flow entries
+ loRuleParams.MatchVid = uint32(of.OfpVlanId_OFPVID_PRESENT)
+ }
+ }
+
+ if loRuleParams.MatchVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+ // no prio/vid filtering requested
+ loRuleParams.TagsToRemove = 0 //no tag pop action
+ loRuleParams.MatchPcp = cPrioIgnoreTag // no vlan tag filtering
+ if loRuleParams.SetPcp == cCopyPrioFromInner {
+ //in case of no filtering and configured PrioCopy ensure default prio setting to 0
+ // which is required for stacking of untagged, but obviously also ensures prio setting for prio/singletagged
+ // might collide with NoMatchVid/CopyPrio(/setVid) setting
+ // this was some precondition setting taken over from py adapter ..
+ loRuleParams.SetPcp = 0
+ }
+ }
+
+ //check if there is some ongoing delete-request running for this flow. If so, block here until this is finished.
+ // might be accordingly rwCore processing runs into timeout in specific situations - needs to be observed ...
+ // this is to protect uniVlanFlowParams from adding new or re-writing the same cookie to the rule currently under deletion
+ oFsm.mutexFlowParams.RLock()
+ if len(oFsm.uniRemoveFlowsSlice) > 0 {
+ for flow, removeUniFlowParams := range oFsm.uniRemoveFlowsSlice {
+ if removeUniFlowParams.vlanRuleParams == loRuleParams {
+ // the flow to add is the same as the one already in progress of deleting
+ logger.Infow(ctx, "UniVlanConfigFsm flow setting - suspending rule-add due to ongoing removal", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": removeUniFlowParams.cookie, "remove-index": flow})
+ if flow >= len(oFsm.uniRemoveFlowsSlice) {
+ logger.Errorw(ctx, "abort UniVlanConfigFsm flow add - inconsistent RemoveFlowsSlice", log.Fields{
+ "device-id": oFsm.deviceID, "slice length": len(oFsm.uniRemoveFlowsSlice)})
+ oFsm.mutexFlowParams.RUnlock()
+ return fmt.Errorf("abort UniVlanConfigFsm flow add - inconsistent RemoveFlowsSlice %s", oFsm.deviceID)
+ }
+ pRemoveParams := &oFsm.uniRemoveFlowsSlice[flow] //wants to modify the uniRemoveFlowsSlice element directly!
+ oFsm.mutexFlowParams.RUnlock()
+ if err := oFsm.suspendAddRule(ctx, pRemoveParams); err != nil {
+ logger.Errorw(ctx, "UniVlanConfigFsm suspension on add aborted - abort complete add-request", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": removeUniFlowParams.cookie})
+ return fmt.Errorf("abort UniVlanConfigFsm suspension on add %s", oFsm.deviceID)
+ }
+ oFsm.mutexFlowParams.RLock()
+ break //this specific rule should only exist once per uniRemoveFlowsSlice
+ }
+ }
+ }
+ oFsm.mutexFlowParams.RUnlock()
+
+ flowEntryMatch := false
+ flowCookieModify := false
+ requestAppendRule := false
+ oFsm.lastFlowToReconcile = lastFlowToReconcile
+ //mutex protection is required for possible concurrent access to FSM members
+ oFsm.mutexFlowParams.Lock()
+ for flow, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+ //TODO: Verify if using e.g. hashes for the structures here for comparison may generate
+ // countable run time optimization (perhaps with including the hash in kvStore storage?)
+ if storedUniFlowParams.VlanRuleParams == loRuleParams {
+ flowEntryMatch = true
+ logger.Debugw(ctx, "UniVlanConfigFsm flow setting - rule already exists", log.Fields{
+ "MatchVid": strconv.FormatInt(int64(loRuleParams.MatchVid), 16),
+ "SetVid": strconv.FormatInt(int64(loRuleParams.SetVid), 16),
+ "SetPcp": loRuleParams.SetPcp,
+ "device-id": oFsm.deviceID, " uni-id": oFsm.pOnuUniPort.UniID})
+ var cookieMatch bool
+ for _, newCookie := range aCookieSlice { // for all cookies available in the arguments
+ cookieMatch = false
+ for _, cookie := range storedUniFlowParams.CookieSlice {
+ if cookie == newCookie {
+ logger.Debugw(ctx, "UniVlanConfigFsm flow setting - and cookie already exists", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": cookie})
+ cookieMatch = true
+ break //found new cookie - no further search for this requested cookie
+ }
+ }
+ if !cookieMatch {
+ delayedCookie := oFsm.delayNewRuleForCookie(ctx, aCookieSlice)
+ if delayedCookie != 0 {
+ //a delay for adding the cookie to this rule is requested
+ // take care of the mutex which is already locked here, need to unlock/lock accordingly to prevent deadlock in suspension
+ oFsm.mutexFlowParams.Unlock()
+ if deleteSuccess := oFsm.suspendNewRule(ctx); !deleteSuccess {
+ logger.Errorw(ctx, "UniVlanConfigFsm suspended add-cookie-to-rule aborted", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": delayedCookie})
+ return fmt.Errorf(" UniVlanConfigFsm suspended add-cookie-to-rule aborted %s", oFsm.deviceID)
+ }
+ flowCookieModify, requestAppendRule = oFsm.reviseFlowConstellation(ctx, delayedCookie, loRuleParams)
+ oFsm.mutexFlowParams.Lock()
+ } else {
+ logger.Debugw(ctx, "UniVlanConfigFsm flow setting -adding new cookie", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": newCookie})
+ //as range works with copies of the slice we have to write to the original slice!!
+ oFsm.uniVlanFlowParamsSlice[flow].CookieSlice = append(oFsm.uniVlanFlowParamsSlice[flow].CookieSlice,
+ newCookie)
+ flowCookieModify = true
+ }
+ }
+ } //for all new cookies
+ break // found rule - no further rule search
+ }
+ }
+ oFsm.mutexFlowParams.Unlock()
+
+ if !flowEntryMatch { //it is (was) a new rule
+ delayedCookie, deleteSuccess := oFsm.suspendIfRequiredNewRule(ctx, aCookieSlice)
+ if !deleteSuccess {
+ logger.Errorw(ctx, "UniVlanConfigFsm suspended add-new-rule aborted", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": delayedCookie})
+ return fmt.Errorf(" UniVlanConfigFsm suspended add-new-rule aborted %s", oFsm.deviceID)
+ }
+ requestAppendRule = true //default assumption here is that rule is to be appended
+ flowCookieModify = true //and that the the flow data base is to be updated
+ if delayedCookie != 0 { //it was suspended
+ flowCookieModify, requestAppendRule = oFsm.reviseFlowConstellation(ctx, delayedCookie, loRuleParams)
+ }
+ }
+ kvStoreWrite := false //default setting is to not write to kvStore immediately - will be done on FSM execution finally
+ if requestAppendRule {
+ oFsm.mutexFlowParams.Lock()
+ if oFsm.NumUniFlows < cMaxAllowedFlows {
+ loFlowParams := cmn.UniVlanFlowParams{VlanRuleParams: loRuleParams}
+ loFlowParams.CookieSlice = make([]uint64, 0)
+ loFlowParams.CookieSlice = append(loFlowParams.CookieSlice, aCookieSlice...)
+ if aMeter != nil {
+ loFlowParams.Meter = aMeter
+ }
+ oFsm.uniVlanFlowParamsSlice = append(oFsm.uniVlanFlowParamsSlice, loFlowParams)
+ logger.Debugw(ctx, "UniVlanConfigFsm flow add", log.Fields{
+ "Cookies": oFsm.uniVlanFlowParamsSlice[oFsm.NumUniFlows].CookieSlice,
+ "MatchVid": strconv.FormatInt(int64(loRuleParams.MatchVid), 16),
+ "SetVid": strconv.FormatInt(int64(loRuleParams.SetVid), 16),
+ "SetPcp": loRuleParams.SetPcp, "numberofFlows": oFsm.NumUniFlows + 1,
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+
+ if oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+ oFsm.reconcileVlanFilterList(ctx, uint16(loRuleParams.SetVid))
+ }
+ oFsm.NumUniFlows++
+ pConfigVlanStateBaseFsm := oFsm.PAdaptFsm.PFsm
+
+ if oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+ logger.Debugw(ctx, "reconciling - skip omci-config of additional vlan rule",
+ log.Fields{"fsmState": oFsm.PAdaptFsm.PFsm.Current(), "device-id": oFsm.deviceID})
+ //attention: take care to release the mutexFlowParams when calling the FSM directly -
+ // synchronous FSM 'event/state' functions may rely on this mutex
+ oFsm.mutexFlowParams.Unlock()
+ if pConfigVlanStateBaseFsm.Is(VlanStConfigDone) {
+ if fsmErr := pConfigVlanStateBaseFsm.Event(VlanEvSkipOmciConfig); fsmErr != nil {
+ logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm!",
+ log.Fields{"fsmState": oFsm.PAdaptFsm.PFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+ }
+ }
+ return nil
+ }
+ // note: theoretical it would be possible to clear the same rule from the remove slice
+ // (for entries that have not yet been started with removal)
+ // but that is getting quite complicated - maybe a future optimization in case it should prove reasonable
+ // anyway the precondition here is that the FSM checks for rules to delete first and adds new rules afterwards
+
+ if pConfigVlanStateBaseFsm.Is(VlanStConfigDone) {
+ //have to re-trigger the FSM to proceed with outstanding incremental flow configuration
+ if oFsm.ConfiguredUniFlow == 0 {
+ // this is a restart with a complete new flow, we can re-use the initial flow config control
+ // including the check, if the related techProfile is (still) available (probably also removed in between)
+ //attention: take care to release the mutexFlowParams when calling the FSM directly -
+ // synchronous FSM 'event/state' functions may rely on this mutex
+ oFsm.mutexFlowParams.Unlock()
+ if fsmErr := pConfigVlanStateBaseFsm.Event(VlanEvRenew); fsmErr != nil {
+ logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm!",
+ log.Fields{"fsmState": pConfigVlanStateBaseFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+ }
+ } else {
+ //some further flows are to be configured
+ //store the actual rule that shall be worked upon in the following transient states
+ if len(oFsm.uniVlanFlowParamsSlice) < int(oFsm.ConfiguredUniFlow) {
+ //check introduced after having observed some panic here
+ logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm - inconsistent counter",
+ log.Fields{"ConfiguredUniFlow": oFsm.ConfiguredUniFlow,
+ "sliceLen": len(oFsm.uniVlanFlowParamsSlice), "device-id": oFsm.deviceID})
+ oFsm.mutexFlowParams.Unlock()
+ return fmt.Errorf("abort UniVlanConfigFsm on add due to internal counter mismatch %s", oFsm.deviceID)
+ }
+ oFsm.actualUniVlanConfigRule = oFsm.uniVlanFlowParamsSlice[oFsm.ConfiguredUniFlow].VlanRuleParams
+ oFsm.actualUniVlanConfigMeter = oFsm.uniVlanFlowParamsSlice[oFsm.ConfiguredUniFlow].Meter
+ //tpId of the next rule to be configured
+ tpID := oFsm.actualUniVlanConfigRule.TpID
+ oFsm.TpIDWaitingFor = tpID
+ loSetVlan := oFsm.actualUniVlanConfigRule.SetVid
+ //attention: take care to release the mutexFlowParams when calling the FSM directly -
+ // synchronous FSM 'event/state' functions may rely on this mutex
+ // but it must be released already before calling getTechProfileDone() as it may already be locked
+ // by the techProfile processing call to VlanFsm.IsFlowRemovePending() (see VOL-4207)
+ oFsm.mutexFlowParams.Unlock()
+ loTechProfDone := oFsm.pUniTechProf.getTechProfileDone(ctx, oFsm.pOnuUniPort.UniID, tpID)
+ logger.Debugw(ctx, "UniVlanConfigFsm - incremental config request (on setConfig)", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+ "set-Vlan": loSetVlan, "tp-id": tpID, "ProfDone": loTechProfDone})
+
+ var fsmErr error
+ if loTechProfDone {
+ // let the vlan processing continue with next rule
+ fsmErr = pConfigVlanStateBaseFsm.Event(VlanEvIncrFlowConfig)
+ } else {
+ // set to waiting for Techprofile
+ fsmErr = pConfigVlanStateBaseFsm.Event(VlanEvWaitTPIncr)
+ }
+ if fsmErr != nil {
+ logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm!",
+ log.Fields{"fsmState": pConfigVlanStateBaseFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+ }
+ }
+ } else {
+ // if not in the appropriate state a new entry will be automatically considered later
+ // when the configDone state is reached
+ oFsm.mutexFlowParams.Unlock()
+ }
+ } else {
+ logger.Errorw(ctx, "UniVlanConfigFsm flow limit exceeded", log.Fields{
+ "device-id": oFsm.deviceID, "flow-number": oFsm.NumUniFlows})
+ oFsm.mutexFlowParams.Unlock()
+ return fmt.Errorf(" UniVlanConfigFsm flow limit exceeded %s", oFsm.deviceID)
+ }
+ } else {
+ // no activity within the FSM for OMCI processing, the deviceReason may be updated immediately
+ kvStoreWrite = true // ensure actual data write to kvStore immediately (no FSM activity)
+ oFsm.mutexFlowParams.RLock()
+ if oFsm.NumUniFlows == oFsm.ConfiguredUniFlow {
+ //all requested rules really have been configured
+ // state transition notification is checked in deviceHandler
+ oFsm.mutexFlowParams.RUnlock()
+ if oFsm.pDeviceHandler != nil {
+ //also the related TechProfile was already configured
+ logger.Debugw(ctx, "UniVlanConfigFsm rule already set - send immediate add-success event for reason update", log.Fields{
+ "device-id": oFsm.deviceID})
+ // success indication without the need to write to kvStore (done already below with updated data from StorePersUniFlowConfig())
+ go oFsm.pDeviceHandler.DeviceProcStatusUpdate(ctx, cmn.OnuDeviceEvent(oFsm.requestEvent+cDeviceEventOffsetAddNoKvStore))
+ }
+ } else {
+ // avoid device reason update as the rule config connected to this flow may still be in progress
+ // and the device reason should only be updated on success of rule config
+ logger.Debugw(ctx, "UniVlanConfigFsm rule already set but configuration ongoing, suppress early add-success event for reason update",
+ log.Fields{"device-id": oFsm.deviceID,
+ "NumberofRules": oFsm.NumUniFlows, "Configured rules": oFsm.ConfiguredUniFlow})
+ oFsm.mutexFlowParams.RUnlock()
+ }
+ }
+
+ if flowCookieModify { // some change was done to the flow entries
+ //permanently store flow config for reconcile case
+ oFsm.mutexFlowParams.RLock()
+ if err := oFsm.pDeviceHandler.StorePersUniFlowConfig(ctx, oFsm.pOnuUniPort.UniID,
+ &oFsm.uniVlanFlowParamsSlice, kvStoreWrite); err != nil {
+ oFsm.mutexFlowParams.RUnlock()
+ logger.Errorw(ctx, err.Error(), log.Fields{"device-id": oFsm.deviceID})
+ return err
+ }
+ oFsm.mutexFlowParams.RUnlock()
+ }
+ return nil
+}
+
+func (oFsm *UniVlanConfigFsm) suspendAddRule(ctx context.Context, apRemoveFlowParams *uniRemoveVlanFlowParams) error {
+ oFsm.mutexFlowParams.Lock()
+ deleteChannel := apRemoveFlowParams.removeChannel
+ apRemoveFlowParams.isSuspendedOnAdd = true
+ oFsm.mutexFlowParams.Unlock()
+
+ // isSuspendedOnAdd is not reset here-after as the assumption is, that after
+ select {
+ case success := <-deleteChannel:
+ //no need to reset isSuspendedOnAdd as in this case the removeElement will be deleted completely
+ if success {
+ logger.Infow(ctx, "resume adding this rule after having completed deletion", log.Fields{
+ "device-id": oFsm.deviceID})
+ return nil
+ }
+ return fmt.Errorf("suspend aborted, also aborting add-activity: %s", oFsm.deviceID)
+ case <-time.After(oFsm.pOmciCC.GetMaxOmciTimeoutWithRetries() * time.Second):
+ oFsm.mutexFlowParams.Lock()
+ if apRemoveFlowParams != nil {
+ apRemoveFlowParams.isSuspendedOnAdd = false
+ }
+ oFsm.mutexFlowParams.Unlock()
+ logger.Errorw(ctx, "timeout waiting for deletion of rule, also aborting add-activity", log.Fields{
+ "device-id": oFsm.deviceID})
+ return fmt.Errorf("suspend aborted on timeout, also aborting add-activity: %s", oFsm.deviceID)
+ }
+}
+
+// VOL-3828 flow config sequence workaround ########### start ##########
+func (oFsm *UniVlanConfigFsm) delayNewRuleForCookie(ctx context.Context, aCookieSlice []uint64) uint64 {
+ //assumes mutexFlowParams.Lock() protection from caller!
+ if oFsm.delayNewRuleCookie == 0 && len(aCookieSlice) == 1 {
+ // if not already waiting, limitation for this workaround is to just have one overlapping cookie/rule
+ // suspend check is done only if there is only one cookie in the request
+ // background: more elements only expected in reconcile use case, where no conflicting sequence is to be expected
+ newCookie := aCookieSlice[0]
+ for _, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+ for _, cookie := range storedUniFlowParams.CookieSlice {
+ if cookie == newCookie {
+ logger.Debugw(ctx, "UniVlanConfigFsm flow setting - new cookie still exists for some rule", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": cookie, "exists with SetVlan": storedUniFlowParams.VlanRuleParams.SetVid})
+ oFsm.delayNewRuleCookie = newCookie
+ return newCookie //found new cookie in some existing rule
+ }
+ } // for all stored cookies of the actual inspected rule
+ } //for all rules
+ }
+ return 0 //no delay requested
+}
+func (oFsm *UniVlanConfigFsm) suspendNewRule(ctx context.Context) bool {
+ oFsm.mutexFlowParams.RLock()
+ logger.Infow(ctx, "Need to suspend adding this rule as long as the cookie is still connected to some other rule", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": oFsm.delayNewRuleCookie})
+ oFsm.mutexFlowParams.RUnlock()
+ cookieDeleted := true //default assumption also for timeout (just try to continue as if removed)
+ select {
+ case cookieDeleted = <-oFsm.chCookieDeleted:
+ logger.Infow(ctx, "resume adding this rule after having deleted cookie in some other rule or abort", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": oFsm.delayNewRuleCookie, "deleted": cookieDeleted})
+ case <-time.After(oFsm.pOmciCC.GetMaxOmciTimeoutWithRetries() * time.Second):
+ logger.Errorw(ctx, "timeout waiting for deletion of cookie in some other rule, just try to continue", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": oFsm.delayNewRuleCookie})
+ }
+ oFsm.mutexFlowParams.Lock()
+ oFsm.delayNewRuleCookie = 0
+ oFsm.mutexFlowParams.Unlock()
+ return cookieDeleted
+}
+func (oFsm *UniVlanConfigFsm) suspendIfRequiredNewRule(ctx context.Context, aCookieSlice []uint64) (uint64, bool) {
+ oFsm.mutexFlowParams.Lock()
+ delayedCookie := oFsm.delayNewRuleForCookie(ctx, aCookieSlice)
+ oFsm.mutexFlowParams.Unlock()
+
+ deleteSuccess := true
+ if delayedCookie != 0 {
+ deleteSuccess = oFsm.suspendNewRule(ctx)
+ }
+ return delayedCookie, deleteSuccess
+}
+
+//returns flowModified, RuleAppendRequest
+func (oFsm *UniVlanConfigFsm) reviseFlowConstellation(ctx context.Context, aCookie uint64, aUniVlanRuleParams cmn.UniVlanRuleParams) (bool, bool) {
+ flowEntryMatch := false
+ oFsm.mutexFlowParams.Lock()
+ defer oFsm.mutexFlowParams.Unlock()
+ for flow, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+ if storedUniFlowParams.VlanRuleParams == aUniVlanRuleParams {
+ flowEntryMatch = true
+ logger.Debugw(ctx, "UniVlanConfigFsm flow revise - rule already exists", log.Fields{
+ "device-id": oFsm.deviceID})
+ cookieMatch := false
+ for _, cookie := range storedUniFlowParams.CookieSlice {
+ if cookie == aCookie {
+ logger.Debugw(ctx, "UniVlanConfigFsm flow revise - and cookie already exists", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": cookie})
+ cookieMatch = true
+ break //found new cookie - no further search for this requested cookie
+ }
+ }
+ if !cookieMatch {
+ logger.Debugw(ctx, "UniVlanConfigFsm flow revise -adding new cookie", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": aCookie})
+ //as range works with copies of the slice we have to write to the original slice!!
+ oFsm.uniVlanFlowParamsSlice[flow].CookieSlice = append(oFsm.uniVlanFlowParamsSlice[flow].CookieSlice,
+ aCookie)
+ return true, false //flowModified, NoRuleAppend
+ }
+ break // found rule - no further rule search
+ }
+ }
+ if !flowEntryMatch { //it is a new rule
+ return true, true //flowModified, RuleAppend
+ }
+ return false, false //flowNotModified, NoRuleAppend
+}
+
+// VOL-3828 flow config sequence workaround ########### end ##########
+
+//RemoveUniFlowParams verifies on existence of flow cookie,
+// if found removes cookie from flow cookie list and if this is empty
+// initiates removal of the flow related configuration from the ONU (via OMCI)
+func (oFsm *UniVlanConfigFsm) RemoveUniFlowParams(ctx context.Context, aCookie uint64) error {
+ var deletedCookie uint64
+ flowCookieMatch := false
+ //mutex protection is required for possible concurrent access to FSM members
+ oFsm.mutexFlowParams.Lock()
+ defer oFsm.mutexFlowParams.Unlock()
+remove_loop:
+ for flow, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+ for i, cookie := range storedUniFlowParams.CookieSlice {
+ if cookie == aCookie {
+ logger.Debugw(ctx, "UniVlanConfigFsm flow removal - cookie found", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": cookie})
+ deletedCookie = aCookie
+ //remove the cookie from the cookie slice and verify it is getting empty
+ if len(storedUniFlowParams.CookieSlice) == 1 {
+ // had to shift content to function due to sca complexity
+ flowCookieMatch = oFsm.removeRuleComplete(ctx, storedUniFlowParams, aCookie)
+ //persistencyData write is now part of removeRuleComplete() (on success)
+ } else {
+ flowCookieMatch = true
+ //cut off the requested cookie by slicing out this element
+ oFsm.uniVlanFlowParamsSlice[flow].CookieSlice = append(
+ oFsm.uniVlanFlowParamsSlice[flow].CookieSlice[:i],
+ oFsm.uniVlanFlowParamsSlice[flow].CookieSlice[i+1:]...)
+ // no activity within the FSM for OMCI processing, the deviceReason may be updated immediately
+ // state transition notification is checked in deviceHandler
+ if oFsm.pDeviceHandler != nil {
+ // success indication without the need to write to kvStore (done already below with updated data from StorePersUniFlowConfig())
+ go oFsm.pDeviceHandler.DeviceProcStatusUpdate(ctx, cmn.OnuDeviceEvent(oFsm.requestEvent+cDeviceEventOffsetRemoveNoKvStore))
+ }
+ logger.Debugw(ctx, "UniVlanConfigFsm flow removal - rule persists with still valid cookies", log.Fields{
+ "device-id": oFsm.deviceID, "cookies": oFsm.uniVlanFlowParamsSlice[flow].CookieSlice})
+ if deletedCookie == oFsm.delayNewRuleCookie {
+ //the delayedNewCookie is the one that is currently deleted, but the rule still exist with other cookies
+ //as long as there are further cookies for this rule indicate there is still some cookie to be deleted
+ //simply use the first one
+ oFsm.delayNewRuleCookie = oFsm.uniVlanFlowParamsSlice[flow].CookieSlice[0]
+ logger.Debugw(ctx, "UniVlanConfigFsm remaining cookie awaited for deletion before new rule add", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": oFsm.delayNewRuleCookie})
+ }
+ //permanently store the modified flow config for reconcile case and immediately write to KvStore
+ if oFsm.pDeviceHandler != nil {
+ if err := oFsm.pDeviceHandler.StorePersUniFlowConfig(ctx, oFsm.pOnuUniPort.UniID,
+ &oFsm.uniVlanFlowParamsSlice, true); err != nil {
+ logger.Errorw(ctx, err.Error(), log.Fields{"device-id": oFsm.deviceID})
+ return err
+ }
+ }
+ }
+ break remove_loop //found the cookie - no further search for this requested cookie
+ }
+ }
+ } //search all flows
+ if !flowCookieMatch { //some cookie remove-request for a cookie that does not exist in the FSM data
+ logger.Warnw(ctx, "UniVlanConfigFsm flow removal - remove-cookie not found", log.Fields{
+ "device-id": oFsm.deviceID, "remove-cookie": aCookie})
+ // but accept the request with success as no such cookie (flow) does exist
+ // no activity within the FSM for OMCI processing, the deviceReason may be updated immediately
+ // state transition notification is checked in deviceHandler
+ if oFsm.pDeviceHandler != nil {
+ // success indication without the need to write to kvStore (no change)
+ go oFsm.pDeviceHandler.DeviceProcStatusUpdate(ctx, cmn.OnuDeviceEvent(oFsm.requestEvent+cDeviceEventOffsetRemoveNoKvStore))
+ }
+ return nil
+ } //unknown cookie
+
+ return nil
+}
+
+// removeRuleComplete initiates the complete removal of a VLAN rule (from single cookie element)
+// requires mutexFlowParams to be locked at call
+func (oFsm *UniVlanConfigFsm) removeRuleComplete(ctx context.Context,
+ aUniFlowParams cmn.UniVlanFlowParams, aCookie uint64) bool {
+ pConfigVlanStateBaseFsm := oFsm.PAdaptFsm.PFsm
+ var cancelPendingConfig bool = false
+ var loRemoveParams uniRemoveVlanFlowParams = uniRemoveVlanFlowParams{}
+ logger.Debugw(ctx, "UniVlanConfigFsm flow removal - full flow removal", log.Fields{
+ "device-id": oFsm.deviceID})
+ //rwCore flow recovery may be the reason for this delete, in which case the flowToBeDeleted may be the same
+ // as the one still waiting in the FSM as toAdd but waiting for TechProfileConfig
+ // so we have to check if we have to abort the outstanding AddRequest and regard the current DelRequest as done
+ // if the Fsm is in some other transient (config) state, we will reach the DelRequest later and correctly process it then
+ if pConfigVlanStateBaseFsm.Is(VlanStWaitingTechProf) {
+ logger.Debugw(ctx, "UniVlanConfigFsm was waiting for TechProf config with add-request, just aborting the outstanding add",
+ log.Fields{"device-id": oFsm.deviceID})
+ cancelPendingConfig = true
+ } else {
+ //create a new element for the removeVlanFlow slice
+ loRemoveParams = uniRemoveVlanFlowParams{
+ vlanRuleParams: aUniFlowParams.VlanRuleParams,
+ cookie: aCookie,
+ }
+ loRemoveParams.removeChannel = make(chan bool)
+ oFsm.uniRemoveFlowsSlice = append(oFsm.uniRemoveFlowsSlice, loRemoveParams)
+ }
+
+ usedTpID := aUniFlowParams.VlanRuleParams.TpID
+ if len(oFsm.uniVlanFlowParamsSlice) <= 1 {
+ //at this point it is evident that no flow anymore will refer to a still possibly active Techprofile
+ //request that this profile gets deleted before a new flow add is allowed (except for some aborted add)
+ if !cancelPendingConfig {
+ // ensure mutexFlowParams not locked before calling some TPProcessing activity (that might already be pending on it)
+ oFsm.mutexFlowParams.Unlock()
+ logger.Debugw(ctx, "UniVlanConfigFsm flow removal requested - set TechProfile to-delete", log.Fields{
+ "device-id": oFsm.deviceID})
+ if oFsm.pUniTechProf != nil {
+ oFsm.pUniTechProf.SetProfileToDelete(oFsm.pOnuUniPort.UniID, usedTpID, true)
+ }
+ oFsm.mutexFlowParams.Lock()
+ }
+ } else {
+ if !cancelPendingConfig {
+ oFsm.updateTechProfileToDelete(ctx, usedTpID)
+ }
+ }
+ //trigger the FSM to remove the relevant rule
+ if cancelPendingConfig {
+ //as the uniFlow parameters are already stored (for add) but no explicit removal is done anymore
+ // the paramSlice has to be updated with rule-removal, which also then updates NumUniFlows
+ //call from 'non-configured' state of the rules
+ if err := oFsm.removeFlowFromParamsSlice(ctx, aCookie, false); err != nil {
+ //something quite inconsistent detected, perhaps just try to recover with FSM reset
+ oFsm.mutexFlowParams.Unlock()
+ if fsmErr := pConfigVlanStateBaseFsm.Event(VlanEvReset); fsmErr != nil {
+ logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm!",
+ log.Fields{"fsmState": pConfigVlanStateBaseFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+ }
+ return false //data base update could not be done, return like cookie not found
+ }
+
+ oFsm.requestEventOffset = uint8(cDeviceEventOffsetRemoveWithKvStore) //offset for last flow-remove activity (with kvStore request)
+ //attention: take care to release and re-take the mutexFlowParams when calling the FSM directly -
+ // synchronous FSM 'event/state' functions may rely on this mutex
+ oFsm.mutexFlowParams.Unlock()
+ if fsmErr := pConfigVlanStateBaseFsm.Event(VlanEvCancelOutstandingConfig); fsmErr != nil {
+ logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm!",
+ log.Fields{"fsmState": pConfigVlanStateBaseFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+ }
+ oFsm.mutexFlowParams.Lock()
+ return true
+ }
+ if pConfigVlanStateBaseFsm.Is(VlanStConfigDone) {
+ logger.Debugw(ctx, "UniVlanConfigFsm rule removal request", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+ "tp-id": loRemoveParams.vlanRuleParams.TpID,
+ "set-Vlan": loRemoveParams.vlanRuleParams.SetVid})
+ //have to re-trigger the FSM to proceed with outstanding incremental flow configuration
+ //attention: take care to release and re-take the mutexFlowParams when calling the FSM directly -
+ // synchronous FSM 'event/state' functions may rely on this mutex
+ oFsm.mutexFlowParams.Unlock()
+ if fsmErr := pConfigVlanStateBaseFsm.Event(VlanEvRemFlowConfig); fsmErr != nil {
+ logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm!",
+ log.Fields{"fsmState": pConfigVlanStateBaseFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+ }
+ oFsm.mutexFlowParams.Lock()
+ } // if not in the appropriate state a new entry will be automatically considered later
+ // when the configDone state is reached
+ return true
+}
+
+//removeFlowFromParamsSlice removes a flow from stored uniVlanFlowParamsSlice based on the cookie
+// it assumes that adding cookies for this flow (including the actual one to delete) was prevented
+// from the start of the deletion request to avoid to much interference
+// so when called, there can only be one cookie active for this flow
+// requires mutexFlowParams to be locked at call
+func (oFsm *UniVlanConfigFsm) removeFlowFromParamsSlice(ctx context.Context, aCookie uint64, aWasConfigured bool) error {
+ logger.Debugw(ctx, "UniVlanConfigFsm flow removal from ParamsSlice", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": aCookie})
+ cookieFound := false
+removeFromSlice_loop:
+ for flow, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+ // if UniFlowParams exists, cookieSlice should always have at least one element
+ cookieSliceLen := len(storedUniFlowParams.CookieSlice)
+ if cookieSliceLen == 1 {
+ if storedUniFlowParams.CookieSlice[0] == aCookie {
+ cookieFound = true
+ }
+ } else if cookieSliceLen == 0 {
+ errStr := "UniVlanConfigFsm unexpected cookie slice length 0 - removal in uniVlanFlowParamsSlice aborted"
+ logger.Errorw(ctx, errStr, log.Fields{"device-id": oFsm.deviceID})
+ return errors.New(errStr)
+ } else {
+ errStr := "UniVlanConfigFsm flow removal unexpected cookie slice length, but rule removal continued"
+ logger.Errorw(ctx, errStr, log.Fields{
+ "cookieSliceLen": len(oFsm.uniVlanFlowParamsSlice), "device-id": oFsm.deviceID})
+ for _, cookie := range storedUniFlowParams.CookieSlice {
+ if cookie == aCookie {
+ cookieFound = true
+ break
+ }
+ }
+ }
+ if cookieFound {
+ logger.Debugw(ctx, "UniVlanConfigFsm flow removal from ParamsSlice - cookie found", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": aCookie})
+ //remove the actual element from the addVlanFlow slice
+ // oFsm.uniVlanFlowParamsSlice[flow].CookieSlice = nil //automatically done by garbage collector
+ if len(oFsm.uniVlanFlowParamsSlice) <= 1 {
+ oFsm.NumUniFlows = 0 //no more flows
+ oFsm.ConfiguredUniFlow = 0 //no more flows configured
+ oFsm.uniVlanFlowParamsSlice = nil //reset the slice
+ //at this point it is evident that no flow anymore refers to a still possibly active Techprofile
+ //request that this profile gets deleted before a new flow add is allowed
+ logger.Debugw(ctx, "UniVlanConfigFsm flow removal from ParamsSlice - no more flows", log.Fields{
+ "device-id": oFsm.deviceID})
+ } else {
+ oFsm.NumUniFlows--
+ if aWasConfigured && oFsm.ConfiguredUniFlow > 0 {
+ oFsm.ConfiguredUniFlow--
+ }
+ //cut off the requested flow by slicing out this element
+ oFsm.uniVlanFlowParamsSlice = append(
+ oFsm.uniVlanFlowParamsSlice[:flow], oFsm.uniVlanFlowParamsSlice[flow+1:]...)
+ logger.Debugw(ctx, "UniVlanConfigFsm flow removal - specific flow removed from data", log.Fields{
+ "device-id": oFsm.deviceID})
+ }
+ break removeFromSlice_loop //found the cookie - no further search for this requested cookie
+ }
+ } //search all flows
+ if !cookieFound {
+ errStr := "UniVlanConfigFsm cookie for removal not found, internal counter not updated"
+ logger.Errorw(ctx, errStr, log.Fields{"device-id": oFsm.deviceID})
+ return errors.New(errStr)
+ }
+ //if the cookie was found and removed from uniVlanFlowParamsSlice above now write the modified persistency data
+ // KVStore update will be done after reaching the requested FSM end state (not immediately here)
+ if err := oFsm.pDeviceHandler.StorePersUniFlowConfig(ctx, oFsm.pOnuUniPort.UniID,
+ &oFsm.uniVlanFlowParamsSlice, false); err != nil {
+ logger.Errorw(ctx, err.Error(), log.Fields{"device-id": oFsm.deviceID})
+ return err
+ }
+ return nil
+}
+
+// requires mutexFlowParams to be locked at call
+func (oFsm *UniVlanConfigFsm) updateTechProfileToDelete(ctx context.Context, usedTpID uint8) {
+ //here we have to check, if there are still other flows referencing to the actual ProfileId
+ // before we can request that this profile gets deleted before a new flow add is allowed
+ tpIDInOtherFlows := false
+ for _, tpUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+ if tpUniFlowParams.VlanRuleParams.TpID == usedTpID {
+ tpIDInOtherFlows = true
+ break // search loop can be left
+ }
+ }
+ if tpIDInOtherFlows {
+ logger.Debugw(ctx, "UniVlanConfigFsm tp-id used in deleted flow is still used in other flows", log.Fields{
+ "device-id": oFsm.deviceID, "tp-id": usedTpID})
+ } else {
+ logger.Debugw(ctx, "UniVlanConfigFsm tp-id used in deleted flow is not used anymore - set TechProfile to-delete", log.Fields{
+ "device-id": oFsm.deviceID, "tp-id": usedTpID})
+ // ensure mutexFlowParams not locked before calling some TPProcessing activity (that might already be pending on it)
+ oFsm.mutexFlowParams.Unlock()
+ if oFsm.pUniTechProf != nil {
+ //request that this profile gets deleted before a new flow add is allowed
+ oFsm.pUniTechProf.SetProfileToDelete(oFsm.pOnuUniPort.UniID, usedTpID, true)
+ }
+ oFsm.mutexFlowParams.Lock()
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) enterPreparing(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniVlanConfigFsm preparing", log.Fields{"device-id": oFsm.deviceID})
+
+ // this FSM is not intended for re-start, needs always new creation for a new run
+ // (self-destroying - compare enterDisabled())
+ oFsm.omciMIdsResponseReceived = make(chan bool)
+ oFsm.chCookieDeleted = make(chan bool)
+ // start go routine for processing of LockState messages
+ go oFsm.processOmciVlanMessages(ctx)
+ //let the state machine run forward from here directly
+ pConfigVlanStateAFsm := oFsm.PAdaptFsm
+ if pConfigVlanStateAFsm != nil {
+ if oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+ logger.Debugw(ctx, "reconciling - skip omci-config of vlan rule",
+ log.Fields{"fsmState": oFsm.PAdaptFsm.PFsm.Current(), "device-id": oFsm.deviceID})
+ // Can't call FSM Event directly, decoupling it
+ go func(a_pAFsm *cmn.AdapterFsm) {
+ _ = a_pAFsm.PFsm.Event(VlanEvSkipOmciConfig)
+ }(pConfigVlanStateAFsm)
+ return
+ }
+ // Can't call FSM Event directly, decoupling it
+ go func(a_pAFsm *cmn.AdapterFsm) {
+ _ = a_pAFsm.PFsm.Event(VlanEvPrepareDone)
+ }(pConfigVlanStateAFsm)
+ return
+ }
+ logger.Errorw(ctx, "UniVlanConfigFsm abort: invalid FSM pointer", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+ //should never happen, else: recovery would be needed from outside the FSM
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigStarting(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniVlanConfigFsm start vlan configuration", log.Fields{"device-id": oFsm.deviceID})
+ pConfigVlanStateAFsm := oFsm.PAdaptFsm
+ if pConfigVlanStateAFsm != nil {
+ oFsm.mutexFlowParams.Lock()
+ //possibly the entry is not valid anymore based on intermediate delete requests
+ //just a basic protection ...
+ if len(oFsm.uniVlanFlowParamsSlice) == 0 {
+ oFsm.mutexFlowParams.Unlock()
+ logger.Debugw(ctx, "UniVlanConfigFsm start: no rule entry anymore available", log.Fields{
+ "device-id": oFsm.deviceID})
+ // Can't call FSM Event directly, decoupling it
+ go func(a_pAFsm *cmn.AdapterFsm) {
+ _ = a_pAFsm.PFsm.Event(VlanEvReset)
+ }(pConfigVlanStateAFsm)
+ return
+ }
+ //access to uniVlanFlowParamsSlice is done on first element only here per definition
+ //store the actual rule that shall be worked upon in the following transient states
+ oFsm.actualUniVlanConfigRule = oFsm.uniVlanFlowParamsSlice[0].VlanRuleParams
+ oFsm.actualUniVlanConfigMeter = oFsm.uniVlanFlowParamsSlice[0].Meter
+ tpID := oFsm.actualUniVlanConfigRule.TpID
+ oFsm.TpIDWaitingFor = tpID
+ //cmp also usage in EVTOCDE create in omci_cc
+ oFsm.evtocdID = cmn.MacBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.MacBpNo)
+ loSetVlan := oFsm.actualUniVlanConfigRule.SetVid
+ //attention: take care to release the mutexFlowParams when calling the FSM directly -
+ // synchronous FSM 'event/state' functions may rely on this mutex
+ // but it must be released already before calling getTechProfileDone() as it may already be locked
+ // by the techProfile processing call to VlanFsm.IsFlowRemovePending() (see VOL-4207)
+ oFsm.mutexFlowParams.Unlock()
+ loTechProfDone := oFsm.pUniTechProf.getTechProfileDone(ctx, oFsm.pOnuUniPort.UniID, uint8(tpID))
+ logger.Debugw(ctx, "UniVlanConfigFsm - start with first rule", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+ "set-Vlan": loSetVlan, "tp-id": tpID, "ProfDone": loTechProfDone})
+
+ // Can't call FSM Event directly, decoupling it
+ go func(aPAFsm *cmn.AdapterFsm, aTechProfDone bool) {
+ if aPAFsm != nil && aPAFsm.PFsm != nil {
+ if aTechProfDone {
+ // let the vlan processing begin
+ _ = aPAFsm.PFsm.Event(VlanEvStartConfig)
+ } else {
+ // set to waiting for Techprofile
+ _ = aPAFsm.PFsm.Event(VlanEvWaitTechProf)
+ }
+ }
+ }(pConfigVlanStateAFsm, loTechProfDone)
+ } else {
+ logger.Errorw(ctx, "UniVlanConfigFsm abort: invalid FSM pointer", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+ //should never happen, else: recovery would be needed from outside the FSM
+ return
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigVtfd(ctx context.Context, e *fsm.Event) {
+ //mutex protection is required for possible concurrent access to FSM members
+ oFsm.mutexFlowParams.Lock()
+ oFsm.TpIDWaitingFor = 0 //reset indication to avoid misinterpretation
+ if oFsm.actualUniVlanConfigRule.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+ // meaning transparent setup - no specific VTFD setting required
+ oFsm.mutexFlowParams.Unlock()
+ logger.Debugw(ctx, "UniVlanConfigFsm: no VTFD config required", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+ // let the FSM proceed ... (from within this state all internal pointers may be expected to be correct)
+ pConfigVlanStateAFsm := oFsm.PAdaptFsm
+ // Can't call FSM Event directly, decoupling it
+ go func(a_pAFsm *cmn.AdapterFsm) {
+ _ = a_pAFsm.PFsm.Event(VlanEvRxConfigVtfd)
+ }(pConfigVlanStateAFsm)
+ } else {
+ // This attribute uniquely identifies each instance of this managed entity. Through an identical ID,
+ // this managed entity is implicitly linked to an instance of the MAC bridge port configuration data ME.
+ vtfdID, _ := cmn.GenerateANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(oFsm.actualUniVlanConfigRule.TpID))
+ logger.Debugw(ctx, "UniVlanConfigFsm create VTFD", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(vtfdID), 16),
+ "in state": e.FSM.Current(), "device-id": oFsm.deviceID,
+ "macBpNo": oFsm.pOnuUniPort.MacBpNo, "TpID": oFsm.actualUniVlanConfigRule.TpID})
+ // setVid is assumed to be masked already by the caller to 12 bit
+ oFsm.vlanFilterList[0] = uint16(oFsm.actualUniVlanConfigRule.SetVid)
+ oFsm.mutexFlowParams.Unlock()
+ vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization
+ vtfdFilterList[0] = oFsm.vlanFilterList[0]
+ oFsm.numVlanFilterEntries = 1
+ meParams := me.ParamData{
+ EntityID: vtfdID,
+ Attributes: me.AttributeValueMap{
+ "VlanFilterList": vtfdFilterList, //omci lib wants a slice for serialization
+ "ForwardOperation": uint8(0x10), //VID investigation
+ "NumberOfEntries": oFsm.numVlanFilterEntries,
+ },
+ }
+ logger.Debugw(ctx, "UniVlanConfigFsm sendcreate VTFD", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendCreateVtfdVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "VTFD create failed, aborting UniVlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ pConfigVlanStateAFsm := oFsm.PAdaptFsm
+ if pConfigVlanStateAFsm != nil {
+ go func(a_pAFsm *cmn.AdapterFsm) {
+ _ = a_pAFsm.PFsm.Event(VlanEvReset)
+ }(pConfigVlanStateAFsm)
+ }
+ return
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ //TODO!!: refactoring improvement requested, here as an example for [VOL-3457]:
+ // send shall return (dual format) error code that can be used here for immediate error treatment
+ // (relevant to all used sendXX() methods in this (and other) FSM's)
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigEvtocd(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniVlanConfigFsm - start config EVTOCD loop", log.Fields{
+ "device-id": oFsm.deviceID})
+ oFsm.requestEventOffset = uint8(cDeviceEventOffsetAddWithKvStore) //0 offset for last flow-add activity
+ go func() {
+ //using the first element in the slice because it's the first flow per definition here
+ errEvto := oFsm.performConfigEvtocdEntries(ctx, 0)
+ //This is correct passing scenario
+ if errEvto == nil {
+ oFsm.mutexFlowParams.RLock()
+ tpID := oFsm.actualUniVlanConfigRule.TpID
+ vlanID := oFsm.actualUniVlanConfigRule.SetVid
+ configuredUniFlows := oFsm.ConfiguredUniFlow
+ // ensure mutexFlowParams not locked before calling some TPProcessing activity (that might already be pending on it)
+ oFsm.mutexFlowParams.RUnlock()
+ for _, gemPort := range oFsm.pUniTechProf.getMulticastGemPorts(ctx, oFsm.pOnuUniPort.UniID, uint8(tpID)) {
+ logger.Infow(ctx, "Setting multicast MEs, with first flow", log.Fields{"deviceID": oFsm.deviceID,
+ "techProfile": tpID, "gemPort": gemPort, "vlanID": vlanID, "ConfiguredUniFlow": configuredUniFlows})
+ errCreateAllMulticastME := oFsm.performSettingMulticastME(ctx, tpID, gemPort,
+ vlanID)
+ if errCreateAllMulticastME != nil {
+ logger.Errorw(ctx, "Multicast ME create failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ }
+ }
+ //If this first flow contains a meter, then create TD for related gems.
+ if oFsm.actualUniVlanConfigMeter != nil {
+ logger.Debugw(ctx, "Creating Traffic Descriptor", log.Fields{"device-id": oFsm.deviceID, "meter": oFsm.actualUniVlanConfigMeter})
+ for _, gemPort := range oFsm.pUniTechProf.getBidirectionalGemPortIDsForTP(ctx, oFsm.pOnuUniPort.UniID, tpID) {
+ logger.Debugw(ctx, "Creating Traffic Descriptor for gem", log.Fields{"device-id": oFsm.deviceID, "meter": oFsm.actualUniVlanConfigMeter, "gem": gemPort})
+ errCreateTrafficDescriptor := oFsm.createTrafficDescriptor(ctx, oFsm.actualUniVlanConfigMeter, tpID,
+ oFsm.pOnuUniPort.UniID, gemPort)
+ if errCreateTrafficDescriptor != nil {
+ logger.Errorw(ctx, "Create Traffic Descriptor create failed, aborting Ani Config FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ }
+ }
+ }
+
+ //TODO Possibly insert new state for multicast --> possibly another jira/later time.
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvRxConfigEvtocd)
+ }
+ }()
+}
+
+func (oFsm *UniVlanConfigFsm) enterVlanConfigDone(ctx context.Context, e *fsm.Event) {
+
+ oFsm.mutexFlowParams.Lock()
+
+ logger.Infow(ctx, "UniVlanConfigFsm config done - checking on more flows", log.Fields{
+ "device-id": oFsm.deviceID,
+ "overall-uni-rules": oFsm.NumUniFlows, "configured-uni-rules": oFsm.ConfiguredUniFlow})
+ pConfigVlanStateAFsm := oFsm.PAdaptFsm
+ if pConfigVlanStateAFsm == nil {
+ oFsm.mutexFlowParams.Unlock()
+ logger.Errorw(ctx, "UniVlanConfigFsm abort: invalid FSM pointer", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+ //should never happen, else: recovery would be needed from outside the FSM
+ return
+ }
+ pConfigVlanStateBaseFsm := pConfigVlanStateAFsm.PFsm
+ if len(oFsm.uniRemoveFlowsSlice) > 0 {
+ //some further flows are to be removed, removal always starts with the first element
+ logger.Debugw(ctx, "UniVlanConfigFsm rule removal from ConfigDone", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+ "tp-id": oFsm.uniRemoveFlowsSlice[0].vlanRuleParams.TpID,
+ "set-Vlan": oFsm.uniRemoveFlowsSlice[0].vlanRuleParams.SetVid})
+ oFsm.mutexFlowParams.Unlock()
+ // Can't call FSM Event directly, decoupling it
+ go func(a_pBaseFsm *fsm.FSM) {
+ _ = a_pBaseFsm.Event(VlanEvRemFlowConfig)
+ }(pConfigVlanStateBaseFsm)
+ return
+ }
+ if oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+ oFsm.ConfiguredUniFlow = oFsm.NumUniFlows
+ if oFsm.lastFlowToReconcile {
+ logger.Debugw(ctx, "reconciling - flow processing finished", log.Fields{"device-id": oFsm.deviceID})
+ oFsm.pOnuDeviceEntry.SetReconcilingFlows(false)
+ oFsm.pOnuDeviceEntry.SetChReconcilingFlowsFinished(true)
+ }
+ logger.Debugw(ctx, "reconciling - skip enterVlanConfigDone processing",
+ log.Fields{"NumUniFlows": oFsm.NumUniFlows, "ConfiguredUniFlow": oFsm.ConfiguredUniFlow, "device-id": oFsm.deviceID})
+ oFsm.mutexFlowParams.Unlock()
+ return
+ }
+ if oFsm.NumUniFlows > oFsm.ConfiguredUniFlow {
+ if oFsm.ConfiguredUniFlow == 0 {
+ oFsm.mutexFlowParams.Unlock()
+ // this is a restart with a complete new flow, we can re-use the initial flow config control
+ // including the check, if the related techProfile is (still) available (probably also removed in between)
+ // Can't call FSM Event directly, decoupling it
+ go func(a_pBaseFsm *fsm.FSM) {
+ _ = a_pBaseFsm.Event(VlanEvRenew)
+ }(pConfigVlanStateBaseFsm)
+ return
+ }
+
+ //some further flows are to be configured
+ //store the actual rule that shall be worked upon in the following transient states
+ if len(oFsm.uniVlanFlowParamsSlice) < int(oFsm.ConfiguredUniFlow) {
+ //check introduced after having observed some panic in this processing
+ logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm in ConfigDone - inconsistent counter",
+ log.Fields{"ConfiguredUniFlow": oFsm.ConfiguredUniFlow,
+ "sliceLen": len(oFsm.uniVlanFlowParamsSlice), "device-id": oFsm.deviceID})
+ oFsm.mutexFlowParams.Unlock()
+ go func(a_pAFsm *cmn.AdapterFsm) {
+ _ = a_pAFsm.PFsm.Event(VlanEvReset)
+ }(pConfigVlanStateAFsm)
+ return
+ }
+ oFsm.actualUniVlanConfigRule = oFsm.uniVlanFlowParamsSlice[oFsm.ConfiguredUniFlow].VlanRuleParams
+ oFsm.actualUniVlanConfigMeter = oFsm.uniVlanFlowParamsSlice[oFsm.ConfiguredUniFlow].Meter
+ //tpId of the next rule to be configured
+ tpID := oFsm.actualUniVlanConfigRule.TpID
+ oFsm.TpIDWaitingFor = tpID
+ loSetVlan := oFsm.actualUniVlanConfigRule.SetVid
+ //attention: take care to release the mutexFlowParams when calling the FSM directly -
+ // synchronous FSM 'event/state' functions may rely on this mutex
+ // but it must be released already before calling getTechProfileDone() as it may already be locked
+ // by the techProfile processing call to VlanFsm.IsFlowRemovePending() (see VOL-4207)
+ oFsm.mutexFlowParams.Unlock()
+ loTechProfDone := oFsm.pUniTechProf.getTechProfileDone(ctx, oFsm.pOnuUniPort.UniID, tpID)
+ logger.Debugw(ctx, "UniVlanConfigFsm - incremental config request", log.Fields{
+ "device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+ "set-Vlan": loSetVlan, "tp-id": tpID, "ProfDone": loTechProfDone})
+
+ // Can't call FSM Event directly, decoupling it
+ go func(aPBaseFsm *fsm.FSM, aTechProfDone bool) {
+ if aTechProfDone {
+ // let the vlan processing continue with next rule
+ _ = aPBaseFsm.Event(VlanEvIncrFlowConfig)
+ } else {
+ // set to waiting for Techprofile
+ _ = aPBaseFsm.Event(VlanEvWaitTPIncr)
+ }
+ }(pConfigVlanStateBaseFsm, loTechProfDone)
+ return
+ }
+ oFsm.mutexFlowParams.Unlock()
+ logger.Debugw(ctx, "UniVlanConfigFsm - VLAN config done: send dh event notification", log.Fields{
+ "device-id": oFsm.deviceID})
+ // it might appear that some flows are requested also after 'flowPushed' event has been generated ...
+ // state transition notification is checked in deviceHandler
+ // note: 'flowPushed' event is only generated if all 'pending' rules are configured
+ if oFsm.pDeviceHandler != nil {
+ //making use of the add->remove successor enum assumption/definition
+ go oFsm.pDeviceHandler.DeviceProcStatusUpdate(ctx, cmn.OnuDeviceEvent(uint8(oFsm.requestEvent)+oFsm.requestEventOffset))
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigIncrFlow(ctx context.Context, e *fsm.Event) {
+
+ if oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+ logger.Debugw(ctx, "reconciling - skip further processing for incremental flow",
+ log.Fields{"fsmState": oFsm.PAdaptFsm.PFsm.Current(), "device-id": oFsm.deviceID})
+ go func(a_pBaseFsm *fsm.FSM) {
+ _ = a_pBaseFsm.Event(VlanEvSkipIncFlowConfig)
+ }(oFsm.PAdaptFsm.PFsm)
+ return
+ }
+ oFsm.mutexFlowParams.Lock()
+ logger.Debugw(ctx, "UniVlanConfigFsm - start config further incremental flow", log.Fields{
+ "recent flow-number": oFsm.ConfiguredUniFlow,
+ "device-id": oFsm.deviceID})
+ oFsm.TpIDWaitingFor = 0 //reset indication to avoid misinterpretation
+
+ if oFsm.actualUniVlanConfigRule.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+ // meaning transparent setup - no specific VTFD setting required
+ logger.Debugw(ctx, "UniVlanConfigFsm: no VTFD config required", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+ } else {
+ //TODO!!!: it was not really intended to keep this enter* FSM method waiting on OMCI response (preventing other state transitions)
+ // so it would be conceptually better to wait for the response in background like for the other multi-entity processing
+ // but as the OMCI sequence must be ensured, a separate new state would be required - perhaps later
+ // in practice should have no influence by now as no other state transition is currently accepted (while cancel() is ensured)
+ if oFsm.numVlanFilterEntries == 0 {
+ // This attribute uniquely identifies each instance of this managed entity. Through an identical ID,
+ // this managed entity is implicitly linked to an instance of the MAC bridge port configuration data ME.
+ vtfdID, _ := cmn.GenerateANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(oFsm.actualUniVlanConfigRule.TpID))
+ //no VTFD yet created
+ logger.Debugw(ctx, "UniVlanConfigFsm create VTFD", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(vtfdID), 16),
+ "device-id": oFsm.deviceID,
+ "macBpNo": oFsm.pOnuUniPort.MacBpNo, "TpID": oFsm.actualUniVlanConfigRule.TpID})
+ // 'SetVid' below is assumed to be masked already by the caller to 12 bit
+ oFsm.vlanFilterList[0] = uint16(oFsm.actualUniVlanConfigRule.SetVid)
+
+ vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization
+ vtfdFilterList[0] = oFsm.vlanFilterList[0]
+ oFsm.numVlanFilterEntries = 1
+ meParams := me.ParamData{
+ EntityID: vtfdID,
+ Attributes: me.AttributeValueMap{
+ "VlanFilterList": vtfdFilterList,
+ "ForwardOperation": uint8(0x10), //VID investigation
+ "NumberOfEntries": oFsm.numVlanFilterEntries,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendCreateVtfdVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ oFsm.mutexFlowParams.Unlock()
+ logger.Errorw(ctx, "VTFD create failed, aborting UniVlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ pConfigVlanStateAFsm := oFsm.PAdaptFsm
+ if pConfigVlanStateAFsm != nil {
+ go func(a_pAFsm *cmn.AdapterFsm) {
+ _ = a_pAFsm.PFsm.Event(VlanEvReset)
+ }(pConfigVlanStateAFsm)
+ }
+ return
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ //TODO!!: refactoring improvement requested, here as an example for [VOL-3457]:
+ // send shall return (dual format) error code that can be used here for immediate error treatment
+ // (relevant to all used sendXX() methods in this (and other) FSM's)
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ } else {
+ // This attribute uniquely identifies each instance of this managed entity. Through an identical ID,
+ // this managed entity is implicitly linked to an instance of the MAC bridge port configuration data ME.
+ vtfdID, _ := cmn.GenerateANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(oFsm.actualUniVlanConfigRule.TpID))
+
+ logger.Debugw(ctx, "UniVlanConfigFsm set VTFD", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(vtfdID), 16),
+ "device-id": oFsm.deviceID,
+ "macBpNo": oFsm.pOnuUniPort.MacBpNo, "TpID": oFsm.actualUniVlanConfigRule.TpID})
+ // setVid is assumed to be masked already by the caller to 12 bit
+ oFsm.vlanFilterList[oFsm.numVlanFilterEntries] =
+ uint16(oFsm.actualUniVlanConfigRule.SetVid)
+ vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization
+
+ // FIXME: VOL-3685: Issues with resetting a table entry in EVTOCD ME
+ // VTFD has to be created afresh with a new entity ID that has the same entity ID as the MBPCD ME for every
+ // new vlan associated with a different TP.
+ vtfdFilterList[0] = uint16(oFsm.actualUniVlanConfigRule.SetVid)
+
+ oFsm.numVlanFilterEntries++
+ meParams := me.ParamData{
+ EntityID: vtfdID,
+ Attributes: me.AttributeValueMap{
+ "VlanFilterList": vtfdFilterList,
+ "ForwardOperation": uint8(0x10), //VID investigation
+ "NumberOfEntries": oFsm.numVlanFilterEntries,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendCreateVtfdVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ oFsm.mutexFlowParams.Unlock()
+ logger.Errorw(ctx, "UniVlanFsm create Vlan Tagging Filter ME result error",
+ log.Fields{"device-id": oFsm.deviceID, "Error": err})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ //TODO!!: refactoring improvement requested, here as an example for [VOL-3457]:
+ // send shall return (dual format) error code that can be used here for immediate error treatment
+ // (relevant to all used sendXX() methods in this (and other) FSM's)
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ }
+ //verify response
+ err := oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ oFsm.mutexFlowParams.Unlock()
+ logger.Errorw(ctx, "VTFD create/set failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ pConfigVlanStateBaseFsm := oFsm.PAdaptFsm.PFsm
+ // Can't call FSM Event directly, decoupling it
+ go func(a_pBaseFsm *fsm.FSM) {
+ _ = a_pBaseFsm.Event(VlanEvReset)
+ }(pConfigVlanStateBaseFsm)
+ return
+ }
+ }
+
+ oFsm.requestEventOffset = uint8(cDeviceEventOffsetAddWithKvStore) //0 offset for last flow-add activity
+ oFsm.mutexFlowParams.Unlock()
+ go func() {
+ oFsm.mutexFlowParams.RLock()
+ tpID := oFsm.actualUniVlanConfigRule.TpID
+ ConfiguredUniFlow := oFsm.ConfiguredUniFlow
+ // ensure mutexFlowParams not locked before calling some TPProcessing activity (that might already be pending on it)
+ oFsm.mutexFlowParams.RUnlock()
+ errEvto := oFsm.performConfigEvtocdEntries(ctx, ConfiguredUniFlow)
+ //This is correct passing scenario
+ if errEvto == nil {
+ //TODO Possibly insert new state for multicast --> possibly another jira/later time.
+ for _, gemPort := range oFsm.pUniTechProf.getMulticastGemPorts(ctx, oFsm.pOnuUniPort.UniID, uint8(tpID)) {
+ oFsm.mutexFlowParams.RLock()
+ vlanID := oFsm.actualUniVlanConfigRule.SetVid
+ logger.Infow(ctx, "Setting multicast MEs for additional flows", log.Fields{"deviceID": oFsm.deviceID,
+ "techProfile": tpID, "gemPort": gemPort,
+ "vlanID": vlanID, "ConfiguredUniFlow": ConfiguredUniFlow})
+ oFsm.mutexFlowParams.RUnlock()
+ errCreateAllMulticastME := oFsm.performSettingMulticastME(ctx, tpID, gemPort, vlanID)
+ if errCreateAllMulticastME != nil {
+ logger.Errorw(ctx, "Multicast ME create failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ }
+ }
+ //If this incremental flow contains a meter, then create TD for related gems.
+ if oFsm.actualUniVlanConfigMeter != nil {
+ for _, gemPort := range oFsm.pUniTechProf.getBidirectionalGemPortIDsForTP(ctx, oFsm.pOnuUniPort.UniID, tpID) {
+ logger.Debugw(ctx, "Creating Traffic Descriptor for gem", log.Fields{"device-id": oFsm.deviceID, "meter": oFsm.actualUniVlanConfigMeter, "gem": gemPort})
+ errCreateTrafficDescriptor := oFsm.createTrafficDescriptor(ctx, oFsm.actualUniVlanConfigMeter, tpID,
+ oFsm.pOnuUniPort.UniID, gemPort)
+ if errCreateTrafficDescriptor != nil {
+ logger.Errorw(ctx, "Create Traffic Descriptor create failed, aborting Ani Config FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ }
+ }
+ }
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvRxConfigEvtocd)
+ }
+ }()
+}
+
+func (oFsm *UniVlanConfigFsm) enterRemoveFlow(ctx context.Context, e *fsm.Event) {
+ oFsm.mutexFlowParams.RLock()
+ logger.Debugw(ctx, "UniVlanConfigFsm - start removing the top remove-flow", log.Fields{
+ "with last cookie": oFsm.uniRemoveFlowsSlice[0].cookie,
+ "device-id": oFsm.deviceID})
+
+ pConfigVlanStateBaseFsm := oFsm.PAdaptFsm.PFsm
+ loAllowSpecificOmciConfig := oFsm.pDeviceHandler.IsReadyForOmciConfig()
+ loVlanEntryClear := uint8(0)
+ loVlanEntryRmPos := uint8(0x80) //with indication 'invalid' in bit 7
+ //shallow copy is sufficient as no reference variables are used within struct
+ loRuleParams := oFsm.uniRemoveFlowsSlice[0].vlanRuleParams
+ oFsm.mutexFlowParams.RUnlock()
+ logger.Debugw(ctx, "UniVlanConfigFsm - remove-flow parameters are", log.Fields{
+ "match vid": loRuleParams.MatchVid, "match Pcp": loRuleParams.MatchPcp,
+ "set vid": strconv.FormatInt(int64(loRuleParams.SetVid), 16),
+ "device-id": oFsm.deviceID})
+
+ if loRuleParams.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+ // meaning transparent setup - no specific VTFD setting required
+ logger.Debugw(ctx, "UniVlanConfigFsm: no VTFD removal required for transparent flow", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+ } else {
+ vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization and 're-copy'
+ if oFsm.numVlanFilterEntries == 1 {
+ vtfdID, _ := cmn.GenerateANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(loRuleParams.TpID))
+ //only one active VLAN entry (hopefully the SetVID we want to remove - should be, but not verified ..)
+ // so we can just delete the VTFD entry
+ logger.Debugw(ctx, "UniVlanConfigFsm: VTFD delete (no more vlan filters)",
+ log.Fields{"current vlan list": oFsm.vlanFilterList, "EntitytId": strconv.FormatInt(int64(vtfdID), 16),
+ "device-id": oFsm.deviceID,
+ "macBpNo": oFsm.pOnuUniPort.MacBpNo, "TpID": loRuleParams.TpID})
+ loVlanEntryClear = 1 //full VlanFilter clear request
+ if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendDeleteVtfd(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, vtfdID)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "UniVlanFsm delete Vlan Tagging Filter ME result error",
+ log.Fields{"device-id": oFsm.deviceID, "Error": err})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return
+ }
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ } else {
+ logger.Debugw(ctx, "UniVlanConfigFsm delete VTFD OMCI handling skipped based on device state", log.Fields{
+ "device-id": oFsm.deviceID, "device-state": oFsm.pDeviceHandler.GetDeviceReasonString()})
+ }
+ } else {
+ //many VTFD already should exists - find and remove the one concerned by the actual remove rule
+ // by updating the VTFD per set command with new valid list
+ logger.Debugw(ctx, "UniVlanConfigFsm: VTFD removal of requested VLAN from the list on OMCI",
+ log.Fields{"current vlan list": oFsm.vlanFilterList,
+ "set-vlan": loRuleParams.SetVid, "device-id": oFsm.deviceID})
+ for i := uint8(0); i < oFsm.numVlanFilterEntries; i++ {
+ if loRuleParams.SetVid == uint32(oFsm.vlanFilterList[i]) {
+ loVlanEntryRmPos = i
+ break //abort search
+ }
+ }
+ if loVlanEntryRmPos < cVtfdTableSize {
+ vtfdID, _ := cmn.GenerateANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(loRuleParams.TpID))
+ //valid entry was found - to be eclipsed
+ loVlanEntryClear = 2 //VlanFilter remove request for a specific entry
+ for i := uint8(0); i < oFsm.numVlanFilterEntries; i++ {
+ if i < loVlanEntryRmPos {
+ vtfdFilterList[i] = oFsm.vlanFilterList[i] //copy original
+ } else if i < (cVtfdTableSize - 1) {
+ vtfdFilterList[i] = oFsm.vlanFilterList[i+1] //copy successor (including 0 elements)
+ } else {
+ vtfdFilterList[i] = 0 //set last byte if needed
+ }
+ }
+ logger.Debugw(ctx, "UniVlanConfigFsm set VTFD", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(vtfdID), 16),
+ "new vlan list": vtfdFilterList, "device-id": oFsm.deviceID,
+ "macBpNo": oFsm.pOnuUniPort.MacBpNo, "TpID": loRuleParams.TpID})
+
+ if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
+ // FIXME: VOL-3685: Issues with resetting a table entry in EVTOCD ME
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendDeleteVtfd(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, vtfdID)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "UniVlanFsm delete Vlan Tagging Filter ME result error",
+ log.Fields{"device-id": oFsm.deviceID, "Error": err})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return
+ }
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ } else {
+ logger.Debugw(ctx, "UniVlanConfigFsm set VTFD OMCI handling skipped based on device state", log.Fields{
+ "device-id": oFsm.deviceID, "device-state": oFsm.pDeviceHandler.GetDeviceReasonString()})
+ }
+ } else {
+ logger.Warnw(ctx, "UniVlanConfigFsm: requested VLAN for removal not found in list - ignore and continue (no VTFD set)",
+ log.Fields{"device-id": oFsm.deviceID})
+ }
+ }
+ if loVlanEntryClear > 0 {
+ if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
+ //waiting on response
+ err := oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "VTFD delete/reset failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ // Can't call FSM Event directly, decoupling it
+ go func(a_pBaseFsm *fsm.FSM) {
+ _ = a_pBaseFsm.Event(VlanEvReset)
+ }(pConfigVlanStateBaseFsm)
+ return
+ }
+ }
+
+ oFsm.mutexFlowParams.Lock()
+ if loVlanEntryClear == 1 {
+ oFsm.vlanFilterList[0] = 0 //first entry is the only that can contain the previous only-one element
+ oFsm.numVlanFilterEntries = 0
+ } else if loVlanEntryClear == 2 {
+ // new VlanFilterList should be one entry smaller now - copy from last configured entry
+ // this loop now includes the 0 element on previous last valid entry
+ for i := uint8(0); i <= oFsm.numVlanFilterEntries; i++ {
+ oFsm.vlanFilterList[i] = vtfdFilterList[i]
+ }
+ oFsm.numVlanFilterEntries--
+ }
+ oFsm.mutexFlowParams.Unlock()
+ }
+ }
+
+ if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
+ go oFsm.removeEvtocdEntries(ctx, loRuleParams)
+ } else {
+ // OMCI processing is not done, expectation is to have the ONU in some basic config state accordingly
+ logger.Debugw(ctx, "UniVlanConfigFsm remove EVTOCD OMCI handling skipped based on device state", log.Fields{
+ "device-id": oFsm.deviceID})
+ // Can't call FSM Event directly, decoupling it
+ go func(a_pBaseFsm *fsm.FSM) {
+ _ = a_pBaseFsm.Event(VlanEvRemFlowDone, loRuleParams.TpID)
+ }(pConfigVlanStateBaseFsm)
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) enterVlanCleanupDone(ctx context.Context, e *fsm.Event) {
+ var tpID uint8
+ // Extract the tpID
+ if len(e.Args) > 0 {
+ tpID = e.Args[0].(uint8)
+ logger.Debugw(ctx, "UniVlanConfigFsm - flow removed for tp id", log.Fields{"device-id": oFsm.deviceID, "tpID": e.Args[0].(uint8)})
+ } else {
+ logger.Warnw(ctx, "UniVlanConfigFsm - tp id not available", log.Fields{"device-id": oFsm.deviceID})
+ }
+ oFsm.mutexFlowParams.Lock()
+ deletedCookie := oFsm.uniRemoveFlowsSlice[0].cookie
+
+ pConfigVlanStateAFsm := oFsm.PAdaptFsm
+ if pConfigVlanStateAFsm == nil {
+ logger.Errorw(ctx, "invalid Fsm pointer - unresolvable - abort",
+ log.Fields{"device-id": oFsm.deviceID})
+ //would have to be fixed from outside somehow
+ return
+ }
+
+ // here we need o finally remove the removed data also from uniVlanFlowParamsSlice and possibly have to
+ // stop the suspension of a add-activity waiting for the end of removal
+ //call from 'configured' state of the rule
+ if err := oFsm.removeFlowFromParamsSlice(ctx, deletedCookie, true); err != nil {
+ //something quite inconsistent detected, perhaps just try to recover with FSM reset
+ oFsm.mutexFlowParams.Unlock()
+ logger.Errorw(ctx, "UniVlanConfigFsm - could not clear database - abort", log.Fields{"device-id": oFsm.deviceID})
+ go func(a_pAFsm *cmn.AdapterFsm) {
+ _ = a_pAFsm.PFsm.Event(VlanEvReset)
+ }(pConfigVlanStateAFsm)
+ return
+ }
+ if oFsm.uniRemoveFlowsSlice[0].isSuspendedOnAdd {
+ removeChannel := oFsm.uniRemoveFlowsSlice[0].removeChannel
+ oFsm.mutexFlowParams.Unlock()
+ removeChannel <- true
+ oFsm.mutexFlowParams.Lock()
+ }
+
+ logger.Debugw(ctx, "UniVlanConfigFsm - removing the removal data", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.deviceID,
+ "removed cookie": deletedCookie, "waitForDeleteCookie": oFsm.delayNewRuleCookie})
+
+ if len(oFsm.uniRemoveFlowsSlice) <= 1 {
+ oFsm.uniRemoveFlowsSlice = nil //reset the slice
+ logger.Debugw(ctx, "UniVlanConfigFsm flow removal - last remove-flow deleted", log.Fields{
+ "device-id": oFsm.deviceID})
+ } else {
+ //cut off the actual flow by slicing out the first element
+ oFsm.uniRemoveFlowsSlice = append(
+ oFsm.uniRemoveFlowsSlice[:0],
+ oFsm.uniRemoveFlowsSlice[1:]...)
+ logger.Debugw(ctx, "UniVlanConfigFsm flow removal - specific flow deleted from data", log.Fields{
+ "device-id": oFsm.deviceID})
+ }
+ oFsm.mutexFlowParams.Unlock()
+
+ oFsm.requestEventOffset = uint8(cDeviceEventOffsetRemoveWithKvStore) //offset for last flow-remove activity (with kvStore request)
+ //return to the basic config verification state
+ // Can't call FSM Event directly, decoupling it
+ go func(a_pAFsm *cmn.AdapterFsm) {
+ _ = a_pAFsm.PFsm.Event(VlanEvFlowDataRemoved)
+ }(pConfigVlanStateAFsm)
+
+ oFsm.mutexFlowParams.Lock()
+ noOfFlowRem := len(oFsm.uniRemoveFlowsSlice)
+ if deletedCookie == oFsm.delayNewRuleCookie {
+ // flush the channel CookieDeleted to ensure it is not lingering from some previous (aborted) activity
+ select {
+ case <-oFsm.chCookieDeleted:
+ logger.Debug(ctx, "flushed CookieDeleted")
+ default:
+ }
+ oFsm.chCookieDeleted <- true // let the waiting AddFlow thread continue
+ }
+ // If all pending flow-removes are completed and TP ID is valid go on processing any pending TP delete
+ if oFsm.signalOnFlowDelete && noOfFlowRem == 0 && tpID > 0 {
+ logger.Debugw(ctx, "signal flow removal for pending TP delete", log.Fields{"device-id": oFsm.deviceID, "tpID": tpID})
+ // If we are here then all flows are removed.
+ if len(oFsm.flowDeleteChannel) == 0 { //channel not yet in use
+ oFsm.flowDeleteChannel <- true
+ oFsm.signalOnFlowDelete = false
+ }
+ }
+ oFsm.mutexFlowParams.Unlock()
+}
+
+func (oFsm *UniVlanConfigFsm) enterResetting(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniVlanConfigFsm resetting", log.Fields{"device-id": oFsm.deviceID})
+
+ pConfigVlanStateAFsm := oFsm.PAdaptFsm
+ if pConfigVlanStateAFsm != nil {
+ // abort running message processing
+ fsmAbortMsg := cmn.Message{
+ Type: cmn.TestMsg,
+ Data: cmn.TestMessage{
+ TestMessageVal: cmn.AbortMessageProcessing,
+ },
+ }
+ pConfigVlanStateAFsm.CommChan <- fsmAbortMsg
+
+ //try to restart the FSM to 'disabled'
+ // Can't call FSM Event directly, decoupling it
+ go func(a_pAFsm *cmn.AdapterFsm) {
+ if a_pAFsm != nil && a_pAFsm.PFsm != nil {
+ _ = a_pAFsm.PFsm.Event(VlanEvRestart)
+ }
+ }(pConfigVlanStateAFsm)
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) enterDisabled(ctx context.Context, e *fsm.Event) {
+ logger.Debugw(ctx, "UniVlanConfigFsm enters disabled state", log.Fields{"device-id": oFsm.deviceID})
+ oFsm.mutexPLastTxMeInstance.Lock()
+ oFsm.pLastTxMeInstance = nil
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ oFsm.mutexFlowParams.RLock()
+ if oFsm.delayNewRuleCookie != 0 {
+ // looks like the waiting AddFlow is stuck
+ oFsm.mutexFlowParams.RUnlock()
+ oFsm.chCookieDeleted <- false // let the waiting AddFlow thread terminate
+ oFsm.mutexFlowParams.RLock()
+ }
+ if len(oFsm.uniRemoveFlowsSlice) > 0 {
+ for _, removeUniFlowParams := range oFsm.uniRemoveFlowsSlice {
+ if removeUniFlowParams.isSuspendedOnAdd {
+ removeChannel := removeUniFlowParams.removeChannel
+ logger.Debugw(ctx, "UniVlanConfigFsm flow clear-up - abort suspended rule-add", log.Fields{
+ "device-id": oFsm.deviceID, "cookie": removeUniFlowParams.cookie})
+ oFsm.mutexFlowParams.RUnlock()
+ removeChannel <- false
+ oFsm.mutexFlowParams.RLock()
+ }
+ }
+ }
+
+ if oFsm.pDeviceHandler != nil {
+ //TODO: to clarify with improved error treatment for VlanConfigFsm (timeout,reception) errors
+ // current code removes the complete FSM including all flow/rule configuration done so far
+ // this might be a bit to much, it would require fully new flow config from rwCore (at least on OnuDown/up)
+ // maybe a more sophisticated approach is possible without clearing the data
+ if oFsm.clearPersistency {
+ //permanently remove possibly stored persistent data
+ if len(oFsm.uniVlanFlowParamsSlice) > 0 {
+ var emptySlice = make([]cmn.UniVlanFlowParams, 0)
+ _ = oFsm.pDeviceHandler.StorePersUniFlowConfig(ctx, oFsm.pOnuUniPort.UniID, &emptySlice, true) //ignore errors
+ }
+ } else {
+ logger.Debugw(ctx, "UniVlanConfigFsm persistency data not cleared", log.Fields{"device-id": oFsm.deviceID})
+ }
+ oFsm.mutexFlowParams.RUnlock()
+ //request removal of 'reference' in the Handler (completely clear the FSM and its data)
+ go oFsm.pDeviceHandler.RemoveVlanFilterFsm(ctx, oFsm.pOnuUniPort)
+ return
+ }
+ oFsm.mutexFlowParams.RUnlock()
+}
+
+func (oFsm *UniVlanConfigFsm) processOmciVlanMessages(ctx context.Context) { //ctx context.Context?
+ logger.Debugw(ctx, "Start UniVlanConfigFsm Msg processing", log.Fields{"for device-id": oFsm.deviceID})
+loop:
+ for {
+ // case <-ctx.Done():
+ // logger.Info(ctx,"MibSync Msg", log.Fields{"Message handling canceled via context for device-id": oFsm.deviceID})
+ // break loop
+ message, ok := <-oFsm.PAdaptFsm.CommChan
+ if !ok {
+ logger.Info(ctx, "UniVlanConfigFsm Rx Msg - could not read from channel", log.Fields{"device-id": oFsm.deviceID})
+ // but then we have to ensure a restart of the FSM as well - as exceptional procedure
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ break loop
+ }
+ logger.Debugw(ctx, "UniVlanConfigFsm Rx Msg", log.Fields{"device-id": oFsm.deviceID})
+
+ switch message.Type {
+ case cmn.TestMsg:
+ msg, _ := message.Data.(cmn.TestMessage)
+ if msg.TestMessageVal == cmn.AbortMessageProcessing {
+ logger.Infow(ctx, "UniVlanConfigFsm abort ProcessMsg", log.Fields{"for device-id": oFsm.deviceID})
+ break loop
+ }
+ logger.Warnw(ctx, "UniVlanConfigFsm unknown TestMessage", log.Fields{"device-id": oFsm.deviceID, "MessageVal": msg.TestMessageVal})
+ case cmn.OMCI:
+ msg, _ := message.Data.(cmn.OmciMessage)
+ oFsm.handleOmciVlanConfigMessage(ctx, msg)
+ default:
+ logger.Warn(ctx, "UniVlanConfigFsm Rx unknown message", log.Fields{"device-id": oFsm.deviceID,
+ "message.Type": message.Type})
+ }
+ }
+ logger.Infow(ctx, "End UniVlanConfigFsm Msg processing", log.Fields{"device-id": oFsm.deviceID})
+}
+
+func (oFsm *UniVlanConfigFsm) handleOmciVlanConfigMessage(ctx context.Context, msg cmn.OmciMessage) {
+ logger.Debugw(ctx, "Rx OMCI UniVlanConfigFsm Msg", log.Fields{"device-id": oFsm.deviceID,
+ "msgType": msg.OmciMsg.MessageType})
+
+ switch msg.OmciMsg.MessageType {
+ case omci.CreateResponseType:
+ { // had to shift that to a method to cope with StaticCodeAnalysis restrictions :-(
+ if err := oFsm.handleOmciCreateResponseMessage(ctx, msg.OmciPacket); err != nil {
+ logger.Warnw(ctx, "CreateResponse handling aborted", log.Fields{"err": err})
+ return
+ }
+ } //CreateResponseType
+ case omci.SetResponseType:
+ { //leave that here as direct code as most often used
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
+ if msgLayer == nil {
+ logger.Errorw(ctx, "Omci Msg layer could not be detected for SetResponse",
+ log.Fields{"device-id": oFsm.deviceID})
+ return
+ }
+ msgObj, msgOk := msgLayer.(*omci.SetResponse)
+ if !msgOk {
+ logger.Errorw(ctx, "Omci Msg layer could not be assigned for SetResponse",
+ log.Fields{"device-id": oFsm.deviceID})
+ return
+ }
+ logger.Debugw(ctx, "UniVlanConfigFsm SetResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
+ if msgObj.Result != me.Success {
+ logger.Errorw(ctx, "UniVlanConfigFsm Omci SetResponse Error - later: drive FSM to abort state ?",
+ log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
+ // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+ return
+ }
+ oFsm.mutexPLastTxMeInstance.RLock()
+ if oFsm.pLastTxMeInstance != nil {
+ if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+ switch oFsm.pLastTxMeInstance.GetName() {
+ case "VlanTaggingFilterData", "ExtendedVlanTaggingOperationConfigurationData", "MulticastOperationsProfile", "GemPortNetworkCtp":
+ { // let the MultiEntity config proceed by stopping the wait function
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ oFsm.omciMIdsResponseReceived <- true
+ return
+ }
+ default:
+ {
+ logger.Warnw(ctx, "Unsupported ME name received!",
+ log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+ }
+ }
+ }
+ } else {
+ logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
+ }
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ } //SetResponseType
+ case omci.DeleteResponseType:
+ { // had to shift that to a method to cope with StaticCodeAnalysis restrictions :-(
+ if err := oFsm.handleOmciDeleteResponseMessage(ctx, msg.OmciPacket); err != nil {
+ logger.Warnw(ctx, "DeleteResponse handling aborted", log.Fields{"err": err})
+ return
+ }
+ } //DeleteResponseType
+ default:
+ {
+ logger.Errorw(ctx, "Rx OMCI unhandled MsgType",
+ log.Fields{"omciMsgType": msg.OmciMsg.MessageType, "device-id": oFsm.deviceID})
+ return
+ }
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) handleOmciCreateResponseMessage(ctx context.Context, apOmciPacket *gp.Packet) error {
+ msgLayer := (*apOmciPacket).Layer(omci.LayerTypeCreateResponse)
+ if msgLayer == nil {
+ logger.Errorw(ctx, "Omci Msg layer could not be detected for CreateResponse",
+ log.Fields{"device-id": oFsm.deviceID})
+ return fmt.Errorf("omci msg layer could not be detected for CreateResponse for device-id %x",
+ oFsm.deviceID)
+ }
+ msgObj, msgOk := msgLayer.(*omci.CreateResponse)
+ if !msgOk {
+ logger.Errorw(ctx, "Omci Msg layer could not be assigned for CreateResponse",
+ log.Fields{"device-id": oFsm.deviceID})
+ return fmt.Errorf("omci msg layer could not be assigned for CreateResponse for device-id %x",
+ oFsm.deviceID)
+ }
+ logger.Debugw(ctx, "UniVlanConfigFsm CreateResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
+ if msgObj.Result != me.Success && msgObj.Result != me.InstanceExists {
+ logger.Errorw(ctx, "Omci CreateResponse Error - later: drive FSM to abort state ?", log.Fields{"device-id": oFsm.deviceID,
+ "Error": msgObj.Result})
+ // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+ return fmt.Errorf("omci CreateResponse Error for device-id %x",
+ oFsm.deviceID)
+ }
+ oFsm.mutexPLastTxMeInstance.RLock()
+ if oFsm.pLastTxMeInstance != nil {
+ if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+ // to satisfy StaticCodeAnalysis I had to move the small processing into a separate method :-(
+ switch oFsm.pLastTxMeInstance.GetName() {
+ case "VlanTaggingFilterData", "MulticastOperationsProfile",
+ "MulticastSubscriberConfigInfo", "MacBridgePortConfigurationData",
+ "ExtendedVlanTaggingOperationConfigurationData", "TrafficDescriptor":
+ {
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ if oFsm.PAdaptFsm.PFsm.Current() == VlanStConfigVtfd {
+ // Only if CreateResponse is received from first flow entry - let the FSM proceed ...
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvRxConfigVtfd)
+ } else { // let the MultiEntity config proceed by stopping the wait function
+ oFsm.omciMIdsResponseReceived <- true
+ }
+ return nil
+ }
+ default:
+ {
+ logger.Warnw(ctx, "Unsupported ME name received!",
+ log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+ }
+ }
+ }
+ } else {
+ logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
+ }
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ return nil
+}
+
+func (oFsm *UniVlanConfigFsm) handleOmciDeleteResponseMessage(ctx context.Context, apOmciPacket *gp.Packet) error {
+ msgLayer := (*apOmciPacket).Layer(omci.LayerTypeDeleteResponse)
+ if msgLayer == nil {
+ logger.Errorw(ctx, "UniVlanConfigFsm - Omci Msg layer could not be detected for DeleteResponse",
+ log.Fields{"device-id": oFsm.deviceID})
+ return fmt.Errorf("omci msg layer could not be detected for DeleteResponse for device-id %x",
+ oFsm.deviceID)
+ }
+ msgObj, msgOk := msgLayer.(*omci.DeleteResponse)
+ if !msgOk {
+ logger.Errorw(ctx, "UniVlanConfigFsm - Omci Msg layer could not be assigned for DeleteResponse",
+ log.Fields{"device-id": oFsm.deviceID})
+ return fmt.Errorf("omci msg layer could not be assigned for DeleteResponse for device-id %x",
+ oFsm.deviceID)
+ }
+ logger.Debugw(ctx, "UniVlanConfigFsm DeleteResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
+ if msgObj.Result != me.Success {
+ logger.Errorw(ctx, "UniVlanConfigFsm - Omci DeleteResponse Error - later: drive FSM to abort state ?",
+ log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
+ // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+ return fmt.Errorf("omci DeleteResponse Error for device-id %x",
+ oFsm.deviceID)
+ }
+ oFsm.mutexPLastTxMeInstance.RLock()
+ if oFsm.pLastTxMeInstance != nil {
+ if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+ switch oFsm.pLastTxMeInstance.GetName() {
+ case "VlanTaggingFilterData", "ExtendedVlanTaggingOperationConfigurationData", "TrafficDescriptor":
+ { // let the MultiEntity config proceed by stopping the wait function
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ oFsm.omciMIdsResponseReceived <- true
+ return nil
+ }
+ default:
+ {
+ logger.Warnw(ctx, "Unsupported ME name received!",
+ log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+ }
+ }
+ }
+ } else {
+ logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
+ }
+ oFsm.mutexPLastTxMeInstance.RUnlock()
+ return nil
+}
+
+func (oFsm *UniVlanConfigFsm) performConfigEvtocdEntries(ctx context.Context, aFlowEntryNo uint8) error {
+ oFsm.mutexFlowParams.RLock()
+ evtocdID := oFsm.evtocdID
+ oFsm.mutexFlowParams.RUnlock()
+
+ if aFlowEntryNo == 0 {
+ // EthType set only at first flow element
+ // EVTOCD ME is expected to exist at this point already from MIB-Download (with AssociationType/Pointer)
+ // we need to extend the configuration by EthType definition and, to be sure, downstream 'inverse' mode
+ logger.Debugw(ctx, "UniVlanConfigFsm Tx Create::EVTOCD", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(evtocdID), 16),
+ "i/oEthType": strconv.FormatInt(int64(cDefaultTpid), 16),
+ "device-id": oFsm.deviceID})
+ associationType := 2 // default to UniPPTP
+ if oFsm.pOnuUniPort.PortType == cmn.UniVEIP {
+ associationType = 10
+ }
+ // Create the EVTOCD ME
+ meParams := me.ParamData{
+ EntityID: evtocdID,
+ Attributes: me.AttributeValueMap{
+ "AssociationType": uint8(associationType),
+ "AssociatedMePointer": oFsm.pOnuUniPort.EntityID,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendCreateEvtocdVar(context.TODO(), oFsm.pDeviceHandler.GetOmciTimeout(),
+ true, oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "CreateEvtocdVar create failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("evtocd instance create failed %s, error %s", oFsm.deviceID, err)
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Evtocd create failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("evtocd create failed %s, error %s", oFsm.deviceID, err)
+ }
+
+ // Set the EVTOCD ME default params
+ meParams = me.ParamData{
+ EntityID: evtocdID,
+ Attributes: me.AttributeValueMap{
+ "InputTpid": uint16(cDefaultTpid), //could be possibly retrieved from flow config one day, by now just like py-code base
+ "OutputTpid": uint16(cDefaultTpid), //could be possibly retrieved from flow config one day, by now just like py-code base
+ "DownstreamMode": uint8(cDefaultDownstreamMode),
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err = oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("evtocd instance set failed %s, error %s", oFsm.deviceID, err)
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Evtocd set TPID failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("evtocd set TPID failed %s, error %s", oFsm.deviceID, err)
+ }
+ } //first flow element
+
+ oFsm.mutexFlowParams.RLock()
+ if oFsm.actualUniVlanConfigRule.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+ //transparent transmission required
+ oFsm.mutexFlowParams.RUnlock()
+ logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD single tagged transparent rule", log.Fields{
+ "device-id": oFsm.deviceID})
+ sliceEvtocdRule := make([]uint8, 16)
+ // fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+ cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+ cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+ cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+ cPrioDefaultFilter<<cFilterPrioOffset| // default inner-tag rule
+ cDoNotFilterVid<<cFilterVidOffset| // Do not filter on inner vid
+ cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+ cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
+ 0<<cTreatTTROffset| // Do not pop any tags
+ cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
+ cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+ cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
+ cDoNotAddPrio<<cTreatPrioOffset| // do not add inner tag
+ cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+ cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+
+ meParams := me.ParamData{
+ EntityID: evtocdID,
+ Attributes: me.AttributeValueMap{
+ "ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("evtocd instance set failed %s, error %s", oFsm.deviceID, err)
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Evtocd set transparent singletagged rule failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("evtocd set transparent singletagged rule failed %s, error %s", oFsm.deviceID, err)
+
+ }
+ } else {
+ // according to py-code acceptIncrementalEvto program option decides upon stacking or translation scenario
+ if oFsm.acceptIncrementalEvtoOption {
+ matchPcp := oFsm.actualUniVlanConfigRule.MatchPcp
+ matchVid := oFsm.actualUniVlanConfigRule.MatchVid
+ setPcp := oFsm.actualUniVlanConfigRule.SetPcp
+ setVid := oFsm.actualUniVlanConfigRule.SetVid
+ // this defines VID translation scenario: singletagged->singletagged (if not transparent)
+ logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD single tagged translation rule", log.Fields{
+ "match-pcp": matchPcp, "match-vid": matchVid, "set-pcp": setPcp, "set-vid:": setVid, "device-id": oFsm.deviceID})
+ sliceEvtocdRule := make([]uint8, 16)
+ // fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+ cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+ cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+ cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+ oFsm.actualUniVlanConfigRule.MatchPcp<<cFilterPrioOffset| // either DNFonPrio or ignore tag (default) on innerVLAN
+ oFsm.actualUniVlanConfigRule.MatchVid<<cFilterVidOffset| // either DNFonVid or real filter VID
+ cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+ cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
+ oFsm.actualUniVlanConfigRule.TagsToRemove<<cTreatTTROffset| // either 1 or 0
+ cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
+ cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+ cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
+ oFsm.actualUniVlanConfigRule.SetPcp<<cTreatPrioOffset| // as configured in flow
+ oFsm.actualUniVlanConfigRule.SetVid<<cTreatVidOffset| //as configured in flow
+ cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+ oFsm.mutexFlowParams.RUnlock()
+
+ meParams := me.ParamData{
+ EntityID: evtocdID,
+ Attributes: me.AttributeValueMap{
+ "ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("evtocd instance set failed %s, error %s", oFsm.deviceID, err)
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Evtocd set singletagged translation rule failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("evtocd set singletagged translation rule failed %s, error %s", oFsm.deviceID, err)
+ }
+ } else {
+ //not transparent and not acceptIncrementalEvtoOption untagged/priotagged->singletagged
+ { // just for local var's
+ // this defines stacking scenario: untagged->singletagged
+ logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD untagged->singletagged rule", log.Fields{
+ "device-id": oFsm.deviceID})
+ sliceEvtocdRule := make([]uint8, 16)
+ // fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+ cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+ cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+ cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+ cPrioIgnoreTag<<cFilterPrioOffset| // Not an inner-tag rule
+ cDoNotFilterVid<<cFilterVidOffset| // Do not filter on inner vid
+ cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+ cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
+ 0<<cTreatTTROffset| // Do not pop any tags
+ cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
+ cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+ cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
+ 0<<cTreatPrioOffset| // vlan prio set to 0
+ // (as done in Py code, maybe better option would be setPcp here, which still could be 0?)
+ oFsm.actualUniVlanConfigRule.SetVid<<cTreatVidOffset| // Outer VID don't care
+ cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+
+ oFsm.mutexFlowParams.RUnlock()
+ meParams := me.ParamData{
+ EntityID: evtocdID,
+ Attributes: me.AttributeValueMap{
+ "ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("evtocd instance set failed %s, error %s", oFsm.deviceID, err)
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Evtocd set untagged->singletagged rule failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("evtocd set untagged->singletagged rule failed %s, error %s", oFsm.deviceID, err)
+
+ }
+ } // just for local var's
+ { // just for local var's
+ // this defines 'stacking' scenario: priotagged->singletagged
+ logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD priotagged->singletagged rule", log.Fields{
+ "device-id": oFsm.deviceID})
+ sliceEvtocdRule := make([]uint8, 16)
+ // fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+ cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+ cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+ cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+ cPrioDoNotFilter<<cFilterPrioOffset| // Do not Filter on innerprio
+ 0<<cFilterVidOffset| // filter on inner vid 0 (prioTagged)
+ cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+ cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
+ 1<<cTreatTTROffset| // pop the prio-tag
+ cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
+ cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+ cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
+
+ oFsm.mutexFlowParams.RLock()
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
+ cCopyPrioFromInner<<cTreatPrioOffset| // vlan copy from PrioTag
+ // (as done in Py code, maybe better option would be setPcp here, which still could be PrioCopy?)
+ oFsm.actualUniVlanConfigRule.SetVid<<cTreatVidOffset| // Outer VID as configured
+ cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+ oFsm.mutexFlowParams.RUnlock()
+
+ meParams := me.ParamData{
+ EntityID: evtocdID,
+ Attributes: me.AttributeValueMap{
+ "ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("evtocd instance set failed %s, error %s", oFsm.deviceID, err)
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Evtocd set priotagged->singletagged rule failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("evtocd set priotagged->singletagged rule failed %s, error %s", oFsm.deviceID, err)
+
+ }
+ } //just for local var's
+ }
+ }
+
+ // if Config has been done for all EVTOCD entries let the FSM proceed
+ logger.Debugw(ctx, "EVTOCD set loop finished", log.Fields{"device-id": oFsm.deviceID})
+ oFsm.mutexFlowParams.Lock()
+ oFsm.ConfiguredUniFlow++ // one (more) flow configured
+ oFsm.mutexFlowParams.Unlock()
+ return nil
+}
+
+func (oFsm *UniVlanConfigFsm) removeEvtocdEntries(ctx context.Context, aRuleParams cmn.UniVlanRuleParams) {
+ oFsm.mutexFlowParams.RLock()
+ evtocdID := oFsm.evtocdID
+ oFsm.mutexFlowParams.RUnlock()
+
+ // configured Input/Output TPID is not modified again - no influence if no filter is applied
+ if aRuleParams.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+ //transparent transmission was set
+ //perhaps the config is not needed for removal,
+ // but the specific InnerTpid setting is removed in favor of the real default forwarding rule
+ logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD reset to default single tagged rule", log.Fields{
+ "device-id": oFsm.deviceID})
+ sliceEvtocdRule := make([]uint8, 16)
+ // fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+ cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+ cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+ cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+ cPrioDefaultFilter<<cFilterPrioOffset| // default inner-tag rule
+ cDoNotFilterVid<<cFilterVidOffset| // Do not filter on inner vid
+ cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+ cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
+ 0<<cTreatTTROffset| // Do not pop any tags
+ cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
+ cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+ cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
+ cDoNotAddPrio<<cTreatPrioOffset| // do not add inner tag
+ cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+ cDontCareTpid<<cTreatTpidOffset) // copy TPID and DEI
+
+ meParams := me.ParamData{
+ EntityID: evtocdID,
+ Attributes: me.AttributeValueMap{
+ "ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Evtocd reset singletagged rule failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return
+ }
+ } else {
+ // according to py-code acceptIncrementalEvto program option decides upon stacking or translation scenario
+ oFsm.mutexFlowParams.RLock()
+ if oFsm.acceptIncrementalEvtoOption {
+ oFsm.mutexFlowParams.RUnlock()
+ // this defines VID translation scenario: singletagged->singletagged (if not transparent)
+ logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD clear single tagged translation rule", log.Fields{
+ "device-id": oFsm.deviceID, "match-vlan": aRuleParams.MatchVid})
+ sliceEvtocdRule := make([]uint8, 16)
+ // fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+ cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+ cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+ cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+ aRuleParams.MatchPcp<<cFilterPrioOffset| // either DNFonPrio or ignore tag (default) on innerVLAN
+ aRuleParams.MatchVid<<cFilterVidOffset| // either DNFonVid or real filter VID
+ cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+ cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+ // delete indication for the indicated Filter
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:], 0xFFFFFFFF)
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:], 0xFFFFFFFF)
+
+ meParams := me.ParamData{
+ EntityID: evtocdID,
+ Attributes: me.AttributeValueMap{
+ "ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Evtocd clear singletagged translation rule failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID, "match-vlan": aRuleParams.MatchVid})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return
+ }
+ } else {
+ // VOL-3685
+ // NOTE: With ALPHA ONUs it was seen that just resetting a particular entry in the EVTOCD table
+ // and re-configuring a new entry would not work. The old entry is removed and new entry is created
+ // indeed, but the traffic landing upstream would carry old vlan sometimes.
+ // This is only a WORKAROUND which basically deletes the entire EVTOCD ME and re-creates it again
+ // later when the flow is being re-installed.
+ // Of course this is applicable to case only where single service (or single tcont) is in use and
+ // there is only one service vlan (oFsm.acceptIncrementalEvtoOption is false in this case).
+ // Interstingly this problem has not been observed in multi-tcont (or multi-service) scenario (in
+ // which case the oFsm.acceptIncrementalEvtoOption is set to true).
+ if oFsm.ConfiguredUniFlow == 1 && !oFsm.acceptIncrementalEvtoOption {
+ oFsm.mutexFlowParams.RUnlock()
+ logger.Debugw(ctx, "UniVlanConfigFsm Tx Remove::EVTOCD", log.Fields{"device-id": oFsm.deviceID})
+ // When there are no more EVTOCD vlan configurations on the ONU and acceptIncrementalEvtoOption
+ // is not enabled, delete the EVTOCD ME.
+ meParams := me.ParamData{
+ EntityID: evtocdID,
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendDeleteEvtocd(log.WithSpanFromContext(context.TODO(), ctx),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "DeleteEvtocdVar delete failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Evtocd delete rule failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return
+ }
+ } else {
+ // NOTE : We should ideally never ether this section when oFsm.acceptIncrementalEvtoOption is set to false
+ // This is true for only ATT/DT workflow
+ logger.Debugw(ctx, "UniVlanConfigFsm: Remove EVTOCD set operation",
+ log.Fields{"configured-flow": oFsm.ConfiguredUniFlow, "incremental-evto": oFsm.acceptIncrementalEvtoOption})
+ oFsm.mutexFlowParams.RUnlock()
+ //not transparent and not acceptIncrementalEvtoOption: untagged/priotagged->singletagged
+ { // just for local var's
+ // this defines stacking scenario: untagged->singletagged
+ //TODO!! in theory there could be different rules running in setting different PCP/VID'S
+ // for untagged/priotagged, last rule wins (and remains the only one), maybe that should be
+ // checked already at flow-add (and rejected) - to be observed if such is possible in Voltha
+ // delete now assumes there is only one such rule!
+ logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD reset untagged rule to default", log.Fields{
+ "device-id": oFsm.deviceID})
+ sliceEvtocdRule := make([]uint8, 16)
+ // fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+ cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+ cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+ cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+ cPrioIgnoreTag<<cFilterPrioOffset| // Not an inner-tag rule
+ cDoNotFilterVid<<cFilterVidOffset| // Do not filter on inner vid
+ cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+ cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
+ 0<<cTreatTTROffset| // Do not pop any tags
+ cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
+ cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+ cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
+ cDoNotAddPrio<<cTreatPrioOffset| // do not add inner tag
+ cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+ cDontCareTpid<<cTreatTpidOffset) // copy TPID and DEI
+
+ meParams := me.ParamData{
+ EntityID: evtocdID,
+ Attributes: me.AttributeValueMap{
+ "ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(context.TODO(),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Evtocd reset untagged rule to default failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return
+ }
+ } // just for local var's
+ { // just for local var's
+ // this defines 'stacking' scenario: priotagged->singletagged
+ logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD delete priotagged rule", log.Fields{
+ "device-id": oFsm.deviceID})
+ sliceEvtocdRule := make([]uint8, 16)
+ // fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+ cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+ cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+ cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+ cPrioDoNotFilter<<cFilterPrioOffset| // Do not Filter on innerprio
+ 0<<cFilterVidOffset| // filter on inner vid 0 (prioTagged)
+ cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+ cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+ // delete indication for the indicated Filter
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:], 0xFFFFFFFF)
+ binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:], 0xFFFFFFFF)
+
+ meParams := me.ParamData{
+ EntityID: evtocdID,
+ Attributes: me.AttributeValueMap{
+ "ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Evtocd delete priotagged rule failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return
+ }
+ }
+ } //just for local var's
+ }
+ }
+ // if Config has been done for all EVTOCD entries let the FSM proceed
+ logger.Debugw(ctx, "EVTOCD filter remove loop finished", log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvRemFlowDone, aRuleParams.TpID)
+}
+
+func (oFsm *UniVlanConfigFsm) waitforOmciResponse(ctx context.Context) error {
+ oFsm.mutexIsAwaitingResponse.Lock()
+ if oFsm.isCanceled {
+ // FSM already canceled before entering wait
+ logger.Debugw(ctx, "UniVlanConfigFsm wait-for-multi-entity-response aborted (on enter)", log.Fields{"for device-id": oFsm.deviceID})
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ return fmt.Errorf(cmn.CErrWaitAborted)
+ }
+ oFsm.isAwaitingResponse = true
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ select {
+ // maybe be also some outside cancel (but no context modeled for the moment ...)
+ // case <-ctx.Done():
+ // logger.Infow(ctx,"LockState-bridge-init message reception canceled", log.Fields{"for device-id": oFsm.deviceID})
+ case <-time.After(oFsm.pOmciCC.GetMaxOmciTimeoutWithRetries() * time.Second): //AS FOR THE OTHER OMCI FSM's
+ logger.Warnw(ctx, "UniVlanConfigFsm multi entity timeout", log.Fields{"for device-id": oFsm.deviceID})
+ oFsm.mutexIsAwaitingResponse.Lock()
+ oFsm.isAwaitingResponse = false
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ return fmt.Errorf("uniVlanConfigFsm multi entity timeout %s", oFsm.deviceID)
+ case success := <-oFsm.omciMIdsResponseReceived:
+ if success {
+ logger.Debugw(ctx, "UniVlanConfigFsm multi entity response received", log.Fields{"for device-id": oFsm.deviceID})
+ oFsm.mutexIsAwaitingResponse.Lock()
+ oFsm.isAwaitingResponse = false
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ return nil
+ }
+ // waiting was aborted (probably on external request)
+ logger.Debugw(ctx, "UniVlanConfigFsm wait-for-multi-entity-response aborted", log.Fields{"for device-id": oFsm.deviceID})
+ oFsm.mutexIsAwaitingResponse.Lock()
+ oFsm.isAwaitingResponse = false
+ oFsm.mutexIsAwaitingResponse.Unlock()
+ return fmt.Errorf(cmn.CErrWaitAborted)
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) performSettingMulticastME(ctx context.Context, tpID uint8, multicastGemPortID uint16, vlanID uint32) error {
+ logger.Debugw(ctx, "Setting Multicast MEs", log.Fields{"device-id": oFsm.deviceID, "tpID": tpID,
+ "multicastGemPortID": multicastGemPortID, "vlanID": vlanID})
+ errCreateMOP := oFsm.performCreatingMulticastOperationProfile(ctx)
+ if errCreateMOP != nil {
+ logger.Errorw(ctx, "MulticastOperationProfile create failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s, error %s", oFsm.deviceID, errCreateMOP)
+ }
+
+ errSettingMOP := oFsm.performSettingMulticastOperationProfile(ctx, multicastGemPortID, vlanID)
+ if errSettingMOP != nil {
+ logger.Errorw(ctx, "MulticastOperationProfile setting failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s, error %s", oFsm.deviceID, errSettingMOP)
+ }
+
+ errCreateMSCI := oFsm.performCreatingMulticastSubscriberConfigInfo(ctx)
+ if errCreateMSCI != nil {
+ logger.Errorw(ctx, "MulticastOperationProfile setting failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s, error %s", oFsm.deviceID, errCreateMSCI)
+ }
+ macBpCdEID, errMacBpCdEID := cmn.GenerateMcastANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo))
+ if errMacBpCdEID != nil {
+ logger.Errorw(ctx, "MulticastMacBridgePortConfigData entity id generation failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("generateMcastANISideMBPCDEID responseError %s, error %s", oFsm.deviceID, errMacBpCdEID)
+
+ }
+ logger.Debugw(ctx, "UniVlanConfigFsm set macBpCdEID for mcast", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(macBpCdEID), 16), "macBpNo": oFsm.pOnuUniPort.MacBpNo,
+ "in state": oFsm.PAdaptFsm.PFsm.Current(), "device-id": oFsm.deviceID})
+ meParams := me.ParamData{
+ EntityID: macBpCdEID,
+ Attributes: me.AttributeValueMap{
+ "BridgeIdPointer": cmn.MacBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.MacBpNo),
+ "PortNum": 0xf0, //fixed unique ANI side indication
+ "TpType": 6, //MCGemIWTP
+ "TpPointer": multicastGemPortID,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendCreateMBPConfigDataVar(context.TODO(),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true, oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "MBPConfigDataVar create failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("creatingMulticastSubscriberConfigInfo createError #{oFsm.deviceID}, error #{err}")
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "CreateMBPConfigData failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID, "MBPConfigDataID": cmn.MacBridgeServiceProfileEID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s, error %s", oFsm.deviceID, err)
+ }
+
+ // ==> Start creating VTFD for mcast vlan
+
+ // This attribute uniquely identifies each instance of this managed entity. Through an identical ID,
+ // this managed entity is implicitly linked to an instance of the MAC bridge port configuration data ME.
+ mcastVtfdID := macBpCdEID
+
+ logger.Debugw(ctx, "UniVlanConfigFsm set VTFD for mcast", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(mcastVtfdID), 16), "mcastVlanID": vlanID,
+ "in state": oFsm.PAdaptFsm.PFsm.Current(), "device-id": oFsm.deviceID})
+ vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization
+
+ // FIXME: VOL-3685: Issues with resetting a table entry in EVTOCD ME
+ // VTFD has to be created afresh with a new entity ID that has the same entity ID as the MBPCD ME for every
+ // new vlan associated with a different TP.
+ vtfdFilterList[0] = uint16(vlanID)
+
+ meParams = me.ParamData{
+ EntityID: mcastVtfdID,
+ Attributes: me.AttributeValueMap{
+ "VlanFilterList": vtfdFilterList,
+ "ForwardOperation": uint8(0x10), //VID investigation
+ "NumberOfEntries": oFsm.numVlanFilterEntries,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err = oFsm.pOmciCC.SendCreateVtfdVar(context.TODO(),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true, oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "CreateVtfdVar create failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("createMcastVlanFilterData creationError %s, error %s", oFsm.deviceID, err)
+ }
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "CreateMcastVlanFilterData failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID, "mcastVtfdID": mcastVtfdID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("createMcastVlanFilterData responseError %s, error %s", oFsm.deviceID, err)
+ }
+
+ return nil
+}
+
+func (oFsm *UniVlanConfigFsm) performCreatingMulticastSubscriberConfigInfo(ctx context.Context) error {
+ instID, err := cmn.GenerateUNISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo))
+ if err != nil {
+ logger.Errorw(ctx, "error generrating me instance id",
+ log.Fields{"device-id": oFsm.deviceID, "error": err})
+ return err
+ }
+ logger.Debugw(ctx, "UniVlanConfigFsm create MulticastSubscriberConfigInfo",
+ log.Fields{"device-id": oFsm.deviceID, "EntityId": instID})
+ meParams := me.ParamData{
+ EntityID: instID,
+ Attributes: me.AttributeValueMap{
+ "MeType": 0,
+ //Direct reference to the Operation profile
+ //TODO ANI side used on UNI side, not the clearest option.
+ "MulticastOperationsProfilePointer": instID,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendCreateMulticastSubConfigInfoVar(context.TODO(),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "CreateMulticastSubConfigInfoVar create failed, aborting UniVlanConfigFSM!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("creatingMulticastSubscriberConfigInfo interface creationError %s, error %s",
+ oFsm.deviceID, err)
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "CreateMulticastSubConfigInfo create failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID, "MulticastSubConfigInfo": instID})
+ return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s", oFsm.deviceID)
+ }
+ return nil
+}
+
+func (oFsm *UniVlanConfigFsm) performCreatingMulticastOperationProfile(ctx context.Context) error {
+ instID, err := cmn.GenerateUNISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo))
+ if err != nil {
+ logger.Errorw(ctx, "error generating me instance id",
+ log.Fields{"device-id": oFsm.deviceID, "error": err})
+ return err
+ }
+ logger.Debugw(ctx, "UniVlanConfigFsm create MulticastOperationProfile",
+ log.Fields{"device-id": oFsm.deviceID, "EntityId": instID})
+ meParams := me.ParamData{
+ EntityID: instID,
+ Attributes: me.AttributeValueMap{
+ "IgmpVersion": 2,
+ "IgmpFunction": 0,
+ //0 means false
+ "ImmediateLeave": 0,
+ "Robustness": 2,
+ "QuerierIp": 0,
+ "QueryInterval": 125,
+ "QuerierMaxResponseTime": 100,
+ "LastMemberResponseTime": 10,
+ //0 means false
+ "UnauthorizedJoinBehaviour": 0,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendCreateMulticastOperationProfileVar(context.TODO(),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "CreateMulticastOperationProfileVar create failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("createMulticastOperationProfileVar responseError %s, error %s", oFsm.deviceID, err)
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "CreateMulticastOperationProfile create failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID, "MulticastOperationProfileID": instID})
+ return fmt.Errorf("createMulticastOperationProfile responseError %s, error %s", oFsm.deviceID, err)
+ }
+ return nil
+}
+
+func (oFsm *UniVlanConfigFsm) performSettingMulticastOperationProfile(ctx context.Context, multicastGemPortID uint16, vlanID uint32) error {
+ instID, err := cmn.GenerateUNISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo))
+ if err != nil {
+ logger.Errorw(ctx, "error generating me instance id",
+ log.Fields{"device-id": oFsm.deviceID, "error": err})
+ return err
+ }
+ logger.Debugw(ctx, "UniVlanConfigFsm set MulticastOperationProfile",
+ log.Fields{"device-id": oFsm.deviceID, "EntityId": instID})
+ //TODO check that this is correct
+ // Table control
+ //setCtrl = 1
+ //rowPartId = 0
+ //test = 0
+ //rowKey = 0
+ tableCtrlStr := "0100000000000000"
+ tableCtrl := cmn.AsByteSlice(tableCtrlStr)
+ dynamicAccessCL := make([]uint8, 24)
+ copy(dynamicAccessCL, tableCtrl)
+ //Multicast GemPortId
+ binary.BigEndian.PutUint16(dynamicAccessCL[2:], multicastGemPortID)
+ // python version waits for installation of flows, see line 723 onward of
+ // brcm_openomci_onu_handler.py
+ binary.BigEndian.PutUint16(dynamicAccessCL[4:], uint16(vlanID))
+ //Source IP all to 0
+ binary.BigEndian.PutUint32(dynamicAccessCL[6:], cmn.IPToInt32(net.IPv4(0, 0, 0, 0)))
+ //TODO start and end are hardcoded, get from TP
+ // Destination IP address start of range
+ binary.BigEndian.PutUint32(dynamicAccessCL[10:], cmn.IPToInt32(net.IPv4(225, 0, 0, 0)))
+ // Destination IP address end of range
+ binary.BigEndian.PutUint32(dynamicAccessCL[14:], cmn.IPToInt32(net.IPv4(239, 255, 255, 255)))
+ //imputed group bandwidth
+ binary.BigEndian.PutUint16(dynamicAccessCL[18:], 0)
+
+ meParams := me.ParamData{
+ EntityID: instID,
+ Attributes: me.AttributeValueMap{
+ "DynamicAccessControlListTable": dynamicAccessCL,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetMulticastOperationProfileVar(context.TODO(),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true,
+ oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "SetMulticastOperationProfileVar set failed, aborting UniVlanConfigFsm!",
+ log.Fields{"device-id": oFsm.deviceID})
+ _ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+ return fmt.Errorf("setMulticastOperationProfile responseError %s, error %s", oFsm.deviceID, err)
+ }
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ //verify response
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "CreateMulticastOperationProfile create failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.deviceID, "MulticastOperationProfileID": instID})
+ return fmt.Errorf("createMulticastOperationProfile responseError %s, error %s", oFsm.deviceID, err)
+ }
+ return nil
+}
+
+func (oFsm *UniVlanConfigFsm) createTrafficDescriptor(ctx context.Context, aMeter *voltha.OfpMeterConfig,
+ tpID uint8, uniID uint8, gemPortID uint16) error {
+ logger.Infow(ctx, "Starting create traffic descriptor", log.Fields{"device-id": oFsm.deviceID, "uniID": uniID, "tpID": tpID})
+ // uniTPKey generate id to Traffic Descriptor ME. We need to create two of them. They should be unique. Because of that
+ // I created unique TD ID by flow direction.
+ // TODO! Traffic descriptor ME ID will check
+ trafficDescriptorID := gemPortID
+ if aMeter == nil {
+ return fmt.Errorf("meter not found %s", oFsm.deviceID)
+ }
+ trafficShapingInfo, err := meters.GetTrafficShapingInfo(ctx, aMeter)
+ if err != nil {
+ logger.Errorw(ctx, "Traffic Shaping Info get failed", log.Fields{"device-id": oFsm.deviceID})
+ return err
+ }
+ cir := trafficShapingInfo.Cir + trafficShapingInfo.Gir
+ cbs := trafficShapingInfo.Cbs
+ pir := trafficShapingInfo.Pir
+ pbs := trafficShapingInfo.Pbs
+
+ logger.Infow(ctx, "cir-pir-cbs-pbs", log.Fields{"device-id": oFsm.deviceID, "cir": cir, "pir": pir, "cbs": cbs, "pbs": pbs})
+ meParams := me.ParamData{
+ EntityID: trafficDescriptorID,
+ Attributes: me.AttributeValueMap{
+ "Cir": cir,
+ "Pir": pir,
+ "Cbs": cbs,
+ "Pbs": pbs,
+ "ColourMode": 1,
+ "IngressColourMarking": 3,
+ "EgressColourMarking": 3,
+ "MeterType": 1,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, errCreateTD := oFsm.pOmciCC.SendCreateTDVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(),
+ true, oFsm.PAdaptFsm.CommChan, meParams)
+ if errCreateTD != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "Traffic Descriptor create failed", log.Fields{"device-id": oFsm.deviceID})
+ return err
+ }
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Traffic Descriptor create failed, aborting VlanConfig FSM!", log.Fields{"device-id": oFsm.deviceID})
+ return err
+ }
+
+ // Note: in the below request the gemport entity id is same as the gemport id and the traffic descriptor entity id is also same as gemport id
+ err = oFsm.setTrafficDescriptorToGemPortNWCTP(ctx, gemPortID, gemPortID)
+ if err != nil {
+ logger.Errorw(ctx, "Traffic Descriptor set failed to Gem Port Network CTP, aborting VlanConfig FSM!", log.Fields{"device-id": oFsm.deviceID})
+ return err
+ }
+ logger.Infow(ctx, "Set TD Info to GemPortNWCTP successfully", log.Fields{"device-id": oFsm.deviceID, "gem-port-id": gemPortID, "td-id": trafficDescriptorID})
+
+ return nil
+}
+
+func (oFsm *UniVlanConfigFsm) setTrafficDescriptorToGemPortNWCTP(ctx context.Context, gemPortEntityID uint16, trafficDescriptorEntityID uint16) error {
+ logger.Debugw(ctx, "Starting Set Traffic Descriptor to GemPortNWCTP",
+ log.Fields{"device-id": oFsm.deviceID, "gem-port-entity-id": gemPortEntityID, "traffic-descriptor-entity-id": trafficDescriptorEntityID})
+ meParams := me.ParamData{
+ EntityID: gemPortEntityID,
+ Attributes: me.AttributeValueMap{
+ "TrafficDescriptorProfilePointerForUpstream": trafficDescriptorEntityID,
+ },
+ }
+ oFsm.mutexPLastTxMeInstance.Lock()
+ meInstance, err := oFsm.pOmciCC.SendSetGemNCTPVar(log.WithSpanFromContext(context.TODO(), ctx),
+ oFsm.pDeviceHandler.GetOmciTimeout(), true, oFsm.PAdaptFsm.CommChan, meParams)
+ if err != nil {
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ logger.Errorw(ctx, "GemNCTP set failed", log.Fields{"device-id": oFsm.deviceID})
+ return err
+ }
+ oFsm.pLastTxMeInstance = meInstance
+ oFsm.mutexPLastTxMeInstance.Unlock()
+ err = oFsm.waitforOmciResponse(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "Upstream Traffic Descriptor set failed, aborting VlanConfig FSM!", log.Fields{"device-id": oFsm.deviceID})
+ return err
+ }
+ return nil
+}
+
+// IsFlowRemovePending returns true if there are pending flows to remove, else false.
+func (oFsm *UniVlanConfigFsm) IsFlowRemovePending(aFlowDeleteChannel chan<- bool) bool {
+ oFsm.mutexFlowParams.Lock()
+ defer oFsm.mutexFlowParams.Unlock()
+ if len(oFsm.uniRemoveFlowsSlice) > 0 {
+ //flow removal is still ongoing/pending
+ oFsm.signalOnFlowDelete = true
+ oFsm.flowDeleteChannel = aFlowDeleteChannel
+ return true
+ }
+ return false
+}
+
+func (oFsm *UniVlanConfigFsm) reconcileVlanFilterList(ctx context.Context, aSetVid uint16) {
+ // VOL-4342 - reconcile vlanFilterList[] for possible later flow removal
+ if aSetVid == uint16(of.OfpVlanId_OFPVID_PRESENT) {
+ logger.Debugw(ctx, "reconciling - transparent setup: no VTFD config was required",
+ log.Fields{"device-id": oFsm.deviceID})
+ } else {
+ oFsm.vlanFilterList[oFsm.numVlanFilterEntries] = aSetVid
+ logger.Debugw(ctx, "reconciling - Vid of VTFD stored in list", log.Fields{
+ "index": oFsm.numVlanFilterEntries,
+ "vid": strconv.FormatInt(int64(oFsm.vlanFilterList[oFsm.numVlanFilterEntries]), 16),
+ "device-id": oFsm.deviceID})
+ oFsm.numVlanFilterEntries++
+ }
+}