blob: f83d35c3ea8bc668eda95c0aa9f7bb0f78ca8b91 [file] [log] [blame]
/*
* Copyright 2018-2024 Open Networking Foundation (ONF) and the ONF Contributors
* 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 devices
import (
"bytes"
"fmt"
"sync"
"github.com/looplab/fsm"
"github.com/opencord/bbsim/internal/common"
"github.com/opencord/voltha-protos/v5/go/openolt"
log "github.com/sirupsen/logrus"
)
var ponLogger = log.WithFields(log.Fields{
"module": "PON",
})
type AllocIDVal struct {
OnuSn *openolt.SerialNumber
AllocID uint16
}
type AllocIDKey struct {
PonID uint32
OnuID uint32
EntityID uint16
}
type PonPort struct {
// BBSIM Internals
ID uint32
Technology common.PonTechnology
NumOnu int
Onus []*Onu
Olt *OltDevice
PacketCount uint64
InternalState *fsm.FSM
// PON Attributes
OperState *fsm.FSM
Type string
// Allocated resources
// Some resources (eg: OnuId, AllocId and GemPorts) have to be unique per PON port
// we are keeping a list so that we can throw an error in cases we receive duplicates
AllocatedGemPorts map[uint16]*openolt.SerialNumber
allocatedGemPortsLock sync.RWMutex
AllocatedOnuIds map[uint32]*openolt.SerialNumber
allocatedOnuIdsLock sync.RWMutex
AllocatedAllocIds map[AllocIDKey]*AllocIDVal // key is AllocIDKey
allocatedAllocIdsLock sync.RWMutex
}
func (p *PonPort) GetAllOnus() []*Onu {
return p.Onus
}
// CreatePonPort creates pon port object
func CreatePonPort(olt *OltDevice, id uint32, tech common.PonTechnology) *PonPort {
ponPort := PonPort{
NumOnu: olt.NumOnuPerPon,
ID: id,
Technology: tech,
Type: "pon",
Olt: olt,
Onus: []*Onu{},
AllocatedGemPorts: make(map[uint16]*openolt.SerialNumber),
AllocatedOnuIds: make(map[uint32]*openolt.SerialNumber),
AllocatedAllocIds: make(map[AllocIDKey]*AllocIDVal),
}
ponPort.InternalState = fsm.NewFSM(
"created",
fsm.Events{
{Name: "enable", Src: []string{"created", "disabled"}, Dst: "enabled"},
{Name: "disable", Src: []string{"enabled"}, Dst: "disabled"},
},
fsm.Callbacks{
"enter_enabled": func(e *fsm.Event) {
ponLogger.WithFields(log.Fields{
"ID": ponPort.ID,
}).Debugf("Changing PON Port InternalState from %s to %s", e.Src, e.Dst)
if e.Src == "created" {
if olt.ControlledActivation == Default || olt.ControlledActivation == OnlyPON {
for _, onu := range ponPort.Onus {
if err := onu.InternalState.Event(OnuTxInitialize); err != nil {
ponLogger.Errorf("Error initializing ONU: %v", err)
continue
}
if err := onu.InternalState.Event(OnuTxDiscover); err != nil {
ponLogger.Errorf("Error discover ONU: %v", err)
}
}
}
} else if e.Src == "disabled" {
if ponPort.Olt.ControlledActivation == OnlyONU || ponPort.Olt.ControlledActivation == Both {
// if ONUs are manually activated then only initialize them
for _, onu := range ponPort.Onus {
if err := onu.InternalState.Event(OnuTxInitialize); err != nil {
ponLogger.WithFields(log.Fields{
"Err": err,
"OnuSn": onu.Sn(),
"IntfId": onu.PonPortID,
}).Error("Error initializing ONU")
continue
}
}
} else {
for _, onu := range ponPort.Onus {
if onu.InternalState.Current() == OnuStatePonDisabled {
if err := onu.InternalState.Event(OnuTxEnable); err != nil {
ponLogger.WithFields(log.Fields{
"Err": err,
"OnuSn": onu.Sn(),
"IntfId": onu.PonPortID,
}).Error("Error enabling ONU")
}
} else if onu.InternalState.Current() == OnuStateDisabled {
if err := onu.InternalState.Event(OnuTxInitialize); err != nil {
ponLogger.WithFields(log.Fields{
"Err": err,
"OnuSn": onu.Sn(),
"IntfId": onu.PonPortID,
}).Error("Error initializing ONU")
continue
}
if err := onu.InternalState.Event(OnuTxDiscover); err != nil {
ponLogger.WithFields(log.Fields{
"Err": err,
"OnuSn": onu.Sn(),
"IntfId": onu.PonPortID,
}).Error("Error discovering ONU")
}
} else if onu.InternalState.Current() == OnuStateInitialized {
if err := onu.InternalState.Event(OnuTxDiscover); err != nil {
ponLogger.WithFields(log.Fields{
"Err": err,
"OnuSn": onu.Sn(),
"IntfId": onu.PonPortID,
}).Error("Error discovering ONU")
}
} else {
// this is to loudly report unexpected states in order to address them
ponLogger.WithFields(log.Fields{
"OnuSn": onu.Sn(),
"IntfId": onu.PonPortID,
"InternalState": onu.InternalState.Current(),
}).Error("Unexpected ONU state in PON enabling")
}
}
}
}
},
"enter_disabled": func(e *fsm.Event) {
for _, onu := range ponPort.Onus {
if onu.InternalState.Current() == OnuStateInitialized || onu.InternalState.Current() == OnuStateDisabled {
continue
}
if err := onu.InternalState.Event(OnuTxPonDisable); err != nil {
ponLogger.Errorf("Failed to move ONU in %s states: %v", OnuStatePonDisabled, err)
}
}
},
},
)
ponPort.OperState = fsm.NewFSM(
"down",
fsm.Events{
{Name: "enable", Src: []string{"down"}, Dst: "up"},
{Name: "disable", Src: []string{"up"}, Dst: "down"},
},
fsm.Callbacks{
"enter_up": func(e *fsm.Event) {
ponLogger.WithFields(log.Fields{
"ID": ponPort.ID,
}).Debugf("Changing PON Port OperState from %s to %s", e.Src, e.Dst)
olt.sendPonIndication(ponPort.ID)
},
"enter_down": func(e *fsm.Event) {
ponLogger.WithFields(log.Fields{
"ID": ponPort.ID,
}).Debugf("Changing PON Port OperState from %s to %s", e.Src, e.Dst)
olt.sendPonIndication(ponPort.ID)
},
},
)
return &ponPort
}
func (p *PonPort) GetOnuBySn(sn *openolt.SerialNumber) (*Onu, error) {
for _, onu := range p.Onus {
if bytes.Equal(onu.SerialNumber.VendorSpecific, sn.VendorSpecific) {
return onu, nil
}
}
return nil, fmt.Errorf("Cannot find Onu with serial number %d in PonPort %d", sn, p.ID)
}
func (p *PonPort) GetOnuById(id uint32) (*Onu, error) {
for _, onu := range p.Onus {
if onu.ID == id {
return onu, nil
}
}
return nil, fmt.Errorf("Cannot find Onu with id %d in PonPort %d", id, p.ID)
}
// GetNumOfActiveOnus returns number of active ONUs for PON port
func (p *PonPort) GetNumOfActiveOnus() uint32 {
var count uint32 = 0
for _, onu := range p.Onus {
if onu.InternalState.Current() == OnuStateInitialized || onu.InternalState.Current() == OnuStateCreated || onu.InternalState.Current() == OnuStateDisabled {
continue
}
count++
}
return count
}
// storeOnuId adds the Id to the ONU Ids already allocated to this PON port
func (p *PonPort) storeOnuId(onuId uint32, onuSn *openolt.SerialNumber) {
p.allocatedOnuIdsLock.Lock()
defer p.allocatedOnuIdsLock.Unlock()
p.AllocatedOnuIds[onuId] = onuSn
}
// removeOnuId removes the OnuId from the allocated resources
func (p *PonPort) removeOnuId(onuId uint32) {
p.allocatedOnuIdsLock.Lock()
defer p.allocatedOnuIdsLock.Unlock()
delete(p.AllocatedOnuIds, onuId)
}
func (p *PonPort) removeAllOnuIds() {
p.allocatedOnuIdsLock.Lock()
defer p.allocatedOnuIdsLock.Unlock()
p.AllocatedOnuIds = make(map[uint32]*openolt.SerialNumber)
}
// isOnuIdAllocated returns whether this OnuId is already in use on this PON
func (p *PonPort) isOnuIdAllocated(onuId uint32) (bool, *openolt.SerialNumber) {
p.allocatedOnuIdsLock.RLock()
defer p.allocatedOnuIdsLock.RUnlock()
if _, ok := p.AllocatedOnuIds[onuId]; ok {
return true, p.AllocatedOnuIds[onuId]
}
return false, nil
}
// storeGemPort adds the gemPortId to the gemports already allocated to this PON port
func (p *PonPort) storeGemPort(gemPortId uint16, onuSn *openolt.SerialNumber) {
p.allocatedGemPortsLock.Lock()
defer p.allocatedGemPortsLock.Unlock()
p.AllocatedGemPorts[gemPortId] = onuSn
}
// removeGemPort removes the gemPortId from the allocated resources
func (p *PonPort) removeGemPort(gemPortId uint16) {
p.allocatedGemPortsLock.Lock()
defer p.allocatedGemPortsLock.Unlock()
delete(p.AllocatedGemPorts, gemPortId)
}
func (p *PonPort) removeGemPortBySn(onuSn *openolt.SerialNumber) {
p.allocatedGemPortsLock.Lock()
defer p.allocatedGemPortsLock.Unlock()
for gemPort, sn := range p.AllocatedGemPorts {
if sn == onuSn {
delete(p.AllocatedGemPorts, gemPort)
}
}
}
func (p *PonPort) removeAllGemPorts() {
p.allocatedGemPortsLock.Lock()
defer p.allocatedGemPortsLock.Unlock()
p.AllocatedGemPorts = make(map[uint16]*openolt.SerialNumber)
}
// isGemPortAllocated returns whether this gemPort is already in use on this PON
func (p *PonPort) isGemPortAllocated(gemPortId uint16) (bool, *openolt.SerialNumber) {
p.allocatedGemPortsLock.RLock()
defer p.allocatedGemPortsLock.RUnlock()
if _, ok := p.AllocatedGemPorts[gemPortId]; ok {
return true, p.AllocatedGemPorts[gemPortId]
}
return false, nil
}
// storeAllocId adds the Id to the ONU Ids already allocated to this PON port
func (p *PonPort) storeAllocId(ponID uint32, onuID uint32, entityID uint16, allocId uint16, onuSn *openolt.SerialNumber) {
p.allocatedAllocIdsLock.Lock()
defer p.allocatedAllocIdsLock.Unlock()
p.AllocatedAllocIds[AllocIDKey{ponID, onuID, entityID}] = &AllocIDVal{onuSn, allocId}
}
// removeAllocId removes the AllocId from the allocated resources
func (p *PonPort) removeAllocId(ponID uint32, onuID uint32, entityID uint16) {
p.allocatedAllocIdsLock.Lock()
defer p.allocatedAllocIdsLock.Unlock()
allocKey := AllocIDKey{ponID, onuID, entityID}
delete(p.AllocatedAllocIds, allocKey)
}
// removeAllocIdsForOnuSn removes the all AllocIds for the given onu serial number
func (p *PonPort) removeAllocIdsForOnuSn(onuSn *openolt.SerialNumber) {
p.allocatedAllocIdsLock.Lock()
defer p.allocatedAllocIdsLock.Unlock()
for id, allocObj := range p.AllocatedAllocIds {
if onuSn == allocObj.OnuSn {
delete(p.AllocatedAllocIds, id)
}
}
}
func (p *PonPort) removeAllAllocIds() {
p.allocatedAllocIdsLock.Lock()
defer p.allocatedAllocIdsLock.Unlock()
p.AllocatedAllocIds = make(map[AllocIDKey]*AllocIDVal)
}
// isAllocIdAllocated returns whether this AllocId is already in use on this PON
func (p *PonPort) isAllocIdAllocated(ponID uint32, onuID uint32, entityID uint16) (bool, *AllocIDVal) {
p.allocatedAllocIdsLock.RLock()
defer p.allocatedAllocIdsLock.RUnlock()
allocKey := AllocIDKey{ponID, onuID, entityID}
if _, ok := p.AllocatedAllocIds[allocKey]; ok {
return true, p.AllocatedAllocIds[allocKey]
}
return false, nil
}