[VOL-1349] EPON ONU adapter (package B)
Change-Id: I609ba349c429bc7e87c74b66bb1121841f9caef6
diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go
new file mode 100644
index 0000000..6be3ebc
--- /dev/null
+++ b/internal/pkg/config/config.go
@@ -0,0 +1,209 @@
+/*
+* Copyright 2018-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 config provides the Log, kvstore, Kafka configuration
+package config
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "time"
+)
+
+// Open ONU default constants
+const (
+ etcdStoreName = "etcd"
+ defaultInstanceid = "epononu"
+ defaultKafkaadapterhost = "172.30.254.100"
+ defaultKafkaadapterport = 9092
+ defaultKafkaclusterhost = "172.30.254.100"
+ defaultKafkaclusterport = 9092
+ defaultKvstoretype = etcdStoreName
+ defaultKvstoretimeout = 5 * time.Second
+ defaultKvstorehost = "localhost"
+ defaultKvstoreport = 2379 // Consul = 8500; Etcd = 2379
+ defaultLoglevel = "DEBUG"
+ defaultBanner = false
+ defaultDisplayVersionOnly = false
+ defaultAccIncrEvto = false
+ defaultTopic = "epononu"
+ defaultCoretopic = "rwcore"
+ defaultEventtopic = "voltha.events"
+ defaultOnunumber = 1
+ defaultProbeHost = ""
+ defaultProbePort = 8080
+ defaultLiveProbeInterval = 60 * time.Second
+ defaultNotLiveProbeInterval = 5 * time.Second // Probe more frequently when not alive
+ defaultHearbeatCheckInterval = 30 * time.Second
+ defaultHearbeatFailReportInterval = 180 * time.Second
+ defaultKafkaReconnectRetries = -1
+ defaultCurrentReplica = 1
+ defaultTotalReplicas = 1
+)
+
+// AdapterFlags represents the set of configurations used by the read-write adaptercore service
+type AdapterFlags struct {
+ InstanceID string
+ KafkaAdapterHost string
+ KafkaAdapterPort int
+ KafkaClusterHost string
+ KafkaClusterPort int
+ KVStoreType string
+ KVStoreTimeout time.Duration
+ KVStoreHost string
+ KVStorePort int
+ Topic string
+ CoreTopic string
+ EventTopic string
+ LogLevel string
+ OnuNumber int
+ Banner bool
+ DisplayVersionOnly bool
+ AccIncrEvto bool
+ ProbeHost string
+ ProbePort int
+ LiveProbeInterval time.Duration
+ NotLiveProbeInterval time.Duration
+ HeartbeatCheckInterval time.Duration
+ HeartbeatFailReportInterval time.Duration
+ KafkaReconnectRetries int
+ CurrentReplica int
+ TotalReplicas int
+}
+
+// NewAdapterFlags returns a new RWCore config
+func NewAdapterFlags() *AdapterFlags {
+ var adapterFlags = AdapterFlags{ // Default values
+ InstanceID: defaultInstanceid,
+ KafkaAdapterHost: defaultKafkaadapterhost,
+ KafkaAdapterPort: defaultKafkaadapterport,
+ KafkaClusterHost: defaultKafkaclusterhost,
+ KafkaClusterPort: defaultKafkaclusterport,
+ KVStoreType: defaultKvstoretype,
+ KVStoreTimeout: defaultKvstoretimeout,
+ KVStoreHost: defaultKvstorehost,
+ KVStorePort: defaultKvstoreport,
+ Topic: defaultTopic,
+ CoreTopic: defaultCoretopic,
+ EventTopic: defaultEventtopic,
+ LogLevel: defaultLoglevel,
+ OnuNumber: defaultOnunumber,
+ Banner: defaultBanner,
+ DisplayVersionOnly: defaultDisplayVersionOnly,
+ AccIncrEvto: defaultAccIncrEvto,
+ ProbeHost: defaultProbeHost,
+ ProbePort: defaultProbePort,
+ LiveProbeInterval: defaultLiveProbeInterval,
+ NotLiveProbeInterval: defaultNotLiveProbeInterval,
+ HeartbeatCheckInterval: defaultHearbeatCheckInterval,
+ HeartbeatFailReportInterval: defaultHearbeatFailReportInterval,
+ KafkaReconnectRetries: defaultKafkaReconnectRetries,
+ CurrentReplica: defaultCurrentReplica,
+ TotalReplicas: defaultTotalReplicas,
+ }
+ return &adapterFlags
+}
+
+// ParseCommandArguments parses the arguments when running read-write adaptercore service
+func (so *AdapterFlags) ParseCommandArguments() {
+
+ help := fmt.Sprintf("Kafka - Adapter messaging host")
+ flag.StringVar(&(so.KafkaAdapterHost), "kafka_adapter_host", defaultKafkaadapterhost, help)
+
+ help = fmt.Sprintf("Kafka - Adapter messaging port")
+ flag.IntVar(&(so.KafkaAdapterPort), "kafka_adapter_port", defaultKafkaadapterport, help)
+
+ help = fmt.Sprintf("Kafka - Cluster messaging host")
+ flag.StringVar(&(so.KafkaClusterHost), "kafka_cluster_host", defaultKafkaclusterhost, help)
+
+ help = fmt.Sprintf("Kafka - Cluster messaging port")
+ flag.IntVar(&(so.KafkaClusterPort), "kafka_cluster_port", defaultKafkaclusterport, help)
+
+ help = fmt.Sprintf("Open ONU topic")
+ flag.StringVar(&(so.Topic), "adapter_topic", defaultTopic, help)
+
+ help = fmt.Sprintf("Core topic")
+ flag.StringVar(&(so.CoreTopic), "core_topic", defaultCoretopic, help)
+
+ help = fmt.Sprintf("Event topic")
+ flag.StringVar(&(so.EventTopic), "event_topic", defaultEventtopic, help)
+
+ help = fmt.Sprintf("KV store type")
+ flag.StringVar(&(so.KVStoreType), "kv_store_type", defaultKvstoretype, help)
+
+ help = fmt.Sprintf("The default timeout when making a kv store request")
+ flag.DurationVar(&(so.KVStoreTimeout), "kv_store_request_timeout", defaultKvstoretimeout, help)
+
+ help = fmt.Sprintf("KV store host")
+ flag.StringVar(&(so.KVStoreHost), "kv_store_host", defaultKvstorehost, help)
+
+ help = fmt.Sprintf("KV store port")
+ flag.IntVar(&(so.KVStorePort), "kv_store_port", defaultKvstoreport, help)
+
+ help = fmt.Sprintf("Log level")
+ flag.StringVar(&(so.LogLevel), "log_level", defaultLoglevel, help)
+
+ help = fmt.Sprintf("Number of ONUs")
+ flag.IntVar(&(so.OnuNumber), "onu_number", defaultOnunumber, help)
+
+ help = fmt.Sprintf("Show startup banner log lines")
+ flag.BoolVar(&(so.Banner), "banner", defaultBanner, help)
+
+ help = fmt.Sprintf("Show version information and exit")
+ flag.BoolVar(&(so.DisplayVersionOnly), "version", defaultDisplayVersionOnly, help)
+
+ help = fmt.Sprintf("Acceptance of incremental EVTOCD configuration")
+ flag.BoolVar(&(so.AccIncrEvto), "accept_incr_evto", defaultAccIncrEvto, help)
+
+ help = fmt.Sprintf("The address on which to listen to answer liveness and readiness probe queries over HTTP.")
+ flag.StringVar(&(so.ProbeHost), "probe_host", defaultProbeHost, help)
+
+ help = fmt.Sprintf("The port on which to listen to answer liveness and readiness probe queries over HTTP.")
+ flag.IntVar(&(so.ProbePort), "probe_port", defaultProbePort, help)
+
+ help = fmt.Sprintf("Number of seconds for the default liveliness check")
+ flag.DurationVar(&(so.LiveProbeInterval), "live_probe_interval", defaultLiveProbeInterval, help)
+
+ help = fmt.Sprintf("Number of seconds for liveliness check if probe is not running")
+ flag.DurationVar(&(so.NotLiveProbeInterval), "not_live_probe_interval", defaultNotLiveProbeInterval, help)
+
+ help = fmt.Sprintf("Number of seconds for heartbeat check interval.")
+ flag.DurationVar(&(so.HeartbeatCheckInterval), "hearbeat_check_interval", defaultHearbeatCheckInterval, help)
+
+ help = fmt.Sprintf("Number of seconds adapter has to wait before reporting core on the hearbeat check failure.")
+ flag.DurationVar(&(so.HeartbeatFailReportInterval), "hearbeat_fail_interval", defaultHearbeatFailReportInterval, help)
+
+ help = fmt.Sprintf("Number of retries to connect to Kafka.")
+ flag.IntVar(&(so.KafkaReconnectRetries), "kafka_reconnect_retries", defaultKafkaReconnectRetries, help)
+
+ help = "Replica number of this particular instance (default: %s)"
+ flag.IntVar(&(so.CurrentReplica), "current_replica", defaultCurrentReplica, help)
+
+ help = "Total number of instances for this adapter"
+ flag.IntVar(&(so.TotalReplicas), "total_replica", defaultTotalReplicas, help)
+
+ flag.Parse()
+ containerName := getContainerInfo()
+ if len(containerName) > 0 {
+ so.InstanceID = containerName
+ }
+
+}
+
+func getContainerInfo() string {
+ return os.Getenv("HOSTNAME")
+}
diff --git a/internal/pkg/onuadaptercore/common.go b/internal/pkg/onuadaptercore/common.go
new file mode 100644
index 0000000..3616281
--- /dev/null
+++ b/internal/pkg/onuadaptercore/common.go
@@ -0,0 +1,32 @@
+/*
+ * 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 (
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+var logger log.Logger
+
+func init() {
+ var err error
+ logger, err = log.AddPackage(log.JSON, log.ErrorLevel, log.Fields{"pkg": "adaptercoreonu"})
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/internal/pkg/onuadaptercore/device_handler.go b/internal/pkg/onuadaptercore/device_handler.go
new file mode 100644
index 0000000..9d00d5a
--- /dev/null
+++ b/internal/pkg/onuadaptercore/device_handler.go
@@ -0,0 +1,1865 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//Package adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+ "context"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "strconv"
+ "sync"
+ "time"
+
+ "github.com/gogo/protobuf/proto"
+ "github.com/golang/protobuf/ptypes"
+ "github.com/looplab/fsm"
+ me "github.com/opencord/omci-lib-go/generated"
+ "github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+ "github.com/opencord/voltha-lib-go/v3/pkg/db"
+ flow "github.com/opencord/voltha-lib-go/v3/pkg/flows"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ vc "github.com/opencord/voltha-protos/v3/go/common"
+ ic "github.com/opencord/voltha-protos/v3/go/inter_container"
+ "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ of "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ oop "github.com/opencord/voltha-protos/v3/go/openolt"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+)
+
+/*
+// Constants for number of retries and for timeout
+const (
+ MaxRetry = 10
+ MaxTimeOutInMs = 500
+)
+*/
+
+const (
+ devEvDeviceInit = "devEvDeviceInit"
+ devEvGrpcConnected = "devEvGrpcConnected"
+ devEvGrpcDisconnected = "devEvGrpcDisconnected"
+ devEvDeviceUpInd = "devEvDeviceUpInd"
+ devEvDeviceDownInd = "devEvDeviceDownInd"
+)
+const (
+ devStNull = "devStNull"
+ devStDown = "devStDown"
+ devStInit = "devStInit"
+ devStConnected = "devStConnected"
+ devStUp = "devStUp"
+)
+
+//Event category and subcategory definitions - same as defiend for OLT in eventmgr.go - should be done more centrally
+const (
+ pon = voltha.EventSubCategory_PON
+ equipment = voltha.EventCategory_EQUIPMENT
+)
+
+const (
+ cEventObjectType = "ONU"
+)
+const (
+ cOnuActivatedEvent = "ONU_ACTIVATED"
+)
+
+//deviceHandler will interact with the ONU ? device.
+type deviceHandler struct {
+ deviceID string
+ DeviceType string
+ adminState string
+ device *voltha.Device
+ logicalDeviceID string
+ ProxyAddressID string
+ ProxyAddressType string
+ parentID string
+ ponPortNumber uint32
+
+ coreProxy adapterif.CoreProxy
+ AdapterProxy adapterif.AdapterProxy
+ EventProxy adapterif.EventProxy
+
+ pOpenOnuAc *OpenONUAC
+ pDeviceStateFsm *fsm.FSM
+ deviceEntrySet chan bool //channel for DeviceEntry set event
+ pOnuOmciDevice *OnuDeviceEntry
+ pOnuTP *onuUniTechProf
+ exitChannel chan int
+ lockDevice sync.RWMutex
+ pOnuIndication *oop.OnuIndication
+ deviceReason string
+ pLockStateFsm *lockStateFsm
+ pUnlockStateFsm *lockStateFsm
+
+
+ stopCollector chan bool
+ stopHeartbeatCheck chan bool
+ activePorts sync.Map
+ uniEntityMap map[uint32]*onuUniPort
+ UniVlanConfigFsmMap map[uint8]*UniVlanConfigFsm
+ reconciling bool
+}
+
+//newDeviceHandler creates a new device handler
+func newDeviceHandler(cp adapterif.CoreProxy, ap adapterif.AdapterProxy, ep adapterif.EventProxy, device *voltha.Device, adapter *OpenONUAC) *deviceHandler {
+ var dh deviceHandler
+ dh.coreProxy = cp
+ dh.AdapterProxy = ap
+ dh.EventProxy = ep
+ cloned := (proto.Clone(device)).(*voltha.Device)
+ dh.deviceID = cloned.Id
+ dh.DeviceType = cloned.Type
+ dh.adminState = "up"
+ dh.device = cloned
+ dh.pOpenOnuAc = adapter
+ dh.exitChannel = make(chan int, 1)
+ dh.lockDevice = sync.RWMutex{}
+ dh.deviceEntrySet = make(chan bool, 1)
+ dh.stopCollector = make(chan bool, 2)
+ dh.stopHeartbeatCheck = make(chan bool, 2)
+ dh.activePorts = sync.Map{}
+ dh.uniEntityMap = make(map[uint32]*onuUniPort)
+ dh.UniVlanConfigFsmMap = make(map[uint8]*UniVlanConfigFsm)
+ dh.reconciling = false
+
+ dh.pDeviceStateFsm = fsm.NewFSM(
+ devStNull,
+ fsm.Events{
+ {Name: devEvDeviceInit, Src: []string{devStNull, devStDown}, Dst: devStInit},
+ {Name: devEvGrpcConnected, Src: []string{devStInit}, Dst: devStConnected},
+ {Name: devEvGrpcDisconnected, Src: []string{devStConnected, devStDown}, Dst: devStInit},
+ {Name: devEvDeviceUpInd, Src: []string{devStConnected, devStDown}, Dst: devStUp},
+ {Name: devEvDeviceDownInd, Src: []string{devStUp}, Dst: devStDown},
+ },
+ fsm.Callbacks{
+ "before_event": func(e *fsm.Event) { dh.logStateChange(e) },
+ ("before_" + devEvDeviceInit): func(e *fsm.Event) { dh.doStateInit(e) },
+ ("after_" + devEvDeviceInit): func(e *fsm.Event) { dh.postInit(e) },
+ ("before_" + devEvGrpcConnected): func(e *fsm.Event) { dh.doStateConnected(e) },
+ ("before_" + devEvGrpcDisconnected): func(e *fsm.Event) { dh.doStateInit(e) },
+ ("after_" + devEvGrpcDisconnected): func(e *fsm.Event) { dh.postInit(e) },
+ ("before_" + devEvDeviceUpInd): func(e *fsm.Event) { dh.doStateUp(e) },
+ ("before_" + devEvDeviceDownInd): func(e *fsm.Event) { dh.doStateDown(e) },
+ },
+ )
+
+ return &dh
+}
+
+// start save the device to the data model
+func (dh *deviceHandler) start(ctx context.Context) {
+ logger.Debugw("starting-device-handler", log.Fields{"device": dh.device, "device-id": dh.deviceID})
+ logger.Debug("device-handler-started")
+}
+
+/*
+// stop stops the device dh. Not much to do for now
+func (dh *deviceHandler) stop(ctx context.Context) {
+ logger.Debug("stopping-device-handler")
+ dh.exitChannel <- 1
+}
+*/
+
+// ##########################################################################################
+// deviceHandler methods that implement the adapters interface requests ##### begin #########
+
+//adoptOrReconcileDevice adopts the OLT device
+func (dh *deviceHandler) adoptOrReconcileDevice(ctx context.Context, device *voltha.Device) {
+ logger.Debugw("Adopt_or_reconcile_device", log.Fields{"device-id": device.Id, "Address": device.GetHostAndPort()})
+
+ logger.Debugw("Device FSM: ", log.Fields{"state": string(dh.pDeviceStateFsm.Current())})
+ if dh.pDeviceStateFsm.Is(devStNull) {
+ if err := dh.pDeviceStateFsm.Event(devEvDeviceInit); err != nil {
+ logger.Errorw("Device FSM: Can't go to state DeviceInit", log.Fields{"err": err})
+ }
+ logger.Debugw("Device FSM: ", log.Fields{"state": string(dh.pDeviceStateFsm.Current())})
+ } else {
+ logger.Debugw("AdoptOrReconcileDevice: Agent/device init already done", log.Fields{"device-id": device.Id})
+ }
+
+}
+
+func (dh *deviceHandler) processInterAdapterOMCIReqMessage(msg *ic.InterAdapterMessage) error {
+ msgBody := msg.GetBody()
+ omciMsg := &ic.InterAdapterOmciMessage{}
+ if err := ptypes.UnmarshalAny(msgBody, omciMsg); err != nil {
+ logger.Warnw("cannot-unmarshal-omci-msg-body", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+ return err
+ }
+
+ logger.Debugw("inter-adapter-recv-omci", log.Fields{
+ "device-id": dh.deviceID, "RxOmciMessage": hex.EncodeToString(omciMsg.Message)})
+ pDevEntry := dh.getOnuDeviceEntry(true)
+ if pDevEntry != nil {
+ return pDevEntry.PDevOmciCC.receiveMessage(context.TODO(), omciMsg.Message)
+ }
+ logger.Errorw("No valid OnuDevice -aborting", log.Fields{"device-id": dh.deviceID})
+ return errors.New("no valid OnuDevice")
+}
+
+func (dh *deviceHandler) processInterAdapterONUIndReqMessage(msg *ic.InterAdapterMessage) error {
+ msgBody := msg.GetBody()
+ onuIndication := &oop.OnuIndication{}
+ if err := ptypes.UnmarshalAny(msgBody, onuIndication); err != nil {
+ logger.Warnw("cannot-unmarshal-onu-indication-msg-body", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+ return err
+ }
+
+ onuOperstate := onuIndication.GetOperState()
+ logger.Debugw("inter-adapter-recv-onu-ind", log.Fields{"OnuId": onuIndication.GetOnuId(),
+ "AdminState": onuIndication.GetAdminState(), "OperState": onuOperstate,
+ "SNR": onuIndication.GetSerialNumber()})
+
+ if onuOperstate == "up" {
+ _ = dh.createInterface(onuIndication)
+ } else if (onuOperstate == "down") || (onuOperstate == "unreachable") {
+ _ = dh.updateInterface(onuIndication)
+ } else {
+ logger.Errorw("unknown-onu-indication operState", log.Fields{"OnuId": onuIndication.GetOnuId()})
+ return errors.New("invalidOperState")
+ }
+ return nil
+}
+
+func (dh *deviceHandler) processInterAdapterTechProfileDownloadReqMessage(
+ msg *ic.InterAdapterMessage) error {
+ if dh.pOnuTP == nil {
+ //should normally not happen ...
+ logger.Warnw("onuTechProf instance not set up for DLMsg request - ignoring request",
+ log.Fields{"device-id": dh.deviceID})
+ return errors.New("techProfile DLMsg request while onuTechProf instance not setup")
+ }
+ if (dh.deviceReason == "stopping-openomci") || (dh.deviceReason == "omci-admin-lock") {
+ // I've seen cases for this request, where the device was already stopped
+ logger.Warnw("TechProf stopped: device-unreachable", log.Fields{"device-id": dh.deviceID})
+ return errors.New("device-unreachable")
+ }
+
+ msgBody := msg.GetBody()
+ techProfMsg := &ic.InterAdapterTechProfileDownloadMessage{}
+ if err := ptypes.UnmarshalAny(msgBody, techProfMsg); err != nil {
+ logger.Warnw("cannot-unmarshal-techprof-msg-body", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+ return err
+ }
+
+ dh.pOnuTP.lockTpProcMutex()
+ if bTpModify := dh.pOnuTP.updateOnuUniTpPath(techProfMsg.UniId, techProfMsg.Path); bTpModify {
+ // if there has been some change for some uni TechProfilePath
+ //in order to allow concurrent calls to other dh instances we do not wait for execution here
+ //but doing so we can not indicate problems to the caller (who does what with that then?)
+ //by now we just assume straightforward successful execution
+ //TODO!!! Generally: In this scheme it would be good to have some means to indicate
+ // possible problems to the caller later autonomously
+
+ // deadline context to ensure completion of background routines waited for
+ //20200721: 10s proved to be less in 8*8 ONU test on local vbox machine with debug, might be further adapted
+ deadline := time.Now().Add(30 * time.Second) //allowed run time to finish before execution
+ dctx, cancel := context.WithDeadline(context.Background(), deadline)
+
+ dh.pOnuTP.resetProcessingErrorIndication()
+ var wg sync.WaitGroup
+ wg.Add(2) // for the 2 go routines to finish
+ // attention: deadline completion check and wg.Done is to be done in both routines
+ go dh.pOnuTP.configureUniTp(dctx, uint8(techProfMsg.UniId), techProfMsg.Path, &wg)
+ go dh.pOnuTP.updateOnuTpPathKvStore(dctx, &wg)
+ //the wait.. function is responsible for tpProcMutex.Unlock()
+ err := dh.pOnuTP.waitForTpCompletion(cancel, &wg) //wait for background process to finish and collect their result
+ return err
+ }
+ dh.pOnuTP.unlockTpProcMutex()
+ return nil
+}
+
+func (dh *deviceHandler) processInterAdapterDeleteGemPortReqMessage(
+ msg *ic.InterAdapterMessage) error {
+
+ if dh.pOnuTP == nil {
+ //should normally not happen ...
+ logger.Warnw("onuTechProf instance not set up for DelGem request - ignoring request",
+ log.Fields{"device-id": dh.deviceID})
+ return errors.New("techProfile DelGem request while onuTechProf instance not setup")
+ }
+
+ msgBody := msg.GetBody()
+ delGemPortMsg := &ic.InterAdapterDeleteGemPortMessage{}
+ if err := ptypes.UnmarshalAny(msgBody, delGemPortMsg); err != nil {
+ logger.Warnw("cannot-unmarshal-delete-gem-msg-body", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+ return err
+ }
+
+ dh.pOnuTP.lockTpProcMutex()
+
+ deadline := time.Now().Add(10 * time.Second) //allowed run time to finish before execution
+ dctx, cancel := context.WithDeadline(context.Background(), deadline)
+
+ dh.pOnuTP.resetProcessingErrorIndication()
+ var wg sync.WaitGroup
+ wg.Add(1) // for the 1 go routine to finish
+ go dh.pOnuTP.deleteTpResource(dctx, delGemPortMsg.UniId, delGemPortMsg.TpPath,
+ cResourceGemPort, delGemPortMsg.GemPortId, &wg)
+ err := dh.pOnuTP.waitForTpCompletion(cancel, &wg) //let that also run off-line to let the IA messaging return!
+ return err
+}
+
+func (dh *deviceHandler) processInterAdapterDeleteTcontReqMessage(
+ msg *ic.InterAdapterMessage) error {
+ if dh.pOnuTP == nil {
+ //should normally not happen ...
+ logger.Warnw("onuTechProf instance not set up for DelTcont request - ignoring request",
+ log.Fields{"device-id": dh.deviceID})
+ return errors.New("techProfile DelTcont request while onuTechProf instance not setup")
+ }
+
+ msgBody := msg.GetBody()
+ delTcontMsg := &ic.InterAdapterDeleteTcontMessage{}
+ if err := ptypes.UnmarshalAny(msgBody, delTcontMsg); err != nil {
+ logger.Warnw("cannot-unmarshal-delete-tcont-msg-body", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+ return err
+ }
+
+ dh.pOnuTP.lockTpProcMutex()
+ if bTpModify := dh.pOnuTP.updateOnuUniTpPath(delTcontMsg.UniId, ""); bTpModify {
+ // deadline context to ensure completion of background routines waited for
+ deadline := time.Now().Add(10 * time.Second) //allowed run time to finish before execution
+ dctx, cancel := context.WithDeadline(context.Background(), deadline)
+
+ dh.pOnuTP.resetProcessingErrorIndication()
+ var wg sync.WaitGroup
+ wg.Add(2) // for the 2 go routines to finish
+ go dh.pOnuTP.deleteTpResource(dctx, delTcontMsg.UniId, delTcontMsg.TpPath,
+ cResourceTcont, delTcontMsg.AllocId, &wg)
+ // Removal of the tcont/alloc id mapping represents the removal of the tech profile
+ go dh.pOnuTP.updateOnuTpPathKvStore(dctx, &wg)
+ //the wait.. function is responsible for tpProcMutex.Unlock()
+ err := dh.pOnuTP.waitForTpCompletion(cancel, &wg) //let that also run off-line to let the IA messaging return!
+ return err
+ }
+ dh.pOnuTP.unlockTpProcMutex()
+ return nil
+}
+
+//processInterAdapterMessage sends the proxied messages to the target device
+// If the proxy address is not found in the unmarshalled message, it first fetches the onu device for which the message
+// is meant, and then send the unmarshalled omci message to this onu
+func (dh *deviceHandler) processInterAdapterMessage(msg *ic.InterAdapterMessage) error {
+ msgID := msg.Header.Id
+ msgType := msg.Header.Type
+ fromTopic := msg.Header.FromTopic
+ toTopic := msg.Header.ToTopic
+ toDeviceID := msg.Header.ToDeviceId
+ proxyDeviceID := msg.Header.ProxyDeviceId
+ logger.Debugw("InterAdapter message header", log.Fields{"msgID": msgID, "msgType": msgType,
+ "fromTopic": fromTopic, "toTopic": toTopic, "toDeviceID": toDeviceID, "proxyDeviceID": proxyDeviceID})
+
+ switch msgType {
+ case ic.InterAdapterMessageType_OMCI_REQUEST:
+ {
+ return dh.processInterAdapterOMCIReqMessage(msg)
+ }
+ case ic.InterAdapterMessageType_ONU_IND_REQUEST:
+ {
+ return dh.processInterAdapterONUIndReqMessage(msg)
+ }
+ case ic.InterAdapterMessageType_TECH_PROFILE_DOWNLOAD_REQUEST:
+ {
+ return dh.processInterAdapterTechProfileDownloadReqMessage(msg)
+ }
+ case ic.InterAdapterMessageType_DELETE_GEM_PORT_REQUEST:
+ {
+ return dh.processInterAdapterDeleteGemPortReqMessage(msg)
+
+ }
+ case ic.InterAdapterMessageType_DELETE_TCONT_REQUEST:
+ {
+ return dh.processInterAdapterDeleteTcontReqMessage(msg)
+ }
+ default:
+ {
+ logger.Errorw("inter-adapter-unhandled-type", log.Fields{
+ "device-id": dh.deviceID, "msgType": msg.Header.Type})
+ return errors.New("unimplemented")
+ }
+ }
+}
+
+//FlowUpdateIncremental removes and/or adds the flow changes on a given device
+func (dh *deviceHandler) FlowUpdateIncremental(apOfFlowChanges *openflow_13.FlowChanges,
+ apOfGroupChanges *openflow_13.FlowGroupChanges, apFlowMetaData *voltha.FlowMetadata) error {
+
+ if apOfFlowChanges.ToRemove != nil {
+ for _, flowItem := range apOfFlowChanges.ToRemove.Items {
+ logger.Debugw("incremental flow item remove", log.Fields{"deviceId": dh.deviceID,
+ "Item": flowItem})
+ }
+ }
+ if apOfFlowChanges.ToAdd != nil {
+ for _, flowItem := range apOfFlowChanges.ToAdd.Items {
+ if flowItem.GetCookie() == 0 {
+ logger.Debugw("incremental flow add - no cookie - ignore", log.Fields{
+ "deviceId": dh.deviceID})
+ continue
+ }
+ flowInPort := flow.GetInPort(flowItem)
+ if flowInPort == uint32(of.OfpPortNo_OFPP_INVALID) {
+ logger.Errorw("flow inPort invalid", log.Fields{"deviceID": dh.deviceID})
+ return errors.New("flow inPort invalid")
+ } else if flowInPort == dh.ponPortNumber {
+ //this is some downstream flow
+ logger.Debugw("incremental flow ignore downstream", log.Fields{
+ "deviceId": dh.deviceID, "inPort": flowInPort})
+ continue
+ } else {
+ // this is the relevant upstream flow
+ var loUniPort *onuUniPort
+ if uniPort, exist := dh.uniEntityMap[flowInPort]; exist {
+ loUniPort = uniPort
+ } else {
+ logger.Errorw("flow inPort not found in UniPorts",
+ log.Fields{"deviceID": dh.deviceID, "inPort": flowInPort})
+ return fmt.Errorf("flow-parameter inPort %d not found in internal UniPorts", flowInPort)
+ }
+ flowOutPort := flow.GetOutPort(flowItem)
+ logger.Debugw("incremental flow-add port indications", log.Fields{
+ "deviceId": dh.deviceID, "inPort": flowInPort, "outPort": flowOutPort,
+ "uniPortName": loUniPort.name})
+ err := dh.addFlowItemToUniPort(flowItem, loUniPort)
+ //abort processing in error case
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+ return nil
+}
+
+//disableDevice locks the ONU and its UNI/VEIP ports (admin lock via OMCI)
+// TODO!!! Clarify usage of this method, it is for sure not used within ONOS (OLT) device disable
+// maybe it is obsolete by now
+func (dh *deviceHandler) disableDevice(device *voltha.Device) {
+ logger.Debugw("disable-device", log.Fields{"device-id": device.Id, "SerialNumber": device.SerialNumber})
+
+ if dh.deviceReason != "omci-admin-lock" {
+ // disable UNI ports/ONU
+ // *** should generate UniAdminStateDone event - unrelated to DeviceProcStatusUpdate!!
+ // here the result of the processing is not checked (trusted in background) *****
+ if dh.pLockStateFsm == nil {
+ dh.createUniLockFsm(true, UniAdminStateDone)
+ } else { //LockStateFSM already init
+ dh.pLockStateFsm.setSuccessEvent(UniAdminStateDone)
+ dh.runUniLockFsm(true)
+ }
+
+ if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "omci-admin-lock"); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-updating-reason-state", log.Fields{"device-id": dh.deviceID, "error": err})
+ }
+ dh.deviceReason = "omci-admin-lock"
+ //200604: ConnState improved to 'unreachable' (was not set in python-code), OperState 'unknown' seems to be best choice
+ if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), dh.deviceID, voltha.ConnectStatus_UNREACHABLE,
+ voltha.OperStatus_UNKNOWN); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-updating-device-state", log.Fields{"device-id": dh.deviceID, "error": err})
+ }
+
+ // update json file
+ // onustat := &OnuStatus{
+ // Id: dh.deviceID,
+ // AdminState: vc.AdminState_Types_name[int32(dh.device.AdminState)],
+ // OpeState: vc.OperStatus_Types_name[int32(dh.device.OperStatus)],
+ // ConnectState: vc.ConnectStatus_Types_name[int32(dh.device.ConnectStatus)],
+ // MacAddress: dh.device.MacAddress,
+ // }
+ // logger.Debugw("UpdateOnu", log.Fields{"Id": dh.deviceID})
+ // err := UpdateOnu(onustat)
+ // logger.Debugw("UpdateOnu", log.Fields{"err": err})
+ }
+}
+
+//reEnableDevice unlocks the ONU and its UNI/VEIP ports (admin unlock via OMCI)
+// TODO!!! Clarify usage of this method, compare above DisableDevice, usage may clarify resulting states
+// maybe it is obsolete by now
+func (dh *deviceHandler) reEnableDevice(device *voltha.Device) {
+ logger.Debugw("reenable-device", log.Fields{"device-id": device.Id, "SerialNumber": device.SerialNumber})
+
+ if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), dh.deviceID, voltha.ConnectStatus_REACHABLE,
+ voltha.OperStatus_ACTIVE); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-updating-device-state", log.Fields{"device-id": dh.deviceID, "error": err})
+ }
+
+ if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "initial-mib-downloaded"); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-updating-reason-state", log.Fields{"device-id": dh.deviceID, "error": err})
+ }
+ dh.deviceReason = "initial-mib-downloaded"
+
+
+ if dh.pUnlockStateFsm == nil {
+ dh.createUniLockFsm(false, UniAdminStateDone)
+ } else { //UnlockStateFSM already init
+ dh.pUnlockStateFsm.setSuccessEvent(UniAdminStateDone)
+ dh.runUniLockFsm(false)
+ }
+}
+
+func (dh *deviceHandler) reconcileDeviceOnuInd() {
+ logger.Debugw("reconciling - simulate onu indication", log.Fields{"device-id": dh.deviceID})
+
+ if err := dh.pOnuTP.restoreFromOnuTpPathKvStore(context.TODO()); err != nil {
+ logger.Errorw("reconciling - restoring OnuTp-data failed - abort", log.Fields{"err": err, "device-id": dh.deviceID})
+ dh.reconciling = false
+ return
+ }
+ var onuIndication oop.OnuIndication
+ onuIndication.IntfId = dh.pOnuTP.sOnuPersistentData.PersIntfID
+ onuIndication.OnuId = dh.pOnuTP.sOnuPersistentData.PersOnuID
+ onuIndication.OperState = dh.pOnuTP.sOnuPersistentData.PersOperState
+ onuIndication.AdminState = dh.pOnuTP.sOnuPersistentData.PersAdminState
+ _ = dh.createInterface(&onuIndication)
+}
+
+func (dh *deviceHandler) reconcileDeviceTechProf() {
+ logger.Debugw("reconciling - trigger tech profile config", log.Fields{"device-id": dh.deviceID})
+
+ dh.pOnuTP.lockTpProcMutex()
+ for _, uniData := range dh.pOnuTP.sOnuPersistentData.PersUniTpPath {
+ //In order to allow concurrent calls to other dh instances we do not wait for execution here
+ //but doing so we can not indicate problems to the caller (who does what with that then?)
+ //by now we just assume straightforward successful execution
+ //TODO!!! Generally: In this scheme it would be good to have some means to indicate
+ // possible problems to the caller later autonomously
+
+ // deadline context to ensure completion of background routines waited for
+ //20200721: 10s proved to be less in 8*8 ONU test on local vbox machine with debug, might be further adapted
+ deadline := time.Now().Add(30 * time.Second) //allowed run time to finish before execution
+ dctx, cancel := context.WithDeadline(context.Background(), deadline)
+
+ dh.pOnuTP.resetProcessingErrorIndication()
+ var wg sync.WaitGroup
+ wg.Add(1) // for the 1 go routines to finish
+ // attention: deadline completion check and wg.Done is to be done in both routines
+ go dh.pOnuTP.configureUniTp(dctx, uint8(uniData.PersUniID), uniData.PersTpPath, &wg)
+ //the wait.. function is responsible for tpProcMutex.Unlock()
+ _ = dh.pOnuTP.waitForTpCompletion(cancel, &wg) //wait for background process to finish and collect their result
+ return
+ }
+ dh.pOnuTP.unlockTpProcMutex()
+ dh.reconciling = false
+}
+
+func (dh *deviceHandler) deleteDevice(device *voltha.Device) error {
+ logger.Debugw("delete-device", log.Fields{"device-id": device.Id, "SerialNumber": device.SerialNumber})
+ if err := dh.pOnuTP.deleteOnuTpPathKvStore(context.TODO()); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (dh *deviceHandler) rebootDevice(device *voltha.Device) error {
+ logger.Debugw("reboot-device", log.Fields{"device-id": device.Id, "SerialNumber": device.SerialNumber})
+ if device.ConnectStatus != voltha.ConnectStatus_REACHABLE {
+ logger.Errorw("device-unreachable", log.Fields{"device-id": device.Id, "SerialNumber": device.SerialNumber})
+ return errors.New("device-unreachable")
+ }
+ if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), dh.deviceID, voltha.ConnectStatus_UNREACHABLE,
+ voltha.OperStatus_DISCOVERED); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-updating-device-state", log.Fields{"device-id": dh.deviceID, "error": err})
+ return err
+ }
+ if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "rebooting-onu"); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-updating-reason-state", log.Fields{"device-id": dh.deviceID, "error": err})
+ return err
+ }
+ dh.deviceReason = "rebooting-onu"
+
+ if err := dh.sendMsgToOlt(context.TODO(), "reboot", nil); err != nil {
+ logger.Debugw("error", log.Fields{"error": err})
+ }
+
+ return nil
+}
+
+// deviceHandler methods that implement the adapters interface requests## end #########
+// #####################################################################################
+
+// ################ to be updated acc. needs of ONU Device ########################
+// deviceHandler StateMachine related state transition methods ##### begin #########
+
+func (dh *deviceHandler) logStateChange(e *fsm.Event) {
+ logger.Debugw("Device FSM: ", log.Fields{"event name": string(e.Event), "src state": string(e.Src), "dst state": string(e.Dst), "device-id": dh.deviceID})
+}
+
+// doStateInit provides the device update to the core
+func (dh *deviceHandler) doStateInit(e *fsm.Event) {
+
+ logger.Debug("doStateInit-started")
+ var err error
+
+ dh.device.Root = false
+ dh.device.Vendor = "OpenONU"
+ dh.device.Model = "go"
+ dh.device.Reason = "activating-onu"
+ dh.deviceReason = "activating-onu"
+
+ dh.logicalDeviceID = dh.deviceID // really needed - what for ??? //TODO!!!
+
+ if !dh.reconciling {
+ _ = dh.coreProxy.DeviceUpdate(context.TODO(), dh.device)
+ } else {
+ logger.Debugw("reconciling - don't notify core about DeviceUpdate",
+ log.Fields{"device-id": dh.deviceID})
+ }
+
+ dh.parentID = dh.device.ParentId
+ dh.ponPortNumber = dh.device.ParentPortNo
+
+ dh.ProxyAddressID = dh.device.ProxyAddress.GetDeviceId()
+ dh.ProxyAddressType = dh.device.ProxyAddress.GetDeviceType()
+ logger.Debugw("device-updated", log.Fields{"device-id": dh.deviceID, "proxyAddressID": dh.ProxyAddressID,
+ "proxyAddressType": dh.ProxyAddressType, "SNR": dh.device.SerialNumber,
+ "ParentId": dh.parentID, "ParentPortNo": dh.ponPortNumber})
+
+ /*
+ self._pon = PonPort.create(self, self._pon_port_number)
+ self._pon.add_peer(self.parent_id, self._pon_port_number)
+ self.logger.debug('adding-pon-port-to-agent',
+ type=self._pon.get_port().type,
+ admin_state=self._pon.get_port().admin_state,
+ oper_status=self._pon.get_port().oper_status,
+ )
+ */
+ if !dh.reconciling {
+ logger.Debugw("adding-pon-port", log.Fields{"deviceID": dh.deviceID, "ponPortNo": dh.ponPortNumber})
+ var ponPortNo uint32 = 1
+ if dh.ponPortNumber != 0 {
+ ponPortNo = dh.ponPortNumber
+ }
+
+ pPonPort := &voltha.Port{
+ PortNo: ponPortNo,
+ //Label: fmt.Sprintf("pon-%d", ponPortNo),
+ Label: "PON port",
+ Type: voltha.Port_PON_ONU,
+ OperStatus: voltha.OperStatus_ACTIVE,
+ Peers: []*voltha.Port_PeerPort{{DeviceId: dh.parentID, // Peer device is OLT
+ PortNo: ponPortNo}}, // Peer port is parent's port number
+ }
+ if err = dh.coreProxy.PortCreated(context.TODO(), dh.deviceID, pPonPort); err != nil {
+ logger.Fatalf("Device FSM: PortCreated-failed-%s", err)
+ e.Cancel(err)
+ return
+ }
+ } else {
+ logger.Debugw("reconciling - pon-port already added", log.Fields{"device-id": dh.deviceID})
+ }
+ logger.Debug("doStateInit-done")
+}
+
+// postInit setups the DeviceEntry for the conerned device
+func (dh *deviceHandler) postInit(e *fsm.Event) {
+
+ logger.Debug("postInit-started")
+ var err error
+ /*
+ dh.Client = oop.NewOpenoltClient(dh.clientCon)
+ dh.pTransitionMap.Handle(ctx, GrpcConnected)
+ return nil
+ */
+ if err = dh.addOnuDeviceEntry(context.TODO()); err != nil {
+ logger.Fatalf("Device FSM: addOnuDeviceEntry-failed-%s", err)
+ e.Cancel(err)
+ return
+ }
+
+ if dh.reconciling {
+ go dh.reconcileDeviceOnuInd()
+ // reconcilement will be continued after mib download is done
+ }
+ /*
+ ############################################################################
+ # Setup Alarm handler
+ self.events = AdapterEvents(self.core_proxy, device.id, self.logical_device_id,
+ device.serial_number)
+ ############################################################################
+ # Setup PM configuration for this device
+ # Pass in ONU specific options
+ kwargs = {
+ OnuPmMetrics.DEFAULT_FREQUENCY_KEY: OnuPmMetrics.DEFAULT_ONU_COLLECTION_FREQUENCY,
+ 'heartbeat': self.heartbeat,
+ OnuOmciPmMetrics.OMCI_DEV_KEY: self._onu_omci_device
+ }
+ self.logger.debug('create-pm-metrics', device_id=device.id, serial_number=device.serial_number)
+ self._pm_metrics = OnuPmMetrics(self.events, self.core_proxy, self.device_id,
+ self.logical_device_id, device.serial_number,
+ grouped=True, freq_override=False, **kwargs)
+ pm_config = self._pm_metrics.make_proto()
+ self._onu_omci_device.set_pm_config(self._pm_metrics.omci_pm.openomci_interval_pm)
+ self.logger.info("initial-pm-config", device_id=device.id, serial_number=device.serial_number)
+ yield self.core_proxy.device_pm_config_update(pm_config, init=True)
+
+ # Note, ONU ID and UNI intf set in add_uni_port method
+ self._onu_omci_device.alarm_synchronizer.set_alarm_params(mgr=self.events,
+ ani_ports=[self._pon])
+
+ # Code to Run OMCI Test Action
+ kwargs_omci_test_action = {
+ OmciTestRequest.DEFAULT_FREQUENCY_KEY:
+ OmciTestRequest.DEFAULT_COLLECTION_FREQUENCY
+ }
+ serial_number = device.serial_number
+ self._test_request = OmciTestRequest(self.core_proxy,
+ self.omci_agent, self.device_id,
+ AniG, serial_number,
+ self.logical_device_id,
+ exclusive=False,
+ **kwargs_omci_test_action)
+
+ self.enabled = True
+ else:
+ self.logger.info('onu-already-activated')
+ */
+ logger.Debug("postInit-done")
+}
+
+// doStateConnected get the device info and update to voltha core
+// for comparison of the original method (not that easy to uncomment): compare here:
+// voltha-openolt-adapter/adaptercore/device_handler.go
+// -> this one obviously initiates all communication interfaces of the device ...?
+func (dh *deviceHandler) doStateConnected(e *fsm.Event) {
+
+ logger.Debug("doStateConnected-started")
+ err := errors.New("device FSM: function not implemented yet")
+ e.Cancel(err)
+ logger.Debug("doStateConnected-done")
+}
+
+// doStateUp handle the onu up indication and update to voltha core
+func (dh *deviceHandler) doStateUp(e *fsm.Event) {
+
+ logger.Debug("doStateUp-started")
+ err := errors.New("device FSM: function not implemented yet")
+ e.Cancel(err)
+ logger.Debug("doStateUp-done")
+
+ /*
+ // Synchronous call to update device state - this method is run in its own go routine
+ if err := dh.coreProxy.DeviceStateUpdate(ctx, dh.device.Id, voltha.ConnectStatus_REACHABLE,
+ voltha.OperStatus_ACTIVE); err != nil {
+ logger.Errorw("Failed to update device with OLT UP indication", log.Fields{"deviceID": dh.device.Id, "error": err})
+ return err
+ }
+ return nil
+ */
+}
+
+// doStateDown handle the onu down indication
+func (dh *deviceHandler) doStateDown(e *fsm.Event) {
+
+ logger.Debug("doStateDown-started")
+ var err error
+
+ device := dh.device
+ if device == nil {
+ /*TODO: needs to handle error scenarios */
+ logger.Error("Failed to fetch handler device")
+ e.Cancel(err)
+ return
+ }
+
+ cloned := proto.Clone(device).(*voltha.Device)
+ logger.Debugw("do-state-down", log.Fields{"ClonedDeviceID": cloned.Id})
+ /*
+ // Update the all ports state on that device to disable
+ if er := dh.coreProxy.PortsStateUpdate(ctx, cloned.Id, voltha.OperStatus_UNKNOWN); er != nil {
+ logger.Errorw("updating-ports-failed", log.Fields{"deviceID": device.Id, "error": er})
+ return er
+ }
+
+ //Update the device oper state and connection status
+ cloned.OperStatus = voltha.OperStatus_UNKNOWN
+ cloned.ConnectStatus = common.ConnectStatus_UNREACHABLE
+ dh.device = cloned
+
+ if er := dh.coreProxy.DeviceStateUpdate(ctx, cloned.Id, cloned.ConnectStatus, cloned.OperStatus); er != nil {
+ logger.Errorw("error-updating-device-state", log.Fields{"deviceID": device.Id, "error": er})
+ return er
+ }
+
+ //get the child device for the parent device
+ onuDevices, err := dh.coreProxy.GetChildDevices(ctx, dh.device.Id)
+ if err != nil {
+ logger.Errorw("failed to get child devices information", log.Fields{"deviceID": dh.device.Id, "error": err})
+ return err
+ }
+ for _, onuDevice := range onuDevices.Items {
+
+ // Update onu state as down in onu adapter
+ onuInd := oop.OnuIndication{}
+ onuInd.OperState = "down"
+ er := dh.AdapterProxy.SendInterAdapterMessage(ctx, &onuInd, ic.InterAdapterMessageType_ONU_IND_REQUEST,
+ "openolt", onuDevice.Type, onuDevice.Id, onuDevice.ProxyAddress.DeviceId, "")
+ if er != nil {
+ logger.Errorw("Failed to send inter-adapter-message", log.Fields{"OnuInd": onuInd,
+ "From Adapter": "openolt", "DevieType": onuDevice.Type, "DeviceID": onuDevice.Id})
+ //Do not return here and continue to process other ONUs
+ }
+ }
+ // * Discovered ONUs entries need to be cleared , since after OLT
+ // is up, it starts sending discovery indications again* /
+ dh.discOnus = sync.Map{}
+ logger.Debugw("do-state-down-end", log.Fields{"deviceID": device.Id})
+ return nil
+ */
+ err = errors.New("device FSM: function not implemented yet")
+ e.Cancel(err)
+ logger.Debug("doStateDown-done")
+}
+
+// deviceHandler StateMachine related state transition methods ##### end #########
+// #################################################################################
+
+// ###################################################
+// deviceHandler utility methods ##### begin #########
+
+//getOnuDeviceEntry getsthe ONU device entry and may wait until its value is defined
+func (dh *deviceHandler) getOnuDeviceEntry(aWait bool) *OnuDeviceEntry {
+ dh.lockDevice.RLock()
+ pOnuDeviceEntry := dh.pOnuOmciDevice
+ if aWait && pOnuDeviceEntry == nil {
+ //keep the read sema short to allow for subsequent write
+ dh.lockDevice.RUnlock()
+ logger.Debugw("Waiting for DeviceEntry to be set ...", log.Fields{"device-id": dh.deviceID})
+ // based on concurrent processing the deviceEntry setup may not yet be finished at his point
+ // so it might be needed to wait here for that event with some timeout
+ select {
+ case <-time.After(60 * time.Second): //timer may be discussed ...
+ logger.Errorw("No valid DeviceEntry set after maxTime", log.Fields{"device-id": dh.deviceID})
+ return nil
+ case <-dh.deviceEntrySet:
+ logger.Debugw("devicEntry ready now - continue", log.Fields{"device-id": dh.deviceID})
+ // if written now, we can return the written value without sema
+ return dh.pOnuOmciDevice
+ }
+ }
+ dh.lockDevice.RUnlock()
+ return pOnuDeviceEntry
+}
+
+//setOnuDeviceEntry sets the ONU device entry within the handler
+func (dh *deviceHandler) setOnuDeviceEntry(
+ apDeviceEntry *OnuDeviceEntry, apOnuTp *onuUniTechProf) {
+ dh.lockDevice.Lock()
+ defer dh.lockDevice.Unlock()
+ dh.pOnuOmciDevice = apDeviceEntry
+ dh.pOnuTP = apOnuTp
+}
+
+//addOnuDeviceEntry creates a new ONU device or returns the existing
+func (dh *deviceHandler) addOnuDeviceEntry(ctx context.Context) error {
+ logger.Debugw("adding-deviceEntry", log.Fields{"device-id": dh.deviceID})
+
+ deviceEntry := dh.getOnuDeviceEntry(false)
+ if deviceEntry == nil {
+ /* costum_me_map in python code seems always to be None,
+ we omit that here first (declaration unclear) -> todo at Adapter specialization ...*/
+ /* also no 'clock' argument - usage open ...*/
+ /* and no alarm_db yet (oo.alarm_db) */
+ deviceEntry = newOnuDeviceEntry(ctx, dh.deviceID, dh.pOpenOnuAc.KVStoreHost,
+ dh.pOpenOnuAc.KVStorePort, dh.pOpenOnuAc.KVStoreType,
+ dh, dh.coreProxy, dh.AdapterProxy,
+ dh.pOpenOnuAc.pSupportedFsms) //nil as FSM pointer would yield deviceEntry internal defaults ...
+ onuTechProfProc := newOnuUniTechProf(ctx, dh.deviceID, dh)
+ //error treatment possible //TODO!!!
+ dh.setOnuDeviceEntry(deviceEntry, onuTechProfProc)
+ // fire deviceEntry ready event to spread to possibly waiting processing
+ dh.deviceEntrySet <- true
+
+ //dh.sendMsgToOlt(ctx, "adopt", nil)
+ // // update json file
+ // onustat := &OnuStatus{
+ // Id: dh.deviceID,
+ // AdminState: vc.AdminState_Types_name[int32(dh.device.AdminState)],
+ // OpeState: vc.OperStatus_Types_name[int32(dh.device.OperStatus)],
+ // ConnectState: vc.ConnectStatus_Types_name[int32(dh.device.ConnectStatus)],
+ // MacAddress: dh.device.MacAddress,
+ // }
+ // logger.Debugw("AddOnu", log.Fields{"Id": onustat.Id, "Admin": onustat.AdminState, "Ope": onustat.OpeState, "Con": onustat.ConnectState, "Mac": onustat.MacAddress})
+ // err := AddOnu(onustat)
+ // logger.Debugw("AddOnu", log.Fields{"err": err})
+ // //
+ // go WatchStatus(ctx, dh.coreProxy)
+
+ // dh.device.Root = true
+ // dh.coreProxy.DeviceUpdate(context.TODO(), dh.device)
+
+ logger.Infow("onuDeviceEntry-added", log.Fields{"device-id": dh.deviceID})
+ } else {
+ logger.Infow("onuDeviceEntry-add: Device already exists", log.Fields{"device-id": dh.deviceID})
+ }
+ return nil
+}
+
+// doStateInit provides the device update to the core
+func (dh *deviceHandler) createInterface(onuind *oop.OnuIndication) error {
+ logger.Debugw("create_interface-started", log.Fields{"OnuId": onuind.GetOnuId(),
+ "OnuIntfId": onuind.GetIntfId(), "OnuSerialNumber": onuind.GetSerialNumber()})
+
+ dh.pOnuIndication = onuind // let's revise if storing the pointer is sufficient...
+
+ if !dh.reconciling {
+ if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), dh.deviceID,
+ voltha.ConnectStatus_REACHABLE, voltha.OperStatus_ACTIVATING); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-updating-device-state", log.Fields{"device-id": dh.deviceID, "error": err})
+ }
+ } else {
+ logger.Debugw("reconciling - don't notify core about DeviceStateUpdate to ACTIVATING",
+ log.Fields{"device-id": dh.deviceID})
+ }
+ /*
+ device, err := dh.coreProxy.GetDevice(context.TODO(), dh.device.Id, dh.device.Id)
+ if err != nil || device == nil {
+ //TODO: needs to handle error scenarios
+ logger.Errorw("Failed to fetch device device at creating If", log.Fields{"err": err})
+ return errors.New("Voltha Device not found")
+ }
+ */
+
+ pDevEntry := dh.getOnuDeviceEntry(true)
+ if pDevEntry != nil {
+ if err := pDevEntry.start(context.TODO()); err != nil {
+ return err
+ }
+ } else {
+ logger.Errorw("No valid OnuDevice -aborting", log.Fields{"device-id": dh.deviceID})
+ return errors.New("no valid OnuDevice")
+ }
+ if !dh.reconciling {
+ if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "starting-openomci"); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-DeviceReasonUpdate to starting-openomci", log.Fields{"device-id": dh.deviceID, "error": err})
+ }
+ } else {
+ logger.Debugw("reconciling - don't notify core about DeviceReasonUpdate to starting-openomci",
+ log.Fields{"device-id": dh.deviceID})
+ }
+ dh.deviceReason = "starting-openomci"
+
+ /* this might be a good time for Omci Verify message? */
+ verifyExec := make(chan bool)
+ omciVerify := newOmciTestRequest(context.TODO(),
+ dh.device.Id, pDevEntry.PDevOmciCC,
+ true, true) //eclusive and allowFailure (anyway not yet checked)
+ omciVerify.performOmciTest(context.TODO(), verifyExec)
+
+ /* give the handler some time here to wait for the OMCi verification result
+ after Timeout start and try MibUpload FSM anyway
+ (to prevent stopping on just not supported OMCI verification from ONU) */
+ select {
+ case <-time.After(2 * time.Second):
+ logger.Warn("omci start-verification timed out (continue normal)")
+ case testresult := <-verifyExec:
+ logger.Infow("Omci start verification done", log.Fields{"result": testresult})
+ }
+
+ /* In py code it looks earlier (on activate ..)
+ # Code to Run OMCI Test Action
+ kwargs_omci_test_action = {
+ OmciTestRequest.DEFAULT_FREQUENCY_KEY:
+ OmciTestRequest.DEFAULT_COLLECTION_FREQUENCY
+ }
+ serial_number = device.serial_number
+ self._test_request = OmciTestRequest(self.core_proxy,
+ self.omci_agent, self.device_id,
+ AniG, serial_number,
+ self.logical_device_id,
+ exclusive=False,
+ **kwargs_omci_test_action)
+ ...
+ # Start test requests after a brief pause
+ if not self._test_request_started:
+ self._test_request_started = True
+ tststart = _STARTUP_RETRY_WAIT * (random.randint(1, 5))
+ reactor.callLater(tststart, self._test_request.start_collector)
+
+ */
+ /* which is then: in omci_test_request.py : */
+ /*
+ def start_collector(self, callback=None):
+ """
+ Start the collection loop for an adapter if the frequency > 0
+
+ :param callback: (callable) Function to call to collect PM data
+ """
+ self.logger.info("starting-pm-collection", device_name=self.name, default_freq=self.default_freq)
+ if callback is None:
+ callback = self.perform_test_omci
+
+ if self.lc is None:
+ self.lc = LoopingCall(callback)
+
+ if self.default_freq > 0:
+ self.lc.start(interval=self.default_freq / 10)
+
+ def perform_test_omci(self):
+ """
+ Perform the initial test request
+ """
+ ani_g_entities = self._device.configuration.ani_g_entities
+ ani_g_entities_ids = list(ani_g_entities.keys()) if ani_g_entities \
+ is not None else None
+ self._entity_id = ani_g_entities_ids[0]
+ self.logger.info('perform-test', entity_class=self._entity_class,
+ entity_id=self._entity_id)
+ try:
+ frame = MEFrame(self._entity_class, self._entity_id, []).test()
+ result = yield self._device.omci_cc.send(frame)
+ if not result.fields['omci_message'].fields['success_code']:
+ self.logger.info('Self-Test Submitted Successfully',
+ code=result.fields[
+ 'omci_message'].fields['success_code'])
+ else:
+ raise TestFailure('Test Failure: {}'.format(
+ result.fields['omci_message'].fields['success_code']))
+ except TimeoutError as e:
+ self.deferred.errback(failure.Failure(e))
+
+ except Exception as e:
+ self.logger.exception('perform-test-Error', e=e,
+ class_id=self._entity_class,
+ entity_id=self._entity_id)
+ self.deferred.errback(failure.Failure(e))
+
+ */
+
+
+ /* Note: Even though FSM calls look 'synchronous' here, FSM is running in background with the effect that possible errors
+ * within the MibUpload are not notified in the OnuIndication response, this might be acceptable here,
+ * as further OltAdapter processing may rely on the deviceReason event 'MibUploadDone' as a result of the FSM processing
+ * otherwise some processing synchronization would be required - cmp. e.g TechProfile processing
+ */
+ pMibUlFsm := pDevEntry.pMibUploadFsm.pFsm
+ if pMibUlFsm != nil {
+ if pMibUlFsm.Is(ulStDisabled) {
+ if err := pMibUlFsm.Event(ulEvStart); err != nil {
+ logger.Errorw("MibSyncFsm: Can't go to state starting", log.Fields{"err": err})
+ return errors.New("can't go to state starting")
+ }
+ logger.Debugw("MibSyncFsm", log.Fields{"state": string(pMibUlFsm.Current())})
+ //Determine ONU status and start/re-start MIB Synchronization tasks
+ //Determine if this ONU has ever synchronized
+ if true { //TODO: insert valid check
+ if err := pMibUlFsm.Event(ulEvResetMib); err != nil {
+ logger.Errorw("MibSyncFsm: Can't go to state resetting_mib", log.Fields{"err": err})
+ return errors.New("can't go to state resetting_mib")
+ }
+ } else {
+ if err := pMibUlFsm.Event(ulEvExamineMds); err != nil {
+ logger.Errorw("MibSyncFsm: Can't go to state examine_mds", log.Fields{"err": err})
+ return errors.New("can't go to examine_mds")
+ }
+ logger.Debugw("state of MibSyncFsm", log.Fields{"state": string(pMibUlFsm.Current())})
+ //Examine the MIB Data Sync
+ // callbacks to be handled:
+ // Event(ulEvSuccess)
+ // Event(ulEvTimeout)
+ // Event(ulEvMismatch)
+ }
+ } else {
+ logger.Errorw("wrong state of MibSyncFsm - want: disabled", log.Fields{"have": string(pMibUlFsm.Current())})
+ return errors.New("wrong state of MibSyncFsm")
+ }
+ } else {
+ logger.Errorw("MibSyncFsm invalid - cannot be executed!!", log.Fields{"device-id": dh.deviceID})
+ return errors.New("cannot execut MibSync")
+ }
+ return nil
+}
+
+func (dh *deviceHandler) updateInterface(onuind *oop.OnuIndication) error {
+ if dh.deviceReason != "stopping-openomci" {
+ logger.Debugw("updateInterface-started - stopping-device", log.Fields{"device-id": dh.deviceID})
+ //stop all running SM processing - make use of the DH-state as mirrored in the deviceReason
+ pDevEntry := dh.getOnuDeviceEntry(false)
+ if pDevEntry == nil {
+ logger.Errorw("No valid OnuDevice -aborting", log.Fields{"device-id": dh.deviceID})
+ return errors.New("no valid OnuDevice")
+ }
+
+ switch dh.deviceReason {
+ case "starting-openomci":
+ { //MIBSync FSM may run
+ pMibUlFsm := pDevEntry.pMibUploadFsm.pFsm
+ if pMibUlFsm != nil {
+ _ = pMibUlFsm.Event(ulEvStop) //TODO!! verify if MibSyncFsm stop-processing is sufficient (to allow it again afterwards)
+ }
+ }
+ case "discovery-mibsync-complete":
+ { //MibDownload may run
+ pMibDlFsm := pDevEntry.pMibDownloadFsm.pFsm
+ if pMibDlFsm != nil {
+ _ = pMibDlFsm.Event(dlEvReset)
+ }
+ }
+ default:
+ {
+ //port lock/unlock FSM's may be active
+ if dh.pUnlockStateFsm != nil {
+ _ = dh.pUnlockStateFsm.pAdaptFsm.pFsm.Event(uniEvReset)
+ }
+ if dh.pLockStateFsm != nil {
+ _ = dh.pLockStateFsm.pAdaptFsm.pFsm.Event(uniEvReset)
+ }
+ //techProfile related PonAniConfigFsm FSM may be active
+ // maybe encapsulated as OnuTP method - perhaps later in context of module splitting
+ if dh.pOnuTP.pAniConfigFsm != nil {
+ _ = dh.pOnuTP.pAniConfigFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+ }
+ for _, uniPort := range dh.uniEntityMap {
+ //reset the TechProfileConfig Done state for all (active) UNI's
+ dh.pOnuTP.setConfigDone(uniPort.uniID, false)
+ // reset the possibly existing VlanConfigFsm
+ if pVlanFilterFsm, exist := dh.UniVlanConfigFsmMap[uniPort.uniID]; exist {
+ //VlanFilterFsm exists and was already started
+ pVlanFilterStatemachine := pVlanFilterFsm.pAdaptFsm.pFsm
+ if pVlanFilterStatemachine != nil {
+ _ = pVlanFilterStatemachine.Event(vlanEvReset)
+ }
+ }
+ }
+ }
+ //TODO!!! care about PM/Alarm processing once started
+ }
+ //TODO: from here the deviceHandler FSM itself may be stuck in some of the initial states
+ // (mainly the still separate 'Event states')
+ // so it is questionable, how this is resolved after some possible re-enable
+ // assumption there is obviously, that the system may continue with some 'after "mib-download-done" state'
+
+ //stop/remove(?) the device entry
+ _ = pDevEntry.stop(context.TODO()) //maybe some more sophisticated context treatment should be used here?
+
+ //TODO!!! remove existing traffic profiles
+ /* from py code, if TP's exist, remove them - not yet implemented
+ self._tp = dict()
+ # Let TP download happen again
+ for uni_id in self._tp_service_specific_task:
+ self._tp_service_specific_task[uni_id].clear()
+ for uni_id in self._tech_profile_download_done:
+ self._tech_profile_download_done[uni_id].clear()
+ */
+
+ dh.disableUniPortStateUpdate()
+
+ if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "stopping-openomci"); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-DeviceReasonUpdate to 'stopping-openomci'",
+ log.Fields{"device-id": dh.deviceID, "error": err})
+ // abort: system behavior is just unstable ...
+ return err
+ }
+ dh.deviceReason = "stopping-openomci"
+
+ if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), dh.deviceID,
+ voltha.ConnectStatus_UNREACHABLE, voltha.OperStatus_DISCOVERED); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-updating-device-state unreachable-discovered",
+ log.Fields{"device-id": dh.deviceID, "error": err})
+ // abort: system behavior is just unstable ...
+ return err
+ }
+ } else {
+ logger.Debugw("updateInterface - device already stopped", log.Fields{"device-id": dh.deviceID})
+ }
+ return nil
+}
+
+func (dh *deviceHandler) processMibDatabaseSyncEvent(devEvent OnuDeviceEvent) {
+ logger.Debugw("MibInSync event received", log.Fields{"device-id": dh.deviceID})
+ if !dh.reconciling {
+ //initiate DevStateUpdate
+ if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "discovery-mibsync-complete"); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-DeviceReasonUpdate to 'mibsync-complete'", log.Fields{
+ "device-id": dh.deviceID, "error": err})
+ } else {
+ logger.Infow("dev reason updated to 'MibSync complete'", log.Fields{"deviceID": dh.deviceID})
+ }
+ } else {
+ logger.Debugw("reconciling - don't notify core about DeviceReasonUpdate to mibsync-complete",
+ log.Fields{"device-id": dh.deviceID})
+ }
+ dh.deviceReason = "discovery-mibsync-complete"
+
+ i := uint8(0) //UNI Port limit: see MaxUnisPerOnu (by now 16) (OMCI supports max 255 p.b.)
+ pDevEntry := dh.getOnuDeviceEntry(false)
+ if unigInstKeys := pDevEntry.pOnuDB.getSortedInstKeys(me.UniGClassID); len(unigInstKeys) > 0 {
+ for _, mgmtEntityID := range unigInstKeys {
+ logger.Debugw("Add UNI port for stored UniG instance:", log.Fields{
+ "device-id": dh.deviceID, "UnigMe EntityID": mgmtEntityID})
+ dh.addUniPort(mgmtEntityID, i, uniPPTP)
+ i++
+ }
+ } else {
+ logger.Debugw("No UniG instances found", log.Fields{"device-id": dh.deviceID})
+ }
+ if veipInstKeys := pDevEntry.pOnuDB.getSortedInstKeys(me.VirtualEthernetInterfacePointClassID); len(veipInstKeys) > 0 {
+ for _, mgmtEntityID := range veipInstKeys {
+ logger.Debugw("Add VEIP acc. to stored VEIP instance:", log.Fields{
+ "device-id": dh.deviceID, "VEIP EntityID": mgmtEntityID})
+ dh.addUniPort(mgmtEntityID, i, uniVEIP)
+ i++
+ }
+ } else {
+ logger.Debugw("No VEIP instances found", log.Fields{"device-id": dh.deviceID})
+ }
+ if i == 0 {
+ logger.Warnw("No PPTP instances found", log.Fields{"device-id": dh.deviceID})
+ }
+
+ /* 200605: lock processing after initial MIBUpload removed now as the ONU should be in the lock state per default here
+ * left the code here as comment in case such processing should prove needed unexpectedly
+ // Init Uni Ports to Admin locked state
+ // maybe not really needed here as UNI ports should be locked by default, but still left as available in python code
+ // *** should generate UniLockStateDone event *****
+ if dh.pLockStateFsm == nil {
+ dh.createUniLockFsm(true, UniLockStateDone)
+ } else { //LockStateFSM already init
+ dh.pLockStateFsm.SetSuccessEvent(UniLockStateDone)
+ dh.runUniLockFsm(true)
+ }
+ }
+ case UniLockStateDone:
+ {
+ logger.Infow("UniLockStateDone event: Starting MIB download", log.Fields{"device-id": dh.deviceID})
+ * lockState processing commented out
+ */
+ /* Mib download procedure -
+ ***** should run over 'downloaded' state and generate MibDownloadDone event *****
+ */
+ pMibDlFsm := pDevEntry.pMibDownloadFsm.pFsm
+ if pMibDlFsm != nil {
+ if pMibDlFsm.Is(dlStDisabled) {
+ if err := pMibDlFsm.Event(dlEvStart); err != nil {
+ logger.Errorw("MibDownloadFsm: Can't go to state starting", log.Fields{"err": err})
+ // maybe try a FSM reset and then again ... - TODO!!!
+ } else {
+ logger.Debugw("MibDownloadFsm", log.Fields{"state": string(pMibDlFsm.Current())})
+ // maybe use more specific states here for the specific download steps ...
+ if err := pMibDlFsm.Event(dlEvCreateGal); err != nil {
+ logger.Errorw("MibDownloadFsm: Can't start CreateGal", log.Fields{"err": err})
+ } else {
+ logger.Debugw("state of MibDownloadFsm", log.Fields{"state": string(pMibDlFsm.Current())})
+ //Begin MIB data download (running autonomously)
+ }
+ }
+ } else {
+ logger.Errorw("wrong state of MibDownloadFsm - want: disabled", log.Fields{"have": string(pMibDlFsm.Current())})
+ // maybe try a FSM reset and then again ... - TODO!!!
+ }
+ /***** Mib download started */
+ } else {
+ logger.Errorw("MibDownloadFsm invalid - cannot be executed!!", log.Fields{"device-id": dh.deviceID})
+ }
+}
+
+func (dh *deviceHandler) processMibDownloadDoneEvent(devEvent OnuDeviceEvent) {
+ logger.Debugw("MibDownloadDone event received", log.Fields{"device-id": dh.deviceID})
+ if !dh.reconciling {
+ if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), dh.deviceID,
+ voltha.ConnectStatus_REACHABLE, voltha.OperStatus_ACTIVE); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-updating-device-state", log.Fields{"device-id": dh.deviceID, "error": err})
+ } else {
+ logger.Debugw("dev state updated to 'Oper.Active'", log.Fields{"device-id": dh.deviceID})
+ }
+ } else {
+ logger.Debugw("reconciling - don't notify core about DeviceStateUpdate to ACTIVE",
+ log.Fields{"device-id": dh.deviceID})
+ }
+ if !dh.reconciling {
+ if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "initial-mib-downloaded"); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-DeviceReasonUpdate to 'initial-mib-downloaded'",
+ log.Fields{"device-id": dh.deviceID, "error": err})
+ } else {
+ logger.Infow("dev reason updated to 'initial-mib-downloaded'", log.Fields{"device-id": dh.deviceID})
+ }
+ } else {
+ logger.Debugw("reconciling - don't notify core about DeviceReasonUpdate to initial-mib-downloaded",
+ log.Fields{"device-id": dh.deviceID})
+ }
+ dh.deviceReason = "initial-mib-downloaded"
+ if dh.pUnlockStateFsm == nil {
+ dh.createUniLockFsm(false, UniUnlockStateDone)
+ } else { //UnlockStateFSM already init
+ dh.pUnlockStateFsm.setSuccessEvent(UniUnlockStateDone)
+ dh.runUniLockFsm(false)
+ }
+}
+
+func (dh *deviceHandler) processUniUnlockStateDoneEvent(devEvent OnuDeviceEvent) {
+ go dh.enableUniPortStateUpdate() //cmp python yield self.enable_ports()
+
+ if !dh.reconciling {
+ logger.Infow("UniUnlockStateDone event: Sending OnuUp event", log.Fields{"device-id": dh.deviceID})
+ raisedTs := time.Now().UnixNano()
+ go dh.sendOnuOperStateEvent(voltha.OperStatus_ACTIVE, dh.deviceID, raisedTs) //cmp python onu_active_event
+ } else {
+ logger.Debugw("reconciling - don't notify core that onu went to active but trigger tech profile config",
+ log.Fields{"device-id": dh.deviceID})
+ go dh.reconcileDeviceTechProf()
+ //TODO: further actions e.g. restore flows, metrics, ...
+ }
+}
+
+func (dh *deviceHandler) processOmciAniConfigDoneEvent(devEvent OnuDeviceEvent) {
+ logger.Debugw("OmciAniConfigDone event received", log.Fields{"device-id": dh.deviceID})
+ if dh.deviceReason != "tech-profile-config-download-success" {
+ // which may be the case from some previous actvity on another UNI Port of the ONU
+ if !dh.reconciling {
+ if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "tech-profile-config-download-success"); err != nil {
+ //TODO with VOL-3045/VOL-3046: return the error and stop further processing
+ logger.Errorw("error-DeviceReasonUpdate to 'tech-profile-config-download-success'",
+ log.Fields{"device-id": dh.deviceID, "error": err})
+ } else {
+ logger.Infow("update dev reason to 'tech-profile-config-download-success'",
+ log.Fields{"device-id": dh.deviceID})
+ }
+ } else {
+ logger.Debugw("reconciling - don't notify core about DeviceReasonUpdate to tech-profile-config-download-success",
+ log.Fields{"device-id": dh.deviceID})
+ }
+ //set internal state anyway - as it was done
+ dh.deviceReason = "tech-profile-config-download-success"
+ }
+}
+
+func (dh *deviceHandler) processOmciVlanFilterDoneEvent(devEvent OnuDeviceEvent) {
+ logger.Debugw("OmciVlanFilterDone event received",
+ log.Fields{"device-id": dh.deviceID})
+
+ if dh.deviceReason != "omci-flows-pushed" {
+ // which may be the case from some previous actvity on another UNI Port of the ONU
+ // or even some previous flow add activity on the same port
+ if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "omci-flows-pushed"); err != nil {
+ logger.Errorw("error-DeviceReasonUpdate to 'omci-flows-pushed'",
+ log.Fields{"device-id": dh.deviceID, "error": err})
+ } else {
+ logger.Infow("updated dev reason to ''omci-flows-pushed'",
+ log.Fields{"device-id": dh.deviceID})
+ }
+ //set internal state anyway - as it was done
+ dh.deviceReason = "omci-flows-pushed"
+ }
+}
+
+//deviceProcStatusUpdate evaluates possible processing events and initiates according next activities
+func (dh *deviceHandler) deviceProcStatusUpdate(devEvent OnuDeviceEvent) {
+ switch devEvent {
+ case MibDatabaseSync:
+ {
+ dh.processMibDatabaseSyncEvent(devEvent)
+ }
+ case MibDownloadDone:
+ {
+ dh.processMibDownloadDoneEvent(devEvent)
+
+ }
+ case UniUnlockStateDone:
+ {
+ dh.processUniUnlockStateDoneEvent(devEvent)
+
+ }
+ case OmciAniConfigDone:
+ {
+ dh.processOmciAniConfigDoneEvent(devEvent)
+
+ }
+ case OmciVlanFilterDone:
+ {
+ dh.processOmciVlanFilterDoneEvent(devEvent)
+
+ }
+ default:
+ {
+ logger.Warnw("unhandled-device-event", log.Fields{"device-id": dh.deviceID, "event": devEvent})
+ }
+ } //switch
+}
+
+func (dh *deviceHandler) addUniPort(aUniInstNo uint16, aUniID uint8, aPortType uniPortType) {
+ uniNo := mkUniPortNum(dh.pOnuIndication.GetIntfId(), dh.pOnuIndication.GetOnuId(),
+ uint32(aUniID))
+ if _, present := dh.uniEntityMap[uniNo]; present {
+ logger.Warnw("onuUniPort-add: Port already exists", log.Fields{"for InstanceId": aUniInstNo})
+ } else {
+ //with arguments aUniID, a_portNo, aPortType
+ pUniPort := newOnuUniPort(aUniID, uniNo, aUniInstNo, aPortType)
+ if pUniPort == nil {
+ logger.Warnw("onuUniPort-add: Could not create Port", log.Fields{"for InstanceId": aUniInstNo})
+ } else {
+ //store UniPort with the System-PortNumber key
+ dh.uniEntityMap[uniNo] = pUniPort
+ if !dh.reconciling {
+ // create announce the UniPort to the core as VOLTHA Port object
+ if err := pUniPort.createVolthaPort(dh); err == nil {
+ logger.Infow("onuUniPort-added", log.Fields{"for PortNo": uniNo})
+ } //error logging already within UniPort method
+ } else {
+ logger.Debugw("reconciling - onuUniPort already added", log.Fields{"for PortNo": uniNo, "device-id": dh.deviceID})
+ }
+ }
+ }
+}
+
+// enableUniPortStateUpdate enables UniPortState and update core port state accordingly
+func (dh *deviceHandler) enableUniPortStateUpdate() {
+
+
+ for uniNo, uniPort := range dh.uniEntityMap {
+ // only if this port is validated for operState transfer
+ if (1<<uniPort.uniID)&activeUniPortStateUpdateMask == (1 << uniPort.uniID) {
+ logger.Infow("onuUniPort-forced-OperState-ACTIVE", log.Fields{"for PortNo": uniNo})
+ uniPort.setOperState(vc.OperStatus_ACTIVE)
+ if !dh.reconciling {
+ //maybe also use getter functions on uniPort - perhaps later ...
+ go dh.coreProxy.PortStateUpdate(context.TODO(), dh.deviceID, voltha.Port_ETHERNET_UNI, uniPort.portNo, uniPort.operState)
+ } else {
+ logger.Debugw("reconciling - don't notify core about PortStateUpdate", log.Fields{"device-id": dh.deviceID})
+ }
+ }
+ }
+}
+
+// Disable UniPortState and update core port state accordingly
+func (dh *deviceHandler) disableUniPortStateUpdate() {
+ for uniNo, uniPort := range dh.uniEntityMap {
+ // only if this port is validated for operState transfer
+ if (1<<uniPort.uniID)&activeUniPortStateUpdateMask == (1 << uniPort.uniID) {
+ logger.Infow("onuUniPort-forced-OperState-UNKNOWN", log.Fields{"for PortNo": uniNo})
+ uniPort.setOperState(vc.OperStatus_UNKNOWN)
+ //maybe also use getter functions on uniPort - perhaps later ...
+ go dh.coreProxy.PortStateUpdate(context.TODO(), dh.deviceID, voltha.Port_ETHERNET_UNI, uniPort.portNo, uniPort.operState)
+ }
+ }
+}
+
+// ONU_Active/Inactive announcement on system KAFKA bus
+// tried to re-use procedure of oltUpDownIndication from openolt_eventmgr.go with used values from Py code
+func (dh *deviceHandler) sendOnuOperStateEvent(aOperState vc.OperStatus_Types, aDeviceID string, raisedTs int64) {
+ var de voltha.DeviceEvent
+ eventContext := make(map[string]string)
+ parentDevice, err := dh.coreProxy.GetDevice(context.TODO(), dh.parentID, dh.parentID)
+ if err != nil || parentDevice == nil {
+ logger.Errorw("Failed to fetch parent device for OnuEvent",
+ log.Fields{"parentID": dh.parentID, "err": err})
+ }
+ oltSerialNumber := parentDevice.SerialNumber
+
+ eventContext["pon-id"] = strconv.FormatUint(uint64(dh.pOnuIndication.IntfId), 10)
+ eventContext["onu-id"] = strconv.FormatUint(uint64(dh.pOnuIndication.OnuId), 10)
+ eventContext["serial-number"] = dh.device.SerialNumber
+ eventContext["olt_serial_number"] = oltSerialNumber
+ eventContext["device_id"] = aDeviceID
+ eventContext["registration_id"] = aDeviceID //py: string(device_id)??
+ logger.Debugw("prepare ONU_ACTIVATED event",
+ log.Fields{"DeviceId": aDeviceID, "EventContext": eventContext})
+
+ /* Populating device event body */
+ de.Context = eventContext
+ de.ResourceId = aDeviceID
+ if aOperState == voltha.OperStatus_ACTIVE {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", cOnuActivatedEvent, "RAISE_EVENT")
+ de.Description = fmt.Sprintf("%s Event - %s - %s",
+ cEventObjectType, cOnuActivatedEvent, "Raised")
+ } else {
+ de.DeviceEventName = fmt.Sprintf("%s_%s", cOnuActivatedEvent, "CLEAR_EVENT")
+ de.Description = fmt.Sprintf("%s Event - %s - %s",
+ cEventObjectType, cOnuActivatedEvent, "Cleared")
+ }
+ /* Send event to KAFKA */
+ if err := dh.EventProxy.SendDeviceEvent(&de, equipment, pon, raisedTs); err != nil {
+ logger.Warnw("could not send ONU_ACTIVATED event",
+ log.Fields{"device-id": aDeviceID, "error": err})
+ }
+ logger.Debugw("ONU_ACTIVATED event sent to KAFKA",
+ log.Fields{"device-id": aDeviceID, "with-EventName": de.DeviceEventName})
+}
+
+// createUniLockFsm initializes and runs the UniLock FSM to transfer the OMCI related commands for port lock/unlock
+func (dh *deviceHandler) createUniLockFsm(aAdminState bool, devEvent OnuDeviceEvent) {
+ chLSFsm := make(chan Message, 2048)
+ var sFsmName string
+ if aAdminState {
+ logger.Infow("createLockStateFSM", log.Fields{"device-id": dh.deviceID})
+ sFsmName = "LockStateFSM"
+ } else {
+ logger.Infow("createUnlockStateFSM", log.Fields{"device-id": dh.deviceID})
+ sFsmName = "UnLockStateFSM"
+ }
+
+ pDevEntry := dh.getOnuDeviceEntry(true)
+ if pDevEntry == nil {
+ logger.Errorw("No valid OnuDevice -aborting", log.Fields{"device-id": dh.deviceID})
+ return
+ }
+ pLSFsm := newLockStateFsm(pDevEntry.PDevOmciCC, aAdminState, devEvent,
+ sFsmName, dh.deviceID, chLSFsm)
+ if pLSFsm != nil {
+ if aAdminState {
+ dh.pLockStateFsm = pLSFsm
+ } else {
+ dh.pUnlockStateFsm = pLSFsm
+ }
+ dh.runUniLockFsm(aAdminState)
+ } else {
+ logger.Errorw("LockStateFSM could not be created - abort!!", log.Fields{"device-id": dh.deviceID})
+ }
+}
+
+// runUniLockFsm starts the UniLock FSM to transfer the OMCI related commands for port lock/unlock
+func (dh *deviceHandler) runUniLockFsm(aAdminState bool) {
+ /* Uni Port lock/unlock procedure -
+ ***** should run via 'adminDone' state and generate the argument requested event *****
+ */
+ var pLSStatemachine *fsm.FSM
+ if aAdminState {
+ pLSStatemachine = dh.pLockStateFsm.pAdaptFsm.pFsm
+ //make sure the opposite FSM is not running and if so, terminate it as not relevant anymore
+ if (dh.pUnlockStateFsm != nil) &&
+ (dh.pUnlockStateFsm.pAdaptFsm.pFsm.Current() != uniStDisabled) {
+ _ = dh.pUnlockStateFsm.pAdaptFsm.pFsm.Event(uniEvReset)
+ }
+ } else {
+ pLSStatemachine = dh.pUnlockStateFsm.pAdaptFsm.pFsm
+ //make sure the opposite FSM is not running and if so, terminate it as not relevant anymore
+ if (dh.pLockStateFsm != nil) &&
+ (dh.pLockStateFsm.pAdaptFsm.pFsm.Current() != uniStDisabled) {
+ _ = dh.pLockStateFsm.pAdaptFsm.pFsm.Event(uniEvReset)
+ }
+ }
+ if pLSStatemachine != nil {
+ if pLSStatemachine.Is(uniStDisabled) {
+ if err := pLSStatemachine.Event(uniEvStart); err != nil {
+ logger.Warnw("LockStateFSM: can't start", log.Fields{"err": err})
+ // maybe try a FSM reset and then again ... - TODO!!!
+ } else {
+ /***** LockStateFSM started */
+ logger.Debugw("LockStateFSM started", log.Fields{
+ "state": pLSStatemachine.Current(), "device-id": dh.deviceID})
+ }
+ } else {
+ logger.Warnw("wrong state of LockStateFSM - want: disabled", log.Fields{
+ "have": pLSStatemachine.Current(), "device-id": dh.deviceID})
+ // maybe try a FSM reset and then again ... - TODO!!!
+ }
+ } else {
+ logger.Errorw("LockStateFSM StateMachine invalid - cannot be executed!!", log.Fields{"device-id": dh.deviceID})
+ // maybe try a FSM reset and then again ... - TODO!!!
+ }
+}
+
+//setBackend provides a DB backend for the specified path on the existing KV client
+func (dh *deviceHandler) setBackend(aBasePathKvStore string) *db.Backend {
+ addr := dh.pOpenOnuAc.KVStoreHost + ":" + strconv.Itoa(dh.pOpenOnuAc.KVStorePort)
+ logger.Debugw("SetKVStoreBackend", log.Fields{"IpTarget": addr,
+ "BasePathKvStore": aBasePathKvStore, "device-id": dh.deviceID})
+ kvbackend := &db.Backend{
+ Client: dh.pOpenOnuAc.kvClient,
+ StoreType: dh.pOpenOnuAc.KVStoreType,
+ /* address config update acc. to [VOL-2736] */
+ Address: addr,
+ Timeout: dh.pOpenOnuAc.KVStoreTimeout,
+ PathPrefix: aBasePathKvStore}
+
+ return kvbackend
+}
+func (dh *deviceHandler) getFlowOfbFields(apFlowItem *ofp.OfpFlowStats, loMatchVlan *uint16,
+ loAddPcp *uint8, loIPProto *uint32) {
+
+ for _, field := range flow.GetOfbFields(apFlowItem) {
+ switch field.Type {
+ case of.OxmOfbFieldTypes_OFPXMT_OFB_ETH_TYPE:
+ {
+ logger.Debugw("FlowAdd type EthType", log.Fields{"device-id": dh.deviceID,
+ "EthType": strconv.FormatInt(int64(field.GetEthType()), 16)})
+ }
+ case of.OxmOfbFieldTypes_OFPXMT_OFB_IP_PROTO:
+ {
+ *loIPProto = field.GetIpProto()
+ logger.Debugw("FlowAdd type IpProto", log.Fields{"device-id": dh.deviceID,
+ "IpProto": strconv.FormatInt(int64(*loIPProto), 16)})
+ if *loIPProto == 2 {
+ // some workaround for TT workflow at proto == 2 (IGMP trap) -> ignore the flow
+ // avoids installing invalid EVTOCD rule
+ logger.Debugw("FlowAdd type IpProto 2: TT workaround: ignore flow",
+ log.Fields{"device-id": dh.deviceID,
+ "IpProto": strconv.FormatInt(int64(*loIPProto), 16)})
+ return
+ }
+ }
+ case of.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID:
+ {
+ *loMatchVlan = uint16(field.GetVlanVid())
+ loMatchVlanMask := uint16(field.GetVlanVidMask())
+ if !(*loMatchVlan == uint16(of.OfpVlanId_OFPVID_PRESENT) &&
+ loMatchVlanMask == uint16(of.OfpVlanId_OFPVID_PRESENT)) {
+ *loMatchVlan = *loMatchVlan & 0xFFF // not transparent: copy only ID bits
+ }
+ logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+ "VID": strconv.FormatInt(int64(*loMatchVlan), 16)})
+ }
+ case of.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP:
+ {
+ *loAddPcp = uint8(field.GetVlanPcp())
+ logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+ "PCP": loAddPcp})
+ }
+ case of.OxmOfbFieldTypes_OFPXMT_OFB_UDP_DST:
+ {
+ logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+ "UDP-DST": strconv.FormatInt(int64(field.GetUdpDst()), 16)})
+ }
+ case of.OxmOfbFieldTypes_OFPXMT_OFB_UDP_SRC:
+ {
+ logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+ "UDP-SRC": strconv.FormatInt(int64(field.GetUdpSrc()), 16)})
+ }
+ case of.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_DST:
+ {
+ logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+ "IPv4-DST": field.GetIpv4Dst()})
+ }
+ case of.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_SRC:
+ {
+ logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+ "IPv4-SRC": field.GetIpv4Src()})
+ }
+ case of.OxmOfbFieldTypes_OFPXMT_OFB_METADATA:
+ {
+ logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+ "Metadata": field.GetTableMetadata()})
+ }
+ /*
+ default:
+ {
+ //all other entires ignored
+ }
+ */
+ }
+ } //for all OfbFields
+}
+
+func (dh *deviceHandler) getFlowActions(apFlowItem *ofp.OfpFlowStats, loSetPcp *uint8, loSetVlan *uint16) {
+ for _, action := range flow.GetActions(apFlowItem) {
+ switch action.Type {
+ /* not used:
+ case of.OfpActionType_OFPAT_OUTPUT:
+ {
+ logger.Debugw("FlowAdd action type", log.Fields{"device-id": dh.deviceID,
+ "Output": action.GetOutput()})
+ }
+ */
+ case of.OfpActionType_OFPAT_PUSH_VLAN:
+ {
+ logger.Debugw("FlowAdd action type", log.Fields{"device-id": dh.deviceID,
+ "PushEthType": strconv.FormatInt(int64(action.GetPush().Ethertype), 16)})
+ }
+ case of.OfpActionType_OFPAT_SET_FIELD:
+ {
+ pActionSetField := action.GetSetField()
+ if pActionSetField.Field.OxmClass != of.OfpOxmClass_OFPXMC_OPENFLOW_BASIC {
+ logger.Warnw("FlowAdd action SetField invalid OxmClass (ignored)", log.Fields{"device-id": dh.deviceID,
+ "OxcmClass": pActionSetField.Field.OxmClass})
+ }
+ if pActionSetField.Field.GetOfbField().Type == of.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID {
+ *loSetVlan = uint16(pActionSetField.Field.GetOfbField().GetVlanVid())
+ logger.Debugw("FlowAdd Set VLAN from SetField action", log.Fields{"device-id": dh.deviceID,
+ "SetVlan": strconv.FormatInt(int64(*loSetVlan), 16)})
+ } else if pActionSetField.Field.GetOfbField().Type == of.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP {
+ *loSetPcp = uint8(pActionSetField.Field.GetOfbField().GetVlanPcp())
+ logger.Debugw("FlowAdd Set PCP from SetField action", log.Fields{"device-id": dh.deviceID,
+ "SetPcp": *loSetPcp})
+ } else {
+ logger.Warnw("FlowAdd action SetField invalid FieldType", log.Fields{"device-id": dh.deviceID,
+ "Type": pActionSetField.Field.GetOfbField().Type})
+ }
+ }
+ /*
+ default:
+ {
+ //all other entires ignored
+ }
+ */
+ }
+ } //for all Actions
+}
+
+//addFlowItemToUniPort parses the actual flow item to add it to the UniPort
+func (dh *deviceHandler) addFlowItemToUniPort(apFlowItem *ofp.OfpFlowStats, apUniPort *onuUniPort) error {
+ var loSetVlan uint16 = uint16(of.OfpVlanId_OFPVID_NONE) //noValidEntry
+ var loMatchVlan uint16 = uint16(of.OfpVlanId_OFPVID_PRESENT) //reserved VLANID entry
+ var loAddPcp, loSetPcp uint8
+ var loIPProto uint32
+ /* the TechProfileId is part of the flow Metadata - compare also comment within
+ * OLT-Adapter:openolt_flowmgr.go
+ * Metadata 8 bytes:
+ * Most Significant 2 Bytes = Inner VLAN
+ * Next 2 Bytes = Tech Profile ID(TPID)
+ * Least Significant 4 Bytes = Port ID
+ * Flow Metadata carries Tech-Profile (TP) ID and is mandatory in all
+ * subscriber related flows.
+ */
+
+ metadata := flow.GetMetadataFromWriteMetadataAction(apFlowItem)
+ if metadata == 0 {
+ logger.Debugw("FlowAdd invalid metadata - abort",
+ log.Fields{"device-id": dh.deviceID})
+ return errors.New("flowAdd invalid metadata")
+ }
+ loTpID := flow.GetTechProfileIDFromWriteMetaData(metadata)
+ logger.Debugw("FlowAdd TechProfileId", log.Fields{"device-id": dh.deviceID, "TP-Id": loTpID})
+
+ dh.getFlowOfbFields(apFlowItem, &loMatchVlan, &loAddPcp, &loIPProto)
+ if loIPProto == 2 {
+ // some workaround for TT workflow at proto == 2 (IGMP trap) -> ignore the flow
+ // avoids installing invalid EVTOCD rule
+ logger.Debugw("FlowAdd type IpProto 2: TT workaround: ignore flow",
+ log.Fields{"device-id": dh.deviceID,
+ "IpProto": strconv.FormatInt(int64(loIPProto), 16)})
+ return nil
+ }
+ dh.getFlowActions(apFlowItem, &loSetPcp, &loSetVlan)
+
+ if loSetVlan == uint16(of.OfpVlanId_OFPVID_NONE) && loMatchVlan != uint16(of.OfpVlanId_OFPVID_PRESENT) {
+ logger.Errorw("FlowAdd aborted - SetVlanId undefined, but MatchVid set", log.Fields{
+ "device-id": dh.deviceID, "UniPort": apUniPort.portNo,
+ "set_vid": strconv.FormatInt(int64(loSetVlan), 16),
+ "match_vid": strconv.FormatInt(int64(loMatchVlan), 16)})
+ //TODO!!: Use DeviceId within the error response to rwCore
+ // likewise also in other error response cases to calling components as requested in [VOL-3458]
+ return errors.New("flowAdd Set/Match VlanId inconsistent")
+ }
+ if loSetVlan == uint16(of.OfpVlanId_OFPVID_NONE) && loMatchVlan == uint16(of.OfpVlanId_OFPVID_PRESENT) {
+ logger.Debugw("FlowAdd vlan-any/copy", log.Fields{"device-id": dh.deviceID})
+ loSetVlan = loMatchVlan //both 'transparent' (copy any)
+ } else {
+ //looks like OMCI value 4097 (copyFromOuter - for Uni double tagged) is not supported here
+ if loSetVlan != uint16(of.OfpVlanId_OFPVID_PRESENT) {
+ // not set to transparent
+ loSetVlan &= 0x0FFF //mask VID bits as prerequisite for vlanConfigFsm
+ }
+ logger.Debugw("FlowAdd vlan-set", log.Fields{"device-id": dh.deviceID})
+ }
+ if _, exist := dh.UniVlanConfigFsmMap[apUniPort.uniID]; exist {
+ logger.Errorw("FlowAdd aborted - FSM already running", log.Fields{
+ "device-id": dh.deviceID, "UniPort": apUniPort.portNo})
+ return errors.New("flowAdd FSM already running")
+ }
+ return dh.createVlanFilterFsm(apUniPort,
+ loTpID, loMatchVlan, loSetVlan, loSetPcp, OmciVlanFilterDone)
+}
+
+// createVlanFilterFsm initializes and runs the VlanFilter FSM to transfer OMCI related VLAN config
+func (dh *deviceHandler) createVlanFilterFsm(apUniPort *onuUniPort,
+ aTpID uint16, aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8, aDevEvent OnuDeviceEvent) error {
+ chVlanFilterFsm := make(chan Message, 2048)
+
+ pDevEntry := dh.getOnuDeviceEntry(true)
+ if pDevEntry == nil {
+ logger.Errorw("No valid OnuDevice -aborting", log.Fields{"device-id": dh.deviceID})
+ return fmt.Errorf("no valid OnuDevice for device-id %x - aborting", dh.deviceID)
+ }
+
+ pVlanFilterFsm := NewUniVlanConfigFsm(dh, pDevEntry.PDevOmciCC, apUniPort, dh.pOnuTP,
+ pDevEntry.pOnuDB, aTpID, aDevEvent, "UniVlanConfigFsm", dh.deviceID, chVlanFilterFsm,
+ dh.pOpenOnuAc.AcceptIncrementalEvto, aMatchVlan, aSetVlan, aSetPcp)
+ if pVlanFilterFsm != nil {
+ dh.UniVlanConfigFsmMap[apUniPort.uniID] = pVlanFilterFsm
+ pVlanFilterStatemachine := pVlanFilterFsm.pAdaptFsm.pFsm
+ if pVlanFilterStatemachine != nil {
+ if pVlanFilterStatemachine.Is(vlanStDisabled) {
+ if err := pVlanFilterStatemachine.Event(vlanEvStart); err != nil {
+ logger.Warnw("UniVlanConfigFsm: can't start", log.Fields{"err": err})
+ return fmt.Errorf("can't start UniVlanConfigFsm for device-id %x", dh.deviceID)
+ }
+ /***** UniVlanConfigFsm started */
+ logger.Debugw("UniVlanConfigFsm started", log.Fields{
+ "state": pVlanFilterStatemachine.Current(), "device-id": dh.deviceID,
+ "UniPort": apUniPort.portNo})
+ } else {
+ logger.Warnw("wrong state of UniVlanConfigFsm - want: disabled", log.Fields{
+ "have": pVlanFilterStatemachine.Current(), "device-id": dh.deviceID})
+ return fmt.Errorf("uniVlanConfigFsm not in expected disabled state for device-id %x", dh.deviceID)
+ }
+ } else {
+ logger.Errorw("UniVlanConfigFsm StateMachine invalid - cannot be executed!!", log.Fields{
+ "device-id": dh.deviceID})
+ return fmt.Errorf("uniVlanConfigFsm invalid for device-id %x", dh.deviceID)
+ }
+ } else {
+ logger.Errorw("UniVlanConfigFsm could not be created - abort!!", log.Fields{
+ "device-id": dh.deviceID, "UniPort": apUniPort.portNo})
+ return fmt.Errorf("uniVlanConfigFsm could not be created for device-id %x", dh.deviceID)
+ }
+ return nil
+}
+
+//verifyUniVlanConfigRequest checks on existence of flow configuration and starts it accordingly
+func (dh *deviceHandler) verifyUniVlanConfigRequest(apUniPort *onuUniPort) {
+ if pVlanFilterFsm, exist := dh.UniVlanConfigFsmMap[apUniPort.uniID]; exist {
+ //VlanFilterFsm exists and was already started (assumed to wait for TechProfile execution here)
+ pVlanFilterStatemachine := pVlanFilterFsm.pAdaptFsm.pFsm
+ if pVlanFilterStatemachine != nil {
+ if pVlanFilterStatemachine.Is(vlanStWaitingTechProf) {
+ if err := pVlanFilterStatemachine.Event(vlanEvContinueConfig); err != nil {
+ logger.Warnw("UniVlanConfigFsm: can't continue processing", log.Fields{"err": err})
+ } else {
+ /***** UniVlanConfigFsm continued */
+ logger.Debugw("UniVlanConfigFsm continued", log.Fields{
+ "state": pVlanFilterStatemachine.Current(), "device-id": dh.deviceID,
+ "UniPort": apUniPort.portNo})
+ }
+ } else {
+ logger.Debugw("no state of UniVlanConfigFsm to be continued", log.Fields{
+ "have": pVlanFilterStatemachine.Current(), "device-id": dh.deviceID})
+ }
+ } else {
+ logger.Debugw("UniVlanConfigFsm StateMachine does not exist, no flow processing", log.Fields{
+ "device-id": dh.deviceID})
+ }
+
+ } // else: nothing to do
+}
+
+//RemoveVlanFilterFsm deletes the stored pointer to the VlanConfigFsm
+// intention is to provide this method to be called from VlanConfigFsm itself, when resources (and methods!) are cleaned up
+func (dh *deviceHandler) RemoveVlanFilterFsm(apUniPort *onuUniPort) {
+ logger.Debugw("remove UniVlanConfigFsm StateMachine", log.Fields{
+ "device-id": dh.deviceID, "uniPort": apUniPort.portNo})
+ delete(dh.UniVlanConfigFsmMap, apUniPort.uniID)
+}
diff --git a/internal/pkg/onuadaptercore/messageTypes.go b/internal/pkg/onuadaptercore/messageTypes.go
new file mode 100644
index 0000000..892d058
--- /dev/null
+++ b/internal/pkg/onuadaptercore/messageTypes.go
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2018-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 (
+ gp "github.com/google/gopacket"
+ "github.com/opencord/omci-lib-go"
+)
+
+// MessageType - Message Protocol Type
+type MessageType uint8
+
+const (
+ TestMsg MessageType = iota
+ OMCI
+)
+
+// String - Return the text representation of the message type based on integer
+func (m MessageType) String() string {
+ names := [...]string{
+ "TestMsg",
+ "OMCI",
+ }
+ return names[m]
+}
+
+// Message - message type and data(OMCI)
+type Message struct {
+ Type MessageType
+ Data interface{}
+}
+
+//TestMessageType - message data for various events
+type TestMessageType uint8
+
+const (
+ LoadMibTemplateOk TestMessageType = iota + 1
+ LoadMibTemplateFailed
+ TimeOutOccurred
+ AbortMessageProcessing
+)
+
+//TestMessage - Struct to hold the message data
+//TODO: place holder to have a second interface variant - to be replaced by real variant later on
+type TestMessage struct {
+ TestMessageVal TestMessageType
+}
+
+//OmciMessage - OMCI protocol messages for managing and monitoring ONUs
+type OmciMessage struct {
+ OmciMsg *omci.OMCI
+ OmciPacket *gp.Packet
+}
diff --git a/internal/pkg/onuadaptercore/mib_download.go b/internal/pkg/onuadaptercore/mib_download.go
new file mode 100644
index 0000000..baf2709
--- /dev/null
+++ b/internal/pkg/onuadaptercore/mib_download.go
@@ -0,0 +1,285 @@
+/*
+ * 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/looplab/fsm"
+
+ "github.com/opencord/omci-lib-go"
+ me "github.com/opencord/omci-lib-go/generated"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+func (onuDeviceEntry *OnuDeviceEntry) enterDLStartingState(e *fsm.Event) {
+ logger.Debugw("MibDownload FSM", log.Fields{"Start downloading OMCI MIB in state": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ if onuDeviceEntry.omciMessageReceived == nil {
+ onuDeviceEntry.omciMessageReceived = make(chan bool)
+ logger.Debug("MibDownload FSM - defining the BridgeInit RxChannel")
+ }
+ go onuDeviceEntry.processMibDownloadMessages()
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterCreatingGalState(e *fsm.Event) {
+ logger.Debugw("MibDownload FSM", log.Fields{"Tx create::GAL Ethernet Profile in state": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ meInstance := onuDeviceEntry.PDevOmciCC.sendCreateGalEthernetProfile(context.TODO(), ConstDefaultOmciTimeout, true)
+ onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterSettingOnu2gState(e *fsm.Event) {
+ logger.Debugw("MibDownload FSM", log.Fields{"Tx Set::ONU2-G in state": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ meInstance := onuDeviceEntry.PDevOmciCC.sendSetOnu2g(context.TODO(), ConstDefaultOmciTimeout, true)
+ onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterBridgeInitState(e *fsm.Event) {
+ logger.Infow("MibDownload FSM - starting bridge config port loop", log.Fields{
+ "in state": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ go onuDeviceEntry.performInitialBridgeSetup()
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterDownloadedState(e *fsm.Event) {
+ logger.Debugw("MibDownload FSM", log.Fields{"send notification to core in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ onuDeviceEntry.transferSystemEvent(MibDownloadDone)
+ pMibDlFsm := onuDeviceEntry.pMibDownloadFsm
+ if pMibDlFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(a_pAFsm *AdapterFsm) {
+ if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+ _ = a_pAFsm.pFsm.Event(dlEvReset)
+ }
+ }(pMibDlFsm)
+ }
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterResettingState(e *fsm.Event) {
+ logger.Debugw("MibDownload FSM resetting", log.Fields{"device-id": onuDeviceEntry.deviceID})
+ pMibDlFsm := onuDeviceEntry.pMibDownloadFsm
+ if pMibDlFsm != nil {
+ // abort running message processing
+ fsmAbortMsg := Message{
+ Type: TestMsg,
+ Data: TestMessage{
+ TestMessageVal: AbortMessageProcessing,
+ },
+ }
+ pMibDlFsm.commChan <- fsmAbortMsg
+
+ //try to restart the FSM to 'disabled'
+ // see DownloadedState: decouple event transfer
+ go func(a_pAFsm *AdapterFsm) {
+ if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+ _ = a_pAFsm.pFsm.Event(dlEvRestart)
+ }
+ }(pMibDlFsm)
+ }
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) processMibDownloadMessages( /*ctx context.Context*/ ) {
+ logger.Debugw("Start MibDownload Msg processing", log.Fields{"for device-id": onuDeviceEntry.deviceID})
+loop:
+ for {
+ // case <-ctx.Done():
+ // logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": onuDeviceEntry.deviceID})
+ // break loop
+ // unless multiple channels are not involved, we should not use select
+ message, ok := <-onuDeviceEntry.pMibDownloadFsm.commChan
+ if !ok {
+ logger.Info("MibDownload Rx Msg", log.Fields{"Message couldn't be read from channel for device-id": onuDeviceEntry.deviceID})
+ // but then we have to ensure a restart of the FSM as well - as exceptional procedure
+ _ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvRestart)
+ break loop
+ }
+ logger.Debugw("MibDownload Rx Msg", log.Fields{"Received message for device-id": onuDeviceEntry.deviceID})
+
+ switch message.Type {
+ case TestMsg:
+ msg, _ := message.Data.(TestMessage)
+ if msg.TestMessageVal == AbortMessageProcessing {
+ logger.Infow("MibDownload abort ProcessMsg", log.Fields{"for device-id": onuDeviceEntry.deviceID})
+ break loop
+ }
+ logger.Warnw("MibDownload unknown TestMessage", log.Fields{"device-id": onuDeviceEntry.deviceID, "MessageVal": msg.TestMessageVal})
+ case OMCI:
+ msg, _ := message.Data.(OmciMessage)
+ onuDeviceEntry.handleOmciMibDownloadMessage(msg)
+ default:
+ logger.Warn("MibDownload Rx Msg", log.Fields{"Unknown message type received for device-id": onuDeviceEntry.deviceID,
+ "message.Type": message.Type})
+ }
+
+ }
+ logger.Infow("End MibDownload Msg processing", log.Fields{"for device-id": onuDeviceEntry.deviceID})
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleOmciMibDownloadMessage(msg OmciMessage) {
+ logger.Debugw("Rx OMCI MibDownload Msg", log.Fields{"device-id": onuDeviceEntry.deviceID,
+ "msgType": msg.OmciMsg.MessageType})
+
+ switch msg.OmciMsg.MessageType {
+ case omci.CreateResponseType:
+ {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeCreateResponse)
+ if msgLayer == nil {
+ logger.Error("Omci Msg layer could not be detected for CreateResponse")
+ return
+ }
+ msgObj, msgOk := msgLayer.(*omci.CreateResponse)
+ if !msgOk {
+ logger.Error("Omci Msg layer could not be assigned for CreateResponse")
+ return
+ }
+ logger.Debugw("CreateResponse Data", log.Fields{"device-id": onuDeviceEntry.deviceID, "data-fields": msgObj})
+ if msgObj.Result != me.Success {
+ logger.Errorw("Omci CreateResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+ // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+ return
+ }
+ // maybe there is a way of pushing the specific create response type generally to the FSM
+ // and let the FSM verify, if the response was according to current state
+ // and possibly store the element to DB and progress - maybe some future option ...
+ // but as that is not straightforward to me I insert the type checkes manually here
+ // and feed the FSM with only 'pre-defined' events ...
+ if msgObj.EntityClass == onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetEntityID() {
+ //store the created ME into DB //TODO??? obviously the Python code does not store the config ...
+ // if, then something like:
+ //onuDeviceEntry.pOnuDB.StoreMe(msgObj)
+
+ // maybe we can use just the same eventName for different state transitions like "forward"
+ // - might be checked, but so far I go for sure and have to inspect the concrete state events ...
+ switch onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetName() {
+ case "GalEthernetProfile":
+ { // let the FSM proceed ...
+ _ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvRxGalResp)
+ }
+ case "MacBridgeServiceProfile",
+ "MacBridgePortConfigurationData",
+ "ExtendedVlanTaggingOperationConfigurationData":
+ { // let bridge init proceed by stopping the wait function
+ onuDeviceEntry.omciMessageReceived <- true
+ }
+ }
+ }
+ } //CreateResponseType
+
+ case omci.SetResponseType:
+ {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
+ if msgLayer == nil {
+ logger.Error("Omci Msg layer could not be detected for SetResponse")
+ return
+ }
+ msgObj, msgOk := msgLayer.(*omci.SetResponse)
+ if !msgOk {
+ logger.Error("Omci Msg layer could not be assigned for SetResponse")
+ return
+ }
+ logger.Debugw("SetResponse Data", log.Fields{"device-id": onuDeviceEntry.deviceID, "data-fields": msgObj})
+ if msgObj.Result != me.Success {
+ logger.Errorw("Omci SetResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+ // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+ return
+ }
+ // compare comments above for CreateResponse (apply also here ...)
+ if msgObj.EntityClass == onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetEntityID() {
+ //store the created ME into DB //TODO??? obviously the Python code does not store the config ...
+ // if, then something like:
+ //onuDeviceEntry.pOnuDB.StoreMe(msgObj)
+
+ switch onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetName() {
+ case "Onu2G":
+ { // let the FSM proceed ...
+ _ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvRxOnu2gResp)
+ }
+ //so far that was the only MibDownlad Set Element ...
+ }
+ }
+ } //SetResponseType
+ default:
+ {
+ logger.Errorw("Rx OMCI MibDownload unhandled MsgType", log.Fields{"omciMsgType": msg.OmciMsg.MessageType})
+ return
+ }
+ } // switch msg.OmciMsg.MessageType
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) performInitialBridgeSetup() {
+ for uniNo, uniPort := range onuDeviceEntry.baseDeviceHandler.uniEntityMap {
+ logger.Debugw("Starting IntialBridgeSetup", log.Fields{
+ "device-id": onuDeviceEntry.deviceID, "for PortNo": uniNo})
+
+ //create MBSP
+ meInstance := onuDeviceEntry.PDevOmciCC.sendCreateMBServiceProfile(
+ context.TODO(), uniPort, ConstDefaultOmciTimeout, true)
+ onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+ //verify response
+ err := onuDeviceEntry.waitforOmciResponse(meInstance)
+ if err != nil {
+ logger.Error("InitialBridgeSetup failed at MBSP, aborting MIB Download!")
+ _ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvReset)
+ return
+ }
+
+ //create MBPCD
+ meInstance = onuDeviceEntry.PDevOmciCC.sendCreateMBPConfigData(
+ context.TODO(), uniPort, ConstDefaultOmciTimeout, true)
+ onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+ //verify response
+ err = onuDeviceEntry.waitforOmciResponse(meInstance)
+ if err != nil {
+ logger.Error("InitialBridgeSetup failed at MBPCD, aborting MIB Download!")
+ _ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvReset)
+ return
+ }
+
+ //create EVTOCD
+ meInstance = onuDeviceEntry.PDevOmciCC.sendCreateEVTOConfigData(
+ context.TODO(), uniPort, ConstDefaultOmciTimeout, true)
+ onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+ //verify response
+ err = onuDeviceEntry.waitforOmciResponse(meInstance)
+ if err != nil {
+ logger.Error("InitialBridgeSetup failed at EVTOCD, aborting MIB Download!")
+ _ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvReset)
+ return
+ }
+ }
+ logger.Infow("IntialBridgeSetup finished", log.Fields{"device-id": onuDeviceEntry.deviceID})
+ _ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvRxBridgeResp)
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) waitforOmciResponse(apMeInstance *me.ManagedEntity) error {
+ select {
+ case <-time.After(30 * time.Second): //3s was detected to be to less in 8*8 bbsim test with debug Info/Debug
+ logger.Warnw("MibDownload-bridge-init timeout", log.Fields{"for device-id": onuDeviceEntry.deviceID})
+ return errors.New("mibDownloadBridgeInit timeout")
+ case success := <-onuDeviceEntry.omciMessageReceived:
+ if success {
+ logger.Debug("MibDownload-bridge-init response received")
+ return nil
+ }
+ // should not happen so far
+ logger.Warnw("MibDownload-bridge-init response error", log.Fields{"for device-id": onuDeviceEntry.deviceID})
+ return errors.New("mibDownloadBridgeInit responseError")
+ }
+}
diff --git a/internal/pkg/onuadaptercore/mib_sync.go b/internal/pkg/onuadaptercore/mib_sync.go
new file mode 100644
index 0000000..a42d3ce
--- /dev/null
+++ b/internal/pkg/onuadaptercore/mib_sync.go
@@ -0,0 +1,533 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//Package adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+ "context"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/looplab/fsm"
+
+ "time"
+
+ "github.com/opencord/omci-lib-go"
+ me "github.com/opencord/omci-lib-go/generated"
+ "github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+var supportedClassIds = []me.ClassID{
+ me.CardholderClassID, // 5
+ me.CircuitPackClassID, // 6
+ me.SoftwareImageClassID, // 7
+ me.PhysicalPathTerminationPointEthernetUniClassID, // 11
+ me.OltGClassID, // 131
+ me.OnuPowerSheddingClassID, // 133
+ me.IpHostConfigDataClassID, // 134
+ me.OnuGClassID, // 256
+ me.Onu2GClassID, // 257
+ me.TContClassID, // 262
+ me.AniGClassID, // 263
+ me.UniGClassID, // 264
+ me.PriorityQueueClassID, // 277
+ me.TrafficSchedulerClassID, // 278
+ me.VirtualEthernetInterfacePointClassID, // 329
+ me.EnhancedSecurityControlClassID, // 332
+ me.OnuDynamicPowerManagementControlClassID, // 336
+}
+
+var fsmMsg TestMessageType
+
+func (onuDeviceEntry *OnuDeviceEntry) enterStartingState(e *fsm.Event) {
+ logger.Debugw("MibSync FSM", log.Fields{"Start processing MibSync-msgs in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ onuDeviceEntry.pOnuDB = newOnuDeviceDB(context.TODO(), onuDeviceEntry)
+ go onuDeviceEntry.processMibSyncMessages()
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterResettingMibState(e *fsm.Event) {
+ logger.Debugw("MibSync FSM", log.Fields{"Start MibTemplate processing in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+
+ logger.Debugw("MibSync FSM", log.Fields{"send mibReset in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ _ = onuDeviceEntry.PDevOmciCC.sendMibReset(context.TODO(), ConstDefaultOmciTimeout, true)
+
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterGettingVendorAndSerialState(e *fsm.Event) {
+ logger.Debugw("MibSync FSM", log.Fields{"Start getting VendorId and SerialNumber in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ requestedAttributes := me.AttributeValueMap{"VendorId": "", "SerialNumber": 0}
+ meInstance := onuDeviceEntry.PDevOmciCC.sendGetMe(context.TODO(), me.OnuGClassID, onugMeID, requestedAttributes, ConstDefaultOmciTimeout, true)
+ onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterGettingEquipmentIDState(e *fsm.Event) {
+ logger.Debugw("MibSync FSM", log.Fields{"Start getting EquipmentId in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ requestedAttributes := me.AttributeValueMap{"EquipmentId": ""}
+ meInstance := onuDeviceEntry.PDevOmciCC.sendGetMe(context.TODO(), me.Onu2GClassID, onu2gMeID, requestedAttributes, ConstDefaultOmciTimeout, true)
+ onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterGettingFirstSwVersionState(e *fsm.Event) {
+ logger.Debugw("MibSync FSM", log.Fields{"Start getting IsActive and Version of first SW-image in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ requestedAttributes := me.AttributeValueMap{"IsActive": 0, "Version": ""}
+ meInstance := onuDeviceEntry.PDevOmciCC.sendGetMe(context.TODO(), me.SoftwareImageClassID, firstSwImageMeID, requestedAttributes, ConstDefaultOmciTimeout, true)
+ onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterGettingSecondSwVersionState(e *fsm.Event) {
+ logger.Debugw("MibSync FSM", log.Fields{"Start getting IsActive and Version of second SW-image in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ requestedAttributes := me.AttributeValueMap{"IsActive": 0, "Version": ""}
+ meInstance := onuDeviceEntry.PDevOmciCC.sendGetMe(context.TODO(), me.SoftwareImageClassID, secondSwImageMeID, requestedAttributes, ConstDefaultOmciTimeout, true)
+ onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterGettingMacAddressState(e *fsm.Event) {
+ logger.Debugw("MibSync FSM", log.Fields{"Start getting MacAddress in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ requestedAttributes := me.AttributeValueMap{"MacAddress": ""}
+ meInstance := onuDeviceEntry.PDevOmciCC.sendGetMe(context.TODO(), me.IpHostConfigDataClassID, ipHostConfigDataMeID, requestedAttributes, ConstDefaultOmciTimeout, true)
+ onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterGettingMibTemplate(e *fsm.Event) {
+
+ for i := firstSwImageMeID; i <= secondSwImageMeID; i++ {
+ if onuDeviceEntry.swImages[i].isActive > 0 {
+ onuDeviceEntry.activeSwVersion = onuDeviceEntry.swImages[i].version
+ }
+ }
+
+ meStoredFromTemplate := false
+ path := fmt.Sprintf(cSuffixMibTemplateKvStore, onuDeviceEntry.vendorID, onuDeviceEntry.equipmentID, onuDeviceEntry.activeSwVersion)
+ logger.Debugw("MibSync FSM - MibTemplate - etcd search string", log.Fields{"path": path})
+ Value, err := onuDeviceEntry.mibTemplateKVStore.Get(context.TODO(), path)
+ if err == nil {
+ if Value != nil {
+ logger.Debugf("MibSync FSM - MibTemplate read: Key: %s, Value: %s %s", Value.Key, Value.Value)
+
+ // swap out tokens with specific data
+ mibTmpString, _ := kvstore.ToString(Value.Value)
+ mibTmpString2 := strings.Replace(mibTmpString, "%SERIAL_NUMBER%", onuDeviceEntry.serialNumber, -1)
+ mibTmpString = strings.Replace(mibTmpString2, "%MAC_ADDRESS%", onuDeviceEntry.macAddress, -1)
+ mibTmpBytes := []byte(mibTmpString)
+ logger.Debugf("MibSync FSM - MibTemplate tokens swapped out: %s", mibTmpBytes)
+
+ var fistLevelMap map[string]interface{}
+ if err = json.Unmarshal(mibTmpBytes, &fistLevelMap); err != nil {
+ logger.Error("MibSync FSM - Failed to unmarshal template", log.Fields{"error": err, "device-id": onuDeviceEntry.deviceID})
+ } else {
+ for fistLevelKey, firstLevelValue := range fistLevelMap {
+ logger.Debugw("MibSync FSM - fistLevelKey", log.Fields{"fistLevelKey": fistLevelKey})
+ if uint16ValidNumber, err := strconv.ParseUint(fistLevelKey, 10, 16); err == nil {
+ meClassID := me.ClassID(uint16ValidNumber)
+ logger.Debugw("MibSync FSM - fistLevelKey is a number in uint16-range", log.Fields{"uint16ValidNumber": uint16ValidNumber})
+ if isSupportedClassID(meClassID) {
+ logger.Debugw("MibSync FSM - fistLevelKey is a supported classID", log.Fields{"meClassID": meClassID})
+ secondLevelMap := firstLevelValue.(map[string]interface{})
+ for secondLevelKey, secondLevelValue := range secondLevelMap {
+ logger.Debugw("MibSync FSM - secondLevelKey", log.Fields{"secondLevelKey": secondLevelKey})
+ if uint16ValidNumber, err := strconv.ParseUint(secondLevelKey, 10, 16); err == nil {
+ meEntityID := uint16(uint16ValidNumber)
+ logger.Debugw("MibSync FSM - secondLevelKey is a number and a valid EntityId", log.Fields{"meEntityID": meEntityID})
+ thirdLevelMap := secondLevelValue.(map[string]interface{})
+ for thirdLevelKey, thirdLevelValue := range thirdLevelMap {
+ if thirdLevelKey == "Attributes" {
+ logger.Debugw("MibSync FSM - thirdLevelKey refers to attributes", log.Fields{"thirdLevelKey": thirdLevelKey})
+ attributesMap := thirdLevelValue.(map[string]interface{})
+ logger.Debugw("MibSync FSM - attributesMap", log.Fields{"attributesMap": attributesMap})
+ onuDeviceEntry.pOnuDB.PutMe(meClassID, meEntityID, attributesMap)
+ meStoredFromTemplate = true
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ logger.Debugw("No MIB template found", log.Fields{"path": path, "device-id": onuDeviceEntry.deviceID})
+ }
+ } else {
+ logger.Errorf("Get from kvstore operation failed for path %s", path)
+ }
+ if meStoredFromTemplate {
+ logger.Debug("MibSync FSM - valid MEs stored from template")
+ onuDeviceEntry.pOnuDB.logMeDb()
+ fsmMsg = LoadMibTemplateOk
+ } else {
+ logger.Debug("MibSync FSM - no valid MEs stored from template - perform MIB-upload!")
+ fsmMsg = LoadMibTemplateFailed
+ }
+
+ mibSyncMsg := Message{
+ Type: TestMsg,
+ Data: TestMessage{
+ TestMessageVal: fsmMsg,
+ },
+ }
+ onuDeviceEntry.pMibUploadFsm.commChan <- mibSyncMsg
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterUploadingState(e *fsm.Event) {
+ logger.Debugw("MibSync FSM", log.Fields{"send MibUpload in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ _ = onuDeviceEntry.PDevOmciCC.sendMibUpload(context.TODO(), ConstDefaultOmciTimeout, true)
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterInSyncState(e *fsm.Event) {
+ logger.Debugw("MibSync FSM", log.Fields{"send notification to core in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ onuDeviceEntry.transferSystemEvent(MibDatabaseSync)
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterExaminingMdsState(e *fsm.Event) {
+ logger.Debugw("MibSync FSM", log.Fields{"Start GetMds processing in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ logger.Debug("function not implemented yet")
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterResynchronizingState(e *fsm.Event) {
+ logger.Debugw("MibSync FSM", log.Fields{"Start MibResync processing in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ logger.Debug("function not implemented yet")
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterAuditingState(e *fsm.Event) {
+ logger.Debugw("MibSync FSM", log.Fields{"Start MibResync processing in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ logger.Debug("function not implemented yet")
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterOutOfSyncState(e *fsm.Event) {
+ logger.Debugw("MibSync FSM", log.Fields{"Start MibReconcile processing in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+ logger.Debug("function not implemented yet")
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) processMibSyncMessages( /*ctx context.Context*/ ) {
+ logger.Debugw("MibSync Msg", log.Fields{"Start routine to process OMCI-messages for device-id": onuDeviceEntry.deviceID})
+loop:
+ for {
+ // case <-ctx.Done():
+ // logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": onuDeviceEntry.deviceID})
+ // break loop
+ message, ok := <-onuDeviceEntry.pMibUploadFsm.commChan
+ if !ok {
+ logger.Info("MibSync Msg", log.Fields{"Message couldn't be read from channel for device-id": onuDeviceEntry.deviceID})
+ break loop
+ }
+ logger.Debugw("MibSync Msg", log.Fields{"Received message on ONU MibSyncChan for device-id": onuDeviceEntry.deviceID})
+
+ switch message.Type {
+ case TestMsg:
+ msg, _ := message.Data.(TestMessage)
+ onuDeviceEntry.handleTestMsg(msg)
+ case OMCI:
+ msg, _ := message.Data.(OmciMessage)
+ onuDeviceEntry.handleOmciMessage(msg)
+ default:
+ logger.Warn("MibSync Msg", log.Fields{"Unknown message type received for device-id": onuDeviceEntry.deviceID, "message.Type": message.Type})
+ }
+ }
+ logger.Info("MibSync Msg", log.Fields{"Stopped handling of MibSyncChan for device-id": onuDeviceEntry.deviceID})
+ _ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvStop)
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleTestMsg(msg TestMessage) {
+
+ logger.Debugw("MibSync Msg", log.Fields{"TestMessage received for device-id": onuDeviceEntry.deviceID, "msg.TestMessageVal": msg.TestMessageVal})
+
+ switch msg.TestMessageVal {
+ case LoadMibTemplateFailed:
+ _ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvUploadMib)
+ logger.Debugw("MibSync Msg", log.Fields{"state": string(onuDeviceEntry.pMibUploadFsm.pFsm.Current())})
+ case LoadMibTemplateOk:
+ _ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvSuccess)
+ logger.Debugw("MibSync Msg", log.Fields{"state": string(onuDeviceEntry.pMibUploadFsm.pFsm.Current())})
+ default:
+ logger.Warn("MibSync Msg", log.Fields{"Unknown message type received for device-id": onuDeviceEntry.deviceID, "msg.TestMessageVal": msg.TestMessageVal})
+ }
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleOmciMibResetResponseMessage(msg OmciMessage) {
+ if onuDeviceEntry.pMibUploadFsm.pFsm.Is(ulStResettingMib) {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeMibResetResponse)
+ if msgLayer != nil {
+ msgObj, msgOk := msgLayer.(*omci.MibResetResponse)
+ if msgOk {
+ logger.Debugw("MibResetResponse Data", log.Fields{"data-fields": msgObj})
+ if msgObj.Result == me.Success {
+ // trigger retrieval of VendorId and SerialNumber
+ _ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvGetVendorAndSerial)
+ return
+ }
+ logger.Errorw("Omci MibResetResponse Error", log.Fields{"Error": msgObj.Result})
+ } else {
+ logger.Error("Omci Msg layer could not be assigned")
+ }
+ } else {
+ logger.Error("Omci Msg layer could not be detected")
+ }
+ } else {
+ logger.Errorw("Omci MibResetResponse received", log.Fields{"in state ": onuDeviceEntry.pMibUploadFsm.pFsm.Current})
+ }
+ logger.Info("MibSync Msg", log.Fields{"Stopped handling of MibSyncChan for device-id": onuDeviceEntry.deviceID})
+ _ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvStop)
+
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleOmciMibUploadResponseMessage(msg OmciMessage) {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeMibUploadResponse)
+ if msgLayer == nil {
+ logger.Error("Omci Msg layer could not be detected")
+ return
+ }
+ msgObj, msgOk := msgLayer.(*omci.MibUploadResponse)
+ if !msgOk {
+ logger.Error("Omci Msg layer could not be assigned")
+ return
+ }
+ logger.Debugw("MibUploadResponse Data for:", log.Fields{"deviceId": onuDeviceEntry.deviceID, "data-fields": msgObj})
+ /* to be verified / reworked !!! */
+ onuDeviceEntry.PDevOmciCC.uploadNoOfCmds = msgObj.NumberOfCommands
+ if onuDeviceEntry.PDevOmciCC.uploadSequNo < onuDeviceEntry.PDevOmciCC.uploadNoOfCmds {
+ _ = onuDeviceEntry.PDevOmciCC.sendMibUploadNext(context.TODO(), ConstDefaultOmciTimeout, true)
+ } else {
+ logger.Error("Invalid number of commands received for:", log.Fields{"deviceId": onuDeviceEntry.deviceID, "uploadNoOfCmds": onuDeviceEntry.PDevOmciCC.uploadNoOfCmds})
+ //TODO right action?
+ _ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvTimeout)
+ }
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleOmciMibUploadNextResponseMessage(msg OmciMessage) {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeMibUploadNextResponse)
+ if msgLayer == nil {
+ logger.Error("Omci Msg layer could not be detected")
+ return
+ }
+ msgObj, msgOk := msgLayer.(*omci.MibUploadNextResponse)
+ if !msgOk {
+ logger.Error("Omci Msg layer could not be assigned")
+ return
+ }
+ if onuDeviceEntry.mibDebugLevel == "VERBOSE" {
+ logger.Debugw("MibUploadNextResponse Data for:", log.Fields{"deviceId": onuDeviceEntry.deviceID, "data-fields": msgObj})
+ }
+ meClassID := msgObj.ReportedME.GetClassID()
+ meEntityID := msgObj.ReportedME.GetEntityID()
+ meAttributes := msgObj.ReportedME.GetAttributeValueMap()
+
+ onuDeviceEntry.pOnuDB.PutMe(meClassID, meEntityID, meAttributes)
+
+ if onuDeviceEntry.PDevOmciCC.uploadSequNo < onuDeviceEntry.PDevOmciCC.uploadNoOfCmds {
+ _ = onuDeviceEntry.PDevOmciCC.sendMibUploadNext(context.TODO(), ConstDefaultOmciTimeout, true)
+ } else {
+ onuDeviceEntry.pOnuDB.logMeDb()
+ err := onuDeviceEntry.createAndPersistMibTemplate()
+ if err != nil {
+ logger.Errorw("MibSync - MibTemplate - Failed to create and persist the mib template", log.Fields{"error": err, "device-id": onuDeviceEntry.deviceID})
+ }
+
+ _ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvSuccess)
+ }
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleOmciGetResponseMessage(msg OmciMessage) {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeGetResponse)
+ if msgLayer != nil {
+ msgObj, msgOk := msgLayer.(*omci.GetResponse)
+ if msgOk {
+ logger.Debugw("MibSync FSM - GetResponse Data", log.Fields{"deviceId": onuDeviceEntry.deviceID, "data-fields": msgObj})
+ if msgObj.Result == me.Success {
+ entityID := onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetEntityID()
+ if msgObj.EntityClass == onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetClassID() && msgObj.EntityInstance == entityID {
+ meAttributes := msgObj.Attributes
+ meInstance := onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetName()
+ if onuDeviceEntry.mibDebugLevel == "VERBOSE" {
+ logger.Debugf("MibSync FSM - GetResponse Data for %s", log.Fields{"deviceId": onuDeviceEntry.deviceID, "data-fields": msgObj}, meInstance)
+ }
+ switch meInstance {
+ case "OnuG":
+ onuDeviceEntry.vendorID = fmt.Sprintf("%s", meAttributes["VendorId"])
+ snBytes, _ := me.InterfaceToOctets(meAttributes["SerialNumber"])
+ if onugSerialNumberLen == len(snBytes) {
+ snVendorPart := fmt.Sprintf("%s", snBytes[:4])
+ snNumberPart := hex.EncodeToString(snBytes[4:])
+ onuDeviceEntry.serialNumber = snVendorPart + snNumberPart
+ logger.Debugw("MibSync FSM - GetResponse Data for Onu-G - VendorId/SerialNumber", log.Fields{"deviceId": onuDeviceEntry.deviceID,
+ "onuDeviceEntry.vendorID": onuDeviceEntry.vendorID, "onuDeviceEntry.serialNumber": onuDeviceEntry.serialNumber})
+ } else {
+ logger.Errorw("MibSync FSM - SerialNumber has wrong length", log.Fields{"deviceId": onuDeviceEntry.deviceID, "length": len(snBytes)})
+ }
+ // trigger retrieval of EquipmentId
+ _ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvGetEquipmentID)
+ return
+ case "Onu2G":
+ onuDeviceEntry.equipmentID = fmt.Sprintf("%s", meAttributes["EquipmentId"])
+ logger.Debugw("MibSync FSM - GetResponse Data for Onu2-G - EquipmentId", log.Fields{"deviceId": onuDeviceEntry.deviceID,
+ "onuDeviceEntry.equipmentID": onuDeviceEntry.equipmentID})
+ // trigger retrieval of 1st SW-image info
+ _ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvGetFirstSwVersion)
+ return
+ case "SoftwareImage":
+ if entityID <= secondSwImageMeID {
+ onuDeviceEntry.swImages[entityID].version = fmt.Sprintf("%s", meAttributes["Version"])
+ onuDeviceEntry.swImages[entityID].isActive = meAttributes["IsActive"].(uint8)
+ logger.Debugw("MibSync FSM - GetResponse Data for SoftwareImage - Version/IsActive",
+ log.Fields{"deviceId": onuDeviceEntry.deviceID, "entityID": entityID,
+ "version": onuDeviceEntry.swImages[entityID].version, "isActive": onuDeviceEntry.swImages[entityID].isActive})
+ } else {
+ //TODO: error handling
+ logger.Errorw("MibSync FSM - Failed to GetResponse Data for SoftwareImage", log.Fields{"deviceId": onuDeviceEntry.deviceID})
+
+ }
+ if firstSwImageMeID == entityID {
+ _ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvGetSecondSwVersion)
+ return
+ } else if secondSwImageMeID == entityID {
+ _ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvGetMacAddress)
+ return
+ }
+ case "IpHostConfigData":
+ macBytes, _ := me.InterfaceToOctets(meAttributes["MacAddress"])
+ if omciMacAddressLen == len(macBytes) {
+ onuDeviceEntry.macAddress = hex.EncodeToString(macBytes[:])
+ logger.Debugw("MibSync FSM - GetResponse Data for IpHostConfigData - MacAddress", log.Fields{"deviceId": onuDeviceEntry.deviceID,
+ "onuDeviceEntry.macAddress": onuDeviceEntry.macAddress})
+ } else {
+ logger.Errorw("MibSync FSM - MacAddress wrong length", log.Fields{"deviceId": onuDeviceEntry.deviceID, "length": len(macBytes)})
+ }
+ // trigger retrieval of mib template
+ _ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvGetMibTemplate)
+ return
+ }
+ }
+ } else {
+ logger.Errorw("Omci GetResponse Error", log.Fields{"Error": msgObj.Result})
+ }
+ } else {
+ logger.Error("Omci Msg layer could not be assigned for GetResponse")
+ }
+ } else {
+ logger.Error("Omci Msg layer could not be detected for GetResponse")
+ }
+ logger.Info("MibSync Msg", log.Fields{"Stopped handling of MibSyncChan for device-id": onuDeviceEntry.deviceID})
+ _ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvStop)
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleOmciMessage(msg OmciMessage) {
+ if onuDeviceEntry.mibDebugLevel == "VERBOSE" {
+ logger.Debugw("MibSync Msg", log.Fields{"OmciMessage received for device-id": onuDeviceEntry.deviceID,
+ "msgType": msg.OmciMsg.MessageType})
+ }
+ switch msg.OmciMsg.MessageType {
+ case omci.MibResetResponseType:
+ onuDeviceEntry.handleOmciMibResetResponseMessage(msg)
+
+ case omci.MibUploadResponseType:
+ onuDeviceEntry.handleOmciMibUploadResponseMessage(msg)
+
+ case omci.MibUploadNextResponseType:
+ onuDeviceEntry.handleOmciMibUploadNextResponseMessage(msg)
+
+ case omci.GetResponseType:
+ onuDeviceEntry.handleOmciGetResponseMessage(msg)
+
+ }
+}
+
+func isSupportedClassID(meClassID me.ClassID) bool {
+ for _, v := range supportedClassIds {
+ if v == meClassID {
+ return true
+ }
+ }
+ return false
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) mibDbVolatileDict() error {
+ logger.Debug("MibVolatileDict- running from default Entry code")
+ return errors.New("not_implemented")
+}
+
+// createAndPersistMibTemplate method creates a mib template for the device id when operator enables the ONU device for the first time.
+// We are creating a placeholder for "SerialNumber" for ME Class ID 6 and 256 and "MacAddress" for ME Class ID 134 in the template
+// and then storing the template into etcd "service/voltha/omci_mibs/templates/verdor_id/equipment_id/software_version" path.
+func (onuDeviceEntry *OnuDeviceEntry) createAndPersistMibTemplate() error {
+ path := fmt.Sprintf(cSuffixMibTemplateKvStore, onuDeviceEntry.vendorID, onuDeviceEntry.equipmentID, onuDeviceEntry.activeSwVersion)
+ logger.Debugw("MibSync - MibTemplate - key name", log.Fields{"path": path})
+ currentTime := time.Now()
+
+ templateMap := make(map[string]interface{})
+ templateMap["TemplateName"] = path
+ templateMap["TemplateCreated"] = currentTime.Format("2006-01-02 15:04:05.000000")
+
+ firstLevelMap := onuDeviceEntry.pOnuDB.meDb
+ for firstLevelKey, firstLevelValue := range firstLevelMap {
+ logger.Debugw("MibSync - MibTemplate - firstLevelKey", log.Fields{"firstLevelKey": firstLevelKey})
+ classID := strconv.Itoa(int(firstLevelKey))
+
+ secondLevelMap := make(map[string]interface{})
+ for secondLevelKey, secondLevelValue := range firstLevelValue {
+ thirdLevelMap := make(map[string]interface{})
+ entityID := strconv.Itoa(int(secondLevelKey))
+ thirdLevelMap["Attributes"] = secondLevelValue
+ thirdLevelMap["InstanceId"] = entityID
+ secondLevelMap[entityID] = thirdLevelMap
+ if classID == "6" || classID == "256" {
+ forthLevelMap := map[string]interface{}(thirdLevelMap["Attributes"].(me.AttributeValueMap))
+ delete(forthLevelMap, "SerialNumber")
+ forthLevelMap["SerialNumber"] = "%SERIAL_NUMBER%"
+
+ }
+ if classID == "134" {
+ forthLevelMap := map[string]interface{}(thirdLevelMap["Attributes"].(me.AttributeValueMap))
+ delete(forthLevelMap, "MacAddress")
+ forthLevelMap["MacAddress"] = "%MAC_ADDRESS%"
+ }
+ }
+ secondLevelMap["ClassId"] = classID
+ templateMap[classID] = secondLevelMap
+ }
+ mibTemplate, err := json.Marshal(&templateMap)
+ if err != nil {
+ logger.Errorw("MibSync - MibTemplate - Failed to marshal mibTemplate", log.Fields{"error": err, "device-id": onuDeviceEntry.deviceID})
+ return err
+ }
+ err = onuDeviceEntry.mibTemplateKVStore.Put(context.TODO(), path, string(mibTemplate))
+ if err != nil {
+ logger.Errorw("MibSync - MibTemplate - Failed to store template in etcd", log.Fields{"error": err, "device-id": onuDeviceEntry.deviceID})
+ return err
+ }
+ logger.Debugw("MibSync - MibTemplate - Stored the template to etcd", log.Fields{"device-id": onuDeviceEntry.deviceID})
+ return nil
+}
+
+// func (onuDeviceEntry *OnuDeviceEntry) MibTemplateTask() error {
+// return errors.New("not_implemented")
+// }
+// func (onuDeviceEntry *OnuDeviceEntry) MibUploadTask() error {
+// return errors.New("not_implemented")
+// }
+// func (onuDeviceEntry *OnuDeviceEntry) GetMdsTask() error {
+// return errors.New("not_implemented")
+// }
+// func (onuDeviceEntry *OnuDeviceEntry) MibResyncTask() error {
+// return errors.New("not_implemented")
+// }
+// func (onuDeviceEntry *OnuDeviceEntry) MibReconcileTask() error {
+// return errors.New("not_implemented")
+// }
diff --git a/internal/pkg/onuadaptercore/olt_onu_msg.go b/internal/pkg/onuadaptercore/olt_onu_msg.go
new file mode 100644
index 0000000..f18910a
--- /dev/null
+++ b/internal/pkg/onuadaptercore/olt_onu_msg.go
@@ -0,0 +1,67 @@
+/*
+ * 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
+
+import (
+ "context"
+ "encoding/json"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ ic "github.com/opencord/voltha-protos/v3/go/inter_container"
+ oop "github.com/opencord/voltha-protos/v3/go/openolt"
+)
+
+//AdditionalMessage ...
+type AdditionalMessage struct {
+}
+
+/*
+func (dh *deviceHandler) receivedMsgFromOlt(msg *ic.InterAdapterMessage) error {
+ msgBody := msg.GetBody()
+ ind := &oop.OnuIndication{}
+
+ err := ptypes.UnmarshalAny(msgBody, ind)
+ if err != nil {
+ logger.Debugw("cannot-unmarshal-onu-indication-body", log.Fields{"error": err})
+ } else {
+ var info AdditionalMessage
+ err = json.Unmarshal(ind.SerialNumber.VendorSpecific, &info)
+ if err != nil {
+ logger.Debugw("cannot-unmarshal-additional-message", log.Fields{"error": err})
+ }
+ }
+
+ return err
+}
+*/
+func (dh *deviceHandler) sendMsgToOlt(ctx context.Context, msg string, option interface{}) error {
+ bytes, _ := json.Marshal(option)
+ info := oop.OnuIndication{
+ SerialNumber: &oop.SerialNumber{
+ VendorSpecific: bytes,
+ },
+ }
+
+ err := dh.AdapterProxy.SendInterAdapterMessage(ctx, &info,
+ ic.InterAdapterMessageType_ONU_IND_REQUEST,
+ dh.DeviceType, dh.ProxyAddressType,
+ dh.deviceID, dh.parentID, msg)
+
+ if err != nil {
+ logger.Debugw("err-sending-message", log.Fields{"device": dh})
+ }
+ return err
+}
diff --git a/internal/pkg/onuadaptercore/omci_ani_config.go b/internal/pkg/onuadaptercore/omci_ani_config.go
new file mode 100644
index 0000000..d7c496c
--- /dev/null
+++ b/internal/pkg/onuadaptercore/omci_ani_config.go
@@ -0,0 +1,782 @@
+/*
+ * 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"
+ "fmt"
+ "strconv"
+ "time"
+
+ "github.com/cevaris/ordered_map"
+ "github.com/looplab/fsm"
+ "github.com/opencord/omci-lib-go"
+ me "github.com/opencord/omci-lib-go/generated"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+const (
+ aniEvStart = "uniEvStart"
+ aniEvStartConfig = "aniEvStartConfig"
+ aniEvRxDot1pmapCResp = "aniEvRxDot1pmapCResp"
+ aniEvRxMbpcdResp = "aniEvRxMbpcdResp"
+ aniEvRxTcontsResp = "aniEvRxTcontsResp"
+ aniEvRxGemntcpsResp = "aniEvRxGemntcpsResp"
+ aniEvRxGemiwsResp = "aniEvRxGemiwsResp"
+ aniEvRxPrioqsResp = "aniEvRxPrioqsResp"
+ aniEvRxDot1pmapSResp = "aniEvRxDot1pmapSResp"
+ aniEvTimeoutSimple = "aniEvTimeoutSimple"
+ aniEvTimeoutMids = "aniEvTimeoutMids"
+ aniEvReset = "aniEvReset"
+ aniEvRestart = "aniEvRestart"
+)
+const (
+ aniStDisabled = "aniStDisabled"
+ aniStStarting = "aniStStarting"
+ aniStCreatingDot1PMapper = "aniStCreatingDot1PMapper"
+ aniStCreatingMBPCD = "aniStCreatingMBPCD"
+ aniStSettingTconts = "aniStSettingTconts"
+ aniStCreatingGemNCTPs = "aniStCreatingGemNCTPs"
+ aniStCreatingGemIWs = "aniStCreatingGemIWs"
+ aniStSettingPQs = "aniStSettingPQs"
+ aniStSettingDot1PMapper = "aniStSettingDot1PMapper"
+ aniStConfigDone = "aniStConfigDone"
+ aniStResetting = "aniStResetting"
+)
+
+type ponAniGemPortAttribs struct {
+ gemPortID uint16
+ upQueueID uint16
+ downQueueID uint16
+ direction uint8
+ qosPolicy string
+ weight uint8
+ pbitString string
+}
+
+//uniPonAniConfigFsm defines the structure for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
+type uniPonAniConfigFsm struct {
+ pOmciCC *omciCC
+ pOnuUniPort *onuUniPort
+ pUniTechProf *onuUniTechProf
+ pOnuDB *onuDeviceDB
+ techProfileID uint16
+ requestEvent OnuDeviceEvent
+ omciMIdsResponseReceived chan bool //separate channel needed for checking multiInstance OMCI message responses
+ pAdaptFsm *AdapterFsm
+ aniConfigCompleted bool
+ chSuccess chan<- uint8
+ procStep uint8
+ chanSet bool
+ mapperSP0ID uint16
+ macBPCD0ID uint16
+ tcont0ID uint16
+ alloc0ID uint16
+ gemPortAttribsSlice []ponAniGemPortAttribs
+}
+
+//newUniPonAniConfigFsm is the 'constructor' for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
+func newUniPonAniConfigFsm(apDevOmciCC *omciCC, apUniPort *onuUniPort, apUniTechProf *onuUniTechProf,
+ apOnuDB *onuDeviceDB, aTechProfileID uint16, aRequestEvent OnuDeviceEvent, aName string,
+ aDeviceID string, aCommChannel chan Message) *uniPonAniConfigFsm {
+ instFsm := &uniPonAniConfigFsm{
+ pOmciCC: apDevOmciCC,
+ pOnuUniPort: apUniPort,
+ pUniTechProf: apUniTechProf,
+ pOnuDB: apOnuDB,
+ techProfileID: aTechProfileID,
+ requestEvent: aRequestEvent,
+ aniConfigCompleted: false,
+ chanSet: false,
+ }
+ instFsm.pAdaptFsm = NewAdapterFsm(aName, aDeviceID, aCommChannel)
+ if instFsm.pAdaptFsm == nil {
+ logger.Errorw("uniPonAniConfigFsm's AdapterFsm could not be instantiated!!", log.Fields{
+ "device-id": aDeviceID})
+ return nil
+ }
+
+ instFsm.pAdaptFsm.pFsm = fsm.NewFSM(
+ aniStDisabled,
+ fsm.Events{
+
+ {Name: aniEvStart, Src: []string{aniStDisabled}, Dst: aniStStarting},
+
+ //Note: .1p-Mapper and MBPCD might also have multi instances (per T-Cont) - by now only one 1 T-Cont considered!
+ {Name: aniEvStartConfig, Src: []string{aniStStarting}, Dst: aniStCreatingDot1PMapper},
+ {Name: aniEvRxDot1pmapCResp, Src: []string{aniStCreatingDot1PMapper}, Dst: aniStCreatingMBPCD},
+ {Name: aniEvRxMbpcdResp, Src: []string{aniStCreatingMBPCD}, Dst: aniStSettingTconts},
+ {Name: aniEvRxTcontsResp, Src: []string{aniStSettingTconts}, Dst: aniStCreatingGemNCTPs},
+ // the creatingGemNCTPs state is used for multi ME config if required for all configured/available GemPorts
+ {Name: aniEvRxGemntcpsResp, Src: []string{aniStCreatingGemNCTPs}, Dst: aniStCreatingGemIWs},
+ // the creatingGemIWs state is used for multi ME config if required for all configured/available GemPorts
+ {Name: aniEvRxGemiwsResp, Src: []string{aniStCreatingGemIWs}, Dst: aniStSettingPQs},
+ // the settingPQs state is used for multi ME config if required for all configured/available upstream PriorityQueues
+ {Name: aniEvRxPrioqsResp, Src: []string{aniStSettingPQs}, Dst: aniStSettingDot1PMapper},
+ {Name: aniEvRxDot1pmapSResp, Src: []string{aniStSettingDot1PMapper}, Dst: aniStConfigDone},
+
+ {Name: aniEvTimeoutSimple, Src: []string{
+ aniStCreatingDot1PMapper, aniStCreatingMBPCD, aniStSettingTconts, aniStSettingDot1PMapper}, Dst: aniStStarting},
+ {Name: aniEvTimeoutMids, Src: []string{
+ aniStCreatingGemNCTPs, aniStCreatingGemIWs, aniStSettingPQs}, Dst: aniStStarting},
+
+ // exceptional treatment for all states except aniStResetting
+ {Name: aniEvReset, Src: []string{aniStStarting, aniStCreatingDot1PMapper, aniStCreatingMBPCD,
+ aniStSettingTconts, aniStCreatingGemNCTPs, aniStCreatingGemIWs, aniStSettingPQs, aniStSettingDot1PMapper,
+ aniStConfigDone}, Dst: aniStResetting},
+ // the only way to get to resource-cleared disabled state again is via "resseting"
+ {Name: aniEvRestart, Src: []string{aniStResetting}, Dst: aniStDisabled},
+ },
+
+ fsm.Callbacks{
+ "enter_state": func(e *fsm.Event) { instFsm.pAdaptFsm.logFsmStateChange(e) },
+ ("enter_" + aniStStarting): func(e *fsm.Event) { instFsm.enterConfigStartingState(e) },
+ ("enter_" + aniStCreatingDot1PMapper): func(e *fsm.Event) { instFsm.enterCreatingDot1PMapper(e) },
+ ("enter_" + aniStCreatingMBPCD): func(e *fsm.Event) { instFsm.enterCreatingMBPCD(e) },
+ ("enter_" + aniStSettingTconts): func(e *fsm.Event) { instFsm.enterSettingTconts(e) },
+ ("enter_" + aniStCreatingGemNCTPs): func(e *fsm.Event) { instFsm.enterCreatingGemNCTPs(e) },
+ ("enter_" + aniStCreatingGemIWs): func(e *fsm.Event) { instFsm.enterCreatingGemIWs(e) },
+ ("enter_" + aniStSettingPQs): func(e *fsm.Event) { instFsm.enterSettingPQs(e) },
+ ("enter_" + aniStSettingDot1PMapper): func(e *fsm.Event) { instFsm.enterSettingDot1PMapper(e) },
+ ("enter_" + aniStConfigDone): func(e *fsm.Event) { instFsm.enterAniConfigDone(e) },
+ ("enter_" + aniStResetting): func(e *fsm.Event) { instFsm.enterResettingState(e) },
+ ("enter_" + aniStDisabled): func(e *fsm.Event) { instFsm.enterDisabledState(e) },
+ },
+ )
+ if instFsm.pAdaptFsm.pFsm == nil {
+ logger.Errorw("uniPonAniConfigFsm's Base FSM could not be instantiated!!", log.Fields{
+ "device-id": aDeviceID})
+ return nil
+ }
+
+ logger.Infow("uniPonAniConfigFsm created", log.Fields{"device-id": aDeviceID})
+ return instFsm
+}
+
+//setFsmCompleteChannel sets the requested channel and channel result for transfer on success
+func (oFsm *uniPonAniConfigFsm) setFsmCompleteChannel(aChSuccess chan<- uint8, aProcStep uint8) {
+ oFsm.chSuccess = aChSuccess
+ oFsm.procStep = aProcStep
+ oFsm.chanSet = true
+}
+
+func (oFsm *uniPonAniConfigFsm) prepareAndEnterConfigState(aPAFsm *AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.pFsm != nil {
+ //stick to pythonAdapter numbering scheme
+ //index 0 in naming refers to possible usage of multiple instances (later)
+ oFsm.mapperSP0ID = ieeeMapperServiceProfileEID + uint16(oFsm.pOnuUniPort.macBpNo) + oFsm.techProfileID
+ oFsm.macBPCD0ID = macBridgePortAniEID + uint16(oFsm.pOnuUniPort.entityID) + oFsm.techProfileID
+
+ // For the time being: if there are multiple T-Conts on the ONU the first one from the entityID-ordered list is used
+ // TODO!: if more T-Conts have to be supported (tcontXID!), then use the first instances of the entity-ordered list
+ // or use the py code approach, which might be a bit more complicated, but also more secure, as it
+ // ensures that the selected T-Cont also has queues (which I would assume per definition from ONU, but who knows ...)
+ // so this approach would search the (sorted) upstream PrioQueue list and use the T-Cont (if available) from highest Bytes
+ // or sndHighByte of relatedPort Attribute (T-Cont Reference) and in case of multiple TConts find the next free TContIndex
+ // that way from PrioQueue.relatedPort list
+ if tcontInstKeys := oFsm.pOnuDB.getSortedInstKeys(me.TContClassID); len(tcontInstKeys) > 0 {
+ oFsm.tcont0ID = tcontInstKeys[0]
+ logger.Debugw("Used TcontId:", log.Fields{"TcontId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
+ "device-id": oFsm.pAdaptFsm.deviceID})
+ } else {
+ logger.Warnw("No TCont instances found", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ }
+ oFsm.alloc0ID = (*(oFsm.pUniTechProf.mapPonAniConfig[oFsm.pOnuUniPort.uniID]))[0].tcontParams.allocID
+ loGemPortAttribs := ponAniGemPortAttribs{}
+ //for all TechProfile set GemIndices
+ for _, gemEntry := range (*(oFsm.pUniTechProf.mapPonAniConfig[oFsm.pOnuUniPort.uniID]))[0].mapGemPortParams {
+ //collect all GemConfigData in a separate Fsm related slice (needed also to avoid mix-up with unsorted mapPonAniConfig)
+
+ if queueInstKeys := oFsm.pOnuDB.getSortedInstKeys(me.PriorityQueueClassID); len(queueInstKeys) > 0 {
+
+ loGemPortAttribs.gemPortID = gemEntry.gemPortID
+ // MibDb usage: upstream PrioQueue.RelatedPort = xxxxyyyy with xxxx=TCont.Entity(incl. slot) and yyyy=prio
+ // i.e.: search PrioQueue list with xxxx=actual T-Cont.Entity,
+ // from that list use the PrioQueue.Entity with gemEntry.prioQueueIndex == yyyy (expect 0..7)
+ usQrelPortMask := uint32((((uint32)(oFsm.tcont0ID)) << 16) + uint32(gemEntry.prioQueueIndex))
+
+ // MibDb usage: downstream PrioQueue.RelatedPort = xxyyzzzz with xx=slot, yy=UniPort and zzzz=prio
+ // i.e.: search PrioQueue list with yy=actual pOnuUniPort.uniID,
+ // from that list use the PrioQueue.Entity with gemEntry.prioQueueIndex == zzzz (expect 0..7)
+ // Note: As we do not maintain any slot numbering, slot number will be excluded from seatch pattern.
+ // Furthermore OMCI Onu port-Id is expected to start with 1 (not 0).
+ dsQrelPortMask := uint32((((uint32)(oFsm.pOnuUniPort.uniID + 1)) << 16) + uint32(gemEntry.prioQueueIndex))
+
+ usQueueFound := false
+ dsQueueFound := false
+ for _, mgmtEntityID := range queueInstKeys {
+ if meAttributes := oFsm.pOnuDB.GetMe(me.PriorityQueueClassID, mgmtEntityID); meAttributes != nil {
+ returnVal := meAttributes["RelatedPort"]
+ if returnVal != nil {
+ if relatedPort, err := oFsm.pOnuDB.getUint32Attrib(returnVal); err == nil {
+ if relatedPort == usQrelPortMask {
+ loGemPortAttribs.upQueueID = mgmtEntityID
+ logger.Debugw("UpQueue for GemPort found:", log.Fields{"gemPortID": loGemPortAttribs.gemPortID,
+ "upQueueID": strconv.FormatInt(int64(loGemPortAttribs.upQueueID), 16), "device-id": oFsm.pAdaptFsm.deviceID})
+ usQueueFound = true
+ } else if (relatedPort&0xFFFFFF) == dsQrelPortMask && mgmtEntityID < 0x8000 {
+ loGemPortAttribs.downQueueID = mgmtEntityID
+ logger.Debugw("DownQueue for GemPort found:", log.Fields{"gemPortID": loGemPortAttribs.gemPortID,
+ "downQueueID": strconv.FormatInt(int64(loGemPortAttribs.downQueueID), 16), "device-id": oFsm.pAdaptFsm.deviceID})
+ dsQueueFound = true
+ }
+ if usQueueFound && dsQueueFound {
+ break
+ }
+ } else {
+ logger.Warnw("Could not convert attribute value", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ }
+ } else {
+ logger.Warnw("'RelatedPort' not found in meAttributes:", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ }
+ } else {
+ logger.Warnw("No attributes available in DB:", log.Fields{"meClassID": me.PriorityQueueClassID,
+ "mgmtEntityID": mgmtEntityID, "device-id": oFsm.pAdaptFsm.deviceID})
+ }
+ }
+ } else {
+ logger.Warnw("No PriorityQueue instances found", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ }
+ loGemPortAttribs.direction = gemEntry.direction
+ loGemPortAttribs.qosPolicy = gemEntry.queueSchedPolicy
+ loGemPortAttribs.weight = gemEntry.queueWeight
+ loGemPortAttribs.pbitString = gemEntry.pbitString
+
+ logger.Debugw("prio-related GemPort attributes:", log.Fields{
+ "gemPortID": loGemPortAttribs.gemPortID,
+ "upQueueID": loGemPortAttribs.upQueueID,
+ "downQueueID": loGemPortAttribs.downQueueID,
+ "pbitString": loGemPortAttribs.pbitString,
+ "prioQueueIndex": gemEntry.prioQueueIndex,
+ })
+
+ oFsm.gemPortAttribsSlice = append(oFsm.gemPortAttribsSlice, loGemPortAttribs)
+ }
+ _ = aPAFsm.pFsm.Event(aniEvStartConfig)
+ }
+}
+
+func (oFsm *uniPonAniConfigFsm) enterConfigStartingState(e *fsm.Event) {
+ logger.Debugw("UniPonAniConfigFsm start", log.Fields{"in state": e.FSM.Current(),
+ "device-id": oFsm.pAdaptFsm.deviceID})
+ if oFsm.omciMIdsResponseReceived == nil {
+ oFsm.omciMIdsResponseReceived = make(chan bool)
+ logger.Debug("uniPonAniConfigFsm - OMCI multiInstance RxChannel defined")
+ } else {
+ // as we may 're-use' this instance of FSM and the connected channel
+ // make sure there is no 'lingering' request in the already existing channel:
+ // (simple loop sufficient as we are the only receiver)
+ for len(oFsm.omciMIdsResponseReceived) > 0 {
+ <-oFsm.omciMIdsResponseReceived
+ }
+ }
+ oFsm.gemPortAttribsSlice = nil
+
+ go oFsm.processOmciAniMessages()
+
+ pConfigAniStateAFsm := oFsm.pAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go oFsm.prepareAndEnterConfigState(pConfigAniStateAFsm)
+
+ }
+}
+
+func (oFsm *uniPonAniConfigFsm) enterCreatingDot1PMapper(e *fsm.Event) {
+ logger.Debugw("uniPonAniConfigFsm Tx Create::Dot1PMapper", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ meInstance := oFsm.pOmciCC.sendCreateDot1PMapper(context.TODO(), ConstDefaultOmciTimeout, true,
+ oFsm.mapperSP0ID, oFsm.pAdaptFsm.commChan)
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (oFsm *uniPonAniConfigFsm) enterCreatingMBPCD(e *fsm.Event) {
+ logger.Debugw("uniPonAniConfigFsm Tx Create::MBPCD", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(oFsm.macBPCD0ID), 16),
+ "TPPtr": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ bridgePtr := macBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.macBpNo) //cmp also omci_cc.go::sendCreateMBServiceProfile
+ meParams := me.ParamData{
+ EntityID: oFsm.macBPCD0ID,
+ Attributes: me.AttributeValueMap{
+ "BridgeIdPointer": bridgePtr,
+ "PortNum": 0xFF, //fixed unique ANI side indication
+ "TpType": 3, //for .1PMapper
+ "TpPointer": oFsm.mapperSP0ID,
+ },
+ }
+ meInstance := oFsm.pOmciCC.sendCreateMBPConfigDataVar(context.TODO(), ConstDefaultOmciTimeout, true,
+ oFsm.pAdaptFsm.commChan, meParams)
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+}
+
+func (oFsm *uniPonAniConfigFsm) enterSettingTconts(e *fsm.Event) {
+ logger.Debugw("uniPonAniConfigFsm Tx Set::Tcont", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
+ "AllocId": strconv.FormatInt(int64(oFsm.alloc0ID), 16),
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ meParams := me.ParamData{
+ EntityID: oFsm.tcont0ID,
+ Attributes: me.AttributeValueMap{
+ "AllocId": oFsm.alloc0ID,
+ },
+ }
+ meInstance := oFsm.pOmciCC.sendSetTcontVar(context.TODO(), ConstDefaultOmciTimeout, true,
+ oFsm.pAdaptFsm.commChan, meParams)
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (oFsm *uniPonAniConfigFsm) enterCreatingGemNCTPs(e *fsm.Event) {
+ logger.Debugw("uniPonAniConfigFsm - start creating GemNWCtp loop", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ go oFsm.performCreatingGemNCTPs()
+}
+
+func (oFsm *uniPonAniConfigFsm) enterCreatingGemIWs(e *fsm.Event) {
+ logger.Debugw("uniPonAniConfigFsm - start creating GemIwTP loop", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ go oFsm.performCreatingGemIWs()
+}
+
+func (oFsm *uniPonAniConfigFsm) enterSettingPQs(e *fsm.Event) {
+ logger.Debugw("uniPonAniConfigFsm - start setting PrioQueue loop", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ go oFsm.performSettingPQs()
+}
+
+func (oFsm *uniPonAniConfigFsm) enterSettingDot1PMapper(e *fsm.Event) {
+ logger.Debugw("uniPonAniConfigFsm Tx Set::.1pMapper with all PBits set", log.Fields{"EntitytId": 0x8042, /*cmp above*/
+ "toGemIw": 1024 /* cmp above */, "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+
+ logger.Debugw("uniPonAniConfigFsm Tx Set::1pMapper", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+
+ meParams := me.ParamData{
+ EntityID: oFsm.mapperSP0ID,
+ Attributes: make(me.AttributeValueMap),
+ }
+
+ var loPrioGemPortArray [8]uint16
+ for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+ for i := 0; i < 8; i++ {
+ // "lenOfPbitMap(8) - i + 1" will give i-th pbit value from LSB position in the pbit map string
+ if prio, err := strconv.Atoi(string(gemPortAttribs.pbitString[7-i])); err == nil {
+ if prio == 1 { // Check this p-bit is set
+ if loPrioGemPortArray[i] == 0 {
+ loPrioGemPortArray[i] = gemPortAttribs.gemPortID //gemPortId=EntityID and unique
+ } else {
+ logger.Warnw("uniPonAniConfigFsm PrioString not unique", log.Fields{
+ "device-id": oFsm.pAdaptFsm.deviceID, "IgnoredGemPort": gemPortAttribs.gemPortID,
+ "SetGemPort": loPrioGemPortArray[i]})
+ }
+ }
+ } else {
+ logger.Warnw("uniPonAniConfigFsm PrioString evaluation error", log.Fields{
+ "device-id": oFsm.pAdaptFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+ "prioString": gemPortAttribs.pbitString, "position": i})
+ }
+
+ }
+ }
+ var foundIwPtr bool = false
+ for index, value := range loPrioGemPortArray {
+ if value != 0 {
+ foundIwPtr = true
+ meAttribute := fmt.Sprintf("InterworkTpPointerForPBitPriority%d", index)
+ logger.Debugf("UniPonAniConfigFsm Set::1pMapper", log.Fields{
+ "IwPtr for Prio%d": strconv.FormatInt(int64(value), 16), "device-id": oFsm.pAdaptFsm.deviceID}, index)
+ meParams.Attributes[meAttribute] = value
+
+ }
+ }
+
+ if !foundIwPtr {
+ logger.Errorw("UniPonAniConfigFsm no GemIwPtr found for .1pMapper - abort", log.Fields{
+ "device-id": oFsm.pAdaptFsm.deviceID})
+ //let's reset the state machine in order to release all resources now
+ pConfigAniStateAFsm := oFsm.pAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.pFsm != nil {
+ _ = aPAFsm.pFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ }
+ }
+
+ meInstance := oFsm.pOmciCC.sendSetDot1PMapperVar(context.TODO(), ConstDefaultOmciTimeout, true,
+ oFsm.pAdaptFsm.commChan, meParams)
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (oFsm *uniPonAniConfigFsm) enterAniConfigDone(e *fsm.Event) {
+
+ oFsm.aniConfigCompleted = true
+
+ pConfigAniStateAFsm := oFsm.pAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(aPAFsm *AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.pFsm != nil {
+ _ = aPAFsm.pFsm.Event(aniEvReset)
+ }
+ }(pConfigAniStateAFsm)
+ }
+}
+
+func (oFsm *uniPonAniConfigFsm) enterResettingState(e *fsm.Event) {
+ logger.Debugw("uniPonAniConfigFsm resetting", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+
+ pConfigAniStateAFsm := oFsm.pAdaptFsm
+ if pConfigAniStateAFsm != nil {
+ // abort running message processing
+ fsmAbortMsg := Message{
+ Type: TestMsg,
+ Data: TestMessage{
+ TestMessageVal: AbortMessageProcessing,
+ },
+ }
+ pConfigAniStateAFsm.commChan <- fsmAbortMsg
+
+ //try to restart the FSM to 'disabled', decouple event transfer
+ go func(aPAFsm *AdapterFsm) {
+ if aPAFsm != nil && aPAFsm.pFsm != nil {
+ _ = aPAFsm.pFsm.Event(aniEvRestart)
+ }
+ }(pConfigAniStateAFsm)
+ }
+}
+
+func (oFsm *uniPonAniConfigFsm) enterDisabledState(e *fsm.Event) {
+ logger.Debugw("uniPonAniConfigFsm enters disabled state", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+
+ if oFsm.aniConfigCompleted {
+ logger.Debugw("uniPonAniConfigFsm send dh event notification", log.Fields{
+ "from_State": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ //use DeviceHandler event notification directly
+ oFsm.pOmciCC.pBaseDeviceHandler.deviceProcStatusUpdate(oFsm.requestEvent)
+ oFsm.aniConfigCompleted = false
+ }
+ oFsm.pUniTechProf.setConfigDone(oFsm.pOnuUniPort.uniID, true)
+ go oFsm.pOmciCC.pBaseDeviceHandler.verifyUniVlanConfigRequest(oFsm.pOnuUniPort)
+
+ if oFsm.chanSet {
+ // indicate processing done to the caller
+ logger.Debugw("uniPonAniConfigFsm processingDone on channel", log.Fields{
+ "ProcessingStep": oFsm.procStep, "from_State": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ oFsm.chSuccess <- oFsm.procStep
+ oFsm.chanSet = false //reset the internal channel state
+ }
+
+}
+
+func (oFsm *uniPonAniConfigFsm) processOmciAniMessages( /*ctx context.Context*/ ) {
+ logger.Debugw("Start uniPonAniConfigFsm Msg processing", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+loop:
+ for {
+ // case <-ctx.Done():
+ // logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": oFsm.pAdaptFsm.deviceID})
+ // break loop
+ message, ok := <-oFsm.pAdaptFsm.commChan
+ if !ok {
+ logger.Info("UniPonAniConfigFsm Rx Msg - could not read from channel", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ // but then we have to ensure a restart of the FSM as well - as exceptional procedure
+ _ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+ break loop
+ }
+ logger.Debugw("UniPonAniConfigFsm Rx Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+
+ switch message.Type {
+ case TestMsg:
+ msg, _ := message.Data.(TestMessage)
+ if msg.TestMessageVal == AbortMessageProcessing {
+ logger.Infow("UniPonAniConfigFsm abort ProcessMsg", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+ break loop
+ }
+ logger.Warnw("UniPonAniConfigFsm unknown TestMessage", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "MessageVal": msg.TestMessageVal})
+ case OMCI:
+ msg, _ := message.Data.(OmciMessage)
+ oFsm.handleOmciAniConfigMessage(msg)
+ default:
+ logger.Warn("UniPonAniConfigFsm Rx unknown message", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
+ "message.Type": message.Type})
+ }
+
+ }
+ logger.Infow("End uniPonAniConfigFsm Msg processing", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+}
+
+func (oFsm *uniPonAniConfigFsm) handleOmciAniConfigCreateResponseMessage(msg OmciMessage) {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeCreateResponse)
+ if msgLayer == nil {
+ logger.Error("Omci Msg layer could not be detected for CreateResponse")
+ return
+ }
+ msgObj, msgOk := msgLayer.(*omci.CreateResponse)
+ if !msgOk {
+ logger.Error("Omci Msg layer could not be assigned for CreateResponse")
+ return
+ }
+ logger.Debugw("CreateResponse Data", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "data-fields": msgObj})
+ if msgObj.Result != me.Success {
+ logger.Errorw("Omci CreateResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+ // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+ return
+ }
+ if msgObj.EntityClass == oFsm.pOmciCC.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == oFsm.pOmciCC.pLastTxMeInstance.GetEntityID() {
+ // maybe we can use just the same eventName for different state transitions like "forward"
+ // - might be checked, but so far I go for sure and have to inspect the concrete state events ...
+ switch oFsm.pOmciCC.pLastTxMeInstance.GetName() {
+ case "Ieee8021PMapperServiceProfile":
+ { // let the FSM proceed ...
+ _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxDot1pmapCResp)
+ }
+ case "MacBridgePortConfigurationData":
+ { // let the FSM proceed ...
+ _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxMbpcdResp)
+ }
+ case "GemPortNetworkCtp", "GemInterworkingTerminationPoint":
+ { // let aniConfig Multi-Id processing proceed by stopping the wait function
+ oFsm.omciMIdsResponseReceived <- true
+ }
+ }
+ }
+}
+
+func (oFsm *uniPonAniConfigFsm) handleOmciAniConfigSetResponseMessage(msg OmciMessage) {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
+ if msgLayer == nil {
+ logger.Error("UniPonAniConfigFsm - Omci Msg layer could not be detected for SetResponse")
+ return
+ }
+ msgObj, msgOk := msgLayer.(*omci.SetResponse)
+ if !msgOk {
+ logger.Error("UniPonAniConfigFsm - Omci Msg layer could not be assigned for SetResponse")
+ return
+ }
+ logger.Debugw("UniPonAniConfigFsm SetResponse Data", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "data-fields": msgObj})
+ if msgObj.Result != me.Success {
+ logger.Errorw("UniPonAniConfigFsm - Omci SetResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+ // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+ return
+ }
+ if msgObj.EntityClass == oFsm.pOmciCC.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == oFsm.pOmciCC.pLastTxMeInstance.GetEntityID() {
+ //store the created ME into DB //TODO??? obviously the Python code does not store the config ...
+ // if, then something like:
+ //oFsm.pOnuDB.StoreMe(msgObj)
+
+ switch oFsm.pOmciCC.pLastTxMeInstance.GetName() {
+ case "TCont":
+ { // let the FSM proceed ...
+ _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxTcontsResp)
+ }
+ case "PriorityQueue":
+ { // let the PrioQueue init proceed by stopping the wait function
+ oFsm.omciMIdsResponseReceived <- true
+ }
+ case "Ieee8021PMapperServiceProfile":
+ { // let the FSM proceed ...
+ _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxDot1pmapSResp)
+ }
+ }
+ }
+}
+
+func (oFsm *uniPonAniConfigFsm) handleOmciAniConfigMessage(msg OmciMessage) {
+ logger.Debugw("Rx OMCI UniPonAniConfigFsm Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
+ "msgType": msg.OmciMsg.MessageType})
+
+ switch msg.OmciMsg.MessageType {
+ case omci.CreateResponseType:
+ {
+ oFsm.handleOmciAniConfigCreateResponseMessage(msg)
+
+ } //CreateResponseType
+ case omci.SetResponseType:
+ {
+ oFsm.handleOmciAniConfigSetResponseMessage(msg)
+
+ } //SetResponseType
+ default:
+ {
+ logger.Errorw("uniPonAniConfigFsm - Rx OMCI unhandled MsgType", log.Fields{"omciMsgType": msg.OmciMsg.MessageType})
+ return
+ }
+ }
+}
+
+func (oFsm *uniPonAniConfigFsm) performCreatingGemNCTPs() {
+ for gemIndex, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+ logger.Debugw("uniPonAniConfigFsm Tx Create::GemNWCtp", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(gemPortAttribs.gemPortID), 16),
+ "TcontId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
+ "device-id": oFsm.pAdaptFsm.deviceID})
+ meParams := me.ParamData{
+ EntityID: gemPortAttribs.gemPortID, //unique, same as PortId
+ Attributes: me.AttributeValueMap{
+ "PortId": gemPortAttribs.gemPortID,
+ "TContPointer": oFsm.tcont0ID,
+ "Direction": gemPortAttribs.direction,
+ //ONU-G.TrafficManagementOption dependency ->PrioQueue or TCont
+ // TODO!! verify dependency and QueueId in case of Multi-GemPort setup!
+ "TrafficManagementPointerForUpstream": gemPortAttribs.upQueueID, //might be different in wrr-only Setup - tcont0ID
+ "PriorityQueuePointerForDownStream": gemPortAttribs.downQueueID,
+ },
+ }
+ meInstance := oFsm.pOmciCC.sendCreateGemNCTPVar(context.TODO(), ConstDefaultOmciTimeout, true,
+ oFsm.pAdaptFsm.commChan, meParams)
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+ //verify response
+ err := oFsm.waitforOmciResponse()
+ if err != nil {
+ logger.Errorw("GemNWCtp create failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "GemIndex": gemIndex})
+ _ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+ return
+ }
+ } //for all GemPorts of this T-Cont
+
+ logger.Debugw("GemNWCtp create loop finished", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxGemntcpsResp)
+}
+
+func (oFsm *uniPonAniConfigFsm) performCreatingGemIWs() {
+ for gemIndex, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+ logger.Debugw("uniPonAniConfigFsm Tx Create::GemIwTp", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(gemPortAttribs.gemPortID), 16),
+ "SPPtr": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+ "device-id": oFsm.pAdaptFsm.deviceID})
+ meParams := me.ParamData{
+ EntityID: gemPortAttribs.gemPortID,
+ Attributes: me.AttributeValueMap{
+ "GemPortNetworkCtpConnectivityPointer": gemPortAttribs.gemPortID, //same as EntityID, see above
+ "InterworkingOption": 5, //fixed model:: G.998 .1pMapper
+ "ServiceProfilePointer": oFsm.mapperSP0ID,
+ "InterworkingTerminationPointPointer": 0, //not used with .1PMapper Mac bridge
+ "GalProfilePointer": galEthernetEID,
+ },
+ }
+ meInstance := oFsm.pOmciCC.sendCreateGemIWTPVar(context.TODO(), ConstDefaultOmciTimeout, true,
+ oFsm.pAdaptFsm.commChan, meParams)
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+ //verify response
+ err := oFsm.waitforOmciResponse()
+ if err != nil {
+ logger.Errorw("GemIwTp create failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "GemIndex": gemIndex})
+ _ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+ return
+ }
+ } //for all GemPort's of this T-Cont
+
+ logger.Debugw("GemIwTp create loop finished", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxGemiwsResp)
+}
+
+func (oFsm *uniPonAniConfigFsm) performSettingPQs() {
+ const cu16StrictPrioWeight uint16 = 0xFFFF
+ loQueueMap := ordered_map.NewOrderedMap()
+ for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+ if gemPortAttribs.qosPolicy == "WRR" {
+ if _, ok := loQueueMap.Get(gemPortAttribs.upQueueID); !ok {
+ //key does not yet exist
+ loQueueMap.Set(gemPortAttribs.upQueueID, uint16(gemPortAttribs.weight))
+ }
+ } else {
+ loQueueMap.Set(gemPortAttribs.upQueueID, cu16StrictPrioWeight) //use invalid weight value to indicate SP
+ }
+ }
+
+
+ loTrafficSchedulerEID := 0x8000
+ iter := loQueueMap.IterFunc()
+ for kv, ok := iter(); ok; kv, ok = iter() {
+ queueIndex := (kv.Key).(uint16)
+ meParams := me.ParamData{
+ EntityID: queueIndex,
+ Attributes: make(me.AttributeValueMap),
+ }
+ if (kv.Value).(uint16) == cu16StrictPrioWeight {
+ //StrictPrio indication
+ logger.Debugw("uniPonAniConfigFsm Tx Set::PrioQueue to StrictPrio", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(queueIndex), 16),
+ "device-id": oFsm.pAdaptFsm.deviceID})
+ meParams.Attributes["TrafficSchedulerPointer"] = 0 //ensure T-Cont defined StrictPrio scheduling
+ } else {
+ //WRR indication
+ logger.Debugw("uniPonAniConfigFsm Tx Set::PrioQueue to WRR", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(queueIndex), 16),
+ "Weight": kv.Value,
+ "device-id": oFsm.pAdaptFsm.deviceID})
+ meParams.Attributes["TrafficSchedulerPointer"] = loTrafficSchedulerEID //ensure assignment of the relevant trafficScheduler
+ meParams.Attributes["Weight"] = uint8(kv.Value.(uint16))
+ }
+ meInstance := oFsm.pOmciCC.sendSetPrioQueueVar(context.TODO(), ConstDefaultOmciTimeout, true,
+ oFsm.pAdaptFsm.commChan, meParams)
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+ //verify response
+ err := oFsm.waitforOmciResponse()
+ if err != nil {
+ logger.Errorw("PrioQueue set failed, aborting AniConfig FSM!",
+ log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "QueueId": strconv.FormatInt(int64(queueIndex), 16)})
+ _ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+ return
+ }
+
+ //TODO: In case of WRR setting of the GemPort/PrioQueue it might further be necessary to
+ // write the assigned trafficScheduler with the requested Prio to be considered in the StrictPrio scheduling
+ // of the (next upstream) assigned T-Cont, which is f(prioQueue[priority]) - in relation to other SP prioQueues
+ // not yet done because of BBSIM TrafficScheduler issues (and not done in py code as well)
+
+ } //for all upstream prioQueues
+
+ logger.Debugw("PrioQueue set loop finished", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ _ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxPrioqsResp)
+}
+
+func (oFsm *uniPonAniConfigFsm) waitforOmciResponse() error {
+ select {
+ case <-time.After(30 * time.Second): //3s was detected to be to less in 8*8 bbsim test with debug Info/Debug
+ logger.Warnw("UniPonAniConfigFsm multi entity timeout", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+ return errors.New("uniPonAniConfigFsm multi entity timeout")
+ case success := <-oFsm.omciMIdsResponseReceived:
+ if success {
+ logger.Debug("uniPonAniConfigFsm multi entity response received")
+ return nil
+ }
+ // should not happen so far
+ logger.Warnw("uniPonAniConfigFsm multi entity response error", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+ return errors.New("uniPonAniConfigFsm multi entity responseError")
+ }
+}
diff --git a/internal/pkg/onuadaptercore/omci_cc.go b/internal/pkg/onuadaptercore/omci_cc.go
new file mode 100644
index 0000000..9cf38ac
--- /dev/null
+++ b/internal/pkg/onuadaptercore/omci_cc.go
@@ -0,0 +1,1452 @@
+/*
+ * 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 (
+ "container/list"
+ "context"
+ "encoding/binary"
+ "encoding/hex"
+ "errors"
+ "strconv"
+ "sync"
+
+
+ "github.com/google/gopacket"
+ gp "github.com/google/gopacket"
+
+ "github.com/opencord/omci-lib-go"
+ me "github.com/opencord/omci-lib-go/generated"
+ "github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ ic "github.com/opencord/voltha-protos/v3/go/inter_container"
+)
+
+// ### OMCI related definitions - retrieved from Python adapter code/trace ####
+
+//ConstDefaultOmciTimeout - Default OMCI Timeout
+const ConstDefaultOmciTimeout = 10 // ( 3 ?) Seconds
+
+const galEthernetEID = uint16(1)
+const maxGemPayloadSize = uint16(48)
+const connectivityModeValue = uint8(5)
+
+//const defaultTPID = uint16(0x8100)
+//const broadComDefaultVID = uint16(4091)
+const macBridgeServiceProfileEID = uint16(0x201) // TODO: most all these need better definition or tuning
+const ieeeMapperServiceProfileEID = uint16(0x8001)
+const macBridgePortAniEID = uint16(0x2102)
+
+// ### OMCI related definitions - end
+
+//callbackPairEntry to be used for OMCI send/receive correlation
+type callbackPairEntry struct {
+ cbRespChannel chan Message
+ cbFunction func(*omci.OMCI, *gp.Packet, chan Message) error
+}
+
+//callbackPair to be used for ReceiveCallback init
+type callbackPair struct {
+ cbKey uint16
+ cbEntry callbackPairEntry
+}
+
+type omciTransferStructure struct {
+ txFrame []byte
+ timeout int
+ retry int
+ highPrio bool
+}
+
+//omciCC structure holds information needed for OMCI communication (to/from OLT Adapter)
+type omciCC struct {
+ enabled bool
+ pOnuDeviceEntry *OnuDeviceEntry
+ deviceID string
+ pBaseDeviceHandler *deviceHandler
+ coreProxy adapterif.CoreProxy
+ adapterProxy adapterif.AdapterProxy
+ supportExtMsg bool
+ txFrames, txOnuFrames uint32
+ rxFrames, rxOnuFrames, rxOnuDiscards uint32
+
+ mutexTid sync.Mutex
+ tid uint16
+ mutexHpTid sync.Mutex
+ hpTid uint16
+ uploadSequNo uint16
+ uploadNoOfCmds uint16
+
+ mutexTxQueue sync.Mutex
+ txQueue *list.List
+ mutexRxSchedMap sync.Mutex
+ rxSchedulerMap map[uint16]callbackPairEntry
+ pLastTxMeInstance *me.ManagedEntity
+}
+
+//newOmciCC constructor returns a new instance of a OmciCC
+//mib_db (as well as not inluded alarm_db not really used in this code? VERIFY!!)
+func newOmciCC(ctx context.Context, onuDeviceEntry *OnuDeviceEntry,
+ deviceID string, deviceHandler *deviceHandler,
+ coreProxy adapterif.CoreProxy, adapterProxy adapterif.AdapterProxy) *omciCC {
+ logger.Infow("init-omciCC", log.Fields{"device-id": deviceID})
+ var omciCC omciCC
+ omciCC.enabled = false
+ omciCC.pOnuDeviceEntry = onuDeviceEntry
+ omciCC.deviceID = deviceID
+ omciCC.pBaseDeviceHandler = deviceHandler
+ omciCC.coreProxy = coreProxy
+ omciCC.adapterProxy = adapterProxy
+ omciCC.supportExtMsg = false
+ omciCC.txFrames = 0
+ omciCC.txOnuFrames = 0
+ omciCC.rxFrames = 0
+ omciCC.rxOnuFrames = 0
+ omciCC.rxOnuDiscards = 0
+ omciCC.tid = 0x1
+ omciCC.hpTid = 0x8000
+ omciCC.uploadSequNo = 0
+ omciCC.uploadNoOfCmds = 0
+
+ omciCC.txQueue = list.New()
+ omciCC.rxSchedulerMap = make(map[uint16]callbackPairEntry)
+
+ return &omciCC
+}
+
+// Rx handler for omci messages
+func (oo *omciCC) receiveOnuMessage(ctx context.Context, omciMsg *omci.OMCI) error {
+ logger.Debugw("rx-onu-autonomous-message", log.Fields{"omciMsgType": omciMsg.MessageType,
+ "payload": hex.EncodeToString(omciMsg.Payload)})
+ /*
+ msgType = rxFrame.fields["message_type"] //assumed OmciOperationsValue
+ rxOnuFrames++
+
+ switch msgType {
+ case AlarmNotification:
+ {
+ logger.Info("Unhandled: received-onu-alarm-message")
+ // python code was:
+ //if msg_type == EntityOperations.AlarmNotification.value:
+ // topic = OMCI_CC.event_bus_topic(self._device_id, RxEvent.Alarm_Notification)
+ // self.reactor.callLater(0, self.event_bus.publish, topic, msg)
+ //
+ return errors.New("RxAlarmNotification unimplemented")
+ }
+ case AttributeValueChange:
+ {
+ logger.Info("Unhandled: received-attribute-value-change")
+ // python code was:
+ //elif msg_type == EntityOperations.AttributeValueChange.value:
+ // topic = OMCI_CC.event_bus_topic(self._device_id, RxEvent.AVC_Notification)
+ // self.reactor.callLater(0, self.event_bus.publish, topic, msg)
+ //
+ return errors.New("RxAttributeValueChange unimplemented")
+ }
+ case TestResult:
+ {
+ logger.Info("Unhandled: received-test-result")
+ // python code was:
+ //elif msg_type == EntityOperations.TestResult.value:
+ // topic = OMCI_CC.event_bus_topic(self._device_id, RxEvent.Test_Result)
+ // self.reactor.callLater(0, self.event_bus.publish, topic, msg)
+ //
+ return errors.New("RxTestResult unimplemented")
+ }
+ default:
+ {
+ logger.Errorw("rx-onu-unsupported-autonomous-message", log.Fields{"msgType": msgType})
+ rxOnuDiscards++
+ return errors.New("RxOnuMsgType unimplemented")
+ }
+ }
+ */
+ return errors.New("receiveOnuMessage unimplemented")
+}
+
+// Rx handler for onu messages
+// e.g. would call ReceiveOnuMessage() in case of TID=0 or Action=test ...
+func (oo *omciCC) receiveMessage(ctx context.Context, rxMsg []byte) error {
+ if len(rxMsg) >= 44 { // then it should normally include the BaseFormat trailer Len
+ // NOTE: autocorrection only valid for OmciBaseFormat, which is not specifically verified here!!!
+ // (am extendedFormat message could be destroyed this way!)
+ trailerLenData := rxMsg[42:44]
+ trailerLen := binary.BigEndian.Uint16(trailerLenData)
+ //logger.Debugw("omci-received-trailer-len", log.Fields{"Length": trailerLen})
+ if trailerLen != 40 { // invalid base Format entry -> autocorrect
+ binary.BigEndian.PutUint16(rxMsg[42:44], 40)
+ logger.Debug("cc-corrected-omci-message: trailer len inserted")
+ }
+ } else {
+ logger.Errorw("received omci-message too small for OmciBaseFormat - abort", log.Fields{"Length": len(rxMsg)})
+ return errors.New("rxOmciMessage too small for BaseFormat")
+ }
+
+ packet := gopacket.NewPacket(rxMsg, omci.LayerTypeOMCI, gopacket.NoCopy)
+ if packet == nil {
+ logger.Error("omci-message could not be decoded")
+ return errors.New("could not decode rxMsg as OMCI")
+ }
+ omciLayer := packet.Layer(omci.LayerTypeOMCI)
+ if omciLayer == nil {
+ logger.Error("omci-message could not decode omci layer")
+ return errors.New("could not decode omci layer")
+ }
+ omciMsg, ok := omciLayer.(*omci.OMCI)
+ if !ok {
+ logger.Error("omci-message could not assign omci layer")
+ return errors.New("could not assign omci layer")
+ }
+ logger.Debugw("omci-message-decoded:", log.Fields{"omciMsgType": omciMsg.MessageType,
+ "transCorrId": strconv.FormatInt(int64(omciMsg.TransactionID), 16), "DeviceIdent": omciMsg.DeviceIdentifier})
+ if byte(omciMsg.MessageType) & ^me.AK == 0 {
+ // Not a response
+ logger.Debug("RxMsg is no Omci Response Message")
+ if omciMsg.TransactionID == 0 {
+ return oo.receiveOnuMessage(ctx, omciMsg)
+ }
+ logger.Errorw("Unexpected TransCorrId != 0 not accepted for autonomous messages",
+ log.Fields{"msgType": omciMsg.MessageType, "payload": hex.EncodeToString(omciMsg.Payload)})
+ return errors.New("autonomous Omci Message with TranSCorrId != 0 not acccepted")
+
+ }
+ oo.mutexRxSchedMap.Lock()
+ rxCallbackEntry, ok := oo.rxSchedulerMap[omciMsg.TransactionID]
+ if ok && rxCallbackEntry.cbFunction != nil {
+ //disadvantage of decoupling: error verification made difficult, but anyway the question is
+ // how to react on erroneous frame reception, maybe can simply be ignored
+ go rxCallbackEntry.cbFunction(omciMsg, &packet, rxCallbackEntry.cbRespChannel)
+ // having posted the response the request is regarded as 'done'
+ delete(oo.rxSchedulerMap, omciMsg.TransactionID)
+ oo.mutexRxSchedMap.Unlock()
+ } else {
+ oo.mutexRxSchedMap.Unlock()
+ logger.Error("omci-message-response for not registered transCorrId")
+ return errors.New("could not find registered response handler tor transCorrId")
+ }
+
+ return nil
+ /* py code was:
+ Receive and OMCI message from the proxy channel to the OLT.
+
+ Call this from your ONU Adapter on a new OMCI Rx on the proxy channel
+ :param msg: (str) OMCI binary message (used as input to Scapy packet decoder)
+ """
+ if not self.enabled:
+ return
+
+ try:
+ now = arrow.utcnow()
+ d = None
+
+ # NOTE: Since we may need to do an independent ME map on a per-ONU basis
+ # save the current value of the entity_id_to_class_map, then
+ # replace it with our custom one before decode, and then finally
+ # restore it later. Tried other ways but really made the code messy.
+ saved_me_map = omci_entities.entity_id_to_class_map
+ omci_entities.entity_id_to_class_map = self._me_map
+
+ try:
+ rx_frame = msg if isinstance(msg, OmciFrame) else OmciFrame(msg)
+ self.logger.debug('recv-omci-msg', omci_msg=hexlify(msg))
+ except KeyError as e:
+ # Unknown, Unsupported, or vendor-specific ME. Key is the unknown classID
+ self.logger.debug('frame-decode-key-error', omci_msg=hexlify(msg), e=e)
+ rx_frame = self._decode_unknown_me(msg)
+ self._rx_unknown_me += 1
+
+ except Exception as e:
+ self.logger.exception('frame-decode', omci_msg=hexlify(msg), e=e)
+ return
+
+ finally:
+ omci_entities.entity_id_to_class_map = saved_me_map # Always restore it.
+
+ rx_tid = rx_frame.fields['transaction_id']
+ msg_type = rx_frame.fields['message_type']
+ self.logger.debug('Received message for rx_tid', rx_tid = rx_tid, msg_type = msg_type)
+ # Filter the Test Result frame and route through receive onu
+ # message method.
+ if rx_tid == 0 or msg_type == EntityOperations.TestResult.value:
+ self.logger.debug('Receive ONU message', rx_tid=0)
+ return self._receive_onu_message(rx_frame)
+
+ # Previously unreachable if this is the very first round-trip Rx or we
+ # have been running consecutive errors
+ if self._rx_frames == 0 or self._consecutive_errors != 0:
+ self.logger.debug('Consecutive errors for rx', err = self._consecutive_errors)
+ self.reactor.callLater(0, self._publish_connectivity_event, True)
+
+ self._rx_frames += 1
+ self._consecutive_errors = 0
+
+ try:
+ high_priority = self._tid_is_high_priority(rx_tid)
+ index = self._get_priority_index(high_priority)
+
+ # (timestamp, defer, frame, timeout, retry, delayedCall)
+ last_tx_tuple = self._tx_request[index]
+
+ if last_tx_tuple is None or \
+ last_tx_tuple[OMCI_CC.REQUEST_FRAME].fields.get('transaction_id') != rx_tid:
+ # Possible late Rx on a message that timed-out
+ if last_tx_tuple:
+ self.logger.debug('Unknown message', rx_tid=rx_tid,
+ tx_id=last_tx_tuple[OMCI_CC.REQUEST_FRAME].fields.get('transaction_id'))
+ self._rx_unknown_tid += 1
+ self._rx_late += 1
+ return
+
+ ts, d, tx_frame, timeout, retry, dc = last_tx_tuple
+ if dc is not None and not dc.cancelled and not dc.called:
+ dc.cancel()
+
+ _secs = self._update_rx_tx_stats(now, ts)
+
+ # Late arrival already serviced by a timeout?
+ if d.called:
+ self._rx_late += 1
+ self.logger.debug('Serviced by timeout. Late arrival', rx_late = self._rx_late)
+ return
+
+ except Exception as e:
+ self.logger.exception('frame-match', msg=hexlify(msg), e=e)
+ if d is not None:
+ return d.errback(failure.Failure(e))
+ return
+
+ # Publish Rx event to listeners in a different task
+ self.logger.debug('Publish rx event', rx_tid = rx_tid,
+ tx_tid = tx_frame.fields['transaction_id'])
+ reactor.callLater(0, self._publish_rx_frame, tx_frame, rx_frame)
+
+ # begin success callback chain (will cancel timeout and queue next Tx message)
+ self._rx_response[index] = rx_frame
+ d.callback(rx_frame)
+
+ except Exception as e:
+ self.logger.exception('rx-msg', e=e)
+ */
+}
+
+/*
+func (oo *omciCC) publishRxResponseFrame(ctx context.Context, txFrame []byte, rxFrame []byte) error {
+ return errors.New("publishRxResponseFrame unimplemented")
+ //def _publish_rx_frame(self, tx_frame, rx_frame):
+}
+*/
+
+//Queue the OMCI Frame for a transmit to the ONU via the proxy_channel
+func (oo *omciCC) send(ctx context.Context, txFrame []byte, timeout int, retry int, highPrio bool,
+ receiveCallbackPair callbackPair) error {
+
+ logger.Debugw("register-response-callback:", log.Fields{"for TansCorrId": receiveCallbackPair.cbKey})
+ oo.mutexRxSchedMap.Lock()
+ oo.rxSchedulerMap[receiveCallbackPair.cbKey] = receiveCallbackPair.cbEntry
+ oo.mutexRxSchedMap.Unlock()
+
+ omciTxRequest := omciTransferStructure{
+ txFrame,
+ timeout,
+ retry,
+ highPrio,
+ }
+ oo.mutexTxQueue.Lock()
+ oo.txQueue.PushBack(omciTxRequest) // enqueue
+ oo.mutexTxQueue.Unlock()
+
+ go oo.sendNextRequest(ctx)
+ return nil
+}
+
+//Pull next tx request and send it
+func (oo *omciCC) sendNextRequest(ctx context.Context) error {
+
+ oo.mutexTxQueue.Lock()
+ defer oo.mutexTxQueue.Unlock()
+ for oo.txQueue.Len() > 0 {
+ queueElement := oo.txQueue.Front() // First element
+ omciTxRequest := queueElement.Value.(omciTransferStructure)
+ /* compare olt device handler code:
+ func (dh *DeviceHandler) omciIndication(omciInd *oop.OmciIndication) {
+ logger.Debugw("omci indication", log.Fields{"intfID": omciInd.IntfId, "onuID": omciInd.OnuId})
+ var deviceType string
+ var deviceID string
+ var proxyDeviceID string
+
+ onuKey := dh.formOnuKey(omciInd.IntfId, omciInd.OnuId)
+
+ if onuInCache, ok := dh.onus.Load(onuKey); !ok {
+
+ logger.Debugw("omci indication for a device not in cache.", log.Fields{"intfID": omciInd.IntfId, "onuID": omciInd.OnuId})
+ ponPort := IntfIDToPortNo(omciInd.GetIntfId(), voltha.Port_PON_OLT)
+ kwargs := make(map[string]interface{})
+ kwargs["onu_id"] = omciInd.OnuId
+ kwargs["parent_port_no"] = ponPort
+
+ onuDevice, err := dh.coreProxy.GetChildDevice(context.TODO(), dh.device.Id, kwargs)
+ if err != nil {
+ logger.Errorw("onu not found", log.Fields{"intfID": omciInd.IntfId, "onuID": omciInd.OnuId, "error": err})
+ return
+ }
+ deviceType = onuDevice.Type
+ deviceID = onuDevice.Id
+ proxyDeviceID = onuDevice.ProxyAddress.DeviceId
+ //if not exist in cache, then add to cache.
+ dh.onus.Store(onuKey, NewOnuDevice(deviceID, deviceType, onuDevice.SerialNumber, omciInd.OnuId, omciInd.IntfId, proxyDeviceID))
+ } else {
+ //found in cache
+ logger.Debugw("omci indication for a device in cache.", log.Fields{"intfID": omciInd.IntfId, "onuID": omciInd.OnuId})
+ deviceType = onuInCache.(*OnuDevice).deviceType
+ deviceID = onuInCache.(*OnuDevice).deviceID
+ proxyDeviceID = onuInCache.(*OnuDevice).proxyDeviceID
+ }
+ */
+ /* and compare onu_adapter py code:
+ omci_msg = InterAdapterOmciMessage(
+ message=bytes(frame),
+ proxy_address=self._proxy_address,
+ connect_status=self._device.connect_status)
+
+ self.logger.debug('sent-omci-msg', tid=tx_tid, omci_msg=hexlify(bytes(frame)))
+
+ yield self._adapter_proxy.send_inter_adapter_message(
+ msg=omci_msg,
+ type=InterAdapterMessageType.OMCI_REQUEST,
+ from_adapter=self._device.type,
+ to_adapter=self._proxy_address.device_type,
+ to_device_id=self._device_id,
+ proxy_device_id=self._proxy_address.device_id
+ )
+ */
+ device, err := oo.coreProxy.GetDevice(ctx,
+ oo.pBaseDeviceHandler.deviceID, oo.deviceID) //parent, child
+ if err != nil || device == nil {
+ /*TODO: needs to handle error scenarios */
+ logger.Errorw("Failed to fetch device", log.Fields{"err": err, "ParentId": oo.pBaseDeviceHandler.deviceID,
+ "ChildId": oo.deviceID})
+ return errors.New("failed to fetch device")
+ }
+
+ logger.Debugw("omci-message-sending", log.Fields{"fromDeviceType": oo.pBaseDeviceHandler.DeviceType,
+ "toDeviceType": oo.pBaseDeviceHandler.ProxyAddressType,
+ "onuDeviceID": oo.deviceID, "proxyDeviceID": oo.pBaseDeviceHandler.ProxyAddressID})
+ logger.Debugw("omci-message-to-send:",
+ log.Fields{"TxOmciMessage": hex.EncodeToString(omciTxRequest.txFrame)})
+
+ omciMsg := &ic.InterAdapterOmciMessage{Message: omciTxRequest.txFrame}
+ if sendErr := oo.adapterProxy.SendInterAdapterMessage(context.Background(), omciMsg,
+ ic.InterAdapterMessageType_OMCI_REQUEST,
+ //fromType,toType,toDevId, ProxyDevId
+ oo.pBaseDeviceHandler.DeviceType, oo.pBaseDeviceHandler.ProxyAddressType,
+ oo.deviceID, oo.pBaseDeviceHandler.ProxyAddressID, ""); sendErr != nil {
+ logger.Errorw("send omci request error", log.Fields{"error": sendErr})
+ return sendErr
+ }
+ oo.txQueue.Remove(queueElement) // Dequeue
+ }
+ return nil
+}
+
+func (oo *omciCC) getNextTid(highPriority bool) uint16 {
+ var next uint16
+ if highPriority {
+ oo.mutexTid.Lock()
+ next = oo.hpTid
+ oo.hpTid++
+ if oo.hpTid < 0x8000 {
+ oo.hpTid = 0x8000
+ }
+ oo.mutexTid.Unlock()
+ } else {
+ oo.mutexHpTid.Lock()
+ next = oo.tid
+ oo.tid++
+ if oo.tid >= 0x8000 {
+ oo.tid = 1
+ }
+ oo.mutexHpTid.Unlock()
+ }
+ return next
+}
+
+// ###################################################################################
+// # utility methods provided to work on OMCI messages
+func serialize(msgType omci.MessageType, request gopacket.SerializableLayer, tid uint16) ([]byte, error) {
+ omciLayer := &omci.OMCI{
+ TransactionID: tid,
+ MessageType: msgType,
+ }
+ return serializeOmciLayer(omciLayer, request)
+}
+
+func serializeOmciLayer(aOmciLayer *omci.OMCI, aRequest gopacket.SerializableLayer) ([]byte, error) {
+ var options gopacket.SerializeOptions
+ options.FixLengths = true
+
+ buffer := gopacket.NewSerializeBuffer()
+ err := gopacket.SerializeLayers(buffer, options, aOmciLayer, aRequest)
+ if err != nil {
+ logger.Errorw("Could not create goPacket Omci serial buffer", log.Fields{"Err": err})
+ return nil, err
+ }
+ return buffer.Bytes(), nil
+}
+
+/*
+func hexEncode(omciPkt []byte) ([]byte, error) {
+ dst := make([]byte, hex.EncodedLen(len(omciPkt)))
+ hex.Encode(dst, omciPkt)
+ return dst, nil
+}
+*/
+
+//supply a response handler for omci response messages to be transferred to the requested FSM
+func (oo *omciCC) receiveOmciResponse(omciMsg *omci.OMCI, packet *gp.Packet, respChan chan Message) error {
+
+ logger.Debugw("omci-message-response - transfer on omciRespChannel", log.Fields{"omciMsgType": omciMsg.MessageType,
+ "transCorrId": strconv.FormatInt(int64(omciMsg.TransactionID), 16), "device-id": oo.deviceID})
+
+ if oo.pOnuDeviceEntry == nil {
+ logger.Errorw("Abort receiving OMCI response, DeviceEntryPointer is nil", log.Fields{
+ "device-id": oo.deviceID})
+ return errors.New("deviceEntryPointer is nil")
+ }
+
+ omciRespMsg := Message{
+ Type: OMCI,
+ Data: OmciMessage{
+ OmciMsg: omciMsg,
+ OmciPacket: packet,
+ },
+ }
+ respChan <- omciRespMsg
+
+ return nil
+}
+
+func (oo *omciCC) sendMibReset(ctx context.Context, timeout int, highPrio bool) error {
+
+ logger.Debugw("send MibReset-msg to:", log.Fields{"device-id": oo.deviceID})
+ request := &omci.MibResetRequest{
+ MeBasePacket: omci.MeBasePacket{
+ EntityClass: me.OnuDataClassID,
+ },
+ }
+ tid := oo.getNextTid(highPrio)
+ pkt, err := serialize(omci.MibResetRequestType, request, tid)
+ if err != nil {
+ logger.Errorw("Cannot serialize MibResetRequest", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return err
+ }
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse},
+ }
+ return oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+}
+
+func (oo *omciCC) sendReboot(ctx context.Context, timeout int, highPrio bool, responseChannel chan Message) error {
+ logger.Debugw("send Reboot-msg to:", log.Fields{"device-id": oo.deviceID})
+ request := &omci.RebootRequest{
+ MeBasePacket: omci.MeBasePacket{
+ EntityClass: me.OnuGClassID,
+ },
+ }
+ tid := oo.getNextTid(highPrio)
+ pkt, err := serialize(omci.RebootRequestType, request, tid)
+ if err != nil {
+ logger.Errorw("Cannot serialize RebootRequest", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return err
+ }
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{oo.pOnuDeviceEntry.omciRebootMessageReceivedChannel, oo.receiveOmciResponse},
+ }
+
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send RebootRequest", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return err
+ }
+ err = oo.pOnuDeviceEntry.waitForRebootResponse(responseChannel)
+ if err != nil {
+ logger.Error("aborting ONU Reboot!")
+ _ = oo.pOnuDeviceEntry.pMibDownloadFsm.pFsm.Event("reset")
+ return err
+ }
+ return nil
+}
+
+func (oo *omciCC) sendMibUpload(ctx context.Context, timeout int, highPrio bool) error {
+ logger.Debugw("send MibUpload-msg to:", log.Fields{"device-id": oo.deviceID})
+ request := &omci.MibUploadRequest{
+ MeBasePacket: omci.MeBasePacket{
+ EntityClass: me.OnuDataClassID,
+ },
+ }
+ tid := oo.getNextTid(highPrio)
+ pkt, err := serialize(omci.MibUploadRequestType, request, tid)
+ if err != nil {
+ logger.Errorw("Cannot serialize MibUploadRequest", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return err
+ }
+ oo.uploadSequNo = 0
+ oo.uploadNoOfCmds = 0
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse},
+ }
+ return oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+}
+
+func (oo *omciCC) sendMibUploadNext(ctx context.Context, timeout int, highPrio bool) error {
+ logger.Debugw("send MibUploadNext-msg to:", log.Fields{"device-id": oo.deviceID, "uploadSequNo": oo.uploadSequNo})
+ request := &omci.MibUploadNextRequest{
+ MeBasePacket: omci.MeBasePacket{
+ EntityClass: me.OnuDataClassID,
+ },
+ CommandSequenceNumber: oo.uploadSequNo,
+ }
+ tid := oo.getNextTid(highPrio)
+ pkt, err := serialize(omci.MibUploadNextRequestType, request, tid)
+ if err != nil {
+ logger.Errorw("Cannot serialize MibUploadNextRequest", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return err
+ }
+ oo.uploadSequNo++
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse},
+ }
+ return oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+}
+
+func (oo *omciCC) sendCreateGalEthernetProfile(ctx context.Context, timeout int, highPrio bool) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send GalEnetProfile-Create-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16)})
+
+ meParams := me.ParamData{
+ EntityID: galEthernetEID,
+ Attributes: me.AttributeValueMap{"MaximumGemPayloadSize": maxGemPayloadSize},
+ }
+ meInstance, omciErr := me.NewGalEthernetProfile(meParams)
+ if omciErr.GetError() == nil {
+ //all setByCreate parameters already set, no default option required ...
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType, omci.TransactionID(tid))
+ if err != nil {
+ logger.Errorw("Cannot encode GalEnetProfileInstance for create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize GalEnetProfile create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send GalEnetProfile create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send GalEnetProfile-Create-msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate GalEnetProfileInstance", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+// might be needed to extend for parameter arguments, here just for setting the ConnectivityMode!!
+func (oo *omciCC) sendSetOnu2g(ctx context.Context, timeout int, highPrio bool) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send ONU2-G-Set-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16)})
+
+ meParams := me.ParamData{
+ EntityID: 0,
+ Attributes: me.AttributeValueMap{"CurrentConnectivityMode": connectivityModeValue},
+ }
+ meInstance, omciErr := me.NewOnu2G(meParams)
+ if omciErr.GetError() == nil {
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+ if err != nil {
+ logger.Errorw("Cannot encode ONU2-G instance for set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize ONU2-G set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send ONU2-G set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send ONU2-G-Set-msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate ONU2-G", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendCreateMBServiceProfile(ctx context.Context,
+ aPUniPort *onuUniPort, timeout int, highPrio bool) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ instID := macBridgeServiceProfileEID + uint16(aPUniPort.macBpNo)
+ logger.Debugw("send MBSP-Create-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16), "InstId": strconv.FormatInt(int64(instID), 16)})
+
+ meParams := me.ParamData{
+ EntityID: instID,
+ Attributes: me.AttributeValueMap{
+ "Priority": 0x8000,
+ "MaxAge": 20 * 256, //20s
+ "HelloTime": 2 * 256, //2s
+ "ForwardDelay": 15 * 256, //15s
+ //note: DynamicFilteringAgeingTime is taken from omci lib default as
+ // which is obviously different from default value used in python lib,
+ // where the value seems to be 0 (ONU defined) - to be considered in case of test artifacts ...
+ },
+ }
+
+ meInstance, omciErr := me.NewMacBridgeServiceProfile(meParams)
+ if omciErr.GetError() == nil {
+ //obviously we have to set all 'untouched' parameters to default by some additional option parameter!!
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+ omci.TransactionID(tid), omci.AddDefaults(true))
+ if err != nil {
+ logger.Errorw("Cannot encode MBSP for create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize MBSP create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send MBSP create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send MBSP-Create-msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate MBSP Instance", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendCreateMBPConfigData(ctx context.Context,
+ aPUniPort *onuUniPort, timeout int, highPrio bool) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ instID := macBridgePortAniEID + aPUniPort.entityID
+ logger.Debugw("send MBPCD-Create-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16), "InstId": strconv.FormatInt(int64(instID), 16)})
+
+ meParams := me.ParamData{
+ EntityID: instID,
+ Attributes: me.AttributeValueMap{
+ "BridgeIdPointer": macBridgeServiceProfileEID + uint16(aPUniPort.macBpNo),
+ "PortNum": aPUniPort.macBpNo,
+ "TpType": uint8(aPUniPort.portType),
+ "TpPointer": aPUniPort.entityID,
+ },
+ }
+ meInstance, omciErr := me.NewMacBridgePortConfigurationData(meParams)
+ if omciErr.GetError() == nil {
+ //obviously we have to set all 'untouched' parameters to default by some additional option parameter!!
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+ omci.TransactionID(tid), omci.AddDefaults(true))
+ if err != nil {
+ logger.Errorw("Cannot encode MBPCD for create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize MBPCD create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send MBPCD create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send MBPCD-Create-msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate MBPCD Instance", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendCreateEVTOConfigData(ctx context.Context,
+ aPUniPort *onuUniPort, timeout int, highPrio bool) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ instID := macBridgeServiceProfileEID + uint16(aPUniPort.macBpNo)
+ logger.Debugw("send EVTOCD-Create-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16), "InstId": strconv.FormatInt(int64(instID), 16)})
+
+ assType := uint8(2) // default AssociationType is PPTPEthUni
+ if aPUniPort.portType == uniVEIP {
+ assType = uint8(10) // for VEIP
+ }
+ meParams := me.ParamData{
+ EntityID: instID,
+ Attributes: me.AttributeValueMap{
+ "AssociationType": assType,
+ "AssociatedMePointer": aPUniPort.entityID,
+ },
+ }
+ meInstance, omciErr := me.NewExtendedVlanTaggingOperationConfigurationData(meParams)
+ if omciErr.GetError() == nil {
+ //all setByCreate parameters already set, no default option required ...
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType, omci.TransactionID(tid))
+ if err != nil {
+ logger.Errorw("Cannot encode EVTOCD for create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize EVTOCD create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send EVTOCD create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send EVTOCD-Create-msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate EVTOCD Instance", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendSetOnuGLS(ctx context.Context, timeout int,
+ highPrio bool, requestedAttributes me.AttributeValueMap, rxChan chan Message) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send ONU-G-Set-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16)})
+
+ meParams := me.ParamData{
+ EntityID: 0,
+ Attributes: requestedAttributes,
+ }
+ meInstance, omciErr := me.NewOnuG(meParams)
+ if omciErr.GetError() == nil {
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+ if err != nil {
+ logger.Errorw("Cannot encode ONU-G instance for set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize ONU-G set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send ONU-G set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send ONU-G-Set-msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate ONU-G", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendSetUniGLS(ctx context.Context, aInstNo uint16, timeout int,
+ highPrio bool, requestedAttributes me.AttributeValueMap, rxChan chan Message) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send UNI-G-Set-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16)})
+
+ meParams := me.ParamData{
+ EntityID: aInstNo,
+ Attributes: requestedAttributes,
+ }
+ meInstance, omciErr := me.NewUniG(meParams)
+ if omciErr.GetError() == nil {
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+ if err != nil {
+ logger.Errorw("Cannot encode UNI-G instance for set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize UNI-G-Set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send UNIG-G-Set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send UNI-G-Set-msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate UNI-G", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendSetVeipLS(ctx context.Context, aInstNo uint16, timeout int,
+ highPrio bool, requestedAttributes me.AttributeValueMap, rxChan chan Message) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send VEIP-Set-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16)})
+
+ meParams := me.ParamData{
+ EntityID: aInstNo,
+ Attributes: requestedAttributes,
+ }
+ meInstance, omciErr := me.NewVirtualEthernetInterfacePoint(meParams)
+ if omciErr.GetError() == nil {
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+ if err != nil {
+ logger.Errorw("Cannot encode VEIP instance for set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize VEIP-Set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send VEIP-Set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send VEIP-Set-msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate VEIP", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendGetMe(ctx context.Context, classID me.ClassID, entityID uint16, requestedAttributes me.AttributeValueMap,
+ timeout int, highPrio bool) *me.ManagedEntity {
+
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send get-request-msg", log.Fields{"classID": classID, "device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16)})
+
+ meParams := me.ParamData{
+ EntityID: entityID,
+ Attributes: requestedAttributes,
+ }
+ meInstance, omciErr := me.LoadManagedEntityDefinition(classID, meParams)
+ if omciErr.GetError() == nil {
+ meClassIDName := meInstance.GetName()
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.GetRequestType, omci.TransactionID(tid))
+ if err != nil {
+ logger.Errorf("Cannot encode instance for get-request", log.Fields{"meClassIDName": meClassIDName, "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize get-request", log.Fields{"meClassIDName": meClassIDName, "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send get-request-msg", log.Fields{"meClassIDName": meClassIDName, "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debugw("send get-request-msg done", log.Fields{"meClassIDName": meClassIDName, "device-id": oo.deviceID})
+ return meInstance
+ }
+ logger.Errorw("Cannot generate meDefinition", log.Fields{"classID": classID, "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendCreateDot1PMapper(ctx context.Context, timeout int, highPrio bool,
+ aInstID uint16, rxChan chan Message) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send .1pMapper-Create-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16), "InstId": strconv.FormatInt(int64(aInstID), 16)})
+
+ meParams := me.ParamData{
+ EntityID: aInstID,
+ Attributes: me.AttributeValueMap{},
+ }
+ meInstance, omciErr := me.NewIeee8021PMapperServiceProfile(meParams)
+ if omciErr.GetError() == nil {
+ //we have to set all 'untouched' parameters to default by some additional option parameter!!
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+ omci.TransactionID(tid), omci.AddDefaults(true))
+ if err != nil {
+ logger.Errorw("Cannot encode .1pMapper for create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize .1pMapper create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send .1pMapper create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send .1pMapper-create-msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate .1pMapper", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendCreateMBPConfigDataVar(ctx context.Context, timeout int, highPrio bool,
+ rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send MBPCD-Create-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16),
+ "InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+ meInstance, omciErr := me.NewMacBridgePortConfigurationData(params[0])
+ if omciErr.GetError() == nil {
+ //obviously we have to set all 'untouched' parameters to default by some additional option parameter!!
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+ omci.TransactionID(tid), omci.AddDefaults(true))
+ if err != nil {
+ logger.Errorw("Cannot encode MBPCD for create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize MBPCD create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send MBPCD create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send MBPCD-Create-msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate MBPCD Instance", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendCreateGemNCTPVar(ctx context.Context, timeout int, highPrio bool,
+ rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send GemNCTP-Create-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16),
+ "InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+ meInstance, omciErr := me.NewGemPortNetworkCtp(params[0])
+ if omciErr.GetError() == nil {
+ //obviously we have to set all 'untouched' parameters to default by some additional option parameter!!
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+ omci.TransactionID(tid), omci.AddDefaults(true))
+ if err != nil {
+ logger.Errorw("Cannot encode GemNCTP for create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize GemNCTP create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send GemNCTP create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send GemNCTP-Create-msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate GemNCTP Instance", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendCreateGemIWTPVar(ctx context.Context, timeout int, highPrio bool,
+ rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send GemIwTp-Create-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16),
+ "InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+ meInstance, omciErr := me.NewGemInterworkingTerminationPoint(params[0])
+ if omciErr.GetError() == nil {
+ //all SetByCreate Parameters (assumed to be) set here, for optimisation no 'AddDefaults'
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+ omci.TransactionID(tid))
+ if err != nil {
+ logger.Errorw("Cannot encode GemIwTp for create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize GemIwTp create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send GemIwTp create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send GemIwTp-Create-msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate GemIwTp Instance", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendSetTcontVar(ctx context.Context, timeout int, highPrio bool,
+ rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send TCont-Set-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16),
+ "InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+ meInstance, omciErr := me.NewTCont(params[0])
+ if omciErr.GetError() == nil {
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+ if err != nil {
+ logger.Errorw("Cannot encode TCont for set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize TCont set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send TCont set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send TCont-set msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate TCont Instance", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendSetPrioQueueVar(ctx context.Context, timeout int, highPrio bool,
+ rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send PrioQueue-Set-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16),
+ "InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+ meInstance, omciErr := me.NewPriorityQueue(params[0])
+ if omciErr.GetError() == nil {
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+ if err != nil {
+ logger.Errorw("Cannot encode PrioQueue for set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize PrioQueue set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send PrioQueue set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send PrioQueue-set msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate PrioQueue Instance", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendSetDot1PMapperVar(ctx context.Context, timeout int, highPrio bool,
+ rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send 1PMapper-Set-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16),
+ "InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+ meInstance, omciErr := me.NewIeee8021PMapperServiceProfile(params[0])
+ if omciErr.GetError() == nil {
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+ if err != nil {
+ logger.Errorw("Cannot encode 1PMapper for set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize 1PMapper set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send 1PMapper set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send 1PMapper-set msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate 1PMapper Instance", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendCreateVtfdVar(ctx context.Context, timeout int, highPrio bool,
+ rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send VTFD-Create-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16),
+ "InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+ meInstance, omciErr := me.NewVlanTaggingFilterData(params[0])
+ if omciErr.GetError() == nil {
+ //all SetByCreate Parameters (assumed to be) set here, for optimisation no 'AddDefaults'
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+ omci.TransactionID(tid))
+ if err != nil {
+ logger.Errorw("Cannot encode VTFD for create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ //TODO!!: refactoring improvement requested, here as an example for [VOL-3457]:
+ // return (dual format) error code that can be used at caller for immediate error treatment
+ // (relevant to all used sendXX() methods and their error conditions)
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize VTFD create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send VTFD create", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send VTFD-Create-msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate VTFD Instance", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
+
+func (oo *omciCC) sendSetEvtocdVar(ctx context.Context, timeout int, highPrio bool,
+ rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+ tid := oo.getNextTid(highPrio)
+ logger.Debugw("send EVTOCD-Set-msg:", log.Fields{"device-id": oo.deviceID,
+ "SequNo": strconv.FormatInt(int64(tid), 16),
+ "InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+ meInstance, omciErr := me.NewExtendedVlanTaggingOperationConfigurationData(params[0])
+ if omciErr.GetError() == nil {
+ omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+ if err != nil {
+ logger.Errorw("Cannot encode EVTOCD for set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+ if err != nil {
+ logger.Errorw("Cannot serialize EVTOCD set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+ }
+ err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+ if err != nil {
+ logger.Errorw("Cannot send EVTOCD set", log.Fields{
+ "Err": err, "device-id": oo.deviceID})
+ return nil
+ }
+ logger.Debug("send EVTOCD-set msg done")
+ return meInstance
+ }
+ logger.Errorw("Cannot generate EVTOCD Instance", log.Fields{
+ "Err": omciErr.GetError(), "device-id": oo.deviceID})
+ return nil
+}
diff --git a/internal/pkg/onuadaptercore/omci_test_request.go b/internal/pkg/onuadaptercore/omci_test_request.go
new file mode 100644
index 0000000..d7d967c
--- /dev/null
+++ b/internal/pkg/onuadaptercore/omci_test_request.go
@@ -0,0 +1,135 @@
+/*
+ * 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"
+
+
+ gp "github.com/google/gopacket"
+ "github.com/opencord/omci-lib-go"
+ me "github.com/opencord/omci-lib-go/generated"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+//omciTestRequest structure holds the information for the OMCI test
+type omciTestRequest struct {
+ deviceID string
+ pDevOmciCC *omciCC
+ started bool
+ result bool
+ exclusiveCc bool
+ allowFailure bool
+ txSeqNo uint16
+ verifyDone chan<- bool
+}
+
+//newOmciTestRequest returns a new instance of OmciTestRequest
+func newOmciTestRequest(ctx context.Context,
+ deviceID string, omciCc *omciCC,
+ exclusive bool, allowFailure bool) *omciTestRequest {
+ logger.Debug("omciTestRequest-init")
+ var omciTestRequest omciTestRequest
+ omciTestRequest.deviceID = deviceID
+ omciTestRequest.pDevOmciCC = omciCc
+ omciTestRequest.started = false
+ omciTestRequest.result = false
+ omciTestRequest.exclusiveCc = exclusive
+ omciTestRequest.allowFailure = allowFailure
+
+ return &omciTestRequest
+}
+
+//
+func (oo *omciTestRequest) performOmciTest(ctx context.Context, execChannel chan<- bool) {
+ logger.Debug("omciTestRequest-start-test")
+
+ if oo.pDevOmciCC != nil {
+ oo.verifyDone = execChannel
+ // test functionality is limited to ONU-2G get request for the moment
+ // without yet checking the received response automatically here (might be improved ??)
+ tid := oo.pDevOmciCC.getNextTid(false)
+ onu2gBaseGet, _ := oo.createOnu2gBaseGet(tid)
+ omciRxCallbackPair := callbackPair{
+ cbKey: tid,
+ cbEntry: callbackPairEntry{nil, oo.receiveOmciVerifyResponse},
+ }
+
+ logger.Debugw("performOmciTest-start sending frame", log.Fields{"for device-id": oo.deviceID})
+ // send with default timeout and normal prio
+ go oo.pDevOmciCC.send(ctx, onu2gBaseGet, ConstDefaultOmciTimeout, 0, false, omciRxCallbackPair)
+
+ } else {
+ logger.Errorw("performOmciTest: Device does not exist", log.Fields{"for device-id": oo.deviceID})
+ }
+}
+
+// these are OMCI related functions, could/should be collected in a separate file? TODO!!!
+// for a simple start just included in here
+//basic approach copied from bbsim, cmp /devices/onu.go and /internal/common/omci/mibpackets.go
+func (oo *omciTestRequest) createOnu2gBaseGet(tid uint16) ([]byte, error) {
+
+ request := &omci.GetRequest{
+ MeBasePacket: omci.MeBasePacket{
+ EntityClass: me.Onu2GClassID,
+ EntityInstance: 0, //there is only the 0 instance of ONU2-G (still hard-coded - TODO!!!)
+ },
+ AttributeMask: 0xE000, //example hardcoded (TODO!!!) request EquId, OmccVersion, VendorCode
+ }
+
+ oo.txSeqNo = tid
+ pkt, err := serialize(omci.GetRequestType, request, tid)
+ if err != nil {
+ //omciLogger.WithFields(log.Fields{ ...
+ logger.Errorw("Cannot serialize Onu2-G GetRequest", log.Fields{"Err": err})
+ return nil, err
+ }
+ return pkt, nil
+}
+
+//supply a response handler - in this testobject the message is evaluated directly, no response channel used
+func (oo *omciTestRequest) receiveOmciVerifyResponse(omciMsg *omci.OMCI, packet *gp.Packet, respChan chan Message) error {
+
+ logger.Debugw("verify-omci-message-response received:", log.Fields{"omciMsgType": omciMsg.MessageType,
+ "transCorrId": omciMsg.TransactionID, "DeviceIdent": omciMsg.DeviceIdentifier})
+
+ if omciMsg.TransactionID == oo.txSeqNo {
+ logger.Debugw("verify-omci-message-response", log.Fields{"correct TransCorrId": omciMsg.TransactionID})
+ } else {
+ logger.Debugw("verify-omci-message-response error", log.Fields{"incorrect TransCorrId": omciMsg.TransactionID,
+ "expected": oo.txSeqNo})
+ oo.verifyDone <- false
+ return errors.New("unexpected TransCorrId")
+ }
+ if omciMsg.MessageType == omci.GetResponseType {
+ logger.Debugw("verify-omci-message-response", log.Fields{"correct RespType": omciMsg.MessageType})
+ } else {
+ logger.Debugw("verify-omci-message-response error", log.Fields{"incorrect RespType": omciMsg.MessageType,
+ "expected": omci.GetResponseType})
+ oo.verifyDone <- false
+ return errors.New("unexpected MessageType")
+ }
+
+
+ oo.result = true
+ oo.verifyDone <- true
+
+ return nil
+}
diff --git a/internal/pkg/onuadaptercore/omci_vlan_config.go b/internal/pkg/onuadaptercore/omci_vlan_config.go
new file mode 100644
index 0000000..15d16df
--- /dev/null
+++ b/internal/pkg/onuadaptercore/omci_vlan_config.go
@@ -0,0 +1,701 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//Package adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+ "context"
+ "encoding/binary"
+ "errors"
+ "strconv"
+ "time"
+
+ "github.com/looplab/fsm"
+ "github.com/opencord/omci-lib-go"
+ me "github.com/opencord/omci-lib-go/generated"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ of "github.com/opencord/voltha-protos/v3/go/openflow_13"
+)
+
+const (
+ cDefaultDownstreamMode = 0
+ cDefaultTpid = 0x8100
+)
+
+const (
+ cFilterPrioOffset = 28
+ cFilterVidOffset = 15
+ cFilterTpidOffset = 12
+ cFilterEtherTypeOffset = 0
+ cTreatTTROffset = 30
+ cTreatPrioOffset = 16
+ cTreatVidOffset = 3
+ cTreatTpidOffset = 0
+)
+const (
+ cFilterOuterOffset = 0
+ cFilterInnerOffset = 4
+ cTreatOuterOffset = 8
+ cTreatInnerOffset = 12
+)
+const (
+ cPrioIgnoreTag uint32 = 15
+ cPrioDefaultFilter uint32 = 14
+ cPrioDoNotFilter uint32 = 8
+ cDoNotFilterVid uint32 = 4096
+ cDoNotFilterTPID uint32 = 0
+ cDoNotFilterEtherType uint32 = 0
+ cDoNotAddPrio uint32 = 15
+ cCopyPrioFromInner uint32 = 8
+ cDontCareVid uint32 = 0
+ cDontCareTpid uint32 = 0
+ cSetOutputTpidCopyDei uint32 = 4
+)
+
+const (
+ vlanEvStart = "vlanEvStart"
+ vlanEvWaitTechProf = "vlanEvWaitTechProf"
+ vlanEvContinueConfig = "vlanEvContinueConfig"
+ vlanEvStartConfig = "vlanEvStartConfig"
+ vlanEvRxConfigVtfd = "vlanEvRxConfigVtfd"
+ vlanEvRxConfigEvtocd = "vlanEvRxConfigEvtocd"
+ vlanEvReset = "vlanEvReset"
+ vlanEvRestart = "vlanEvRestart"
+)
+const (
+ vlanStDisabled = "vlanStDisabled"
+ vlanStStarting = "vlanStStarting"
+ vlanStWaitingTechProf = "vlanStWaitingTechProf"
+ vlanStConfigVtfd = "vlanStConfigVtfd"
+ vlanStConfigEvtocd = "vlanStConfigEvtocd"
+ vlanStConfigDone = "vlanStConfigDone"
+ vlanStCleanEvtocd = "vlanStCleanEvtocd"
+ vlanStCleanVtfd = "vlanStCleanVtfd"
+ vlanStCleanupDone = "vlanStCleanupDone"
+ vlanStResetting = "vlanStResetting"
+)
+
+//UniVlanConfigFsm defines the structure for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
+type UniVlanConfigFsm struct {
+ pDeviceHandler *deviceHandler
+ pOmciCC *omciCC
+ pOnuUniPort *onuUniPort
+ pUniTechProf *onuUniTechProf
+ pOnuDB *onuDeviceDB
+ techProfileID uint16
+ requestEvent OnuDeviceEvent
+ omciMIdsResponseReceived chan bool //seperate channel needed for checking multiInstance OMCI message responses
+ pAdaptFsm *AdapterFsm
+ acceptIncrementalEvtoOption bool
+ matchVid uint32
+ matchPcp uint32
+ tagsToRemove uint32
+ setVid uint32
+ setPcp uint32
+ vtfdID uint16
+ evtocdID uint16
+}
+
+//NewUniVlanConfigFsm is the 'constructor' for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
+func NewUniVlanConfigFsm(apDeviceHandler *deviceHandler, apDevOmciCC *omciCC, apUniPort *onuUniPort, apUniTechProf *onuUniTechProf,
+ apOnuDB *onuDeviceDB, aTechProfileID uint16, aRequestEvent OnuDeviceEvent, aName string,
+ aDeviceID string, aCommChannel chan Message,
+ aAcceptIncrementalEvto bool, aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8) *UniVlanConfigFsm {
+ instFsm := &UniVlanConfigFsm{
+ pDeviceHandler: apDeviceHandler,
+ pOmciCC: apDevOmciCC,
+ pOnuUniPort: apUniPort,
+ pUniTechProf: apUniTechProf,
+ pOnuDB: apOnuDB,
+ techProfileID: aTechProfileID,
+ requestEvent: aRequestEvent,
+ acceptIncrementalEvtoOption: aAcceptIncrementalEvto,
+ matchVid: uint32(aMatchVlan),
+ setVid: uint32(aSetVlan),
+ setPcp: uint32(aSetPcp),
+ }
+ instFsm.tagsToRemove = 1 //one tag to remove as default setting
+ instFsm.matchPcp = cPrioDoNotFilter // do not Filter on prio as default
+ if instFsm.matchVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+ // no prio/vid filtering requested
+ instFsm.tagsToRemove = 0 //no tag pop action
+ instFsm.matchPcp = cPrioIgnoreTag // no vlan tag filtering
+ if instFsm.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 ..
+ instFsm.setPcp = 0
+ }
+ }
+
+ instFsm.pAdaptFsm = NewAdapterFsm(aName, aDeviceID, aCommChannel)
+ if instFsm.pAdaptFsm == nil {
+ logger.Errorw("UniVlanConfigFsm's AdapterFsm could not be instantiated!!", log.Fields{
+ "device-id": aDeviceID})
+ return nil
+ }
+
+ instFsm.pAdaptFsm.pFsm = fsm.NewFSM(
+ vlanStDisabled,
+ fsm.Events{
+ {Name: vlanEvStart, Src: []string{vlanStDisabled}, Dst: vlanStStarting},
+ {Name: vlanEvWaitTechProf, Src: []string{vlanStStarting}, Dst: vlanStWaitingTechProf},
+ {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}, Dst: vlanStConfigDone},
+ //TODO:!!! Also define state transitions for cleanup states and timeouts
+ /*
+ {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,
+ vlanStCleanEvtocd, vlanStCleanVtfd, vlanStCleanupDone},
+ Dst: vlanStResetting},
+ // the only way to get to resource-cleared disabled state again is via "resseting"
+ {Name: vlanEvRestart, Src: []string{vlanStResetting}, Dst: vlanStDisabled},
+ },
+
+ fsm.Callbacks{
+ "enter_state": func(e *fsm.Event) { instFsm.pAdaptFsm.logFsmStateChange(e) },
+ ("enter_" + vlanStStarting): func(e *fsm.Event) { instFsm.enterConfigStarting(e) },
+ ("enter_" + vlanStConfigVtfd): func(e *fsm.Event) { instFsm.enterConfigVtfd(e) },
+ ("enter_" + vlanStConfigEvtocd): func(e *fsm.Event) { instFsm.enterConfigEvtocd(e) },
+ ("enter_" + vlanStConfigDone): func(e *fsm.Event) { instFsm.enterVlanConfigDone(e) },
+ ("enter_" + vlanStCleanVtfd): func(e *fsm.Event) { instFsm.enterCleanVtfd(e) },
+ ("enter_" + vlanStCleanEvtocd): func(e *fsm.Event) { instFsm.enterCleanEvtocd(e) },
+ ("enter_" + vlanStCleanupDone): func(e *fsm.Event) { instFsm.enterVlanCleanupDone(e) },
+ ("enter_" + vlanStResetting): func(e *fsm.Event) { instFsm.enterResetting(e) },
+ ("enter_" + vlanStDisabled): func(e *fsm.Event) { instFsm.enterDisabled(e) },
+ },
+ )
+ if instFsm.pAdaptFsm.pFsm == nil {
+ logger.Errorw("UniVlanConfigFsm's Base FSM could not be instantiated!!", log.Fields{
+ "device-id": aDeviceID})
+ return nil
+ }
+
+ logger.Infow("UniVlanConfigFsm created", log.Fields{"device-id": aDeviceID,
+ "accIncrEvto": instFsm.acceptIncrementalEvtoOption,
+ "matchVid": strconv.FormatInt(int64(instFsm.matchVid), 16),
+ "setVid": strconv.FormatInt(int64(instFsm.setVid), 16),
+ "setPcp": instFsm.setPcp})
+ return instFsm
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigStarting(e *fsm.Event) {
+ logger.Debugw("UniVlanConfigFsm start", log.Fields{"in state": e.FSM.Current(),
+ "device-id": oFsm.pAdaptFsm.deviceID})
+
+ oFsm.omciMIdsResponseReceived = make(chan bool)
+ go oFsm.processOmciVlanMessages()
+ pConfigVlanStateAFsm := oFsm.pAdaptFsm
+ if pConfigVlanStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(a_pAFsm *AdapterFsm) {
+ if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+ //stick to pythonAdapter numbering scheme
+ oFsm.vtfdID = macBridgePortAniEID + oFsm.pOnuUniPort.entityID + oFsm.techProfileID
+ //cmp also usage in EVTOCDE create in omci_cc
+ oFsm.evtocdID = macBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.macBpNo)
+
+ if oFsm.pUniTechProf.getTechProfileDone(oFsm.pOnuUniPort.uniID, oFsm.techProfileID) {
+ // let the vlan processing begin
+ _ = a_pAFsm.pFsm.Event(vlanEvStartConfig)
+ } else {
+ // set to waiting for Techprofile
+ _ = a_pAFsm.pFsm.Event(vlanEvWaitTechProf)
+ }
+ }
+ }(pConfigVlanStateAFsm)
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigVtfd(e *fsm.Event) {
+ if oFsm.setVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+ // meaning transparent setup - no specific VTFD setting required
+ logger.Debugw("UniVlanConfigFsm: no VTFD config required", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ // let the FSM proceed ... (from within this state all internal pointers may be expected to be correct)
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ pConfigVlanStateAFsm := oFsm.pAdaptFsm
+ go func(a_pAFsm *AdapterFsm) {
+ _ = a_pAFsm.pFsm.Event(vlanEvRxConfigVtfd)
+ }(pConfigVlanStateAFsm)
+ } else {
+ logger.Debugw("UniVlanConfigFsm create VTFD", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(oFsm.vtfdID), 16),
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ vlanFilterList := make([]uint16, 12)
+ vlanFilterList[0] = uint16(oFsm.setVid) // setVid is assumed to be masked already by the caller to 12 bit
+ meParams := me.ParamData{
+ EntityID: oFsm.vtfdID,
+ Attributes: me.AttributeValueMap{
+ "VlanFilterList": vlanFilterList,
+ "ForwardOperation": uint8(0x10), //VID investigation
+ "NumberOfEntries": uint8(1),
+ },
+ }
+ meInstance := oFsm.pOmciCC.sendCreateVtfdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+ oFsm.pAdaptFsm.commChan, meParams)
+ //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.pOmciCC.pLastTxMeInstance = meInstance
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigEvtocd(e *fsm.Event) {
+ logger.Debugw("UniVlanConfigFsm - start config EVTOCD loop", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ go oFsm.performConfigEvtocdEntries()
+}
+
+func (oFsm *UniVlanConfigFsm) enterVlanConfigDone(e *fsm.Event) {
+ logger.Debugw("UniVlanConfigFsm - VLAN config done: send dh event notification", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ if oFsm.pDeviceHandler != nil {
+ oFsm.pDeviceHandler.deviceProcStatusUpdate(oFsm.requestEvent)
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) enterCleanVtfd(e *fsm.Event) {
+ logger.Debugw("UniVlanConfigFsm Tx Delete::VTFD", log.Fields{
+ /*"EntitytId": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),*/
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+}
+
+func (oFsm *UniVlanConfigFsm) enterCleanEvtocd(e *fsm.Event) {
+ logger.Debugw("UniVlanConfigFsm cleanup EVTOCD", log.Fields{
+ /*"EntitytId": strconv.FormatInt(int64(oFsm.macBPCD0ID), 16),
+ "TPPtr": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),*/
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+}
+
+func (oFsm *UniVlanConfigFsm) enterVlanCleanupDone(e *fsm.Event) {
+ logger.Debugw("UniVlanConfigFsm - VLAN cleanup done", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+
+ pConfigVlanStateAFsm := oFsm.pAdaptFsm
+ if pConfigVlanStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(a_pAFsm *AdapterFsm) {
+ if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+ _ = a_pAFsm.pFsm.Event(vlanEvReset)
+ }
+ }(pConfigVlanStateAFsm)
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) enterResetting(e *fsm.Event) {
+ logger.Debugw("UniVlanConfigFsm resetting", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+
+ pConfigVlanStateAFsm := oFsm.pAdaptFsm
+ if pConfigVlanStateAFsm != nil {
+ // abort running message processing
+ fsmAbortMsg := Message{
+ Type: TestMsg,
+ Data: TestMessage{
+ TestMessageVal: AbortMessageProcessing,
+ },
+ }
+ pConfigVlanStateAFsm.commChan <- fsmAbortMsg
+
+ //try to restart the FSM to 'disabled', decouple event transfer
+ go func(a_pAFsm *AdapterFsm) {
+ if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+ _ = a_pAFsm.pFsm.Event(vlanEvRestart)
+ }
+ }(pConfigVlanStateAFsm)
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) enterDisabled(e *fsm.Event) {
+ logger.Debugw("UniVlanConfigFsm enters disabled state", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ if oFsm.pDeviceHandler != nil {
+ //request removal of 'reference' in the Handler (completely clear the FSM)
+ go oFsm.pDeviceHandler.RemoveVlanFilterFsm(oFsm.pOnuUniPort)
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) processOmciVlanMessages() { //ctx context.Context?
+ logger.Debugw("Start UniVlanConfigFsm Msg processing", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+loop:
+ for {
+ // case <-ctx.Done():
+ // logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": oFsm.pAdaptFsm.deviceID})
+ // break loop
+ message, ok := <-oFsm.pAdaptFsm.commChan
+ if !ok {
+ logger.Info("UniVlanConfigFsm Rx Msg - could not read from channel", log.Fields{"device-id": oFsm.pAdaptFsm.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("UniVlanConfigFsm Rx Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+
+ switch message.Type {
+ case TestMsg:
+ msg, _ := message.Data.(TestMessage)
+ if msg.TestMessageVal == AbortMessageProcessing {
+ logger.Infow("UniVlanConfigFsm abort ProcessMsg", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+ break loop
+ }
+ logger.Warnw("UniVlanConfigFsm unknown TestMessage", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "MessageVal": msg.TestMessageVal})
+ case OMCI:
+ msg, _ := message.Data.(OmciMessage)
+ oFsm.handleOmciVlanConfigMessage(msg)
+ default:
+ logger.Warn("UniVlanConfigFsm Rx unknown message", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
+ "message.Type": message.Type})
+ }
+ }
+ logger.Infow("End UniVlanConfigFsm Msg processing", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+}
+
+func (oFsm *UniVlanConfigFsm) handleOmciVlanConfigMessage(msg OmciMessage) {
+ logger.Debugw("Rx OMCI UniVlanConfigFsm Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
+ "msgType": msg.OmciMsg.MessageType})
+
+ switch msg.OmciMsg.MessageType {
+ case omci.CreateResponseType:
+ {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeCreateResponse)
+ if msgLayer == nil {
+ logger.Error("Omci Msg layer could not be detected for CreateResponse")
+ return
+ }
+ msgObj, msgOk := msgLayer.(*omci.CreateResponse)
+ if !msgOk {
+ logger.Error("Omci Msg layer could not be assigned for CreateResponse")
+ return
+ }
+ logger.Debugw("CreateResponse Data", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "data-fields": msgObj})
+ if msgObj.Result != me.Success {
+ logger.Errorw("Omci CreateResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+ // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+ return
+ }
+ if msgObj.EntityClass == oFsm.pOmciCC.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == oFsm.pOmciCC.pLastTxMeInstance.GetEntityID() {
+ // maybe we can use just the same eventName for different state transitions like "forward"
+ // - might be checked, but so far I go for sure and have to inspect the concrete state events ...
+ switch oFsm.pOmciCC.pLastTxMeInstance.GetName() {
+ case "VlanTaggingFilterData":
+ { // let the FSM proceed ...
+ _ = oFsm.pAdaptFsm.pFsm.Event(vlanEvRxConfigVtfd)
+ }
+ }
+ }
+ } //CreateResponseType
+ case omci.SetResponseType:
+ {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
+ if msgLayer == nil {
+ logger.Error("UniVlanConfigFsm - Omci Msg layer could not be detected for SetResponse")
+ return
+ }
+ msgObj, msgOk := msgLayer.(*omci.SetResponse)
+ if !msgOk {
+ logger.Error("UniVlanConfigFsm - Omci Msg layer could not be assigned for SetResponse")
+ return
+ }
+ logger.Debugw("UniVlanConfigFsm SetResponse Data", log.Fields{"deviceId": oFsm.pAdaptFsm.deviceID, "data-fields": msgObj})
+ if msgObj.Result != me.Success {
+ logger.Errorw("UniVlanConfigFsm - Omci SetResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+ // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+ return
+ }
+ if msgObj.EntityClass == oFsm.pOmciCC.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == oFsm.pOmciCC.pLastTxMeInstance.GetEntityID() {
+ switch oFsm.pOmciCC.pLastTxMeInstance.GetName() {
+ case "ExtendedVlanTaggingOperationConfigurationData":
+ { // let the EVTO config proceed by stopping the wait function
+ oFsm.omciMIdsResponseReceived <- true
+ }
+ }
+ }
+ } //SetResponseType
+ default:
+ {
+ logger.Errorw("UniVlanConfigFsm - Rx OMCI unhandled MsgType", log.Fields{"omciMsgType": msg.OmciMsg.MessageType})
+ return
+ }
+ }
+}
+
+func (oFsm *UniVlanConfigFsm) performConfigEvtocdEntries() {
+ { // for local var
+ // 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("UniVlanConfigFsm Tx Set::EVTOCD", log.Fields{
+ "EntitytId": strconv.FormatInt(int64(oFsm.evtocdID), 16),
+ "i/oEthType": strconv.FormatInt(int64(cDefaultTpid), 16),
+ "device-id": oFsm.pAdaptFsm.deviceID})
+ meParams := me.ParamData{
+ EntityID: oFsm.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),
+ },
+ }
+ meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+ oFsm.pAdaptFsm.commChan, meParams)
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+ //verify response
+ err := oFsm.waitforOmciResponse()
+ if err != nil {
+ logger.Errorw("Evtocd set TPID failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ _ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+ return
+ }
+ } //for local var
+
+ if oFsm.setVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+ //transparent transmission required
+ logger.Debugw("UniVlanConfigFsm Tx Set::EVTOCD single tagged transparent rule", log.Fields{
+ "device-id": oFsm.pAdaptFsm.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: oFsm.evtocdID,
+ Attributes: me.AttributeValueMap{
+ "ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+ },
+ }
+ meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+ oFsm.pAdaptFsm.commChan, meParams)
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+ //verify response
+ err := oFsm.waitforOmciResponse()
+ if err != nil {
+ logger.Errorw("Evtocd set transparent singletagged rule failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ _ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+ return
+ }
+ } else {
+ // according to py-code acceptIncrementalEvto program option decides upon stacking or translation scenario
+ if oFsm.acceptIncrementalEvtoOption {
+ // this defines VID translation scenario: singletagged->singletagged (if not transparent)
+ logger.Debugw("UniVlanConfigFsm Tx Set::EVTOCD single tagged translation rule", log.Fields{
+ "device-id": oFsm.pAdaptFsm.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.matchPcp<<cFilterPrioOffset| // either DNFonPrio or ignore tag (default) on innerVLAN
+ oFsm.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.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.setPcp<<cTreatPrioOffset| // as configured in flow
+ oFsm.setVid<<cTreatVidOffset| //as configured in flow
+ cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+
+ meParams := me.ParamData{
+ EntityID: oFsm.evtocdID,
+ Attributes: me.AttributeValueMap{
+ "ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+ },
+ }
+ meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+ oFsm.pAdaptFsm.commChan, meParams)
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+ //verify response
+ err := oFsm.waitforOmciResponse()
+ if err != nil {
+ logger.Errorw("Evtocd set singletagged translation rule failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ _ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+ return
+ }
+ } else {
+ //not transparent and not acceptIncrementalEvtoOption untagged/priotagged->singletagged
+ { // just for local var's
+ // this defines stacking scenario: untagged->singletagged
+ logger.Debugw("UniVlanConfigFsm Tx Set::EVTOCD untagged->singletagged rule", log.Fields{
+ "device-id": oFsm.pAdaptFsm.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.setVid<<cTreatVidOffset| // Outer VID don't care
+ cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+
+ meParams := me.ParamData{
+ EntityID: oFsm.evtocdID,
+ Attributes: me.AttributeValueMap{
+ "ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+ },
+ }
+ meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+ oFsm.pAdaptFsm.commChan, meParams)
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+ //verify response
+ err := oFsm.waitforOmciResponse()
+ if err != nil {
+ logger.Errorw("Evtocd set untagged->singletagged rule failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.pAdaptFsm.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("UniVlanConfigFsm Tx Set::EVTOCD priotagged->singletagged rule", log.Fields{
+ "device-id": oFsm.pAdaptFsm.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
+
+ 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.setVid<<cTreatVidOffset| // Outer VID as configured
+ cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+
+ meParams := me.ParamData{
+ EntityID: oFsm.evtocdID,
+ Attributes: me.AttributeValueMap{
+ "ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+ },
+ }
+ meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+ oFsm.pAdaptFsm.commChan, meParams)
+ //accept also nil as (error) return value for writing to LastTx
+ // - this avoids misinterpretation of new received OMCI messages
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+ //verify response
+ err := oFsm.waitforOmciResponse()
+ if err != nil {
+ logger.Errorw("Evtocd set priotagged->singletagged rule failed, aborting VlanConfig FSM!",
+ log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ _ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+ return
+ }
+ } //just for local var's
+ }
+ }
+
+ logger.Debugw("EVTOCD set loop finished", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ _ = oFsm.pAdaptFsm.pFsm.Event(vlanEvRxConfigEvtocd)
+}
+
+func (oFsm *UniVlanConfigFsm) waitforOmciResponse() error {
+ select {
+ case <-time.After(30 * time.Second): //AS FOR THE OTHER OMCI FSM's
+ logger.Warnw("UniVlanConfigFsm multi entity timeout", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+ return errors.New("uniVlanConfigFsm multi entity timeout")
+ case success := <-oFsm.omciMIdsResponseReceived:
+ if success {
+ logger.Debug("UniVlanConfigFsm multi entity response received")
+ return nil
+ }
+ // should not happen so far
+ logger.Warnw("UniVlanConfigFsm multi entity response error", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+ return errors.New("uniVlanConfigFsm multi entity responseError")
+ }
+}
diff --git a/internal/pkg/onuadaptercore/onu_device_db.go b/internal/pkg/onuadaptercore/onu_device_db.go
new file mode 100644
index 0000000..ca954d6
--- /dev/null
+++ b/internal/pkg/onuadaptercore/onu_device_db.go
@@ -0,0 +1,131 @@
+/*
+ * 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"
+ "fmt"
+ "reflect"
+ "sort"
+
+ me "github.com/opencord/omci-lib-go/generated"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+type meDbMap map[me.ClassID]map[uint16]me.AttributeValueMap
+
+//onuDeviceDB structure holds information about known ME's
+type onuDeviceDB struct {
+ ctx context.Context
+ pOnuDeviceEntry *OnuDeviceEntry
+ meDb meDbMap
+}
+
+//newOnuDeviceDB returns a new instance for a specific ONU_Device_Entry
+func newOnuDeviceDB(ctx context.Context, aPOnuDeviceEntry *OnuDeviceEntry) *onuDeviceDB {
+ logger.Debugw("Init OnuDeviceDB for:", log.Fields{"device-id": aPOnuDeviceEntry.deviceID})
+ var onuDeviceDB onuDeviceDB
+ onuDeviceDB.ctx = ctx
+ onuDeviceDB.pOnuDeviceEntry = aPOnuDeviceEntry
+ onuDeviceDB.meDb = make(meDbMap)
+
+ return &onuDeviceDB
+}
+
+func (onuDeviceDB *onuDeviceDB) PutMe(meClassID me.ClassID, meEntityID uint16, meAttributes me.AttributeValueMap) {
+
+ if me.OnuDataClassID == meClassID {
+ return
+ }
+
+ meInstMap, ok := onuDeviceDB.meDb[meClassID]
+ if !ok {
+ logger.Debugw("meClassID not found - add to db :", log.Fields{"device-id": onuDeviceDB.pOnuDeviceEntry.deviceID})
+ meInstMap = make(map[uint16]me.AttributeValueMap)
+ onuDeviceDB.meDb[meClassID] = meInstMap
+ onuDeviceDB.meDb[meClassID][meEntityID] = meAttributes
+ } else {
+ meAttribs, ok := meInstMap[meEntityID]
+ if !ok {
+ /* verbose logging, avoid in >= debug level
+ logger.Debugw("meEntityId not found - add to db :", log.Fields{"device-id": onuDeviceDB.pOnuDeviceEntry.deviceID})
+ */
+ meInstMap[meEntityID] = meAttributes
+ } else {
+ /* verbose logging, avoid in >= debug level
+ logger.Debugw("ME-Instance exists already: merge attribute data :", log.Fields{"device-id": onuDeviceDB.pOnuDeviceEntry.deviceID, "meAttribs": meAttribs})
+ */
+ for k, v := range meAttributes {
+ meAttribs[k] = v
+ }
+ meInstMap[meEntityID] = meAttribs
+ /* verbose logging, avoid in >= debug level
+ logger.Debugw("ME-Instance updated :", log.Fields{"device-id": onuDeviceDB.pOnuDeviceEntry.deviceID, "meAttribs": meAttribs})
+ */
+ }
+ }
+}
+
+func (onuDeviceDB *onuDeviceDB) GetMe(meClassID me.ClassID, meEntityID uint16) me.AttributeValueMap {
+
+ if meAttributes, present := onuDeviceDB.meDb[meClassID][meEntityID]; present {
+ /* verbose logging, avoid in >= debug level
+ logger.Debugw("ME found:", log.Fields{"meClassID": meClassID, "meEntityID": meEntityID, "meAttributes": meAttributes,
+ "device-id": onuDeviceDB.pOnuDeviceEntry.deviceID})
+ */
+ return meAttributes
+ }
+ return nil
+}
+
+func (onuDeviceDB *onuDeviceDB) getUint32Attrib(meAttribute interface{}) (uint32, error) {
+
+ switch reflect.TypeOf(meAttribute).Kind() {
+ case reflect.Float64:
+ //JSON numbers by default are unmarshaled into values of float64 type if type information is not present
+ return uint32(meAttribute.(float64)), nil
+ case reflect.Uint32:
+ return uint32(meAttribute.(uint32)), nil
+ default:
+ return uint32(0), fmt.Errorf(fmt.Sprintf("wrong interface-type received-%s", onuDeviceDB.pOnuDeviceEntry.deviceID))
+ }
+}
+
+func (onuDeviceDB *onuDeviceDB) getSortedInstKeys(meClassID me.ClassID) []uint16 {
+
+ var meInstKeys []uint16
+
+ meInstMap := onuDeviceDB.meDb[meClassID]
+
+ for k := range meInstMap {
+ meInstKeys = append(meInstKeys, k)
+ }
+ logger.Debugw("meInstKeys - input order :", log.Fields{"meInstKeys": meInstKeys}) //TODO: delete the line after test phase!
+ sort.Slice(meInstKeys, func(i, j int) bool { return meInstKeys[i] < meInstKeys[j] })
+ logger.Debugw("meInstKeys - output order :", log.Fields{"meInstKeys": meInstKeys}) //TODO: delete the line after test phase!
+ return meInstKeys
+}
+
+func (onuDeviceDB *onuDeviceDB) logMeDb() {
+ logger.Debugw("ME instances stored for :", log.Fields{"device-id": onuDeviceDB.pOnuDeviceEntry.deviceID})
+ for meClassID, meInstMap := range onuDeviceDB.meDb {
+ for meEntityID, meAttribs := range meInstMap {
+ logger.Debugw("ME instance: ", log.Fields{"meClassID": meClassID, "meEntityID": meEntityID, "meAttribs": meAttribs, "device-id": onuDeviceDB.pOnuDeviceEntry.deviceID})
+ }
+ }
+}
diff --git a/internal/pkg/onuadaptercore/onu_device_entry.go b/internal/pkg/onuadaptercore/onu_device_entry.go
new file mode 100644
index 0000000..5d888b1
--- /dev/null
+++ b/internal/pkg/onuadaptercore/onu_device_entry.go
@@ -0,0 +1,436 @@
+/*
+ * 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})
+ }
+}
diff --git a/internal/pkg/onuadaptercore/onu_list_access.go b/internal/pkg/onuadaptercore/onu_list_access.go
new file mode 100644
index 0000000..427cf19
--- /dev/null
+++ b/internal/pkg/onuadaptercore/onu_list_access.go
@@ -0,0 +1,136 @@
+/*
+ * 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
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+)
+
+// Jsonファイル名
+const jsonname = "/onu_list.json"
+
+// OnuStatus ONU状態
+type OnuStatus struct {
+ ID string `json:"id"`
+ AdminState string `json:"admin_state"`
+ OpeState string `json:"ope_state"`
+ ConnectState string `json:"con_state"`
+ MacAddress string `json:"mac_addr"`
+ RebootState string `json:"reboot_state"`
+}
+
+//ReadOnuStatusList Jsonファイル読込み
+func ReadOnuStatusList() ([]OnuStatus, error) {
+ bytes, err := ioutil.ReadFile(os.Getenv("HOME") + jsonname)
+ if err != nil && os.IsNotExist(err) {
+ return nil, nil
+ }
+ var onuList []OnuStatus
+ if err := json.Unmarshal(bytes, &onuList); err != nil {
+ return nil, err
+ }
+ return onuList, nil
+}
+
+//WriteOnuStatusList Jsonファイル書き込み
+func WriteOnuStatusList(list []OnuStatus) error {
+ bytes, err := json.Marshal(list)
+ if err != nil {
+ return err
+ }
+ return ioutil.WriteFile(os.Getenv("HOME")+jsonname, bytes, 0644)
+}
+
+//AddOnu ONU追加
+func AddOnu(sts *OnuStatus) error {
+ list, err := ReadOnuStatusList()
+ if err != nil {
+ return err
+ }
+ if list == nil {
+ newList := []OnuStatus{*sts}
+ return WriteOnuStatusList(newList)
+ }
+ return WriteOnuStatusList(append(list, *sts))
+}
+
+//UpdateOnu ONU状態更新
+func UpdateOnu(upSts *OnuStatus) error {
+ list, err := ReadOnuStatusList()
+ if (err != nil) || (list == nil) {
+ return err
+ }
+ newList := []OnuStatus{}
+ for _, sts := range list {
+ if sts.ID == upSts.ID {
+ newList = append(newList, *upSts)
+ } else {
+ newList = append(newList, sts)
+ }
+ }
+ return WriteOnuStatusList(newList)
+}
+
+//RemoveOnu ONU削除
+func RemoveOnu(id string) error {
+ list, err := ReadOnuStatusList()
+ if (err != nil) || (list == nil) {
+ return err
+ }
+ newList := []OnuStatus{}
+ for _, sts := range list {
+ if sts.ID != id {
+ newList = append(newList, sts)
+ }
+ }
+ return WriteOnuStatusList(newList)
+}
+
+//GetOnuFromDeviceID ONU状態取得
+func GetOnuFromDeviceID(id string) (*OnuStatus, error) {
+ list, err := ReadOnuStatusList()
+ if err != nil {
+ return nil, err
+ }
+ if list == nil {
+ return nil, nil
+ }
+ for _, sts := range list {
+ if sts.ID != id {
+ return &sts, nil
+ }
+ }
+ return nil, nil
+}
+
+//GetOnuFromMacAddr ONU状態取得
+func GetOnuFromMacAddr(addr string) (*OnuStatus, error) {
+ list, err := ReadOnuStatusList()
+ if err != nil {
+ return nil, err
+ }
+ if list == nil {
+ return nil, nil
+ }
+ for _, sts := range list {
+ if sts.MacAddress == addr {
+ return &sts, nil
+ }
+ }
+ return nil, nil
+}
diff --git a/internal/pkg/onuadaptercore/onu_uni_port.go b/internal/pkg/onuadaptercore/onu_uni_port.go
new file mode 100644
index 0000000..9bfe1e9
--- /dev/null
+++ b/internal/pkg/onuadaptercore/onu_uni_port.go
@@ -0,0 +1,180 @@
+/*
+ * 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"
+ "fmt"
+ "strconv"
+ "strings"
+
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ vc "github.com/opencord/voltha-protos/v3/go/common"
+ of "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+)
+
+type uniPortType uint8
+
+// UniPPTP Interface type - re-use values from G.988 TP type definition (directly used in OMCI!)
+const (
+ uniPPTP uniPortType = 1 // relates to PPTP
+ uniVEIP uniPortType = 11 // relates to VEIP
+)
+
+//onuUniPort structure holds information about the ONU attached Uni Ports
+type onuUniPort struct {
+ enabled bool
+ name string
+ portNo uint32
+ portType uniPortType
+ ofpPortNo string
+ uniID uint8
+ macBpNo uint8
+ entityID uint16
+ adminState vc.AdminState_Types
+ operState vc.OperStatus_Types
+ pPort *voltha.Port
+}
+
+//newOnuUniPort returns a new instance of a OnuUniPort
+func newOnuUniPort(aUniID uint8, aPortNo uint32, aInstNo uint16,
+ aPortType uniPortType) *onuUniPort {
+ logger.Infow("init-onuUniPort", log.Fields{"uniID": aUniID,
+ "portNo": aPortNo, "InstNo": aInstNo, "type": aPortType})
+ var onuUniPort onuUniPort
+ onuUniPort.enabled = false
+ onuUniPort.name = "uni-" + strconv.FormatUint(uint64(aPortNo), 10)
+ onuUniPort.portNo = aPortNo
+ onuUniPort.portType = aPortType
+ onuUniPort.ofpPortNo = onuUniPort.name
+ onuUniPort.uniID = aUniID
+ onuUniPort.macBpNo = aUniID + 1 //ensure >0 instanceNo
+ onuUniPort.entityID = aInstNo
+ onuUniPort.adminState = vc.AdminState_ENABLED //enabled per create
+ onuUniPort.operState = vc.OperStatus_UNKNOWN
+ onuUniPort.pPort = nil // to be set on create
+ return &onuUniPort
+}
+
+//createVolthaPort creates the Voltha port based on ONU UNI Port and informs the core about it
+func (oo *onuUniPort) createVolthaPort(apDeviceHandler *deviceHandler) error {
+ logger.Debugw("creating-voltha-uni-port", log.Fields{
+ "device-id": apDeviceHandler.device.Id, "portNo": oo.portNo})
+ name := apDeviceHandler.device.SerialNumber + "-" + strconv.FormatUint(uint64(oo.macBpNo), 10)
+
+ var macOctets [6]uint8
+ macOctets[5] = 0x08
+ macOctets[4] = uint8(apDeviceHandler.ponPortNumber >> 8)
+ macOctets[3] = uint8(apDeviceHandler.ponPortNumber)
+ macOctets[2] = uint8(oo.portNo >> 16)
+ macOctets[1] = uint8(oo.portNo >> 8)
+ macOctets[0] = uint8(oo.portNo)
+ onuEntry := apDeviceHandler.getOnuDeviceEntry(false)
+ if onuEntry == nil {
+ logger.Debugw("not-found-onu-device-entry", log.Fields{"error": "not found"})
+ } else {
+ if len(onuEntry.macAddress) == 12 {
+ for idx := 0; idx < 6; idx++ {
+ if v, err := strconv.ParseInt(onuEntry.macAddress[idx*2:(idx*2)+2], 16, 8); err == nil {
+ macOctets[5-idx] = uint8(v)
+ }
+ }
+ }
+ }
+ hwAddr := genMacFromOctets(macOctets)
+ ofHwAddr := macAddressToUint32Array(hwAddr)
+ capacity := uint32(of.OfpPortFeatures_OFPPF_1GB_FD | of.OfpPortFeatures_OFPPF_FIBER)
+ ofUniPortState := of.OfpPortState_OFPPS_LINK_DOWN
+ /* as the VOLTHA port create is only called directly after Uni Port create
+ the OfPortOperState is always Down
+ Note: this way the OfPortOperState won't ever change (directly in adapter)
+ maybe that was already always the case, but looks a bit weird - to be kept in mind ...
+ if pUniPort.operState == vc.OperStatus_ACTIVE {
+ ofUniPortState = of.OfpPortState_OFPPS_LIVE
+ }
+ */
+ logger.Debugw("ofPort values", log.Fields{
+ "forUniPortName": oo.name, "forMacBase": hwAddr,
+ "name": name, "hwAddr": ofHwAddr, "OperState": ofUniPortState})
+
+ pUniPort := &voltha.Port{
+ PortNo: oo.portNo,
+ Label: oo.name,
+ Type: voltha.Port_ETHERNET_UNI,
+ AdminState: oo.adminState,
+ OperStatus: oo.operState,
+ // obviously empty peer setting
+ OfpPort: &of.OfpPort{
+ Name: name,
+ HwAddr: ofHwAddr,
+ Config: 0,
+ State: uint32(ofUniPortState),
+ Curr: capacity,
+ Advertised: capacity,
+ Peer: capacity,
+ CurrSpeed: uint32(of.OfpPortFeatures_OFPPF_1GB_FD),
+ MaxSpeed: uint32(of.OfpPortFeatures_OFPPF_1GB_FD),
+ },
+ }
+ if pUniPort != nil {
+ if err := apDeviceHandler.coreProxy.PortCreated(context.TODO(),
+ apDeviceHandler.deviceID, pUniPort); err != nil {
+ logger.Fatalf("adding-uni-port: create-VOLTHA-Port-failed-%s", err)
+ return err
+ }
+ logger.Infow("Voltha onuUniPort-added", log.Fields{
+ "device-id": apDeviceHandler.device.Id, "PortNo": oo.portNo})
+ oo.pPort = pUniPort
+ oo.operState = vc.OperStatus_DISCOVERED
+ } else {
+ logger.Warnw("could not create Voltha UniPort", log.Fields{
+ "device-id": apDeviceHandler.device.Id, "PortNo": oo.portNo})
+ return errors.New("create Voltha UniPort failed")
+ }
+ return nil
+}
+
+//setOperState modifies OperState of the the UniPort
+func (oo *onuUniPort) setOperState(aNewOperState vc.OperStatus_Types) {
+ oo.operState = aNewOperState
+}
+
+// uni port related utility functions (so far only used here)
+func genMacFromOctets(aOctets [6]uint8) string {
+ return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x",
+ aOctets[5], aOctets[4], aOctets[3],
+ aOctets[2], aOctets[1], aOctets[0])
+}
+
+//copied from OLT Adapter: unify centrally ?
+func macAddressToUint32Array(mac string) []uint32 {
+ slist := strings.Split(mac, ":")
+ result := make([]uint32, len(slist))
+ var err error
+ var tmp int64
+ for index, val := range slist {
+ if tmp, err = strconv.ParseInt(val, 16, 32); err != nil {
+ return []uint32{1, 2, 3, 4, 5, 6}
+ }
+ result[index] = uint32(tmp)
+ }
+ return result
+}
diff --git a/internal/pkg/onuadaptercore/onu_uni_tp.go b/internal/pkg/onuadaptercore/onu_uni_tp.go
new file mode 100644
index 0000000..09ae7bf
--- /dev/null
+++ b/internal/pkg/onuadaptercore/onu_uni_tp.go
@@ -0,0 +1,681 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//Package adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/db"
+ "github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ tp "github.com/opencord/voltha-lib-go/v3/pkg/techprofile"
+)
+
+const cBasePathTechProfileKVStore = "service/voltha/technology_profiles"
+const cBasePathOnuKVStore = "service/voltha/openonu"
+
+//definitions for TechProfileProcessing - copied from OltAdapter:openolt_flowmgr.go
+// could perhaps be defined more globally
+const (
+ binaryStringPrefix = "0b"
+)
+
+type resourceEntry int
+
+const (
+ cResourceGemPort resourceEntry = 1
+ cResourceTcont resourceEntry = 2
+)
+
+type uniPersData struct {
+ PersUniID uint32 `json:"uni_id"`
+ PersTpPath string `json:"tp_path"`
+}
+
+type onuPersistentData struct {
+ PersOnuID uint32 `json:"onu_id"`
+ PersIntfID uint32 `json:"intf_id"`
+ PersSnr string `json:"serial_number"`
+ PersAdminState string `json:"admin_state"`
+ PersOperState string `json:"oper_state"`
+ PersUniTpPath []uniPersData `json:"uni_config"`
+}
+
+type tTechProfileIndication struct {
+ techProfileType string
+ techProfileID uint16
+ techProfileConfigDone bool
+}
+
+type tcontParamStruct struct {
+ allocID uint16
+ schedPolicy uint8
+}
+type gemPortParamStruct struct {
+ gemPortID uint16
+ direction uint8
+ gemPortEncState uint8
+ prioQueueIndex uint8
+ pbitString string
+ discardPolicy string
+ queueSchedPolicy string
+ queueWeight uint8
+}
+
+//refers to one tcont and its properties and all assigned GemPorts and their properties
+type tcontGemList struct {
+ tcontParams tcontParamStruct
+ mapGemPortParams map[uint16]*gemPortParamStruct
+}
+
+//refers to all tcont and their Tcont/GemPort Parameters
+type tMapPonAniConfig map[uint16]*tcontGemList
+
+//onuUniTechProf structure holds information about the TechProfiles attached to Uni Ports of the ONU
+type onuUniTechProf struct {
+ deviceID string
+ baseDeviceHandler *deviceHandler
+ tpProcMutex sync.RWMutex
+ mapUniTpPath map[uint32]string
+ sOnuPersistentData onuPersistentData
+ techProfileKVStore *db.Backend
+ onuKVStore *db.Backend
+ onuKVStorePath string
+ chTpConfigProcessingStep chan uint8
+ chTpKvProcessingStep chan uint8
+ mapUniTpIndication map[uint8]*tTechProfileIndication //use pointer values to ease assignments to the map
+ mapPonAniConfig map[uint8]*tMapPonAniConfig //per UNI: use pointer values to ease assignments to the map
+ pAniConfigFsm *uniPonAniConfigFsm
+ procResult error //error indication of processing
+ mutexTPState sync.Mutex
+}
+
+//newOnuUniTechProf returns the instance of a OnuUniTechProf
+//(one instance per ONU/deviceHandler for all possible UNI's)
+func newOnuUniTechProf(ctx context.Context, aDeviceID string, aDeviceHandler *deviceHandler) *onuUniTechProf {
+ logger.Infow("init-OnuUniTechProf", log.Fields{"device-id": aDeviceID})
+ var onuTP onuUniTechProf
+ onuTP.deviceID = aDeviceID
+ onuTP.baseDeviceHandler = aDeviceHandler
+ onuTP.tpProcMutex = sync.RWMutex{}
+ onuTP.mapUniTpPath = make(map[uint32]string)
+ onuTP.sOnuPersistentData.PersUniTpPath = make([]uniPersData, 1)
+ onuTP.chTpConfigProcessingStep = make(chan uint8)
+ onuTP.chTpKvProcessingStep = make(chan uint8)
+ onuTP.mapUniTpIndication = make(map[uint8]*tTechProfileIndication)
+ onuTP.mapPonAniConfig = make(map[uint8]*tMapPonAniConfig)
+ onuTP.procResult = nil //default assumption processing done with success
+
+ onuTP.techProfileKVStore = aDeviceHandler.setBackend(cBasePathTechProfileKVStore)
+ if onuTP.techProfileKVStore == nil {
+ logger.Errorw("Can't access techProfileKVStore - no backend connection to service",
+ log.Fields{"device-id": aDeviceID, "service": cBasePathTechProfileKVStore})
+ }
+
+ onuTP.onuKVStorePath = onuTP.deviceID
+ onuTP.onuKVStore = aDeviceHandler.setBackend(cBasePathOnuKVStore)
+ if onuTP.onuKVStore == nil {
+ logger.Errorw("Can't access onuKVStore - no backend connection to service",
+ log.Fields{"device-id": aDeviceID, "service": cBasePathOnuKVStore})
+ }
+ return &onuTP
+}
+
+// lockTpProcMutex locks OnuUniTechProf processing mutex
+func (onuTP *onuUniTechProf) lockTpProcMutex() {
+ onuTP.tpProcMutex.Lock()
+}
+
+// unlockTpProcMutex unlocks OnuUniTechProf processing mutex
+func (onuTP *onuUniTechProf) unlockTpProcMutex() {
+ onuTP.tpProcMutex.Unlock()
+}
+
+// resetProcessingErrorIndication resets the internal error indication
+// need to be called before evaluation of any subsequent processing (given by waitForTpCompletion())
+func (onuTP *onuUniTechProf) resetProcessingErrorIndication() {
+ onuTP.procResult = nil
+}
+
+// updateOnuUniTpPath verifies and updates changes in the kvStore onuUniTpPath
+func (onuTP *onuUniTechProf) updateOnuUniTpPath(aUniID uint32, aPathString string) bool {
+ /* within some specific InterAdapter processing request write/read access to data is ensured to be sequentially,
+ as also the complete sequence is ensured to 'run to completion' before some new request is accepted
+ no specific concurrency protection to sOnuPersistentData is required here
+ */
+ if existingPath, present := onuTP.mapUniTpPath[aUniID]; present {
+ // uni entry already exists
+ //logger.Debugw(" already exists", log.Fields{"for InstanceId": a_uniInstNo})
+ if existingPath != aPathString {
+ if aPathString == "" {
+ //existing entry to be deleted
+ logger.Debugw("UniTp path delete", log.Fields{
+ "device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
+ delete(onuTP.mapUniTpPath, aUniID)
+ } else {
+ //existing entry to be modified
+ logger.Debugw("UniTp path modify", log.Fields{
+ "device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
+ onuTP.mapUniTpPath[aUniID] = aPathString
+ }
+ return true
+ }
+ //entry already exists
+ logger.Debugw("UniTp path already exists", log.Fields{
+ "device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
+ return false
+ }
+ if aPathString == "" {
+ //delete request in non-existing state , accept as no change
+ logger.Debugw("UniTp path already removed", log.Fields{
+ "device-id": onuTP.deviceID, "uniID": aUniID})
+ return false
+ }
+ logger.Debugw("New UniTp path set", log.Fields{
+ "device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
+ onuTP.mapUniTpPath[aUniID] = aPathString
+ return true
+}
+
+func (onuTP *onuUniTechProf) waitForTpCompletion(cancel context.CancelFunc, wg *sync.WaitGroup) error {
+ defer cancel() //ensure termination of context (may be pro forma)
+ wg.Wait()
+ logger.Debug("some TechProfile Processing completed")
+ onuTP.tpProcMutex.Unlock() //allow further TP related processing
+ return onuTP.procResult
+}
+
+// configureUniTp checks existing tp resources to delete and starts the corresponding OMCI configuation of the UNI port
+// all possibly blocking processing must be run in background to allow for deadline supervision!
+// but take care on sequential background processing when needed (logical dependencies)
+// use waitForTimeoutOrCompletion(ctx, chTpConfigProcessingStep, processingStep) for internal synchronization
+func (onuTP *onuUniTechProf) configureUniTp(ctx context.Context,
+ aUniID uint8, aPathString string, wg *sync.WaitGroup) {
+ defer wg.Done() //always decrement the waitGroup on return
+ logger.Debugw("configure the Uni according to TpPath", log.Fields{
+ "device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
+
+ if onuTP.techProfileKVStore == nil {
+ logger.Debug("techProfileKVStore not set - abort")
+ onuTP.procResult = errors.New("techProfile config aborted: techProfileKVStore not set")
+ return
+ }
+
+ var pCurrentUniPort *onuUniPort
+ for _, uniPort := range onuTP.baseDeviceHandler.uniEntityMap {
+ // only if this port is validated for operState transfer
+ if uniPort.uniID == uint8(aUniID) {
+ pCurrentUniPort = uniPort
+ break //found - end search loop
+ }
+ }
+ if pCurrentUniPort == nil {
+ logger.Errorw("TechProfile configuration aborted: requested uniID not found in PortDB",
+ log.Fields{"device-id": onuTP.deviceID, "uniID": aUniID})
+ onuTP.procResult = errors.New("techProfile config aborted: requested uniID not found")
+ return
+ }
+
+ var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpConfigProcessingStep
+
+ /* if tcontMap not empty {
+ go onuTP.deleteAniSideConfig(ctx, aUniID, processingStep)
+ if !onuTP.waitForTimeoutOrCompletion(ctx, chTpConfigProcessingStep, processingStep) {
+ //timeout or error detected
+ return
+ }
+ clear tcontMap
+ }
+
+ processingStep++
+ */
+ go onuTP.readAniSideConfigFromTechProfile(ctx, aUniID, aPathString, processingStep)
+ if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpConfigProcessingStep, processingStep) {
+ //timeout or error detected
+ logger.Debugw("tech-profile related configuration aborted on read",
+ log.Fields{"device-id": onuTP.deviceID, "UniId": aUniID})
+ onuTP.procResult = errors.New("techProfile config aborted: tech-profile read issue")
+ return
+ }
+
+ processingStep++
+ if valuePA, existPA := onuTP.mapPonAniConfig[aUniID]; existPA {
+ if _, existTG := (*valuePA)[0]; existTG {
+ //Config data for this uni and and at least TCont Index 0 exist
+ go onuTP.setAniSideConfigFromTechProfile(ctx, aUniID, pCurrentUniPort, processingStep)
+ if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpConfigProcessingStep, processingStep) {
+ //timeout or error detected
+ logger.Debugw("tech-profile related configuration aborted on set",
+ log.Fields{"device-id": onuTP.deviceID, "UniId": aUniID})
+ onuTP.procResult = errors.New("techProfile config aborted: Omci AniSideConfig failed")
+ //this issue here means that the AniConfigFsm has not finished successfully
+ //which requires to reset it to allow for new usage, e.g. also on a different UNI
+ //(without that it would be reset on device down indication latest)
+ _ = onuTP.pAniConfigFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+ return
+ }
+ } else {
+ // strange: UNI entry exists, but no ANI data, maybe such situation should be cleared up (if observed)
+ logger.Debugw("no Tcont/Gem data for this UNI found - abort", log.Fields{
+ "device-id": onuTP.deviceID, "uniID": aUniID})
+ onuTP.procResult = errors.New("techProfile config aborted: no Tcont/Gem data found for this UNI")
+ return
+ }
+ } else {
+ logger.Debugw("no PonAni data for this UNI found - abort", log.Fields{
+ "device-id": onuTP.deviceID, "uniID": aUniID})
+ onuTP.procResult = errors.New("techProfile config aborted: no AniSide data found for this UNI")
+ return
+ }
+}
+
+func (onuTP *onuUniTechProf) updateOnuTpPathKvStore(ctx context.Context, wg *sync.WaitGroup) {
+ defer wg.Done()
+
+ if onuTP.onuKVStore == nil {
+ logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
+ onuTP.procResult = errors.New("onu/tp-data update aborted: onuKVStore not set")
+ return
+ }
+ var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpKvProcessingStep
+ go onuTP.storePersistentData(ctx, processingStep)
+ if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpKvProcessingStep, processingStep) {
+ //timeout or error detected
+ logger.Debugw("ONU/TP-data not written - abort", log.Fields{"device-id": onuTP.deviceID})
+ onuTP.procResult = errors.New("onu/tp-data update aborted: during writing process")
+ return
+ }
+}
+
+func (onuTP *onuUniTechProf) restoreFromOnuTpPathKvStore(ctx context.Context) error {
+ if onuTP.onuKVStore == nil {
+ logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
+ return fmt.Errorf(fmt.Sprintf("onuKVStore-not-set-abort-%s", onuTP.deviceID))
+ }
+ if err := onuTP.restorePersistentData(ctx); err != nil {
+ logger.Debugw("ONU/TP-data not read - abort", log.Fields{"device-id": onuTP.deviceID})
+ return err
+ }
+ return nil
+}
+
+func (onuTP *onuUniTechProf) deleteOnuTpPathKvStore(ctx context.Context) error {
+ if onuTP.onuKVStore == nil {
+ logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
+ return fmt.Errorf(fmt.Sprintf("onuKVStore-not-set-abort-%s", onuTP.deviceID))
+ }
+ if err := onuTP.deletePersistentData(ctx); err != nil {
+ logger.Debugw("ONU/TP-data not read - abort", log.Fields{"device-id": onuTP.deviceID})
+ return err
+ }
+ return nil
+}
+
+// deleteTpResource removes Resources from the ONU's specified Uni
+func (onuTP *onuUniTechProf) deleteTpResource(ctx context.Context,
+ aUniID uint32, aPathString string, aResource resourceEntry, aEntryID uint32,
+ wg *sync.WaitGroup) {
+ defer wg.Done()
+ logger.Debugw("this would remove TP resources from ONU's UNI", log.Fields{
+ "device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString, "Resource": aResource})
+ /*
+ var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpConfigProcessingStep
+ go onuTp.deleteAniResource(ctx, processingStep)
+ if !onuTP.waitForTimeoutOrCompletion(ctx, chTpConfigProcessingStep, processingStep) {
+ //timeout or error detected
+ return
+ }
+ */
+}
+
+/* internal methods *********************/
+
+func (onuTP *onuUniTechProf) storePersistentData(ctx context.Context, aProcessingStep uint8) {
+
+ onuTP.sOnuPersistentData.PersOnuID = onuTP.baseDeviceHandler.pOnuIndication.OnuId
+ onuTP.sOnuPersistentData.PersIntfID = onuTP.baseDeviceHandler.pOnuIndication.IntfId
+ onuTP.sOnuPersistentData.PersSnr = onuTP.baseDeviceHandler.pOnuOmciDevice.serialNumber
+ onuTP.sOnuPersistentData.PersAdminState = "up"
+ onuTP.sOnuPersistentData.PersOperState = "active"
+
+ onuTP.sOnuPersistentData.PersUniTpPath = onuTP.sOnuPersistentData.PersUniTpPath[:0]
+
+ for k, v := range onuTP.mapUniTpPath {
+ onuTP.sOnuPersistentData.PersUniTpPath =
+ append(onuTP.sOnuPersistentData.PersUniTpPath, uniPersData{PersUniID: k, PersTpPath: v})
+ }
+ logger.Debugw("Update ONU/TP-data in KVStore", log.Fields{"device-id": onuTP.deviceID, "onuTP.sOnuPersistentData": onuTP.sOnuPersistentData})
+
+ Value, err := json.Marshal(onuTP.sOnuPersistentData)
+ if err != nil {
+ logger.Errorw("unable to marshal ONU/TP-data", log.Fields{"onuTP.sOnuPersistentData": onuTP.sOnuPersistentData,
+ "device-id": onuTP.deviceID, "err": err})
+ onuTP.chTpKvProcessingStep <- 0 //error indication
+ return
+ }
+ err = onuTP.onuKVStore.Put(ctx, onuTP.onuKVStorePath, Value)
+ if err != nil {
+ logger.Errorw("unable to write ONU/TP-data into KVstore", log.Fields{"device-id": onuTP.deviceID, "err": err})
+ onuTP.chTpKvProcessingStep <- 0 //error indication
+ return
+ }
+ onuTP.chTpKvProcessingStep <- aProcessingStep //done
+}
+
+func (onuTP *onuUniTechProf) restorePersistentData(ctx context.Context) error {
+
+ onuTP.mapUniTpPath = make(map[uint32]string)
+ onuTP.sOnuPersistentData = onuPersistentData{0, 0, "", "", "", make([]uniPersData, 0)}
+
+ Value, err := onuTP.onuKVStore.Get(ctx, onuTP.onuKVStorePath)
+ if err == nil {
+ if Value != nil {
+ logger.Debugw("ONU/TP-data read",
+ log.Fields{"Key": Value.Key, "device-id": onuTP.deviceID})
+ tpTmpBytes, _ := kvstore.ToByte(Value.Value)
+
+ if err = json.Unmarshal(tpTmpBytes, &onuTP.sOnuPersistentData); err != nil {
+ logger.Errorw("unable to unmarshal ONU/TP-data", log.Fields{"error": err, "device-id": onuTP.deviceID})
+ return fmt.Errorf(fmt.Sprintf("unable-to-unmarshal-ONU/TP-data-%s", onuTP.deviceID))
+ }
+ logger.Debugw("ONU/TP-data", log.Fields{"onuTP.sOnuPersistentData": onuTP.sOnuPersistentData,
+ "device-id": onuTP.deviceID})
+
+ for _, uniData := range onuTP.sOnuPersistentData.PersUniTpPath {
+ onuTP.mapUniTpPath[uniData.PersUniID] = uniData.PersTpPath
+ }
+ logger.Debugw("TpPath map", log.Fields{"onuTP.mapUniTpPath": onuTP.mapUniTpPath,
+ "device-id": onuTP.deviceID})
+ } else {
+ logger.Errorw("no ONU/TP-data found", log.Fields{"path": onuTP.onuKVStorePath, "device-id": onuTP.deviceID})
+ return fmt.Errorf(fmt.Sprintf("no-ONU/TP-data-found-%s", onuTP.deviceID))
+ }
+ } else {
+ logger.Errorw("unable to read from KVstore", log.Fields{"device-id": onuTP.deviceID})
+ return fmt.Errorf(fmt.Sprintf("unable-to-read-from-KVstore-%s", onuTP.deviceID))
+ }
+ return nil
+}
+
+func (onuTP *onuUniTechProf) deletePersistentData(ctx context.Context) error {
+
+ logger.Debugw("delete ONU/TP-data in KVStore", log.Fields{"device-id": onuTP.deviceID})
+ err := onuTP.onuKVStore.Delete(ctx, onuTP.onuKVStorePath)
+ if err != nil {
+ logger.Errorw("unable to delete in KVstore", log.Fields{"device-id": onuTP.deviceID, "err": err})
+ return fmt.Errorf(fmt.Sprintf("unable-delete-in-KVstore-%s", onuTP.deviceID))
+ }
+ return nil
+}
+
+func (onuTP *onuUniTechProf) readAniSideConfigFromTechProfile(
+ ctx context.Context, aUniID uint8, aPathString string, aProcessingStep uint8) {
+ var tpInst tp.TechProfile
+
+ subStringSlice := strings.Split(aPathString, "/")
+ if len(subStringSlice) <= 2 {
+ logger.Errorw("invalid path name format",
+ log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
+ onuTP.chTpConfigProcessingStep <- 0 //error indication
+ return
+ }
+
+ if _, existTP := onuTP.mapUniTpIndication[aUniID]; existTP {
+ logger.Warnw("Some active profile entry at reading new TechProfile",
+ log.Fields{"path": aPathString, "device-id": onuTP.deviceID,
+ "UniId": aUniID, "wrongProfile": onuTP.mapUniTpIndication[aUniID].techProfileID})
+ //delete on the mapUniTpIndication map not needed, just overwritten later
+ //delete on the PonAniConfig map should be safe, even if not existing
+ delete(onuTP.mapPonAniConfig, aUniID)
+ } else {
+ // this is normal processing
+ onuTP.mapUniTpIndication[aUniID] = &tTechProfileIndication{} //need to assign some (empty) struct memory first!
+ }
+
+ onuTP.mapUniTpIndication[aUniID].techProfileType = subStringSlice[0]
+ profID, err := strconv.ParseUint(subStringSlice[1], 10, 32)
+ if err != nil {
+ logger.Errorw("invalid ProfileId from path",
+ log.Fields{"ParseErr": err})
+ onuTP.chTpConfigProcessingStep <- 0 //error indication
+ return
+ }
+
+ onuTP.mapUniTpIndication[aUniID].techProfileID = uint16(profID)
+ logger.Debugw("tech-profile path indications",
+ log.Fields{"device-id": onuTP.deviceID, "UniId": aUniID,
+ "profType": onuTP.mapUniTpIndication[aUniID].techProfileType,
+ "profID": onuTP.mapUniTpIndication[aUniID].techProfileID})
+
+ Value, err := onuTP.techProfileKVStore.Get(ctx, aPathString)
+ if err == nil {
+ if Value != nil {
+ logger.Debugw("tech-profile read",
+ log.Fields{"Key": Value.Key, "device-id": onuTP.deviceID})
+ tpTmpBytes, _ := kvstore.ToByte(Value.Value)
+
+ if err = json.Unmarshal(tpTmpBytes, &tpInst); err != nil {
+ logger.Errorw("TechProf - Failed to unmarshal tech-profile into tpInst",
+ log.Fields{"error": err, "device-id": onuTP.deviceID})
+ onuTP.chTpConfigProcessingStep <- 0 //error indication
+ return
+ }
+ logger.Debugw("TechProf - tpInst", log.Fields{"tpInst": tpInst})
+ // access examples
+ logger.Debugw("TechProf content", log.Fields{"Name": tpInst.Name,
+ "MaxGemPayloadSize": tpInst.InstanceCtrl.MaxGemPayloadSize,
+ "DownstreamGemDiscardmaxThreshold": tpInst.DownstreamGemPortAttributeList[0].DiscardConfig.MaxThreshold})
+ } else {
+ logger.Errorw("No tech-profile found",
+ log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
+ onuTP.chTpConfigProcessingStep <- 0 //error indication
+ return
+ }
+ } else {
+ logger.Errorw("kvstore-get failed for path",
+ log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
+ onuTP.chTpConfigProcessingStep <- 0 //error indication
+ return
+ }
+
+ localMapGemPortParams := make(map[uint16]*gemPortParamStruct)
+ localMapGemPortParams[0] = &gemPortParamStruct{}
+ localMapPonAniConfig := make(map[uint16]*tcontGemList)
+ localMapPonAniConfig[0] = &tcontGemList{tcontParamStruct{}, localMapGemPortParams}
+ onuTP.mapPonAniConfig[aUniID] = (*tMapPonAniConfig)(&localMapPonAniConfig)
+
+ (*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.allocID = uint16(tpInst.UsScheduler.AllocID)
+ if tpInst.UsScheduler.QSchedPolicy == "StrictPrio" {
+ (*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.schedPolicy = 1 //for the moment fixed value acc. G.988 //TODO: defines!
+ } else {
+ //default profile defines "Hybrid" - which probably comes down to WRR with some weigthts for SP
+ (*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.schedPolicy = 2 //for G.988 WRR
+ }
+ loNumGemPorts := tpInst.NumGemPorts
+ loGemPortRead := false
+ for pos, content := range tpInst.UpstreamGemPortAttributeList {
+ if uint32(pos) == loNumGemPorts {
+ logger.Debugw("PonAniConfig abort GemPortList - GemList exceeds set NumberOfGemPorts",
+ log.Fields{"device-id": onuTP.deviceID, "index": pos, "NumGem": loNumGemPorts})
+ break
+ }
+ if pos == 0 {
+ //at least one upstream GemPort should always exist (else traffic profile makes no sense)
+ loGemPortRead = true
+ } else {
+ //for all further GemPorts we need to extend the mapGemPortParams
+ (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)] = &gemPortParamStruct{}
+ }
+ (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].gemPortID =
+ uint16(content.GemportID)
+ //direction can be correlated later with Downstream list,
+ // for now just assume bidirectional (upstream never exists alone)
+ (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].direction = 3 //as defined in G.988
+ // expected Prio-Queue values 0..7 with 7 for highest PrioQueue, QueueIndex=Prio = 0..7
+ if 7 < content.PriorityQueue {
+ logger.Errorw("PonAniConfig reject on GemPortList - PrioQueue value invalid",
+ log.Fields{"device-id": onuTP.deviceID, "index": pos, "PrioQueue": content.PriorityQueue})
+ //remove PonAniConfig as done so far, delete map should be safe, even if not existing
+ delete(onuTP.mapPonAniConfig, aUniID)
+ onuTP.chTpConfigProcessingStep <- 0 //error indication
+ return
+ }
+ (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].prioQueueIndex =
+ uint8(content.PriorityQueue)
+ (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].pbitString =
+ strings.TrimPrefix(content.PbitMap, binaryStringPrefix)
+ if content.AesEncryption == "True" {
+ (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].gemPortEncState = 1
+ } else {
+ (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].gemPortEncState = 0
+ }
+ (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].discardPolicy =
+ content.DiscardPolicy
+ (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].queueSchedPolicy =
+ content.SchedulingPolicy
+ //'GemWeight' looks strange in default profile, for now we just copy the weight to first queue
+ (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].queueWeight =
+ uint8(content.Weight)
+ }
+ if !loGemPortRead {
+ logger.Errorw("PonAniConfig reject - no GemPort could be read from TechProfile",
+ log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
+ //remove PonAniConfig as done so far, delete map should be safe, even if not existing
+ delete(onuTP.mapPonAniConfig, aUniID)
+ onuTP.chTpConfigProcessingStep <- 0 //error indication
+ return
+ }
+
+ logger.Debugw("PonAniConfig read from TechProfile", log.Fields{
+ "device-id": onuTP.deviceID,
+ "AllocId": (*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.allocID})
+ for gemIndex, gemEntry := range (*(onuTP.mapPonAniConfig[0]))[0].mapGemPortParams {
+ logger.Debugw("PonAniConfig read from TechProfile", log.Fields{
+ "GemIndex": gemIndex,
+ "GemPort": gemEntry.gemPortID,
+ "QueueScheduling": gemEntry.queueSchedPolicy})
+ }
+
+ onuTP.chTpConfigProcessingStep <- aProcessingStep //done
+}
+
+func (onuTP *onuUniTechProf) setAniSideConfigFromTechProfile(
+ ctx context.Context, aUniID uint8, apCurrentUniPort *onuUniPort, aProcessingStep uint8) {
+
+ if onuTP.pAniConfigFsm == nil {
+ onuTP.createAniConfigFsm(aUniID, apCurrentUniPort, OmciAniConfigDone, aProcessingStep)
+ } else { //AniConfigFsm already init
+ onuTP.runAniConfigFsm(aProcessingStep)
+ }
+}
+
+func (onuTP *onuUniTechProf) waitForTimeoutOrCompletion(
+ ctx context.Context, aChTpProcessingStep <-chan uint8, aProcessingStep uint8) bool {
+ select {
+ case <-ctx.Done():
+ logger.Warnw("processing not completed in-time: force release of TpProcMutex!",
+ log.Fields{"device-id": onuTP.deviceID, "error": ctx.Err()})
+ return false
+ case rxStep := <-aChTpProcessingStep:
+ if rxStep == aProcessingStep {
+ return true
+ }
+ //all other values are not accepted - including 0 for error indication
+ logger.Warnw("Invalid processing step received: abort and force release of TpProcMutex!",
+ log.Fields{"device-id": onuTP.deviceID,
+ "wantedStep": aProcessingStep, "haveStep": rxStep})
+ return false
+ }
+}
+
+// createUniLockFsm initializes and runs the AniConfig FSM to transfer the OMCI related commands for ANI side configuration
+func (onuTP *onuUniTechProf) createAniConfigFsm(aUniID uint8,
+ apCurrentUniPort *onuUniPort, devEvent OnuDeviceEvent, aProcessingStep uint8) {
+ logger.Debugw("createAniConfigFsm", log.Fields{"device-id": onuTP.deviceID})
+ chAniConfigFsm := make(chan Message, 2048)
+ pDevEntry := onuTP.baseDeviceHandler.getOnuDeviceEntry(true)
+ if pDevEntry == nil {
+ logger.Errorw("No valid OnuDevice - aborting", log.Fields{"device-id": onuTP.deviceID})
+ return
+ }
+ pAniCfgFsm := newUniPonAniConfigFsm(pDevEntry.PDevOmciCC, apCurrentUniPort, onuTP,
+ pDevEntry.pOnuDB, onuTP.mapUniTpIndication[aUniID].techProfileID, devEvent,
+ "AniConfigFsm", onuTP.deviceID, chAniConfigFsm)
+ if pAniCfgFsm != nil {
+ onuTP.pAniConfigFsm = pAniCfgFsm
+ onuTP.runAniConfigFsm(aProcessingStep)
+ } else {
+ logger.Errorw("AniConfigFSM could not be created - abort!!", log.Fields{"device-id": onuTP.deviceID})
+ }
+}
+
+// runAniConfigFsm starts the AniConfig FSM to transfer the OMCI related commands for ANI side configuration
+func (onuTP *onuUniTechProf) runAniConfigFsm(aProcessingStep uint8) {
+ /* Uni related ANI config procedure -
+ ***** should run via 'aniConfigDone' state and generate the argument requested event *****
+ */
+ pACStatemachine := onuTP.pAniConfigFsm.pAdaptFsm.pFsm
+ if pACStatemachine != nil {
+ if pACStatemachine.Is(aniStDisabled) {
+ //FSM init requirement to get informed abou FSM completion! (otherwise timeout of the TechProf config)
+ onuTP.pAniConfigFsm.setFsmCompleteChannel(onuTP.chTpConfigProcessingStep, aProcessingStep)
+ if err := pACStatemachine.Event(aniEvStart); err != nil {
+ logger.Warnw("AniConfigFSM: can't start", log.Fields{"err": err})
+ // maybe try a FSM reset and then again ... - TODO!!!
+ } else {
+ /***** AniConfigFSM started */
+ logger.Debugw("AniConfigFSM started", log.Fields{
+ "state": pACStatemachine.Current(), "device-id": onuTP.deviceID})
+ }
+ } else {
+ logger.Warnw("wrong state of AniConfigFSM - want: disabled", log.Fields{
+ "have": pACStatemachine.Current(), "device-id": onuTP.deviceID})
+ // maybe try a FSM reset and then again ... - TODO!!!
+ }
+ } else {
+ logger.Errorw("AniConfigFSM StateMachine invalid - cannot be executed!!", log.Fields{"device-id": onuTP.deviceID})
+ // maybe try a FSM reset and then again ... - TODO!!!
+ }
+}
+
+// setConfigDone sets the requested techProfile config state (if possible)
+func (onuTP *onuUniTechProf) setConfigDone(aUniID uint8, aState bool) {
+ if _, existTP := onuTP.mapUniTpIndication[aUniID]; existTP {
+ onuTP.mutexTPState.Lock()
+ onuTP.mapUniTpIndication[aUniID].techProfileConfigDone = aState
+ onuTP.mutexTPState.Unlock()
+ } //else: the state is just ignored (does not exist)
+}
+
+// getTechProfileDone checks if the Techprofile processing with the requested TechProfile ID was done
+func (onuTP *onuUniTechProf) getTechProfileDone(aUniID uint8, aTpID uint16) bool {
+ if _, existTP := onuTP.mapUniTpIndication[aUniID]; existTP {
+ if onuTP.mapUniTpIndication[aUniID].techProfileID == aTpID {
+ onuTP.mutexTPState.Lock()
+ defer onuTP.mutexTPState.Unlock()
+ return onuTP.mapUniTpIndication[aUniID].techProfileConfigDone
+ }
+ }
+ return false
+}
diff --git a/internal/pkg/onuadaptercore/openonu.go b/internal/pkg/onuadaptercore/openonu.go
new file mode 100644
index 0000000..a8a84d4
--- /dev/null
+++ b/internal/pkg/onuadaptercore/openonu.go
@@ -0,0 +1,428 @@
+/*
+ * 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"
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+ "github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
+ "github.com/opencord/voltha-lib-go/v3/pkg/kafka"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+ ic "github.com/opencord/voltha-protos/v3/go/inter_container"
+ "github.com/opencord/voltha-protos/v3/go/openflow_13"
+ "github.com/opencord/voltha-protos/v3/go/voltha"
+
+ "test.internal/openadapter/internal/pkg/config"
+)
+
+//OpenONUAC structure holds the ONU core information
+type OpenONUAC struct {
+ deviceHandlers map[string]*deviceHandler
+ coreProxy adapterif.CoreProxy
+ adapterProxy adapterif.AdapterProxy
+ eventProxy adapterif.EventProxy
+ kafkaICProxy kafka.InterContainerProxy
+ kvClient kvstore.Client
+ config *config.AdapterFlags
+ numOnus int
+ KVStoreHost string
+ KVStorePort int
+ KVStoreType string
+ KVStoreTimeout time.Duration
+ exitChannel chan int
+ HeartbeatCheckInterval time.Duration
+ HeartbeatFailReportInterval time.Duration
+ AcceptIncrementalEvto bool
+ lockDeviceHandlersMap sync.RWMutex
+ pSupportedFsms *OmciDeviceFsms
+}
+
+//NewOpenONUAC returns a new instance of OpenONU_AC
+func NewOpenONUAC(ctx context.Context, kafkaICProxy kafka.InterContainerProxy,
+ coreProxy adapterif.CoreProxy, adapterProxy adapterif.AdapterProxy,
+ eventProxy adapterif.EventProxy, kvClient kvstore.Client, cfg *config.AdapterFlags) *OpenONUAC {
+ var openOnuAc OpenONUAC
+ openOnuAc.exitChannel = make(chan int, 1)
+ openOnuAc.deviceHandlers = make(map[string]*deviceHandler)
+ openOnuAc.kafkaICProxy = kafkaICProxy
+ openOnuAc.config = cfg
+ openOnuAc.numOnus = cfg.OnuNumber
+ openOnuAc.coreProxy = coreProxy
+ openOnuAc.adapterProxy = adapterProxy
+ openOnuAc.eventProxy = eventProxy
+ openOnuAc.kvClient = kvClient
+ openOnuAc.KVStoreHost = cfg.KVStoreHost
+ openOnuAc.KVStorePort = cfg.KVStorePort
+ openOnuAc.KVStoreType = cfg.KVStoreType
+ openOnuAc.KVStoreTimeout = cfg.KVStoreTimeout
+ openOnuAc.HeartbeatCheckInterval = cfg.HeartbeatCheckInterval
+ openOnuAc.HeartbeatFailReportInterval = cfg.HeartbeatFailReportInterval
+ openOnuAc.AcceptIncrementalEvto = cfg.AccIncrEvto
+ openOnuAc.lockDeviceHandlersMap = sync.RWMutex{}
+
+ openOnuAc.pSupportedFsms = &OmciDeviceFsms{
+ "mib-synchronizer": {
+ //mibSyncFsm, // Implements the MIB synchronization state machine
+ mibDbVolatileDictImpl, // Implements volatile ME MIB database
+ //true, // Advertise events on OpenOMCI event bus
+ cMibAuditDelayImpl, // 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,
+ // },
+ },
+ }
+
+ return &openOnuAc
+}
+
+//Start starts (logs) the adapter
+func (oo *OpenONUAC) Start(ctx context.Context) error {
+ logger.Info("starting-openonu-adapter")
+ logger.Info("openonu-adapter-started")
+ return nil
+}
+
+/*
+//stop terminates the session
+func (oo *OpenONUAC) stop(ctx context.Context) error {
+ logger.Info("stopping-device-manager")
+ oo.exitChannel <- 1
+ logger.Info("device-manager-stopped")
+ return nil
+}
+*/
+
+func (oo *OpenONUAC) addDeviceHandlerToMap(ctx context.Context, agent *deviceHandler) {
+ oo.lockDeviceHandlersMap.Lock()
+ defer oo.lockDeviceHandlersMap.Unlock()
+ if _, exist := oo.deviceHandlers[agent.deviceID]; !exist {
+ oo.deviceHandlers[agent.deviceID] = agent
+ oo.deviceHandlers[agent.deviceID].start(ctx)
+ }
+}
+
+/*
+func (oo *OpenONUAC) deleteDeviceHandlerToMap(agent *deviceHandler) {
+ oo.lockDeviceHandlersMap.Lock()
+ defer oo.lockDeviceHandlersMap.Unlock()
+ delete(oo.deviceHandlers, agent.deviceID)
+}
+*/
+
+func (oo *OpenONUAC) getDeviceHandler(deviceID string) *deviceHandler {
+ oo.lockDeviceHandlersMap.Lock()
+ defer oo.lockDeviceHandlersMap.Unlock()
+ if agent, ok := oo.deviceHandlers[deviceID]; ok {
+ return agent
+ }
+ return nil
+}
+
+// Adapter interface required methods ############## begin #########
+// #################################################################
+
+// for original content compare: (needs according deviceHandler methods)
+// /voltha-openolt-adapter/adaptercore/openolt.go
+
+// Adopt_device creates a new device handler if not present already and then adopts the device
+func (oo *OpenONUAC) Adopt_device(device *voltha.Device) error {
+ if device == nil {
+ logger.Warn("voltha-device-is-nil")
+ return errors.New("nil-device")
+ }
+ ctx := context.Background()
+ logger.Infow("adopt-device", log.Fields{"device-id": device.Id})
+ var handler *deviceHandler
+ if handler = oo.getDeviceHandler(device.Id); handler == nil {
+ handler := newDeviceHandler(oo.coreProxy, oo.adapterProxy, oo.eventProxy, device, oo)
+ oo.addDeviceHandlerToMap(ctx, handler)
+ go handler.adoptOrReconcileDevice(ctx, device)
+ // Launch the creation of the device topic
+ // go oo.createDeviceTopic(device)
+ }
+ return nil
+}
+
+//Get_ofp_device_info returns OFP information for the given device
+func (oo *OpenONUAC) Get_ofp_device_info(device *voltha.Device) (*ic.SwitchCapability, error) {
+ logger.Errorw("device-handler-not-set", log.Fields{"device-id": device.Id})
+ return nil, errors.New("device-handler-not-set")
+}
+
+//Get_ofp_port_info returns OFP port information for the given device
+//200630: method removed as per [VOL-3202]: OF port info is now to be delivered within UniPort create
+// cmp changes in onu_uni_port.go::CreateVolthaPort()
+
+//Process_inter_adapter_message sends messages to a target device (between adapters)
+func (oo *OpenONUAC) Process_inter_adapter_message(msg *ic.InterAdapterMessage) error {
+ logger.Debugw("Process_inter_adapter_message", log.Fields{"msgId": msg.Header.Id,
+ "msgProxyDeviceId": msg.Header.ProxyDeviceId, "msgToDeviceId": msg.Header.ToDeviceId})
+
+ targetDevice := msg.Header.ToDeviceId
+ if handler := oo.getDeviceHandler(targetDevice); handler != nil {
+ /* 200724: modification towards synchronous implementation - possible errors within processing shall be
+ * in the accordingly delayed response, some timing effect might result in Techprofile processing for multiple UNI's
+ */
+ return handler.processInterAdapterMessage(msg)
+ /* so far the processing has been in background with according commented error treatment restrictions:
+ go handler.ProcessInterAdapterMessage(msg)
+ // error treatment might be more sophisticated
+ // by now let's just accept the message on 'communication layer'
+ // message content problems have to be evaluated then in the handler
+ // and are by now not reported to the calling party (to force what reaction there?)
+ return nil
+ */
+ }
+ logger.Warnw("no handler found for received Inter-Proxy-message", log.Fields{
+ "msgToDeviceId": targetDevice})
+ return fmt.Errorf(fmt.Sprintf("handler-not-found-%s", targetDevice))
+}
+
+//Adapter_descriptor not implemented
+func (oo *OpenONUAC) Adapter_descriptor() error {
+ return errors.New("unImplemented")
+}
+
+//Device_types unimplemented
+func (oo *OpenONUAC) Device_types() (*voltha.DeviceTypes, error) {
+ return nil, errors.New("unImplemented")
+}
+
+//Health returns unimplemented
+func (oo *OpenONUAC) Health() (*voltha.HealthStatus, error) {
+ return nil, errors.New("unImplemented")
+}
+
+//Reconcile_device is called once when the adapter needs to re-create device - usually on core restart
+func (oo *OpenONUAC) Reconcile_device(device *voltha.Device) error {
+ if device == nil {
+ logger.Warn("voltha-device-is-nil")
+ return errors.New("nil-device")
+ }
+ ctx := context.Background()
+ logger.Infow("Reconcile_device", log.Fields{"device-id": device.Id})
+ var handler *deviceHandler
+ if handler = oo.getDeviceHandler(device.Id); handler == nil {
+ handler := newDeviceHandler(oo.coreProxy, oo.adapterProxy, oo.eventProxy, device, oo)
+ oo.addDeviceHandlerToMap(ctx, handler)
+ handler.device = device
+ handler.reconciling = true
+ go handler.adoptOrReconcileDevice(ctx, handler.device)
+ // reconcilement will be continued after onu-device entry is added
+ } else {
+ return fmt.Errorf(fmt.Sprintf("device-already-reconciled-or-active-%s", device.Id))
+ }
+ return nil
+}
+
+//Abandon_device unimplemented
+func (oo *OpenONUAC) Abandon_device(device *voltha.Device) error {
+ return errors.New("unImplemented")
+}
+
+var disableOnuFunc bool = true
+
+//Disable_device disables the given device
+func (oo *OpenONUAC) Disable_device(device *voltha.Device) error {
+ logger.Debugw("Disable_device", log.Fields{"device-id": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ if err := handler.sendMsgToOlt(context.TODO(), "disable", nil); err != nil {
+ logger.Debugw("Disable_device", log.Fields{"error": err})
+ }
+ if disableOnuFunc {
+ return nil
+ }
+ //go handler.disableDevice(device)
+ //return nil
+ }
+ logger.Warnw("no handler found for device-disable", log.Fields{"device-id": device.Id})
+ return fmt.Errorf(fmt.Sprintf("handler-not-found-%s", device.Id))
+}
+
+//Reenable_device enables the onu device after disable
+func (oo *OpenONUAC) Reenable_device(device *voltha.Device) error {
+ logger.Debugw("Reenable_device", log.Fields{"device-id": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ if err := handler.sendMsgToOlt(context.TODO(), "reenable", nil); err != nil {
+ logger.Debugw("Disable_device", log.Fields{"error": err})
+ }
+ if disableOnuFunc {
+ return nil
+ }
+ go handler.reEnableDevice(device)
+ return nil
+ }
+ logger.Warnw("no handler found for device-reenable", log.Fields{"device-id": device.Id})
+ return fmt.Errorf(fmt.Sprintf("handler-not-found-%s", device.Id))
+}
+
+//Reboot_device reboots the given device
+func (oo *OpenONUAC) Reboot_device(device *voltha.Device) error {
+ logger.Debugw("Reboot-device", log.Fields{"device-id": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ go handler.rebootDevice(device)
+ return nil
+ }
+ logger.Warnw("no handler found for device-reboot", log.Fields{"device-id": device.Id})
+ return fmt.Errorf(fmt.Sprintf("handler-not-found-#{device.Id}"))
+}
+
+//Self_test_device unimplemented
+func (oo *OpenONUAC) Self_test_device(device *voltha.Device) error {
+ return errors.New("unImplemented")
+}
+
+// Delete_device deletes the given device
+func (oo *OpenONUAC) Delete_device(device *voltha.Device) error {
+ logger.Debugw("Delete_device", log.Fields{"device-id": device.Id})
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ if err := handler.deleteDevice(device); err != nil {
+ return err
+ }
+ } else {
+ logger.Warnw("no handler found for device-reconcilement", log.Fields{"device-id": device.Id})
+ return fmt.Errorf(fmt.Sprintf("handler-not-found-%s", device.Id))
+ }
+ return nil
+}
+
+//Get_device_details unimplemented
+func (oo *OpenONUAC) Get_device_details(device *voltha.Device) error {
+ return errors.New("unImplemented")
+}
+
+//Update_flows_bulk returns
+func (oo *OpenONUAC) Update_flows_bulk(device *voltha.Device, flows *voltha.Flows, groups *voltha.FlowGroups, flowMetadata *voltha.FlowMetadata) error {
+ return errors.New("unImplemented")
+}
+
+var disableUpdateFlows = true
+
+//Update_flows_incrementally updates (add/remove) the flows on a given device
+func (oo *OpenONUAC) Update_flows_incrementally(device *voltha.Device,
+ flows *openflow_13.FlowChanges, groups *openflow_13.FlowGroupChanges, flowMetadata *voltha.FlowMetadata) error {
+ if disableUpdateFlows {
+ return nil
+ }
+ if device.ConnectStatus != voltha.ConnectStatus_REACHABLE ||
+ device.AdminState != voltha.AdminState_ENABLED {
+ logger.Warnw("device disabled or offline - skipping flow-update", log.Fields{"deviceId": device.Id})
+ return errors.New("non-matching device state")
+ }
+
+ if groups.ToAdd != nil && groups.ToAdd.Items != nil {
+ logger.Debugw("Update-flow-incr: group add not supported (ignored)", log.Fields{"deviceId": device.Id})
+ }
+ if groups.ToRemove != nil && groups.ToRemove.Items != nil {
+ logger.Debugw("Update-flow-incr: group remove not supported (ignored)", log.Fields{"deviceId": device.Id})
+ }
+ if groups.ToUpdate != nil && groups.ToUpdate.Items != nil {
+ logger.Debugw("Update-flow-incr: group update not supported (ignored)", log.Fields{"deviceId": device.Id})
+ }
+
+ if handler := oo.getDeviceHandler(device.Id); handler != nil {
+ err := handler.FlowUpdateIncremental(flows, groups, flowMetadata)
+ return err
+ }
+ logger.Warnw("no handler found for incremental flow update", log.Fields{"deviceId": device.Id})
+ return fmt.Errorf(fmt.Sprintf("handler-not-found-%s", device.Id))
+}
+
+//Update_pm_config returns PmConfigs nil or error
+func (oo *OpenONUAC) Update_pm_config(device *voltha.Device, pmConfigs *voltha.PmConfigs) error {
+ return errors.New("unImplemented")
+}
+
+//Receive_packet_out sends packet out to the device
+func (oo *OpenONUAC) Receive_packet_out(deviceID string, egressPortNo int, packet *openflow_13.OfpPacketOut) error {
+ return errors.New("unImplemented")
+}
+
+//Suppress_event unimplemented
+func (oo *OpenONUAC) Suppress_event(filter *voltha.EventFilter) error {
+ return errors.New("unImplemented")
+}
+
+//Unsuppress_event unimplemented
+func (oo *OpenONUAC) Unsuppress_event(filter *voltha.EventFilter) error {
+ return errors.New("unImplemented")
+}
+
+//Download_image unimplemented
+func (oo *OpenONUAC) Download_image(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, errors.New("unImplemented")
+}
+
+//Get_image_download_status unimplemented
+func (oo *OpenONUAC) Get_image_download_status(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, errors.New("unImplemented")
+}
+
+//Cancel_image_download unimplemented
+func (oo *OpenONUAC) Cancel_image_download(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, errors.New("unImplemented")
+}
+
+//Activate_image_update unimplemented
+func (oo *OpenONUAC) Activate_image_update(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, errors.New("unImplemented")
+}
+
+//Revert_image_update unimplemented
+func (oo *OpenONUAC) Revert_image_update(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+ return nil, errors.New("unImplemented")
+}
+
+// Enable_port to Enable PON/NNI interface - seems not to be used/required according to python code
+func (oo *OpenONUAC) Enable_port(deviceID string, port *voltha.Port) error {
+ return errors.New("unImplemented")
+}
+
+// Disable_port to Disable pon/nni interface - seems not to be used/required according to python code
+func (oo *OpenONUAC) Disable_port(deviceID string, port *voltha.Port) error {
+ return errors.New("unImplemented")
+}
+
+//Child_device_lost - unimplemented
+//needed for if update >= 3.1.x
+func (oo *OpenONUAC) Child_device_lost(deviceID string, pPortNo uint32, onuID uint32) error {
+ return errors.New("unImplemented")
+}
+
+// Start_omci_test unimplemented
+func (oo *OpenONUAC) Start_omci_test(device *voltha.Device, request *voltha.OmciTestRequest) (*voltha.TestResponse, error) {
+ return nil, errors.New("unImplemented")
+}
+
+// Get_ext_value - unimplemented
+func (oo *OpenONUAC) Get_ext_value(deviceID string, device *voltha.Device, valueparam voltha.ValueType_Type) (*voltha.ReturnValues, error) {
+ return nil, errors.New("unImplemented")
+}
+
+// Adapter interface required methods ################ end #########
+// #################################################################
diff --git a/internal/pkg/onuadaptercore/openonuimpl.go b/internal/pkg/onuadaptercore/openonuimpl.go
new file mode 100644
index 0000000..15109cb
--- /dev/null
+++ b/internal/pkg/onuadaptercore/openonuimpl.go
@@ -0,0 +1,101 @@
+/*
+ * 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 (
+ "errors"
+)
+
+/*
+OpenOmciAgentDefaults = {
+ 'mib-synchronizer': {
+ 'state-machine': MibSynchronizer, # Implements the MIB synchronization state machine
+ 'database': MibDbVolatileDict, # Implements volatile ME MIB database
+ # 'database': MibDbExternal, # Implements persistent ME MIB database
+ 'advertise-events': True, # Advertise events on OpenOMCI event bus
+ 'audit-delay': 60, # Time to wait between MIB audits. 0 to disable audits.
+ 'tasks': {
+ 'mib-upload': MibUploadTask,
+ 'mib-template': MibTemplateTask,
+ 'get-mds': GetMdsTask,
+ 'mib-audit': GetMdsTask,
+ 'mib-resync': MibResyncTask,
+ 'mib-reconcile': MibReconcileTask
+ }
+ },
+ 'omci-capabilities': {
+ 'state-machine': OnuOmciCapabilities, # Implements OMCI capabilities state machine
+ 'advertise-events': False, # Advertise events on OpenOMCI event bus
+ 'tasks': {
+ 'get-capabilities': OnuCapabilitiesTask # Get supported ME and Commands
+ }
+ },
+ 'performance-intervals': {
+ 'state-machine': PerformanceIntervals, # Implements PM Intervals State machine
+ 'advertise-events': False, # Advertise events on OpenOMCI event bus
+ 'tasks': {
+ 'sync-time': SyncTimeTask,
+ 'collect-data': IntervalDataTask,
+ 'create-pm': OmciCreatePMRequest,
+ 'delete-pm': OmciDeletePMRequest,
+ },
+ },
+ 'alarm-synchronizer': {
+ 'state-machine': AlarmSynchronizer, # Implements the Alarm sync state machine
+ 'database': AlarmDbExternal, # For any State storage needs
+ 'advertise-events': True, # Advertise events on OpenOMCI event bus
+ 'tasks': {
+ 'alarm-resync': AlarmResyncTask
+ }
+ },
+ 'image_downloader': {
+ 'state-machine': ImageDownloadeSTM,
+ 'advertise-event': True,
+ 'tasks': {
+ 'download-file': FileDownloadTask
+ }
+ },
+ 'image_upgrader': {
+ 'state-machine': OmciSoftwareImageDownloadSTM,
+ 'advertise-event': True,
+ 'tasks': {
+ 'omci_upgrade_task': OmciSwImageUpgradeTask
+ }
+ }
+ # 'image_activator': {
+ # 'state-machine': OmciSoftwareImageActivateSTM,
+ # 'advertise-event': True,
+ # }
+}
+*/
+
+// do not use MibAudit
+const cMibAuditDelayImpl = 0
+
+//suppose global methods per adapter ...
+func mibDbVolatileDictImpl() error {
+ logger.Debug("MibVolatileDict-called")
+ return errors.New("not_implemented")
+}
+
+/*
+func alarmDbDictImpl() error {
+ logger.Debug("AlarmDb-called")
+ return errors.New("not_implemented")
+}
+*/
diff --git a/internal/pkg/onuadaptercore/platform.go b/internal/pkg/onuadaptercore/platform.go
new file mode 100644
index 0000000..fa1ff6e
--- /dev/null
+++ b/internal/pkg/onuadaptercore/platform.go
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2018-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
+
+//Attention: this file is more or less a coopy of file olt_platform.go from the voltha-openolt-adapter
+// which includes system wide definitions and thus normally should be stored more centrally (within some voltha libs)!!
+
+/*=====================================================================
+
+@TODO: Looks like this Flow id concept below is not used anywhere
+ Propose to remove the below documentation of Flow Id on confirmation
+ of the same
+
+Flow id
+
+ Identifies a flow within a single OLT
+ Flow Id is unique per OLT
+ Multiple GEM ports can map to same flow id
+
+ 13 11 4 0
+ +--------+--------------+------+
+ | pon id | onu id | Flow |
+ | | | idx |
+ +--------+--------------+------+
+
+ 14 bits = 16384 flows (per OLT).
+
+ pon id = 4 bits = 16 PON ports
+ onu id = 7 bits = 128 ONUss per PON port
+ Flow index = 3 bits = 4 bi-directional flows per ONU
+ = 8 uni-directional flows per ONU
+
+
+Logical (OF) UNI port number
+
+ OpenFlow port number corresponding to PON UNI
+
+ 20 12 4 0
+ +--+--------+--------------+------+
+ |0 | pon id | onu id |uni id|
+ +--+--------+--------------+------+
+
+ pon id = 8 bits = 256 PON ports
+ onu id = 8 bits = 256 ONUs per PON port
+
+Logical (OF) NNI port number
+
+ OpenFlow port number corresponding to PON NNI
+
+ 20 0
+ +--+----------------------------+
+ |1 | intf_id |
+ +--+----------------------------+
+
+ No overlap with UNI port number space
+
+
+PON OLT (OF) port number
+
+ OpenFlow port number corresponding to PON OLT ports
+
+ 31 28 0
+ +--------+------------------------~~~------+
+ | 0x2 | pon intf id |
+ +--------+------------------------~~~------+
+*/
+
+const (
+ bitsForUniID = 4
+ bitsForONUID = 8
+ bitsForPONID = 8
+ /*
+ // Number of bits to differentiate between UNI and NNI Logical Port
+ bitsForUNINNIDiff = 1
+ */
+ maxOnusPerPon = (1 << bitsForONUID)
+ maxPonsPerOlt = (1 << bitsForPONID)
+ maxUnisPerOnu = (1 << bitsForUniID)
+ /*
+ //Bit position where the differentiation bit is located
+ nniUniDiffPos = (bitsForUniID + bitsForONUID + bitsForPONID)
+ //Bit position where the marker for PON port type of OF port is present
+ ponIntfMarkerPos = 28
+ //Value of marker used to distinguish PON port type of OF port
+ ponIntfMarkerValue = 0x2
+ // Number of bits for NNI ID
+ bitsforNNIID = 20
+ // minNniIntPortNum is used to store start range of nni port number (1 << 20) 1048576
+ minNniIntPortNum = (1 << bitsforNNIID)
+ // maxNniPortNum is used to store the maximum range of nni port number ((1 << 21)-1) 2097151
+ maxNniPortNum = ((1 << (bitsforNNIID + 1)) - 1)
+ */
+)
+
+//Mask to indicate which possibly active ONU UNI state is really reported to the core
+// compare python code - at the moment restrict active state to the first ONU UNI port
+// check is limited to max 16 uni ports - cmp above UNI limit!!!
+var activeUniPortStateUpdateMask = 0x0001
+
+/*
+//MinUpstreamPortID value
+var minUpstreamPortID = 0xfffd
+
+//MaxUpstreamPortID value
+var maxUpstreamPortID = 0xfffffffd
+
+var controllerPorts = []uint32{0xfffd, 0x7ffffffd, 0xfffffffd}
+*/
+
+//mkUniPortNum returns new UNIportNum based on intfID, inuID and uniID
+func mkUniPortNum(intfID, onuID, uniID uint32) uint32 {
+ var limit = int(intfID)
+ if limit > maxPonsPerOlt {
+ logger.Warn("Warning: exceeded the MAX pons per OLT")
+ }
+ limit = int(onuID)
+ if limit > maxOnusPerPon {
+ logger.Warn("Warning: exceeded the MAX ONUS per PON")
+ }
+ limit = int(uniID)
+ if limit > maxUnisPerOnu {
+ logger.Warn("Warning: exceeded the MAX UNIS per ONU")
+ }
+ return (intfID << (bitsForUniID + bitsForONUID)) | (onuID << bitsForUniID) | uniID
+}
+
+/*
+//onuIDFromPortNum returns ONUID derived from portNumber
+func onuIDFromPortNum(portNum uint32) uint32 {
+ return (portNum >> bitsForUniID) & (maxOnusPerPon - 1)
+}
+
+//intfIDFromUniPortNum returns IntfID derived from portNum
+func intfIDFromUniPortNum(portNum uint32) uint32 {
+ return (portNum >> (bitsForUniID + bitsForONUID)) & (maxPonsPerOlt - 1)
+}
+
+//uniIDFromPortNum return UniID derived from portNum
+func uniIDFromPortNum(portNum uint32) uint32 {
+ return (portNum) & (maxUnisPerOnu - 1)
+}
+
+//intfIDToPortNo returns portId derived from intftype, intfId and portType
+func intfIDToPortNo(intfID uint32, intfType voltha.Port_PortType) uint32 {
+ if (intfType) == voltha.Port_ETHERNET_NNI {
+ return (1 << nniUniDiffPos) | intfID
+ }
+ if (intfType) == voltha.Port_PON_OLT {
+ return (ponIntfMarkerValue << ponIntfMarkerPos) | intfID
+ }
+ return 0
+}
+
+//portNoToIntfID returns portnumber derived from interfaceID
+func portNoToIntfID(portno uint32, intfType voltha.Port_PortType) uint32 {
+ if (intfType) == voltha.Port_ETHERNET_NNI {
+ return (1 << nniUniDiffPos) ^ portno
+ }
+ if (intfType) == voltha.Port_PON_OLT {
+ return (ponIntfMarkerValue << ponIntfMarkerPos) ^ portno
+ }
+ return 0
+}
+
+//intfIDFromNniPortNum returns Intf ID derived from portNum
+func intfIDFromNniPortNum(portNum uint32) (uint32, error) {
+ if portNum < minNniIntPortNum || portNum > maxNniPortNum {
+ logger.Errorw("NNIPortNumber is not in valid range", log.Fields{"portNum": portNum})
+ return uint32(0), errors.New("invalid-port-range") //olterrors.ErrInvalidPortRange
+ }
+ return (portNum & 0xFFFF), nil
+}
+
+//intfIDToPortTypeName returns port type derived from the intfId
+func intfIDToPortTypeName(intfID uint32) voltha.Port_PortType {
+ if ((ponIntfMarkerValue << ponIntfMarkerPos) ^ intfID) < maxPonsPerOlt {
+ return voltha.Port_PON_OLT
+ }
+ if (intfID & (1 << nniUniDiffPos)) == (1 << nniUniDiffPos) {
+ return voltha.Port_ETHERNET_NNI
+ }
+ return voltha.Port_ETHERNET_UNI
+}
+
+//extractAccessFromFlow returns AccessDevice information
+func extractAccessFromFlow(inPort, outPort uint32) (uint32, uint32, uint32, uint32) {
+ if isUpstream(outPort) {
+ return inPort, intfIDFromUniPortNum(inPort), onuIDFromPortNum(inPort), uniIDFromPortNum(inPort)
+ }
+ return outPort, intfIDFromUniPortNum(outPort), onuIDFromPortNum(outPort), uniIDFromPortNum(outPort)
+}
+
+//isUpstream returns true for Upstream and false for downstream
+func isUpstream(outPort uint32) bool {
+ for _, port := range controllerPorts {
+ if port == outPort {
+ return true
+ }
+ }
+ return (outPort & (1 << nniUniDiffPos)) == (1 << nniUniDiffPos)
+}
+
+//isControllerBoundFlow returns true/false
+func isControllerBoundFlow(outPort uint32) bool {
+ for _, port := range controllerPorts {
+ if port == outPort {
+ return true
+ }
+ }
+ return false
+}
+
+//onuIDFromUniPortNum returns onuId from give portNum information.
+func onuIDFromUniPortNum(portNum uint32) uint32 {
+ return (portNum >> bitsForUniID) & (maxOnusPerPon - 1)
+}
+
+//flowExtractInfo fetches uniport from the flow, based on which it gets and returns ponInf, onuID, uniID, inPort and ethType
+func flowExtractInfo(flow *ofp.OfpFlowStats, flowDirection string) (uint32, uint32, uint32, uint32, uint32, uint32, error) {
+ var uniPortNo uint32
+ var ponIntf uint32
+ var onuID uint32
+ var uniID uint32
+ var inPort uint32
+ var ethType uint32
+
+ if flowDirection == "upstream" {
+ if uniPortNo = flows.GetChildPortFromTunnelId(flow); uniPortNo == 0 {
+ for _, field := range flows.GetOfbFields(flow) {
+ if field.GetType() == flows.IN_PORT {
+ uniPortNo = field.GetPort()
+ break
+ }
+ }
+ }
+ } else if flowDirection == "downstream" {
+ if uniPortNo = flows.GetChildPortFromTunnelId(flow); uniPortNo == 0 {
+ for _, field := range flows.GetOfbFields(flow) {
+ if field.GetType() == flows.METADATA {
+ for _, action := range flows.GetActions(flow) {
+ if action.Type == flows.OUTPUT {
+ if out := action.GetOutput(); out != nil {
+ uniPortNo = out.GetPort()
+ }
+ break
+ }
+ }
+ } else if field.GetType() == flows.IN_PORT {
+ inPort = field.GetPort()
+ } else if field.GetType() == flows.ETH_TYPE {
+ ethType = field.GetEthType()
+ }
+ }
+ }
+ }
+
+ if uniPortNo == 0 {
+ return 0, 0, 0, 0, 0, 0, errors.New("notFound: pon-interface (flowDirection)")
+ // olterrors.NewErrNotFound("pon-interface", log.Fields{"flow-direction": flowDirection}, nil)
+ }
+
+ ponIntf = intfIDFromUniPortNum(uniPortNo)
+ onuID = onuIDFromUniPortNum(uniPortNo)
+ uniID = uniIDFromPortNum(uniPortNo)
+
+ logger.Debugw("flow extract info result",
+ log.Fields{"uniPortNo": uniPortNo, "ponIntf": ponIntf,
+ "onuID": onuID, "uniID": uniID, "inPort": inPort, "ethType": ethType})
+
+ return uniPortNo, ponIntf, onuID, uniID, inPort, ethType, nil
+}
+*/
diff --git a/internal/pkg/onuadaptercore/status.go b/internal/pkg/onuadaptercore/status.go
new file mode 100644
index 0000000..d43fd57
--- /dev/null
+++ b/internal/pkg/onuadaptercore/status.go
@@ -0,0 +1,54 @@
+/*
+ * 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
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+ "github.com/opencord/voltha-protos/v3/go/common"
+)
+
+var startWatchStatus bool = false
+
+//WatchStatus ...
+func WatchStatus(ctx context.Context, cp adapterif.CoreProxy) {
+ if startWatchStatus {
+ return
+ }
+ startWatchStatus = true
+ logger.Debug(ctx, fmt.Sprintf("Start WatchStatus()"))
+ ticker := time.NewTicker(1000 * time.Millisecond)
+ prevOnuList := make(map[string]OnuStatus)
+ for range ticker.C {
+ onuList, err := ReadOnuStatusList()
+ if err == nil {
+ for _, onu := range onuList {
+ current, ok := prevOnuList[onu.MacAddress]
+ if !ok || onu.OpeState != current.OpeState || onu.ConnectState != current.ConnectState {
+ con := common.ConnectStatus_Types(common.ConnectStatus_Types_value[onu.ConnectState])
+ ope := common.OperStatus_Types(common.OperStatus_Types_value[onu.OpeState])
+ err2 := cp.DeviceStateUpdate(ctx, onu.ID, con, ope)
+ logger.Debug(ctx, fmt.Sprintf("WatchStatus() update: %s, OpeState: %v, ConnectState: %v, error: %v",
+ onu.ID, onu.OpeState, onu.ConnectState, err2))
+ prevOnuList[onu.MacAddress] = onu
+ }
+ }
+ }
+ }
+}
diff --git a/internal/pkg/onuadaptercore/uniportadmin.go b/internal/pkg/onuadaptercore/uniportadmin.go
new file mode 100644
index 0000000..36b7fb7
--- /dev/null
+++ b/internal/pkg/onuadaptercore/uniportadmin.go
@@ -0,0 +1,373 @@
+/*
+ * 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/looplab/fsm"
+
+ "github.com/opencord/omci-lib-go"
+ me "github.com/opencord/omci-lib-go/generated"
+ "github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+//lockStateFsm defines the structure for the state machine to lock/unlock the ONU UNI ports via OMCI
+type lockStateFsm struct {
+ pOmciCC *omciCC
+ adminState bool
+ requestEvent OnuDeviceEvent
+ omciLockResponseReceived chan bool //seperate channel needed for checking UNI port OMCi message responses
+ pAdaptFsm *AdapterFsm
+}
+
+const (
+ uniEvStart = "uniEvStart"
+ uniEvStartAdmin = "uniEvStartAdmin"
+ uniEvRxUnisResp = "uniEvRxUnisResp"
+ uniEvRxOnugResp = "uniEvRxOnugResp"
+ uniEvTimeoutSimple = "uniEvTimeoutSimple"
+ uniEvTimeoutUnis = "uniEvTimeoutUnis"
+ uniEvReset = "uniEvReset"
+ uniEvRestart = "uniEvRestart"
+)
+const (
+ uniStDisabled = "uniStDisabled"
+ uniStStarting = "uniStStarting"
+ uniStSettingUnis = "uniStSettingUnis"
+ uniStSettingOnuG = "uniStSettingOnuG"
+ uniStAdminDone = "uniStAdminDone"
+ uniStResetting = "uniStResetting"
+)
+
+//newLockStateFsm is the 'constructor' for the state machine to lock/unlock the ONU UNI ports via OMCI
+func newLockStateFsm(apDevOmciCC *omciCC, aAdminState bool, aRequestEvent OnuDeviceEvent,
+ aName string, aDeviceID string, aCommChannel chan Message) *lockStateFsm {
+ instFsm := &lockStateFsm{
+ pOmciCC: apDevOmciCC,
+ adminState: aAdminState,
+ requestEvent: aRequestEvent,
+ }
+ instFsm.pAdaptFsm = NewAdapterFsm(aName, aDeviceID, aCommChannel)
+ if instFsm.pAdaptFsm == nil {
+ logger.Errorw("LockStateFsm's AdapterFsm could not be instantiated!!", log.Fields{
+ "device-id": aDeviceID})
+ return nil
+ }
+ if aAdminState { //port locking requested
+ instFsm.pAdaptFsm.pFsm = fsm.NewFSM(
+ uniStDisabled,
+ fsm.Events{
+
+ {Name: uniEvStart, Src: []string{uniStDisabled}, Dst: uniStStarting},
+
+ {Name: uniEvStartAdmin, Src: []string{uniStStarting}, Dst: uniStSettingUnis},
+ // the settingUnis state is used for multi ME config for all 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: uniEvRxUnisResp, Src: []string{uniStSettingUnis}, Dst: uniStSettingOnuG},
+ {Name: uniEvRxOnugResp, Src: []string{uniStSettingOnuG}, Dst: uniStAdminDone},
+
+ {Name: uniEvTimeoutSimple, Src: []string{uniStSettingOnuG}, Dst: uniStStarting},
+ {Name: uniEvTimeoutUnis, Src: []string{uniStSettingUnis}, Dst: uniStStarting},
+
+ {Name: uniEvReset, Src: []string{uniStStarting, uniStSettingOnuG, uniStSettingUnis,
+ uniStAdminDone}, Dst: uniStResetting},
+ // exceptional treatment for all states except uniStResetting
+ {Name: uniEvRestart, Src: []string{uniStStarting, uniStSettingOnuG, uniStSettingUnis,
+ uniStAdminDone, uniStResetting}, Dst: uniStDisabled},
+ },
+
+ fsm.Callbacks{
+ "enter_state": func(e *fsm.Event) { instFsm.pAdaptFsm.logFsmStateChange(e) },
+ ("enter_" + uniStStarting): func(e *fsm.Event) { instFsm.enterAdminStartingState(e) },
+ ("enter_" + uniStSettingOnuG): func(e *fsm.Event) { instFsm.enterSettingOnuGState(e) },
+ ("enter_" + uniStSettingUnis): func(e *fsm.Event) { instFsm.enterSettingUnisState(e) },
+ ("enter_" + uniStAdminDone): func(e *fsm.Event) { instFsm.enterAdminDoneState(e) },
+ ("enter_" + uniStResetting): func(e *fsm.Event) { instFsm.enterResettingState(e) },
+ },
+ )
+ } else { //port unlocking requested
+ instFsm.pAdaptFsm.pFsm = fsm.NewFSM(
+ uniStDisabled,
+ fsm.Events{
+
+ {Name: uniEvStart, Src: []string{uniStDisabled}, Dst: uniStStarting},
+
+ {Name: uniEvStartAdmin, Src: []string{uniStStarting}, Dst: uniStSettingOnuG},
+ {Name: uniEvRxOnugResp, Src: []string{uniStSettingOnuG}, Dst: uniStSettingUnis},
+ // the settingUnis state is used for multi ME config for all 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: uniEvRxUnisResp, Src: []string{uniStSettingUnis}, Dst: uniStAdminDone},
+
+ {Name: uniEvTimeoutSimple, Src: []string{uniStSettingOnuG}, Dst: uniStStarting},
+ {Name: uniEvTimeoutUnis, Src: []string{uniStSettingUnis}, Dst: uniStStarting},
+
+ {Name: uniEvReset, Src: []string{uniStStarting, uniStSettingOnuG, uniStSettingUnis,
+ uniStAdminDone}, Dst: uniStResetting},
+ // exceptional treatment for all states except uniStResetting
+ {Name: uniEvRestart, Src: []string{uniStStarting, uniStSettingOnuG, uniStSettingUnis,
+ uniStAdminDone, uniStResetting}, Dst: uniStDisabled},
+ },
+
+ fsm.Callbacks{
+ "enter_state": func(e *fsm.Event) { instFsm.pAdaptFsm.logFsmStateChange(e) },
+ ("enter_" + uniStStarting): func(e *fsm.Event) { instFsm.enterAdminStartingState(e) },
+ ("enter_" + uniStSettingOnuG): func(e *fsm.Event) { instFsm.enterSettingOnuGState(e) },
+ ("enter_" + uniStSettingUnis): func(e *fsm.Event) { instFsm.enterSettingUnisState(e) },
+ ("enter_" + uniStAdminDone): func(e *fsm.Event) { instFsm.enterAdminDoneState(e) },
+ ("enter_" + uniStResetting): func(e *fsm.Event) { instFsm.enterResettingState(e) },
+ },
+ )
+ }
+ if instFsm.pAdaptFsm.pFsm == nil {
+ logger.Errorw("LockStateFsm's Base FSM could not be instantiated!!", log.Fields{
+ "device-id": aDeviceID})
+ return nil
+ }
+
+ logger.Infow("LockStateFsm created", log.Fields{"device-id": aDeviceID})
+ return instFsm
+}
+
+//setSuccessEvent modifies the requested event notified on success
+//assumption is that this is only called in the disabled (idle) state of the FSM, hence no sem protection required
+func (oFsm *lockStateFsm) setSuccessEvent(aEvent OnuDeviceEvent) {
+ oFsm.requestEvent = aEvent
+}
+
+func (oFsm *lockStateFsm) enterAdminStartingState(e *fsm.Event) {
+ logger.Debugw("LockStateFSM start", log.Fields{"in state": e.FSM.Current(),
+ "device-id": oFsm.pAdaptFsm.deviceID})
+ if oFsm.omciLockResponseReceived == nil {
+ oFsm.omciLockResponseReceived = make(chan bool)
+ logger.Debug("LockStateFSM - OMCI UniLock RxChannel defined")
+ } else {
+ // as we may 're-use' this instance of FSM and the connected channel
+ // make sure there is no 'lingering' request in the already existing channel:
+ // (simple loop sufficient as we are the only receiver)
+ for len(oFsm.omciLockResponseReceived) > 0 {
+ <-oFsm.omciLockResponseReceived
+ }
+ }
+ go oFsm.processOmciLockMessages()
+
+ pLockStateAFsm := oFsm.pAdaptFsm
+ if pLockStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(a_pAFsm *AdapterFsm) {
+ if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+ _ = a_pAFsm.pFsm.Event(uniEvStartAdmin)
+ }
+ }(pLockStateAFsm)
+ }
+}
+
+func (oFsm *lockStateFsm) enterSettingOnuGState(e *fsm.Event) {
+ var omciAdminState uint8 = 1 //default locked
+ if !oFsm.adminState {
+ omciAdminState = 0
+ }
+ logger.Debugw("LockStateFSM Tx Set::ONU-G:admin", log.Fields{
+ "omciAdmin": omciAdminState, "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ requestedAttributes := me.AttributeValueMap{"AdministrativeState": omciAdminState}
+ meInstance := oFsm.pOmciCC.sendSetOnuGLS(context.TODO(), ConstDefaultOmciTimeout, true,
+ requestedAttributes, oFsm.pAdaptFsm.commChan)
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (oFsm *lockStateFsm) enterSettingUnisState(e *fsm.Event) {
+ logger.Infow("LockStateFSM - starting PPTP config loop", log.Fields{
+ "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID, "LockState": oFsm.adminState})
+ go oFsm.performUniPortAdminSet()
+}
+
+func (oFsm *lockStateFsm) enterAdminDoneState(e *fsm.Event) {
+ logger.Debugw("LockStateFSM", log.Fields{"send notification to core in State": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+ oFsm.pOmciCC.pBaseDeviceHandler.deviceProcStatusUpdate(oFsm.requestEvent)
+ pLockStateAFsm := oFsm.pAdaptFsm
+ if pLockStateAFsm != nil {
+ // obviously calling some FSM event here directly does not work - so trying to decouple it ...
+ go func(a_pAFsm *AdapterFsm) {
+ if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+ _ = a_pAFsm.pFsm.Event(uniEvReset)
+ }
+ }(pLockStateAFsm)
+ }
+}
+
+func (oFsm *lockStateFsm) enterResettingState(e *fsm.Event) {
+ logger.Debugw("LockStateFSM resetting", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ pLockStateAFsm := oFsm.pAdaptFsm
+ if pLockStateAFsm != nil {
+ // abort running message processing
+ fsmAbortMsg := Message{
+ Type: TestMsg,
+ Data: TestMessage{
+ TestMessageVal: AbortMessageProcessing,
+ },
+ }
+ pLockStateAFsm.commChan <- fsmAbortMsg
+
+ //try to restart the FSM to 'disabled'
+ // see DownloadedState: decouple event transfer
+ go func(a_pAFsm *AdapterFsm) {
+ if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+ _ = a_pAFsm.pFsm.Event(uniEvRestart)
+ }
+ }(pLockStateAFsm)
+ }
+}
+
+func (oFsm *lockStateFsm) processOmciLockMessages( /*ctx context.Context*/ ) {
+ logger.Debugw("Start LockStateFsm Msg processing", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+loop:
+ for {
+ // case <-ctx.Done():
+ // logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": oFsm.pAdaptFsm.deviceID})
+ // break loop
+ message, ok := <-oFsm.pAdaptFsm.commChan
+ if !ok {
+ logger.Info("LockStateFsm Rx Msg - could not read from channel", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ // but then we have to ensure a restart of the FSM as well - as exceptional procedure
+ _ = oFsm.pAdaptFsm.pFsm.Event(uniEvRestart)
+ break loop
+ }
+ logger.Debugw("LockStateFsm Rx Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+
+ switch message.Type {
+ case TestMsg:
+ msg, _ := message.Data.(TestMessage)
+ if msg.TestMessageVal == AbortMessageProcessing {
+ logger.Infow("LockStateFsm abort ProcessMsg", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+ break loop
+ }
+ logger.Warnw("LockStateFsm unknown TestMessage", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "MessageVal": msg.TestMessageVal})
+ case OMCI:
+ msg, _ := message.Data.(OmciMessage)
+ oFsm.handleOmciLockStateMessage(msg)
+ default:
+ logger.Warn("LockStateFsm Rx unknown message", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
+ "message.Type": message.Type})
+ }
+ }
+ logger.Infow("End LockStateFsm Msg processing", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+}
+
+func (oFsm *lockStateFsm) handleOmciLockStateMessage(msg OmciMessage) {
+ logger.Debugw("Rx OMCI LockStateFsm Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
+ "msgType": msg.OmciMsg.MessageType})
+
+ if msg.OmciMsg.MessageType == omci.SetResponseType {
+ msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
+ if msgLayer == nil {
+ logger.Error("LockStateFsm - Omci Msg layer could not be detected for SetResponse")
+ return
+ }
+ msgObj, msgOk := msgLayer.(*omci.SetResponse)
+ if !msgOk {
+ logger.Error("LockStateFsm - Omci Msg layer could not be assigned for SetResponse")
+ return
+ }
+ logger.Debugw("LockStateFsm SetResponse Data", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "data-fields": msgObj})
+ if msgObj.Result != me.Success {
+ logger.Errorw("LockStateFsm - Omci SetResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+ // possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+ return
+ }
+ // compare comments above for CreateResponse (apply also here ...)
+ if msgObj.EntityClass == oFsm.pOmciCC.pLastTxMeInstance.GetClassID() &&
+ msgObj.EntityInstance == oFsm.pOmciCC.pLastTxMeInstance.GetEntityID() {
+ //store the created ME into DB //TODO??? obviously the Python code does not store the config ...
+ // if, then something like:
+ //oFsm.pOnuDB.StoreMe(msgObj)
+
+ switch oFsm.pOmciCC.pLastTxMeInstance.GetName() {
+ case "OnuG":
+ { // let the FSM proceed ...
+ _ = oFsm.pAdaptFsm.pFsm.Event(uniEvRxOnugResp)
+ }
+ case "UniG", "VEIP":
+ { // let the PPTP init proceed by stopping the wait function
+ oFsm.omciLockResponseReceived <- true
+ }
+ }
+ }
+ } else {
+ logger.Errorw("LockStateFsm - Rx OMCI unhandled MsgType", log.Fields{"omciMsgType": msg.OmciMsg.MessageType})
+ return
+ }
+}
+
+func (oFsm *lockStateFsm) performUniPortAdminSet() {
+ var omciAdminState uint8 = 1 //default locked
+ if !oFsm.adminState {
+ omciAdminState = 0
+ }
+ requestedAttributes := me.AttributeValueMap{"AdministrativeState": omciAdminState}
+
+ for uniNo, uniPort := range oFsm.pOmciCC.pBaseDeviceHandler.uniEntityMap {
+ logger.Debugw("Setting PPTP admin state", log.Fields{
+ "device-id": oFsm.pAdaptFsm.deviceID, "for PortNo": uniNo})
+
+ var meInstance *me.ManagedEntity
+ if uniPort.portType == uniPPTP {
+ meInstance = oFsm.pOmciCC.sendSetUniGLS(context.TODO(), uniPort.entityID, ConstDefaultOmciTimeout,
+ true, requestedAttributes, oFsm.pAdaptFsm.commChan)
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+ } else if uniPort.portType == uniVEIP {
+ meInstance = oFsm.pOmciCC.sendSetVeipLS(context.TODO(), uniPort.entityID, ConstDefaultOmciTimeout,
+ true, requestedAttributes, oFsm.pAdaptFsm.commChan)
+ oFsm.pOmciCC.pLastTxMeInstance = meInstance
+ } else {
+ logger.Warnw("Unsupported PPTP type - skip",
+ log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "Port": uniNo})
+ continue
+ }
+
+ //verify response
+ err := oFsm.waitforOmciResponse(meInstance)
+ if err != nil {
+ logger.Errorw("PPTP Admin State set failed, aborting LockState set!",
+ log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "Port": uniNo})
+ _ = oFsm.pAdaptFsm.pFsm.Event(uniEvReset)
+ return
+ }
+ } //for all UNI ports
+ logger.Infow("PPTP config loop finished", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+ _ = oFsm.pAdaptFsm.pFsm.Event(uniEvRxUnisResp)
+}
+
+func (oFsm *lockStateFsm) waitforOmciResponse(apMeInstance *me.ManagedEntity) error {
+ select {
+ case <-time.After(30 * time.Second): //3s was detected to be to less in 8*8 bbsim test with debug Info/Debug
+ logger.Warnw("LockStateFSM uni-set timeout", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+ return errors.New("lockStateFsm uni-set timeout")
+ case success := <-oFsm.omciLockResponseReceived:
+ if success {
+ logger.Debug("LockStateFSM uni-set response received")
+ return nil
+ }
+ // should not happen so far
+ logger.Warnw("LockStateFSM uni-set response error", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+ return errors.New("lockStateFsm uni-set responseError")
+ }
+}