blob: 5d888b12d6663f1e7ba252e2fe101249916b9957 [file] [log] [blame]
/*
* Copyright 2020-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Package adaptercoreonu provides the utility for onu devices, flows and statistics
package adaptercoreonu
import (
"context"
"errors"
"time"
"github.com/opencord/omci-lib-go"
me "github.com/opencord/omci-lib-go/generated"
"github.com/looplab/fsm"
"github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
"github.com/opencord/voltha-lib-go/v3/pkg/db"
"github.com/opencord/voltha-lib-go/v3/pkg/log"
)
const (
ulEvStart = "ulEvStart"
ulEvResetMib = "ulEvResetMib"
ulEvGetVendorAndSerial = "ulEvGetVendorAndSerial"
ulEvGetEquipmentID = "ulEvGetEquipmentId"
ulEvGetFirstSwVersion = "ulEvGetFirstSwVersion"
ulEvGetSecondSwVersion = "ulEvGetSecondSwVersion"
ulEvGetMacAddress = "ulEvGetMacAddress"
ulEvGetMibTemplate = "ulEvGetMibTemplate"
ulEvUploadMib = "ulEvUploadMib"
ulEvExamineMds = "ulEvExamineMds"
ulEvSuccess = "ulEvSuccess"
ulEvMismatch = "ulEvMismatch"
ulEvAuditMib = "ulEvAuditMib"
ulEvForceResync = "ulEvForceResync"
ulEvDiffsFound = "ulEvDiffsFound"
ulEvTimeout = "ulEvTimeout"
ulEvStop = "ulEvStop"
)
const (
ulStDisabled = "ulStDisabled"
ulStStarting = "ulStStarting"
ulStResettingMib = "ulStResettingMib"
ulStGettingVendorAndSerial = "ulStGettingVendorAndSerial"
ulStGettingEquipmentID = "ulStGettingEquipmentID"
ulStGettingFirstSwVersion = "ulStGettingFirstSwVersion"
ulStGettingSecondSwVersion = "ulStGettingSecondSwVersion"
ulStGettingMacAddress = "ulStGettingMacAddress"
ulStGettingMibTemplate = "ulStGettingMibTemplate"
ulStUploading = "ulStUploading"
ulStInSync = "ulStInSync"
ulStExaminingMds = "ulStExaminingMds"
ulStResynchronizing = "ulStResynchronizing"
ulStAuditing = "ulStAuditing"
ulStOutOfSync = "ulStOutOfSync"
)
const (
dlEvStart = "dlEvStart"
dlEvCreateGal = "dlEvCreateGal"
dlEvRxGalResp = "dlEvRxGalResp"
dlEvRxOnu2gResp = "dlEvRxOnu2gResp"
dlEvRxBridgeResp = "dlEvRxBridgeResp"
dlEvTimeoutSimple = "dlEvTimeoutSimple"
dlEvTimeoutBridge = "dlEvTimeoutBridge"
dlEvReset = "dlEvReset"
dlEvRestart = "dlEvRestart"
)
const (
dlStDisabled = "dlStDisabled"
dlStStarting = "dlStStarting"
dlStCreatingGal = "dlStCreatingGal"
dlStSettingOnu2g = "dlStSettingOnu2g"
dlStBridgeInit = "dlStBridgeInit"
dlStDownloaded = "dlStDownloaded"
dlStResetting = "dlStResetting"
)
const (
cBasePathMibTemplateKvStore = "service/voltha/omci_mibs/go_templates"
cSuffixMibTemplateKvStore = "%s/%s/%s"
)
// OnuDeviceEvent - event of interest to Device Adapters and OpenOMCI State Machines
type OnuDeviceEvent int
const (
DeviceStatusInit OnuDeviceEvent = 0
MibDatabaseSync OnuDeviceEvent = 1
OmciCapabilitiesDone OnuDeviceEvent = 2
MibDownloadDone OnuDeviceEvent = 3
UniLockStateDone OnuDeviceEvent = 4
UniUnlockStateDone OnuDeviceEvent = 5
UniAdminStateDone OnuDeviceEvent = 6
PortLinkUp OnuDeviceEvent = 7
PortLinkDw OnuDeviceEvent = 8
OmciAniConfigDone OnuDeviceEvent = 9
OmciVlanFilterDone OnuDeviceEvent = 10
)
type activityDescr struct {
databaseClass func() error
auditDelay uint16
}
// OmciDeviceFsms - FSM event mapping to database class and time to wait between audits
type OmciDeviceFsms map[string]activityDescr
// AdapterFsm - Adapter FSM details including channel, event and device
type AdapterFsm struct {
fsmName string
deviceID string
commChan chan Message
pFsm *fsm.FSM
}
//NewAdapterFsm - FSM details including event, device and channel.
func NewAdapterFsm(aName string, aDeviceID string, aCommChannel chan Message) *AdapterFsm {
aFsm := &AdapterFsm{
fsmName: aName,
deviceID: aDeviceID,
commChan: aCommChannel,
}
return aFsm
}
//Start starts (logs) the omci agent
func (oo *AdapterFsm) logFsmStateChange(e *fsm.Event) {
logger.Debugw("FSM state change", log.Fields{"device-id": oo.deviceID, "FSM name": oo.fsmName,
"event name": string(e.Event), "src state": string(e.Src), "dst state": string(e.Dst)})
}
//OntDeviceEntry structure holds information about the attached FSM'as and their communication
const (
firstSwImageMeID = 0
secondSwImageMeID = 1
)
const onugMeID = 0
const onu2gMeID = 0
const ipHostConfigDataMeID = 1
const onugSerialNumberLen = 8
const omciMacAddressLen = 6
type swImages struct {
version string
isActive uint8
}
// OnuDeviceEntry - ONU device info and FSM events.
type OnuDeviceEntry struct {
deviceID string
baseDeviceHandler *deviceHandler
coreProxy adapterif.CoreProxy
adapterProxy adapterif.AdapterProxy
started bool
PDevOmciCC *omciCC
pOnuDB *onuDeviceDB
mibTemplateKVStore *db.Backend
vendorID string
serialNumber string
equipmentID string
swImages [secondSwImageMeID + 1]swImages
activeSwVersion string
macAddress string
mibDbClass func() error
supportedFsms OmciDeviceFsms
devState OnuDeviceEvent
mibAuditDelay uint16
mibDebugLevel string
pMibUploadFsm *AdapterFsm //could be handled dynamically and more general as pAdapterFsm - perhaps later
pMibDownloadFsm *AdapterFsm //could be handled dynamically and more general as pAdapterFsm - perhaps later
omciMessageReceived chan bool //seperate channel needed by DownloadFsm
omciRebootMessageReceivedChannel chan Message // channel needed by Reboot request
}
//newOnuDeviceEntry returns a new instance of a OnuDeviceEntry
//mib_db (as well as not inluded alarm_db not really used in this code? VERIFY!!)
func newOnuDeviceEntry(ctx context.Context, deviceID string, kVStoreHost string, kVStorePort int, kvStoreType string, deviceHandler *deviceHandler,
coreProxy adapterif.CoreProxy, adapterProxy adapterif.AdapterProxy,
supportedFsmsPtr *OmciDeviceFsms) *OnuDeviceEntry {
logger.Infow("init-onuDeviceEntry", log.Fields{"device-id": deviceID})
var onuDeviceEntry OnuDeviceEntry
onuDeviceEntry.started = false
onuDeviceEntry.deviceID = deviceID
onuDeviceEntry.baseDeviceHandler = deviceHandler
onuDeviceEntry.coreProxy = coreProxy
onuDeviceEntry.adapterProxy = adapterProxy
onuDeviceEntry.devState = DeviceStatusInit
onuDeviceEntry.omciRebootMessageReceivedChannel = make(chan Message, 2048)
if supportedFsmsPtr != nil {
onuDeviceEntry.supportedFsms = *supportedFsmsPtr
} else {
//var mibSyncFsm = NewMibSynchronizer()
// use some internaƶ defaults, if not defined from outside
onuDeviceEntry.supportedFsms = OmciDeviceFsms{
"mib-synchronizer": {
//mibSyncFsm, // Implements the MIB synchronization state machine
onuDeviceEntry.mibDbVolatileDict, // Implements volatile ME MIB database
//true, // Advertise events on OpenOMCI event bus
60, // Time to wait between MIB audits. 0 to disable audits.
// map[string]func() error{
// "mib-upload": onuDeviceEntry.MibUploadTask,
// "mib-template": onuDeviceEntry.MibTemplateTask,
// "get-mds": onuDeviceEntry.GetMdsTask,
// "mib-audit": onuDeviceEntry.GetMdsTask,
// "mib-resync": onuDeviceEntry.MibResyncTask,
// "mib-reconcile": onuDeviceEntry.MibReconcileTask,
// },
},
}
}
onuDeviceEntry.mibDbClass = onuDeviceEntry.supportedFsms["mib-synchronizer"].databaseClass
logger.Debug("access2mibDbClass")
go onuDeviceEntry.mibDbClass()
onuDeviceEntry.mibAuditDelay = onuDeviceEntry.supportedFsms["mib-synchronizer"].auditDelay
logger.Debugw("MibAudit is set to", log.Fields{"Delay": onuDeviceEntry.mibAuditDelay})
onuDeviceEntry.mibDebugLevel = "normal" //set to "verbose" if you want to have all output, possibly later also per config option!
mibUploadChan := make(chan Message, 2048)
onuDeviceEntry.pMibUploadFsm = NewAdapterFsm("MibUpload", deviceID, mibUploadChan)
onuDeviceEntry.pMibUploadFsm.pFsm = fsm.NewFSM(
ulStDisabled,
fsm.Events{
{Name: ulEvStart, Src: []string{ulStDisabled}, Dst: ulStStarting},
{Name: ulEvResetMib, Src: []string{ulStStarting}, Dst: ulStResettingMib},
{Name: ulEvGetVendorAndSerial, Src: []string{ulStResettingMib}, Dst: ulStGettingVendorAndSerial},
{Name: ulEvGetEquipmentID, Src: []string{ulStGettingVendorAndSerial}, Dst: ulStGettingEquipmentID},
{Name: ulEvGetFirstSwVersion, Src: []string{ulStGettingEquipmentID}, Dst: ulStGettingFirstSwVersion},
{Name: ulEvGetSecondSwVersion, Src: []string{ulStGettingFirstSwVersion}, Dst: ulStGettingSecondSwVersion},
{Name: ulEvGetMacAddress, Src: []string{ulStGettingSecondSwVersion}, Dst: ulStGettingMacAddress},
{Name: ulEvGetMibTemplate, Src: []string{ulStGettingMacAddress}, Dst: ulStGettingMibTemplate},
{Name: ulEvUploadMib, Src: []string{ulStGettingMibTemplate}, Dst: ulStUploading},
{Name: ulEvExamineMds, Src: []string{ulStStarting}, Dst: ulStExaminingMds},
{Name: ulEvSuccess, Src: []string{ulStGettingMibTemplate}, Dst: ulStInSync},
{Name: ulEvSuccess, Src: []string{ulStUploading}, Dst: ulStInSync},
{Name: ulEvSuccess, Src: []string{ulStExaminingMds}, Dst: ulStInSync},
{Name: ulEvMismatch, Src: []string{ulStExaminingMds}, Dst: ulStResynchronizing},
{Name: ulEvAuditMib, Src: []string{ulStInSync}, Dst: ulStAuditing},
{Name: ulEvSuccess, Src: []string{ulStOutOfSync}, Dst: ulStInSync},
{Name: ulEvAuditMib, Src: []string{ulStOutOfSync}, Dst: ulStAuditing},
{Name: ulEvSuccess, Src: []string{ulStAuditing}, Dst: ulStInSync},
{Name: ulEvMismatch, Src: []string{ulStAuditing}, Dst: ulStResynchronizing},
{Name: ulEvForceResync, Src: []string{ulStAuditing}, Dst: ulStResynchronizing},
{Name: ulEvSuccess, Src: []string{ulStResynchronizing}, Dst: ulStInSync},
{Name: ulEvDiffsFound, Src: []string{ulStResynchronizing}, Dst: ulStOutOfSync},
{Name: ulEvTimeout, Src: []string{ulStResettingMib, ulStGettingVendorAndSerial, ulStGettingEquipmentID, ulStGettingFirstSwVersion,
ulStGettingSecondSwVersion, ulStGettingMacAddress, ulStGettingMibTemplate, ulStUploading, ulStResynchronizing, ulStExaminingMds,
ulStInSync, ulStOutOfSync, ulStAuditing}, Dst: ulStStarting},
{Name: ulEvStop, Src: []string{ulStStarting, ulStResettingMib, ulStGettingVendorAndSerial, ulStGettingEquipmentID, ulStGettingFirstSwVersion,
ulStGettingSecondSwVersion, ulStGettingMacAddress, ulStGettingMibTemplate, ulStUploading, ulStResynchronizing, ulStExaminingMds,
ulStInSync, ulStOutOfSync, ulStAuditing}, Dst: ulStDisabled},
},
fsm.Callbacks{
"enter_state": func(e *fsm.Event) { onuDeviceEntry.pMibUploadFsm.logFsmStateChange(e) },
("enter_" + ulStStarting): func(e *fsm.Event) { onuDeviceEntry.enterStartingState(e) },
("enter_" + ulStResettingMib): func(e *fsm.Event) { onuDeviceEntry.enterResettingMibState(e) },
("enter_" + ulStGettingVendorAndSerial): func(e *fsm.Event) { onuDeviceEntry.enterGettingVendorAndSerialState(e) },
("enter_" + ulStGettingEquipmentID): func(e *fsm.Event) { onuDeviceEntry.enterGettingEquipmentIDState(e) },
("enter_" + ulStGettingFirstSwVersion): func(e *fsm.Event) { onuDeviceEntry.enterGettingFirstSwVersionState(e) },
("enter_" + ulStGettingSecondSwVersion): func(e *fsm.Event) { onuDeviceEntry.enterGettingSecondSwVersionState(e) },
("enter_" + ulStGettingMacAddress): func(e *fsm.Event) { onuDeviceEntry.enterGettingMacAddressState(e) },
("enter_" + ulStGettingMibTemplate): func(e *fsm.Event) { onuDeviceEntry.enterGettingMibTemplate(e) },
("enter_" + ulStUploading): func(e *fsm.Event) { onuDeviceEntry.enterUploadingState(e) },
("enter_" + ulStExaminingMds): func(e *fsm.Event) { onuDeviceEntry.enterExaminingMdsState(e) },
("enter_" + ulStResynchronizing): func(e *fsm.Event) { onuDeviceEntry.enterResynchronizingState(e) },
("enter_" + ulStAuditing): func(e *fsm.Event) { onuDeviceEntry.enterAuditingState(e) },
("enter_" + ulStOutOfSync): func(e *fsm.Event) { onuDeviceEntry.enterOutOfSyncState(e) },
("enter_" + ulStInSync): func(e *fsm.Event) { onuDeviceEntry.enterInSyncState(e) },
},
)
mibDownloadChan := make(chan Message, 2048)
onuDeviceEntry.pMibDownloadFsm = NewAdapterFsm("MibDownload", deviceID, mibDownloadChan)
onuDeviceEntry.pMibDownloadFsm.pFsm = fsm.NewFSM(
dlStDisabled,
fsm.Events{
{Name: dlEvStart, Src: []string{dlStDisabled}, Dst: dlStStarting},
{Name: dlEvCreateGal, Src: []string{dlStStarting}, Dst: dlStCreatingGal},
{Name: dlEvRxGalResp, Src: []string{dlStCreatingGal}, Dst: dlStSettingOnu2g},
{Name: dlEvRxOnu2gResp, Src: []string{dlStSettingOnu2g}, Dst: dlStBridgeInit},
// the bridge state is used for multi ME config for alle UNI related ports
// maybe such could be reflected in the state machine as well (port number parametrized)
// but that looks not straightforward here - so we keep it simple here for the beginning(?)
{Name: dlEvRxBridgeResp, Src: []string{dlStBridgeInit}, Dst: dlStDownloaded},
{Name: dlEvTimeoutSimple, Src: []string{dlStCreatingGal, dlStSettingOnu2g}, Dst: dlStStarting},
{Name: dlEvTimeoutBridge, Src: []string{dlStBridgeInit}, Dst: dlStStarting},
{Name: dlEvReset, Src: []string{dlStStarting, dlStCreatingGal, dlStSettingOnu2g,
dlStBridgeInit, dlStDownloaded}, Dst: dlStResetting},
// exceptional treatment for all states except dlStResetting
{Name: dlEvRestart, Src: []string{dlStStarting, dlStCreatingGal, dlStSettingOnu2g,
dlStBridgeInit, dlStDownloaded, dlStResetting}, Dst: dlStDisabled},
},
fsm.Callbacks{
"enter_state": func(e *fsm.Event) { onuDeviceEntry.pMibDownloadFsm.logFsmStateChange(e) },
("enter_" + dlStStarting): func(e *fsm.Event) { onuDeviceEntry.enterDLStartingState(e) },
("enter_" + dlStCreatingGal): func(e *fsm.Event) { onuDeviceEntry.enterCreatingGalState(e) },
("enter_" + dlStSettingOnu2g): func(e *fsm.Event) { onuDeviceEntry.enterSettingOnu2gState(e) },
("enter_" + dlStBridgeInit): func(e *fsm.Event) { onuDeviceEntry.enterBridgeInitState(e) },
("enter_" + dlStDownloaded): func(e *fsm.Event) { onuDeviceEntry.enterDownloadedState(e) },
("enter_" + dlStResetting): func(e *fsm.Event) { onuDeviceEntry.enterResettingState(e) },
},
)
if onuDeviceEntry.pMibDownloadFsm == nil || onuDeviceEntry.pMibDownloadFsm.pFsm == nil {
logger.Error("MibDownloadFsm could not be instantiated!!")
// some specifc error treatment - or waiting for crash ???
}
onuDeviceEntry.mibTemplateKVStore = onuDeviceEntry.baseDeviceHandler.setBackend(cBasePathMibTemplateKvStore)
if onuDeviceEntry.mibTemplateKVStore == nil {
logger.Errorw("Failed to setup mibTemplateKVStore", log.Fields{"device-id": deviceID})
}
return &onuDeviceEntry
}
//start starts (logs) the omci agent
func (oo *OnuDeviceEntry) start(ctx context.Context) error {
logger.Info("starting-OnuDeviceEntry")
oo.PDevOmciCC = newOmciCC(ctx, oo, oo.deviceID, oo.baseDeviceHandler,
oo.coreProxy, oo.adapterProxy)
if oo.PDevOmciCC == nil {
logger.Errorw("Could not create devOmciCc - abort", log.Fields{"for device-id": oo.deviceID})
return errors.New("could not create devOmciCc")
}
oo.started = true
logger.Info("OnuDeviceEntry-started")
return nil
}
//stop terminates the session
func (oo *OnuDeviceEntry) stop(ctx context.Context) error {
logger.Info("stopping-OnuDeviceEntry")
oo.started = false
logger.Info("OnuDeviceEntry-stopped")
return nil
}
func (oo *OnuDeviceEntry) reboot(ctx context.Context) error {
logger.Info("reboot-OnuDeviceEntry")
if err := oo.PDevOmciCC.sendReboot(context.TODO(), ConstDefaultOmciTimeout, true, oo.omciRebootMessageReceivedChannel); err != nil {
logger.Errorw("onu didn't reboot", log.Fields{"for device-id": oo.deviceID})
return err
}
logger.Info("OnuDeviceEntry-reboot")
return nil
}
func (oo *OnuDeviceEntry) waitForRebootResponse(responseChannel chan Message) error {
select {
case <-time.After(3 * time.Second): //3s was detected to be to less in 8*8 bbsim test with debug Info/Debug
logger.Warnw("Reboot timeout", log.Fields{"for device-id": oo.deviceID})
return errors.New("rebootTimeout")
case data := <-responseChannel:
switch data.Data.(OmciMessage).OmciMsg.MessageType {
case omci.RebootResponseType:
{
msgLayer := (*data.Data.(OmciMessage).OmciPacket).Layer(omci.LayerTypeRebootResponse)
if msgLayer == nil {
return errors.New("omci Msg layer could not be detected for RebootResponseType")
}
msgObj, msgOk := msgLayer.(*omci.GetResponse)
if !msgOk {
return errors.New("omci Msg layer could not be assigned for RebootResponseType")
}
logger.Debugw("CreateResponse Data", log.Fields{"device-id": oo.deviceID, "data-fields": msgObj})
if msgObj.Result != me.Success {
logger.Errorw("Omci RebootResponseType Error ", log.Fields{"Error": msgObj.Result})
// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
return errors.New("omci RebootResponse Result Error indication")
}
return nil
}
}
logger.Warnw("Reboot response error", log.Fields{"for device-id": oo.deviceID})
return errors.New("unexpected OmciResponse type received")
}
}
//Relay the InSync message via Handler to Rw core - Status update
func (oo *OnuDeviceEntry) transferSystemEvent(devEvent OnuDeviceEvent) {
logger.Debugw("relaying system-event", log.Fields{"Event": devEvent})
if devEvent == MibDatabaseSync {
if oo.devState < MibDatabaseSync { //devState has not been synced yet
oo.devState = MibDatabaseSync
go oo.baseDeviceHandler.deviceProcStatusUpdate(devEvent)
//TODO!!! device control: next step: start MIB capability verification from here ?!!!
} else {
logger.Debugw("mibinsync-event in some already synced state - ignored", log.Fields{"state": oo.devState})
}
} else if devEvent == MibDownloadDone {
if oo.devState < MibDownloadDone { //devState has not been synced yet
oo.devState = MibDownloadDone
go oo.baseDeviceHandler.deviceProcStatusUpdate(devEvent)
} else {
logger.Debugw("mibdownloaddone-event was already seen - ignored", log.Fields{"state": oo.devState})
}
} else {
logger.Warnw("device-event not yet handled", log.Fields{"state": devEvent})
}
}